1 /* 2 * Copyright 2004 The WebRTC Project Authors. All rights reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 #include <algorithm> 12 #include <iostream> 13 #include <map> 14 #include <sstream> 15 #include <string> 16 #include <vector> 17 #include "webrtc/libjingle/xmpp/constants.h" 18 #include "webrtc/libjingle/xmpp/rostermoduleimpl.h" 19 #include "webrtc/base/common.h" 20 #include "webrtc/base/stringencode.h" 21 22 namespace buzz { 23 24 // enum prase and persist helpers ---------------------------------------------- 25 static bool 26 StringToPresenceShow(const std::string& input, XmppPresenceShow* show) { 27 // If this becomes a perf issue we can use a hash or a map here 28 if (STR_SHOW_AWAY == input) 29 *show = XMPP_PRESENCE_AWAY; 30 else if (STR_SHOW_DND == input) 31 *show = XMPP_PRESENCE_DND; 32 else if (STR_SHOW_XA == input) 33 *show = XMPP_PRESENCE_XA; 34 else if (STR_SHOW_CHAT == input) 35 *show = XMPP_PRESENCE_CHAT; 36 else if (STR_EMPTY == input) 37 *show = XMPP_PRESENCE_DEFAULT; 38 else 39 return false; 40 41 return true; 42 } 43 44 static bool 45 PresenceShowToString(XmppPresenceShow show, const char** output) { 46 switch(show) { 47 case XMPP_PRESENCE_AWAY: 48 *output = STR_SHOW_AWAY; 49 return true; 50 case XMPP_PRESENCE_CHAT: 51 *output = STR_SHOW_CHAT; 52 return true; 53 case XMPP_PRESENCE_XA: 54 *output = STR_SHOW_XA; 55 return true; 56 case XMPP_PRESENCE_DND: 57 *output = STR_SHOW_DND; 58 return true; 59 case XMPP_PRESENCE_DEFAULT: 60 *output = STR_EMPTY; 61 return true; 62 } 63 64 *output = STR_EMPTY; 65 return false; 66 } 67 68 static bool 69 StringToSubscriptionState(const std::string& subscription, 70 const std::string& ask, 71 XmppSubscriptionState* state) 72 { 73 if (ask == "subscribe") 74 { 75 if (subscription == "none") { 76 *state = XMPP_SUBSCRIPTION_NONE_ASKED; 77 return true; 78 } 79 if (subscription == "from") { 80 *state = XMPP_SUBSCRIPTION_FROM_ASKED; 81 return true; 82 } 83 } else if (ask == STR_EMPTY) 84 { 85 if (subscription == "none") { 86 *state = XMPP_SUBSCRIPTION_NONE; 87 return true; 88 } 89 if (subscription == "from") { 90 *state = XMPP_SUBSCRIPTION_FROM; 91 return true; 92 } 93 if (subscription == "to") { 94 *state = XMPP_SUBSCRIPTION_TO; 95 return true; 96 } 97 if (subscription == "both") { 98 *state = XMPP_SUBSCRIPTION_BOTH; 99 return true; 100 } 101 } 102 103 return false; 104 } 105 106 static bool 107 StringToSubscriptionRequestType(const std::string& string, 108 XmppSubscriptionRequestType* type) 109 { 110 if (string == "subscribe") 111 *type = XMPP_REQUEST_SUBSCRIBE; 112 else if (string == "unsubscribe") 113 *type = XMPP_REQUEST_UNSUBSCRIBE; 114 else if (string == "subscribed") 115 *type = XMPP_REQUEST_SUBSCRIBED; 116 else if (string == "unsubscribed") 117 *type = XMPP_REQUEST_UNSUBSCRIBED; 118 else 119 return false; 120 return true; 121 } 122 123 // XmppPresenceImpl class ------------------------------------------------------ 124 XmppPresence* 125 XmppPresence::Create() { 126 return new XmppPresenceImpl(); 127 } 128 129 XmppPresenceImpl::XmppPresenceImpl() { 130 } 131 132 const Jid 133 XmppPresenceImpl::jid() const { 134 if (!raw_xml_) 135 return Jid(); 136 137 return Jid(raw_xml_->Attr(QN_FROM)); 138 } 139 140 XmppPresenceAvailable 141 XmppPresenceImpl::available() const { 142 if (!raw_xml_) 143 return XMPP_PRESENCE_UNAVAILABLE; 144 145 if (raw_xml_->Attr(QN_TYPE) == "unavailable") 146 return XMPP_PRESENCE_UNAVAILABLE; 147 else if (raw_xml_->Attr(QN_TYPE) == "error") 148 return XMPP_PRESENCE_ERROR; 149 else 150 return XMPP_PRESENCE_AVAILABLE; 151 } 152 153 XmppReturnStatus 154 XmppPresenceImpl::set_available(XmppPresenceAvailable available) { 155 if (!raw_xml_) 156 CreateRawXmlSkeleton(); 157 158 if (available == XMPP_PRESENCE_AVAILABLE) 159 raw_xml_->ClearAttr(QN_TYPE); 160 else if (available == XMPP_PRESENCE_UNAVAILABLE) 161 raw_xml_->SetAttr(QN_TYPE, "unavailable"); 162 else if (available == XMPP_PRESENCE_ERROR) 163 raw_xml_->SetAttr(QN_TYPE, "error"); 164 return XMPP_RETURN_OK; 165 } 166 167 XmppPresenceShow 168 XmppPresenceImpl::presence_show() const { 169 if (!raw_xml_) 170 return XMPP_PRESENCE_DEFAULT; 171 172 XmppPresenceShow show = XMPP_PRESENCE_DEFAULT; 173 StringToPresenceShow(raw_xml_->TextNamed(QN_SHOW), &show); 174 return show; 175 } 176 177 XmppReturnStatus 178 XmppPresenceImpl::set_presence_show(XmppPresenceShow show) { 179 if (!raw_xml_) 180 CreateRawXmlSkeleton(); 181 182 const char* show_string; 183 184 if(!PresenceShowToString(show, &show_string)) 185 return XMPP_RETURN_BADARGUMENT; 186 187 raw_xml_->ClearNamedChildren(QN_SHOW); 188 189 if (show!=XMPP_PRESENCE_DEFAULT) { 190 raw_xml_->AddElement(new XmlElement(QN_SHOW)); 191 raw_xml_->AddText(show_string, 1); 192 } 193 194 return XMPP_RETURN_OK; 195 } 196 197 int 198 XmppPresenceImpl::priority() const { 199 if (!raw_xml_) 200 return 0; 201 202 int raw_priority = 0; 203 if (!rtc::FromString(raw_xml_->TextNamed(QN_PRIORITY), &raw_priority)) 204 raw_priority = 0; 205 if (raw_priority < -128) 206 raw_priority = -128; 207 if (raw_priority > 127) 208 raw_priority = 127; 209 210 return raw_priority; 211 } 212 213 XmppReturnStatus 214 XmppPresenceImpl::set_priority(int priority) { 215 if (!raw_xml_) 216 CreateRawXmlSkeleton(); 217 218 if (priority < -128 || priority > 127) 219 return XMPP_RETURN_BADARGUMENT; 220 221 raw_xml_->ClearNamedChildren(QN_PRIORITY); 222 if (0 != priority) { 223 std::string priority_string; 224 if (rtc::ToString(priority, &priority_string)) { 225 raw_xml_->AddElement(new XmlElement(QN_PRIORITY)); 226 raw_xml_->AddText(priority_string, 1); 227 } 228 } 229 230 return XMPP_RETURN_OK; 231 } 232 233 const std::string 234 XmppPresenceImpl::status() const { 235 if (!raw_xml_) 236 return STR_EMPTY; 237 238 XmlElement* status_element; 239 XmlElement* element; 240 241 // Search for a status element with no xml:lang attribute on it. if we can't 242 // find that then just return the first status element in the stanza. 243 for (status_element = element = raw_xml_->FirstNamed(QN_STATUS); 244 element; 245 element = element->NextNamed(QN_STATUS)) { 246 if (!element->HasAttr(QN_XML_LANG)) { 247 status_element = element; 248 break; 249 } 250 } 251 252 if (status_element) { 253 return status_element->BodyText(); 254 } 255 256 return STR_EMPTY; 257 } 258 259 XmppReturnStatus 260 XmppPresenceImpl::set_status(const std::string& status) { 261 if (!raw_xml_) 262 CreateRawXmlSkeleton(); 263 264 raw_xml_->ClearNamedChildren(QN_STATUS); 265 266 if (status != STR_EMPTY) { 267 raw_xml_->AddElement(new XmlElement(QN_STATUS)); 268 raw_xml_->AddText(status, 1); 269 } 270 271 return XMPP_RETURN_OK; 272 } 273 274 XmppPresenceConnectionStatus 275 XmppPresenceImpl::connection_status() const { 276 if (!raw_xml_) 277 return XMPP_CONNECTION_STATUS_UNKNOWN; 278 279 XmlElement* con = raw_xml_->FirstNamed(QN_GOOGLE_PSTN_CONFERENCE_STATUS); 280 if (con) { 281 std::string status = con->Attr(QN_ATTR_STATUS); 282 if (status == STR_PSTN_CONFERENCE_STATUS_CONNECTING) 283 return XMPP_CONNECTION_STATUS_CONNECTING; 284 else if (status == STR_PSTN_CONFERENCE_STATUS_CONNECTED) 285 return XMPP_CONNECTION_STATUS_CONNECTED; 286 else if (status == STR_PSTN_CONFERENCE_STATUS_JOINING) 287 return XMPP_CONNECTION_STATUS_JOINING; 288 else if (status == STR_PSTN_CONFERENCE_STATUS_HANGUP) 289 return XMPP_CONNECTION_STATUS_HANGUP; 290 } 291 292 return XMPP_CONNECTION_STATUS_CONNECTED; 293 } 294 295 const std::string 296 XmppPresenceImpl::google_user_id() const { 297 if (!raw_xml_) 298 return std::string(); 299 300 XmlElement* muc_user_x = raw_xml_->FirstNamed(QN_MUC_USER_X); 301 if (muc_user_x) { 302 XmlElement* muc_user_item = muc_user_x->FirstNamed(QN_MUC_USER_ITEM); 303 if (muc_user_item) { 304 return muc_user_item->Attr(QN_GOOGLE_USER_ID); 305 } 306 } 307 308 return std::string(); 309 } 310 311 const std::string 312 XmppPresenceImpl::nickname() const { 313 if (!raw_xml_) 314 return std::string(); 315 316 XmlElement* nickname = raw_xml_->FirstNamed(QN_NICKNAME); 317 if (nickname) { 318 return nickname->BodyText(); 319 } 320 321 return std::string(); 322 } 323 324 const XmlElement* 325 XmppPresenceImpl::raw_xml() const { 326 if (!raw_xml_) 327 const_cast<XmppPresenceImpl*>(this)->CreateRawXmlSkeleton(); 328 return raw_xml_.get(); 329 } 330 331 XmppReturnStatus 332 XmppPresenceImpl::set_raw_xml(const XmlElement * xml) { 333 if (!xml || 334 xml->Name() != QN_PRESENCE) 335 return XMPP_RETURN_BADARGUMENT; 336 337 raw_xml_.reset(new XmlElement(*xml)); 338 return XMPP_RETURN_OK; 339 } 340 341 void 342 XmppPresenceImpl::CreateRawXmlSkeleton() { 343 raw_xml_.reset(new XmlElement(QN_PRESENCE)); 344 } 345 346 // XmppRosterContactImpl ------------------------------------------------------- 347 XmppRosterContact* 348 XmppRosterContact::Create() { 349 return new XmppRosterContactImpl(); 350 } 351 352 XmppRosterContactImpl::XmppRosterContactImpl() { 353 ResetGroupCache(); 354 } 355 356 void 357 XmppRosterContactImpl::SetXmlFromWire(const XmlElement* xml) { 358 ResetGroupCache(); 359 if (xml) 360 raw_xml_.reset(new XmlElement(*xml)); 361 else 362 raw_xml_.reset(NULL); 363 } 364 365 void 366 XmppRosterContactImpl::ResetGroupCache() { 367 group_count_ = -1; 368 group_index_returned_ = -1; 369 group_returned_ = NULL; 370 } 371 372 const Jid 373 XmppRosterContactImpl::jid() const { 374 return Jid(raw_xml_->Attr(QN_JID)); 375 } 376 377 XmppReturnStatus 378 XmppRosterContactImpl::set_jid(const Jid& jid) 379 { 380 if (!raw_xml_) 381 CreateRawXmlSkeleton(); 382 383 if (!jid.IsValid()) 384 return XMPP_RETURN_BADARGUMENT; 385 386 raw_xml_->SetAttr(QN_JID, jid.Str()); 387 388 return XMPP_RETURN_OK; 389 } 390 391 const std::string 392 XmppRosterContactImpl::name() const { 393 return raw_xml_->Attr(QN_NAME); 394 } 395 396 XmppReturnStatus 397 XmppRosterContactImpl::set_name(const std::string& name) { 398 if (!raw_xml_) 399 CreateRawXmlSkeleton(); 400 401 if (name == STR_EMPTY) 402 raw_xml_->ClearAttr(QN_NAME); 403 else 404 raw_xml_->SetAttr(QN_NAME, name); 405 406 return XMPP_RETURN_OK; 407 } 408 409 XmppSubscriptionState 410 XmppRosterContactImpl::subscription_state() const { 411 if (!raw_xml_) 412 return XMPP_SUBSCRIPTION_NONE; 413 414 XmppSubscriptionState state = XMPP_SUBSCRIPTION_NONE; 415 416 if (StringToSubscriptionState(raw_xml_->Attr(QN_SUBSCRIPTION), 417 raw_xml_->Attr(QN_ASK), 418 &state)) 419 return state; 420 421 return XMPP_SUBSCRIPTION_NONE; 422 } 423 424 size_t 425 XmppRosterContactImpl::GetGroupCount() const { 426 if (!raw_xml_) 427 return 0; 428 429 if (-1 == group_count_) { 430 XmlElement *group_element = raw_xml_->FirstNamed(QN_ROSTER_GROUP); 431 int group_count = 0; 432 while(group_element) { 433 group_count++; 434 group_element = group_element->NextNamed(QN_ROSTER_GROUP); 435 } 436 437 ASSERT(group_count > 0); // protect the cast 438 XmppRosterContactImpl * me = const_cast<XmppRosterContactImpl*>(this); 439 me->group_count_ = group_count; 440 } 441 442 return group_count_; 443 } 444 445 const std::string 446 XmppRosterContactImpl::GetGroup(size_t index) const { 447 if (index >= GetGroupCount()) 448 return STR_EMPTY; 449 450 // We cache the last group index and element that we returned. This way 451 // going through the groups in order is order n and not n^2. This could be 452 // enhanced if necessary by starting at the cached value if the index asked 453 // is after the cached one. 454 if (group_index_returned_ >= 0 && 455 index == static_cast<size_t>(group_index_returned_) + 1) 456 { 457 XmppRosterContactImpl * me = const_cast<XmppRosterContactImpl*>(this); 458 me->group_returned_ = group_returned_->NextNamed(QN_ROSTER_GROUP); 459 ASSERT(group_returned_ != NULL); 460 me->group_index_returned_++; 461 } else if (group_index_returned_ < 0 || 462 static_cast<size_t>(group_index_returned_) != index) { 463 XmlElement * group_element = raw_xml_->FirstNamed(QN_ROSTER_GROUP); 464 size_t group_index = 0; 465 while(group_index < index) { 466 ASSERT(group_element != NULL); 467 group_index++; 468 group_element = group_element->NextNamed(QN_ROSTER_GROUP); 469 } 470 471 XmppRosterContactImpl * me = const_cast<XmppRosterContactImpl*>(this); 472 me->group_index_returned_ = static_cast<int>(group_index); 473 me->group_returned_ = group_element; 474 } 475 476 return group_returned_->BodyText(); 477 } 478 479 XmppReturnStatus 480 XmppRosterContactImpl::AddGroup(const std::string& group) { 481 if (group == STR_EMPTY) 482 return XMPP_RETURN_BADARGUMENT; 483 484 if (!raw_xml_) 485 CreateRawXmlSkeleton(); 486 487 if (FindGroup(group, NULL, NULL)) 488 return XMPP_RETURN_OK; 489 490 raw_xml_->AddElement(new XmlElement(QN_ROSTER_GROUP)); 491 raw_xml_->AddText(group, 1); 492 ++group_count_; 493 494 return XMPP_RETURN_OK; 495 } 496 497 XmppReturnStatus 498 XmppRosterContactImpl::RemoveGroup(const std::string& group) { 499 if (group == STR_EMPTY) 500 return XMPP_RETURN_BADARGUMENT; 501 502 if (!raw_xml_) 503 return XMPP_RETURN_OK; 504 505 XmlChild * child_before; 506 if (FindGroup(group, NULL, &child_before)) { 507 raw_xml_->RemoveChildAfter(child_before); 508 ResetGroupCache(); 509 } 510 return XMPP_RETURN_OK; 511 } 512 513 bool 514 XmppRosterContactImpl::FindGroup(const std::string& group, 515 XmlElement** element, 516 XmlChild** child_before) { 517 XmlChild * prev_child = NULL; 518 XmlChild * next_child; 519 XmlChild * child; 520 for (child = raw_xml_->FirstChild(); child; child = next_child) { 521 next_child = child->NextChild(); 522 if (!child->IsText() && 523 child->AsElement()->Name() == QN_ROSTER_GROUP && 524 child->AsElement()->BodyText() == group) { 525 if (element) 526 *element = child->AsElement(); 527 if (child_before) 528 *child_before = prev_child; 529 return true; 530 } 531 prev_child = child; 532 } 533 534 return false; 535 } 536 537 const XmlElement* 538 XmppRosterContactImpl::raw_xml() const { 539 if (!raw_xml_) 540 const_cast<XmppRosterContactImpl*>(this)->CreateRawXmlSkeleton(); 541 return raw_xml_.get(); 542 } 543 544 XmppReturnStatus 545 XmppRosterContactImpl::set_raw_xml(const XmlElement* xml) { 546 if (!xml || 547 xml->Name() != QN_ROSTER_ITEM || 548 xml->HasAttr(QN_SUBSCRIPTION) || 549 xml->HasAttr(QN_ASK)) 550 return XMPP_RETURN_BADARGUMENT; 551 552 ResetGroupCache(); 553 554 raw_xml_.reset(new XmlElement(*xml)); 555 556 return XMPP_RETURN_OK; 557 } 558 559 void 560 XmppRosterContactImpl::CreateRawXmlSkeleton() { 561 raw_xml_.reset(new XmlElement(QN_ROSTER_ITEM)); 562 } 563 564 // XmppRosterModuleImpl -------------------------------------------------------- 565 XmppRosterModule * 566 XmppRosterModule::Create() { 567 return new XmppRosterModuleImpl(); 568 } 569 570 XmppRosterModuleImpl::XmppRosterModuleImpl() : 571 roster_handler_(NULL), 572 incoming_presence_map_(new JidPresenceVectorMap()), 573 incoming_presence_vector_(new PresenceVector()), 574 contacts_(new ContactVector()) { 575 576 } 577 578 XmppRosterModuleImpl::~XmppRosterModuleImpl() { 579 DeleteIncomingPresence(); 580 DeleteContacts(); 581 } 582 583 XmppReturnStatus 584 XmppRosterModuleImpl::set_roster_handler(XmppRosterHandler * handler) { 585 roster_handler_ = handler; 586 return XMPP_RETURN_OK; 587 } 588 589 XmppRosterHandler* 590 XmppRosterModuleImpl::roster_handler() { 591 return roster_handler_; 592 } 593 594 XmppPresence* 595 XmppRosterModuleImpl::outgoing_presence() { 596 return &outgoing_presence_; 597 } 598 599 XmppReturnStatus 600 XmppRosterModuleImpl::BroadcastPresence() { 601 // Scrub the outgoing presence 602 const XmlElement* element = outgoing_presence_.raw_xml(); 603 604 ASSERT(!element->HasAttr(QN_TO) && 605 !element->HasAttr(QN_FROM) && 606 (element->Attr(QN_TYPE) == STR_EMPTY || 607 element->Attr(QN_TYPE) == "unavailable")); 608 609 if (!engine()) 610 return XMPP_RETURN_BADSTATE; 611 612 return engine()->SendStanza(element); 613 } 614 615 XmppReturnStatus 616 XmppRosterModuleImpl::SendDirectedPresence(const XmppPresence* presence, 617 const Jid& to_jid) { 618 if (!presence) 619 return XMPP_RETURN_BADARGUMENT; 620 621 if (!engine()) 622 return XMPP_RETURN_BADSTATE; 623 624 XmlElement element(*(presence->raw_xml())); 625 626 if (element.Name() != QN_PRESENCE || 627 element.HasAttr(QN_TO) || 628 element.HasAttr(QN_FROM)) 629 return XMPP_RETURN_BADARGUMENT; 630 631 if (element.HasAttr(QN_TYPE)) { 632 if (element.Attr(QN_TYPE) != STR_EMPTY && 633 element.Attr(QN_TYPE) != "unavailable") { 634 return XMPP_RETURN_BADARGUMENT; 635 } 636 } 637 638 element.SetAttr(QN_TO, to_jid.Str()); 639 640 return engine()->SendStanza(&element); 641 } 642 643 size_t 644 XmppRosterModuleImpl::GetIncomingPresenceCount() { 645 return incoming_presence_vector_->size(); 646 } 647 648 const XmppPresence* 649 XmppRosterModuleImpl::GetIncomingPresence(size_t index) { 650 if (index >= incoming_presence_vector_->size()) 651 return NULL; 652 return (*incoming_presence_vector_)[index]; 653 } 654 655 size_t 656 XmppRosterModuleImpl::GetIncomingPresenceForJidCount(const Jid& jid) 657 { 658 // find the vector in the map 659 JidPresenceVectorMap::iterator pos; 660 pos = incoming_presence_map_->find(jid); 661 if (pos == incoming_presence_map_->end()) 662 return 0; 663 664 ASSERT(pos->second != NULL); 665 666 return pos->second->size(); 667 } 668 669 const XmppPresence* 670 XmppRosterModuleImpl::GetIncomingPresenceForJid(const Jid& jid, 671 size_t index) { 672 JidPresenceVectorMap::iterator pos; 673 pos = incoming_presence_map_->find(jid); 674 if (pos == incoming_presence_map_->end()) 675 return NULL; 676 677 ASSERT(pos->second != NULL); 678 679 if (index >= pos->second->size()) 680 return NULL; 681 682 return (*pos->second)[index]; 683 } 684 685 XmppReturnStatus 686 XmppRosterModuleImpl::RequestRosterUpdate() { 687 if (!engine()) 688 return XMPP_RETURN_BADSTATE; 689 690 XmlElement roster_get(QN_IQ); 691 roster_get.AddAttr(QN_TYPE, "get"); 692 roster_get.AddAttr(QN_ID, engine()->NextId()); 693 roster_get.AddElement(new XmlElement(QN_ROSTER_QUERY, true)); 694 return engine()->SendIq(&roster_get, this, NULL); 695 } 696 697 size_t 698 XmppRosterModuleImpl::GetRosterContactCount() { 699 return contacts_->size(); 700 } 701 702 const XmppRosterContact* 703 XmppRosterModuleImpl::GetRosterContact(size_t index) { 704 if (index >= contacts_->size()) 705 return NULL; 706 return (*contacts_)[index]; 707 } 708 709 class RosterPredicate { 710 public: 711 explicit RosterPredicate(const Jid& jid) : jid_(jid) { 712 } 713 714 bool operator() (XmppRosterContactImpl *& contact) { 715 return contact->jid() == jid_; 716 } 717 718 private: 719 Jid jid_; 720 }; 721 722 const XmppRosterContact* 723 XmppRosterModuleImpl::FindRosterContact(const Jid& jid) { 724 ContactVector::iterator pos; 725 726 pos = std::find_if(contacts_->begin(), 727 contacts_->end(), 728 RosterPredicate(jid)); 729 if (pos == contacts_->end()) 730 return NULL; 731 732 return *pos; 733 } 734 735 XmppReturnStatus 736 XmppRosterModuleImpl::RequestRosterChange( 737 const XmppRosterContact* contact) { 738 if (!contact) 739 return XMPP_RETURN_BADARGUMENT; 740 741 Jid jid = contact->jid(); 742 743 if (!jid.IsValid()) 744 return XMPP_RETURN_BADARGUMENT; 745 746 if (!engine()) 747 return XMPP_RETURN_BADSTATE; 748 749 const XmlElement* contact_xml = contact->raw_xml(); 750 if (contact_xml->Name() != QN_ROSTER_ITEM || 751 contact_xml->HasAttr(QN_SUBSCRIPTION) || 752 contact_xml->HasAttr(QN_ASK)) 753 return XMPP_RETURN_BADARGUMENT; 754 755 XmlElement roster_add(QN_IQ); 756 roster_add.AddAttr(QN_TYPE, "set"); 757 roster_add.AddAttr(QN_ID, engine()->NextId()); 758 roster_add.AddElement(new XmlElement(QN_ROSTER_QUERY, true)); 759 roster_add.AddElement(new XmlElement(*contact_xml), 1); 760 761 return engine()->SendIq(&roster_add, this, NULL); 762 } 763 764 XmppReturnStatus 765 XmppRosterModuleImpl::RequestRosterRemove(const Jid& jid) { 766 if (!jid.IsValid()) 767 return XMPP_RETURN_BADARGUMENT; 768 769 if (!engine()) 770 return XMPP_RETURN_BADSTATE; 771 772 XmlElement roster_add(QN_IQ); 773 roster_add.AddAttr(QN_TYPE, "set"); 774 roster_add.AddAttr(QN_ID, engine()->NextId()); 775 roster_add.AddElement(new XmlElement(QN_ROSTER_QUERY, true)); 776 roster_add.AddAttr(QN_JID, jid.Str(), 1); 777 roster_add.AddAttr(QN_SUBSCRIPTION, "remove", 1); 778 779 return engine()->SendIq(&roster_add, this, NULL); 780 } 781 782 XmppReturnStatus 783 XmppRosterModuleImpl::RequestSubscription(const Jid& jid) { 784 return SendSubscriptionRequest(jid, "subscribe"); 785 } 786 787 XmppReturnStatus 788 XmppRosterModuleImpl::CancelSubscription(const Jid& jid) { 789 return SendSubscriptionRequest(jid, "unsubscribe"); 790 } 791 792 XmppReturnStatus 793 XmppRosterModuleImpl::ApproveSubscriber(const Jid& jid) { 794 return SendSubscriptionRequest(jid, "subscribed"); 795 } 796 797 XmppReturnStatus 798 XmppRosterModuleImpl::CancelSubscriber(const Jid& jid) { 799 return SendSubscriptionRequest(jid, "unsubscribed"); 800 } 801 802 void 803 XmppRosterModuleImpl::IqResponse(XmppIqCookie, const XmlElement * stanza) { 804 // The only real Iq response that we expect to recieve are initial roster 805 // population 806 if (stanza->Attr(QN_TYPE) == "error") 807 { 808 if (roster_handler_) 809 roster_handler_->RosterError(this, stanza); 810 811 return; 812 } 813 814 ASSERT(stanza->Attr(QN_TYPE) == "result"); 815 816 InternalRosterItems(stanza); 817 } 818 819 bool 820 XmppRosterModuleImpl::HandleStanza(const XmlElement * stanza) 821 { 822 ASSERT(engine() != NULL); 823 824 // There are two types of stanzas that we care about: presence and roster push 825 // Iqs 826 if (stanza->Name() == QN_PRESENCE) { 827 const std::string& jid_string = stanza->Attr(QN_FROM); 828 Jid jid(jid_string); 829 830 if (!jid.IsValid()) 831 return false; // if the Jid isn't valid, don't process 832 833 const std::string& type = stanza->Attr(QN_TYPE); 834 XmppSubscriptionRequestType request_type; 835 if (StringToSubscriptionRequestType(type, &request_type)) 836 InternalSubscriptionRequest(jid, stanza, request_type); 837 else if (type == "unavailable" || type == STR_EMPTY) 838 InternalIncomingPresence(jid, stanza); 839 else if (type == "error") 840 InternalIncomingPresenceError(jid, stanza); 841 else 842 return false; 843 844 return true; 845 } else if (stanza->Name() == QN_IQ) { 846 const XmlElement * roster_query = stanza->FirstNamed(QN_ROSTER_QUERY); 847 if (!roster_query || stanza->Attr(QN_TYPE) != "set") 848 return false; 849 850 InternalRosterItems(stanza); 851 852 // respond to the IQ 853 XmlElement result(QN_IQ); 854 result.AddAttr(QN_TYPE, "result"); 855 result.AddAttr(QN_TO, stanza->Attr(QN_FROM)); 856 result.AddAttr(QN_ID, stanza->Attr(QN_ID)); 857 858 engine()->SendStanza(&result); 859 return true; 860 } 861 862 return false; 863 } 864 865 void 866 XmppRosterModuleImpl::DeleteIncomingPresence() { 867 // Clear out the vector of all presence notifications 868 { 869 PresenceVector::iterator pos; 870 for (pos = incoming_presence_vector_->begin(); 871 pos < incoming_presence_vector_->end(); 872 ++pos) { 873 XmppPresenceImpl * presence = *pos; 874 *pos = NULL; 875 delete presence; 876 } 877 incoming_presence_vector_->clear(); 878 } 879 880 // Clear out all of the small presence vectors per Jid 881 { 882 JidPresenceVectorMap::iterator pos; 883 for (pos = incoming_presence_map_->begin(); 884 pos != incoming_presence_map_->end(); 885 ++pos) { 886 PresenceVector* presence_vector = pos->second; 887 pos->second = NULL; 888 delete presence_vector; 889 } 890 incoming_presence_map_->clear(); 891 } 892 } 893 894 void 895 XmppRosterModuleImpl::DeleteContacts() { 896 ContactVector::iterator pos; 897 for (pos = contacts_->begin(); 898 pos < contacts_->end(); 899 ++pos) { 900 XmppRosterContact* contact = *pos; 901 *pos = NULL; 902 delete contact; 903 } 904 contacts_->clear(); 905 } 906 907 XmppReturnStatus 908 XmppRosterModuleImpl::SendSubscriptionRequest(const Jid& jid, 909 const std::string& type) { 910 if (!jid.IsValid()) 911 return XMPP_RETURN_BADARGUMENT; 912 913 if (!engine()) 914 return XMPP_RETURN_BADSTATE; 915 916 XmlElement presence_request(QN_PRESENCE); 917 presence_request.AddAttr(QN_TO, jid.Str()); 918 presence_request.AddAttr(QN_TYPE, type); 919 920 return engine()->SendStanza(&presence_request); 921 } 922 923 924 void 925 XmppRosterModuleImpl::InternalSubscriptionRequest(const Jid& jid, 926 const XmlElement* stanza, 927 XmppSubscriptionRequestType 928 request_type) { 929 if (roster_handler_) 930 roster_handler_->SubscriptionRequest(this, jid, request_type, stanza); 931 } 932 933 class PresencePredicate { 934 public: 935 explicit PresencePredicate(const Jid& jid) : jid_(jid) { 936 } 937 938 bool operator() (XmppPresenceImpl *& contact) { 939 return contact->jid() == jid_; 940 } 941 942 private: 943 Jid jid_; 944 }; 945 946 void 947 XmppRosterModuleImpl::InternalIncomingPresence(const Jid& jid, 948 const XmlElement* stanza) { 949 bool added = false; 950 Jid bare_jid = jid.BareJid(); 951 952 // First add the presence to the map 953 JidPresenceVectorMap::iterator pos; 954 pos = incoming_presence_map_->find(jid.BareJid()); 955 if (pos == incoming_presence_map_->end()) { 956 // Insert a new entry into the map. Get the position of this new entry 957 pos = (incoming_presence_map_->insert( 958 std::make_pair(bare_jid, new PresenceVector()))).first; 959 } 960 961 PresenceVector * presence_vector = pos->second; 962 ASSERT(presence_vector != NULL); 963 964 // Try to find this jid in the bare jid bucket 965 PresenceVector::iterator presence_pos; 966 XmppPresenceImpl* presence; 967 presence_pos = std::find_if(presence_vector->begin(), 968 presence_vector->end(), 969 PresencePredicate(jid)); 970 971 // Update/add it to the bucket 972 if (presence_pos == presence_vector->end()) { 973 presence = new XmppPresenceImpl(); 974 if (XMPP_RETURN_OK == presence->set_raw_xml(stanza)) { 975 added = true; 976 presence_vector->push_back(presence); 977 } else { 978 delete presence; 979 presence = NULL; 980 } 981 } else { 982 presence = *presence_pos; 983 presence->set_raw_xml(stanza); 984 } 985 986 // now add to the comprehensive vector 987 if (added) 988 incoming_presence_vector_->push_back(presence); 989 990 // Call back to the user with the changed presence information 991 if (roster_handler_) 992 roster_handler_->IncomingPresenceChanged(this, presence); 993 } 994 995 996 void 997 XmppRosterModuleImpl::InternalIncomingPresenceError(const Jid& jid, 998 const XmlElement* stanza) { 999 if (roster_handler_) 1000 roster_handler_->SubscriptionError(this, jid, stanza); 1001 } 1002 1003 void 1004 XmppRosterModuleImpl::InternalRosterItems(const XmlElement* stanza) { 1005 const XmlElement* result_data = stanza->FirstNamed(QN_ROSTER_QUERY); 1006 if (!result_data) 1007 return; // unknown stuff in result! 1008 1009 bool all_new = contacts_->empty(); 1010 1011 for (const XmlElement* roster_item = result_data->FirstNamed(QN_ROSTER_ITEM); 1012 roster_item; 1013 roster_item = roster_item->NextNamed(QN_ROSTER_ITEM)) 1014 { 1015 const std::string& jid_string = roster_item->Attr(QN_JID); 1016 Jid jid(jid_string); 1017 if (!jid.IsValid()) 1018 continue; 1019 1020 // This algorithm is N^2 on the number of incoming contacts after the 1021 // initial load. There is no way to do this faster without allowing 1022 // duplicates, introducing more data structures or write a custom data 1023 // structure. We'll see if this becomes a perf problem and fix it if it 1024 // does. 1025 ContactVector::iterator pos = contacts_->end(); 1026 1027 if (!all_new) { 1028 pos = std::find_if(contacts_->begin(), 1029 contacts_->end(), 1030 RosterPredicate(jid)); 1031 } 1032 1033 if (pos != contacts_->end()) { // Update/remove a current contact 1034 if (roster_item->Attr(QN_SUBSCRIPTION) == "remove") { 1035 XmppRosterContact* contact = *pos; 1036 contacts_->erase(pos); 1037 if (roster_handler_) 1038 roster_handler_->ContactRemoved(this, contact, 1039 std::distance(contacts_->begin(), pos)); 1040 delete contact; 1041 } else { 1042 XmppRosterContact* old_contact = *pos; 1043 *pos = new XmppRosterContactImpl(); 1044 (*pos)->SetXmlFromWire(roster_item); 1045 if (roster_handler_) 1046 roster_handler_->ContactChanged(this, old_contact, 1047 std::distance(contacts_->begin(), pos)); 1048 delete old_contact; 1049 } 1050 } else { // Add a new contact 1051 XmppRosterContactImpl* contact = new XmppRosterContactImpl(); 1052 contact->SetXmlFromWire(roster_item); 1053 contacts_->push_back(contact); 1054 if (roster_handler_ && !all_new) 1055 roster_handler_->ContactsAdded(this, contacts_->size() - 1, 1); 1056 } 1057 } 1058 1059 // Send a consolidated update if all contacts are new 1060 if (roster_handler_ && all_new) 1061 roster_handler_->ContactsAdded(this, 0, contacts_->size()); 1062 } 1063 1064 } 1065