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