Home | History | Annotate | Download | only in xmpp
      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