Home | History | Annotate | Download | only in xmpp
      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 <algorithm>
     29 #include <iostream>
     30 #include <map>
     31 #include <sstream>
     32 #include <string>
     33 #include <vector>
     34 #include "talk/xmpp/constants.h"
     35 #include "talk/xmpp/rostermoduleimpl.h"
     36 #include "webrtc/base/common.h"
     37 #include "webrtc/base/stringencode.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 (!rtc::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 (rtc::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   raw_xml_.reset(new XmlElement(*xml));
    355   return XMPP_RETURN_OK;
    356 }
    357 
    358 void
    359 XmppPresenceImpl::CreateRawXmlSkeleton() {
    360   raw_xml_.reset(new XmlElement(QN_PRESENCE));
    361 }
    362 
    363 // XmppRosterContactImpl -------------------------------------------------------
    364 XmppRosterContact*
    365 XmppRosterContact::Create() {
    366   return new XmppRosterContactImpl();
    367 }
    368 
    369 XmppRosterContactImpl::XmppRosterContactImpl() {
    370   ResetGroupCache();
    371 }
    372 
    373 void
    374 XmppRosterContactImpl::SetXmlFromWire(const XmlElement* xml) {
    375   ResetGroupCache();
    376   if (xml)
    377     raw_xml_.reset(new XmlElement(*xml));
    378   else
    379     raw_xml_.reset(NULL);
    380 }
    381 
    382 void
    383 XmppRosterContactImpl::ResetGroupCache() {
    384   group_count_ = -1;
    385   group_index_returned_ = -1;
    386   group_returned_ = NULL;
    387 }
    388 
    389 const Jid
    390 XmppRosterContactImpl::jid() const {
    391   return Jid(raw_xml_->Attr(QN_JID));
    392 }
    393 
    394 XmppReturnStatus
    395 XmppRosterContactImpl::set_jid(const Jid& jid)
    396 {
    397   if (!raw_xml_)
    398     CreateRawXmlSkeleton();
    399 
    400   if (!jid.IsValid())
    401     return XMPP_RETURN_BADARGUMENT;
    402 
    403   raw_xml_->SetAttr(QN_JID, jid.Str());
    404 
    405   return XMPP_RETURN_OK;
    406 }
    407 
    408 const std::string
    409 XmppRosterContactImpl::name() const {
    410   return raw_xml_->Attr(QN_NAME);
    411 }
    412 
    413 XmppReturnStatus
    414 XmppRosterContactImpl::set_name(const std::string& name) {
    415   if (!raw_xml_)
    416     CreateRawXmlSkeleton();
    417 
    418   if (name == STR_EMPTY)
    419     raw_xml_->ClearAttr(QN_NAME);
    420   else
    421     raw_xml_->SetAttr(QN_NAME, name);
    422 
    423   return XMPP_RETURN_OK;
    424 }
    425 
    426 XmppSubscriptionState
    427 XmppRosterContactImpl::subscription_state() const {
    428   if (!raw_xml_)
    429     return XMPP_SUBSCRIPTION_NONE;
    430 
    431   XmppSubscriptionState state = XMPP_SUBSCRIPTION_NONE;
    432 
    433   if (StringToSubscriptionState(raw_xml_->Attr(QN_SUBSCRIPTION),
    434                                 raw_xml_->Attr(QN_ASK),
    435                                 &state))
    436     return state;
    437 
    438   return XMPP_SUBSCRIPTION_NONE;
    439 }
    440 
    441 size_t
    442 XmppRosterContactImpl::GetGroupCount() const {
    443   if (!raw_xml_)
    444     return 0;
    445 
    446   if (-1 == group_count_) {
    447     XmlElement *group_element = raw_xml_->FirstNamed(QN_ROSTER_GROUP);
    448     int group_count = 0;
    449     while(group_element) {
    450       group_count++;
    451       group_element = group_element->NextNamed(QN_ROSTER_GROUP);
    452     }
    453 
    454     ASSERT(group_count > 0); // protect the cast
    455     XmppRosterContactImpl * me = const_cast<XmppRosterContactImpl*>(this);
    456     me->group_count_ = group_count;
    457   }
    458 
    459   return group_count_;
    460 }
    461 
    462 const std::string
    463 XmppRosterContactImpl::GetGroup(size_t index) const {
    464   if (index >= GetGroupCount())
    465     return STR_EMPTY;
    466 
    467   // We cache the last group index and element that we returned.  This way
    468   // going through the groups in order is order n and not n^2.  This could be
    469   // enhanced if necessary by starting at the cached value if the index asked
    470   // is after the cached one.
    471   if (group_index_returned_ >= 0 &&
    472       index == static_cast<size_t>(group_index_returned_) + 1)
    473   {
    474     XmppRosterContactImpl * me = const_cast<XmppRosterContactImpl*>(this);
    475     me->group_returned_ = group_returned_->NextNamed(QN_ROSTER_GROUP);
    476     ASSERT(group_returned_ != NULL);
    477     me->group_index_returned_++;
    478   } else if (group_index_returned_ < 0 ||
    479              static_cast<size_t>(group_index_returned_) != index) {
    480     XmlElement * group_element = raw_xml_->FirstNamed(QN_ROSTER_GROUP);
    481     size_t group_index = 0;
    482     while(group_index < index) {
    483       ASSERT(group_element != NULL);
    484       group_index++;
    485       group_element = group_element->NextNamed(QN_ROSTER_GROUP);
    486     }
    487 
    488     XmppRosterContactImpl * me = const_cast<XmppRosterContactImpl*>(this);
    489     me->group_index_returned_ = static_cast<int>(group_index);
    490     me->group_returned_ = group_element;
    491   }
    492 
    493   return group_returned_->BodyText();
    494 }
    495 
    496 XmppReturnStatus
    497 XmppRosterContactImpl::AddGroup(const std::string& group) {
    498   if (group == STR_EMPTY)
    499     return XMPP_RETURN_BADARGUMENT;
    500 
    501   if (!raw_xml_)
    502     CreateRawXmlSkeleton();
    503 
    504   if (FindGroup(group, NULL, NULL))
    505     return XMPP_RETURN_OK;
    506 
    507   raw_xml_->AddElement(new XmlElement(QN_ROSTER_GROUP));
    508   raw_xml_->AddText(group, 1);
    509   ++group_count_;
    510 
    511   return XMPP_RETURN_OK;
    512 }
    513 
    514 XmppReturnStatus
    515 XmppRosterContactImpl::RemoveGroup(const std::string& group) {
    516   if (group == STR_EMPTY)
    517     return XMPP_RETURN_BADARGUMENT;
    518 
    519   if (!raw_xml_)
    520     return XMPP_RETURN_OK;
    521 
    522   XmlChild * child_before;
    523   if (FindGroup(group, NULL, &child_before)) {
    524     raw_xml_->RemoveChildAfter(child_before);
    525     ResetGroupCache();
    526   }
    527   return XMPP_RETURN_OK;
    528 }
    529 
    530 bool
    531 XmppRosterContactImpl::FindGroup(const std::string& group,
    532                                  XmlElement** element,
    533                                  XmlChild** child_before) {
    534   XmlChild * prev_child = NULL;
    535   XmlChild * next_child;
    536   XmlChild * child;
    537   for (child = raw_xml_->FirstChild(); child; child = next_child) {
    538     next_child = child->NextChild();
    539     if (!child->IsText() &&
    540         child->AsElement()->Name() == QN_ROSTER_GROUP &&
    541         child->AsElement()->BodyText() == group) {
    542       if (element)
    543         *element = child->AsElement();
    544       if (child_before)
    545         *child_before = prev_child;
    546       return true;
    547     }
    548     prev_child = child;
    549   }
    550 
    551   return false;
    552 }
    553 
    554 const XmlElement*
    555 XmppRosterContactImpl::raw_xml() const {
    556   if (!raw_xml_)
    557     const_cast<XmppRosterContactImpl*>(this)->CreateRawXmlSkeleton();
    558   return raw_xml_.get();
    559 }
    560 
    561 XmppReturnStatus
    562 XmppRosterContactImpl::set_raw_xml(const XmlElement* xml) {
    563   if (!xml ||
    564       xml->Name() != QN_ROSTER_ITEM ||
    565       xml->HasAttr(QN_SUBSCRIPTION) ||
    566       xml->HasAttr(QN_ASK))
    567     return XMPP_RETURN_BADARGUMENT;
    568 
    569   ResetGroupCache();
    570 
    571   raw_xml_.reset(new XmlElement(*xml));
    572 
    573   return XMPP_RETURN_OK;
    574 }
    575 
    576 void
    577 XmppRosterContactImpl::CreateRawXmlSkeleton() {
    578   raw_xml_.reset(new XmlElement(QN_ROSTER_ITEM));
    579 }
    580 
    581 // XmppRosterModuleImpl --------------------------------------------------------
    582 XmppRosterModule *
    583 XmppRosterModule::Create() {
    584   return new XmppRosterModuleImpl();
    585 }
    586 
    587 XmppRosterModuleImpl::XmppRosterModuleImpl() :
    588   roster_handler_(NULL),
    589   incoming_presence_map_(new JidPresenceVectorMap()),
    590   incoming_presence_vector_(new PresenceVector()),
    591   contacts_(new ContactVector()) {
    592 
    593 }
    594 
    595 XmppRosterModuleImpl::~XmppRosterModuleImpl() {
    596   DeleteIncomingPresence();
    597   DeleteContacts();
    598 }
    599 
    600 XmppReturnStatus
    601 XmppRosterModuleImpl::set_roster_handler(XmppRosterHandler * handler) {
    602   roster_handler_ = handler;
    603   return XMPP_RETURN_OK;
    604 }
    605 
    606 XmppRosterHandler*
    607 XmppRosterModuleImpl::roster_handler() {
    608   return roster_handler_;
    609 }
    610 
    611 XmppPresence*
    612 XmppRosterModuleImpl::outgoing_presence() {
    613   return &outgoing_presence_;
    614 }
    615 
    616 XmppReturnStatus
    617 XmppRosterModuleImpl::BroadcastPresence() {
    618   // Scrub the outgoing presence
    619   const XmlElement* element = outgoing_presence_.raw_xml();
    620 
    621   ASSERT(!element->HasAttr(QN_TO) &&
    622          !element->HasAttr(QN_FROM) &&
    623           (element->Attr(QN_TYPE) == STR_EMPTY ||
    624            element->Attr(QN_TYPE) == "unavailable"));
    625 
    626   if (!engine())
    627     return XMPP_RETURN_BADSTATE;
    628 
    629   return engine()->SendStanza(element);
    630 }
    631 
    632 XmppReturnStatus
    633 XmppRosterModuleImpl::SendDirectedPresence(const XmppPresence* presence,
    634                                            const Jid& to_jid) {
    635   if (!presence)
    636     return XMPP_RETURN_BADARGUMENT;
    637 
    638   if (!engine())
    639     return XMPP_RETURN_BADSTATE;
    640 
    641   XmlElement element(*(presence->raw_xml()));
    642 
    643   if (element.Name() != QN_PRESENCE ||
    644       element.HasAttr(QN_TO) ||
    645       element.HasAttr(QN_FROM))
    646     return XMPP_RETURN_BADARGUMENT;
    647 
    648   if (element.HasAttr(QN_TYPE)) {
    649     if (element.Attr(QN_TYPE) != STR_EMPTY &&
    650         element.Attr(QN_TYPE) != "unavailable") {
    651       return XMPP_RETURN_BADARGUMENT;
    652     }
    653   }
    654 
    655   element.SetAttr(QN_TO, to_jid.Str());
    656 
    657   return engine()->SendStanza(&element);
    658 }
    659 
    660 size_t
    661 XmppRosterModuleImpl::GetIncomingPresenceCount() {
    662   return incoming_presence_vector_->size();
    663 }
    664 
    665 const XmppPresence*
    666 XmppRosterModuleImpl::GetIncomingPresence(size_t index) {
    667   if (index >= incoming_presence_vector_->size())
    668     return NULL;
    669   return (*incoming_presence_vector_)[index];
    670 }
    671 
    672 size_t
    673 XmppRosterModuleImpl::GetIncomingPresenceForJidCount(const Jid& jid)
    674 {
    675   // find the vector in the map
    676   JidPresenceVectorMap::iterator pos;
    677   pos = incoming_presence_map_->find(jid);
    678   if (pos == incoming_presence_map_->end())
    679     return 0;
    680 
    681   ASSERT(pos->second != NULL);
    682 
    683   return pos->second->size();
    684 }
    685 
    686 const XmppPresence*
    687 XmppRosterModuleImpl::GetIncomingPresenceForJid(const Jid& jid,
    688                                                 size_t index) {
    689   JidPresenceVectorMap::iterator pos;
    690   pos = incoming_presence_map_->find(jid);
    691   if (pos == incoming_presence_map_->end())
    692     return NULL;
    693 
    694   ASSERT(pos->second != NULL);
    695 
    696   if (index >= pos->second->size())
    697     return NULL;
    698 
    699   return (*pos->second)[index];
    700 }
    701 
    702 XmppReturnStatus
    703 XmppRosterModuleImpl::RequestRosterUpdate() {
    704   if (!engine())
    705     return XMPP_RETURN_BADSTATE;
    706 
    707   XmlElement roster_get(QN_IQ);
    708   roster_get.AddAttr(QN_TYPE, "get");
    709   roster_get.AddAttr(QN_ID, engine()->NextId());
    710   roster_get.AddElement(new XmlElement(QN_ROSTER_QUERY, true));
    711   return engine()->SendIq(&roster_get, this, NULL);
    712 }
    713 
    714 size_t
    715 XmppRosterModuleImpl::GetRosterContactCount() {
    716   return contacts_->size();
    717 }
    718 
    719 const XmppRosterContact*
    720 XmppRosterModuleImpl::GetRosterContact(size_t index) {
    721   if (index >= contacts_->size())
    722     return NULL;
    723   return (*contacts_)[index];
    724 }
    725 
    726 class RosterPredicate {
    727 public:
    728   explicit RosterPredicate(const Jid& jid) : jid_(jid) {
    729   }
    730 
    731   bool operator() (XmppRosterContactImpl *& contact) {
    732     return contact->jid() == jid_;
    733   }
    734 
    735 private:
    736   Jid jid_;
    737 };
    738 
    739 const XmppRosterContact*
    740 XmppRosterModuleImpl::FindRosterContact(const Jid& jid) {
    741   ContactVector::iterator pos;
    742 
    743   pos = std::find_if(contacts_->begin(),
    744                      contacts_->end(),
    745                      RosterPredicate(jid));
    746   if (pos == contacts_->end())
    747     return NULL;
    748 
    749   return *pos;
    750 }
    751 
    752 XmppReturnStatus
    753 XmppRosterModuleImpl::RequestRosterChange(
    754   const XmppRosterContact* contact) {
    755   if (!contact)
    756     return XMPP_RETURN_BADARGUMENT;
    757 
    758   Jid jid = contact->jid();
    759 
    760   if (!jid.IsValid())
    761     return XMPP_RETURN_BADARGUMENT;
    762 
    763   if (!engine())
    764     return XMPP_RETURN_BADSTATE;
    765 
    766   const XmlElement* contact_xml = contact->raw_xml();
    767   if (contact_xml->Name() != QN_ROSTER_ITEM ||
    768       contact_xml->HasAttr(QN_SUBSCRIPTION) ||
    769       contact_xml->HasAttr(QN_ASK))
    770     return XMPP_RETURN_BADARGUMENT;
    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.AddElement(new XmlElement(*contact_xml), 1);
    777 
    778   return engine()->SendIq(&roster_add, this, NULL);
    779 }
    780 
    781 XmppReturnStatus
    782 XmppRosterModuleImpl::RequestRosterRemove(const Jid& jid) {
    783   if (!jid.IsValid())
    784     return XMPP_RETURN_BADARGUMENT;
    785 
    786   if (!engine())
    787     return XMPP_RETURN_BADSTATE;
    788 
    789   XmlElement roster_add(QN_IQ);
    790   roster_add.AddAttr(QN_TYPE, "set");
    791   roster_add.AddAttr(QN_ID, engine()->NextId());
    792   roster_add.AddElement(new XmlElement(QN_ROSTER_QUERY, true));
    793   roster_add.AddAttr(QN_JID, jid.Str(), 1);
    794   roster_add.AddAttr(QN_SUBSCRIPTION, "remove", 1);
    795 
    796   return engine()->SendIq(&roster_add, this, NULL);
    797 }
    798 
    799 XmppReturnStatus
    800 XmppRosterModuleImpl::RequestSubscription(const Jid& jid) {
    801   return SendSubscriptionRequest(jid, "subscribe");
    802 }
    803 
    804 XmppReturnStatus
    805 XmppRosterModuleImpl::CancelSubscription(const Jid& jid) {
    806   return SendSubscriptionRequest(jid, "unsubscribe");
    807 }
    808 
    809 XmppReturnStatus
    810 XmppRosterModuleImpl::ApproveSubscriber(const Jid& jid) {
    811   return SendSubscriptionRequest(jid, "subscribed");
    812 }
    813 
    814 XmppReturnStatus
    815 XmppRosterModuleImpl::CancelSubscriber(const Jid& jid) {
    816   return SendSubscriptionRequest(jid, "unsubscribed");
    817 }
    818 
    819 void
    820 XmppRosterModuleImpl::IqResponse(XmppIqCookie, const XmlElement * stanza) {
    821   // The only real Iq response that we expect to recieve are initial roster
    822   // population
    823   if (stanza->Attr(QN_TYPE) == "error")
    824   {
    825     if (roster_handler_)
    826       roster_handler_->RosterError(this, stanza);
    827 
    828     return;
    829   }
    830 
    831   ASSERT(stanza->Attr(QN_TYPE) == "result");
    832 
    833   InternalRosterItems(stanza);
    834 }
    835 
    836 bool
    837 XmppRosterModuleImpl::HandleStanza(const XmlElement * stanza)
    838 {
    839   ASSERT(engine() != NULL);
    840 
    841   // There are two types of stanzas that we care about: presence and roster push
    842   // Iqs
    843   if (stanza->Name() == QN_PRESENCE) {
    844     const std::string&  jid_string = stanza->Attr(QN_FROM);
    845     Jid jid(jid_string);
    846 
    847     if (!jid.IsValid())
    848       return false; // if the Jid isn't valid, don't process
    849 
    850     const std::string& type = stanza->Attr(QN_TYPE);
    851     XmppSubscriptionRequestType request_type;
    852     if (StringToSubscriptionRequestType(type, &request_type))
    853       InternalSubscriptionRequest(jid, stanza, request_type);
    854     else if (type == "unavailable" || type == STR_EMPTY)
    855       InternalIncomingPresence(jid, stanza);
    856     else if (type == "error")
    857       InternalIncomingPresenceError(jid, stanza);
    858     else
    859       return false;
    860 
    861     return true;
    862   } else if (stanza->Name() == QN_IQ) {
    863     const XmlElement * roster_query = stanza->FirstNamed(QN_ROSTER_QUERY);
    864     if (!roster_query || stanza->Attr(QN_TYPE) != "set")
    865       return false;
    866 
    867     InternalRosterItems(stanza);
    868 
    869     // respond to the IQ
    870     XmlElement result(QN_IQ);
    871     result.AddAttr(QN_TYPE, "result");
    872     result.AddAttr(QN_TO, stanza->Attr(QN_FROM));
    873     result.AddAttr(QN_ID, stanza->Attr(QN_ID));
    874 
    875     engine()->SendStanza(&result);
    876     return true;
    877   }
    878 
    879   return false;
    880 }
    881 
    882 void
    883 XmppRosterModuleImpl::DeleteIncomingPresence() {
    884   // Clear out the vector of all presence notifications
    885   {
    886     PresenceVector::iterator pos;
    887     for (pos = incoming_presence_vector_->begin();
    888          pos < incoming_presence_vector_->end();
    889          ++pos) {
    890       XmppPresenceImpl * presence = *pos;
    891       *pos = NULL;
    892       delete presence;
    893     }
    894     incoming_presence_vector_->clear();
    895   }
    896 
    897   // Clear out all of the small presence vectors per Jid
    898   {
    899     JidPresenceVectorMap::iterator pos;
    900     for (pos = incoming_presence_map_->begin();
    901          pos != incoming_presence_map_->end();
    902          ++pos) {
    903       PresenceVector* presence_vector = pos->second;
    904       pos->second = NULL;
    905       delete presence_vector;
    906     }
    907     incoming_presence_map_->clear();
    908   }
    909 }
    910 
    911 void
    912 XmppRosterModuleImpl::DeleteContacts() {
    913   ContactVector::iterator pos;
    914   for (pos = contacts_->begin();
    915        pos < contacts_->end();
    916        ++pos) {
    917     XmppRosterContact* contact = *pos;
    918     *pos = NULL;
    919     delete contact;
    920   }
    921   contacts_->clear();
    922 }
    923 
    924 XmppReturnStatus
    925 XmppRosterModuleImpl::SendSubscriptionRequest(const Jid& jid,
    926                                               const std::string& type) {
    927   if (!jid.IsValid())
    928     return XMPP_RETURN_BADARGUMENT;
    929 
    930   if (!engine())
    931     return XMPP_RETURN_BADSTATE;
    932 
    933   XmlElement presence_request(QN_PRESENCE);
    934   presence_request.AddAttr(QN_TO, jid.Str());
    935   presence_request.AddAttr(QN_TYPE, type);
    936 
    937   return engine()->SendStanza(&presence_request);
    938 }
    939 
    940 
    941 void
    942 XmppRosterModuleImpl::InternalSubscriptionRequest(const Jid& jid,
    943                                                   const XmlElement* stanza,
    944                                                   XmppSubscriptionRequestType
    945                                                     request_type) {
    946   if (roster_handler_)
    947     roster_handler_->SubscriptionRequest(this, jid, request_type, stanza);
    948 }
    949 
    950 class PresencePredicate {
    951 public:
    952   explicit PresencePredicate(const Jid& jid) : jid_(jid) {
    953   }
    954 
    955   bool operator() (XmppPresenceImpl *& contact) {
    956     return contact->jid() == jid_;
    957   }
    958 
    959 private:
    960   Jid jid_;
    961 };
    962 
    963 void
    964 XmppRosterModuleImpl::InternalIncomingPresence(const Jid& jid,
    965                                                const XmlElement* stanza) {
    966   bool added = false;
    967   Jid bare_jid = jid.BareJid();
    968 
    969   // First add the presence to the map
    970   JidPresenceVectorMap::iterator pos;
    971   pos = incoming_presence_map_->find(jid.BareJid());
    972   if (pos == incoming_presence_map_->end()) {
    973     // Insert a new entry into the map.  Get the position of this new entry
    974     pos = (incoming_presence_map_->insert(
    975             std::make_pair(bare_jid, new PresenceVector()))).first;
    976   }
    977 
    978   PresenceVector * presence_vector = pos->second;
    979   ASSERT(presence_vector != NULL);
    980 
    981   // Try to find this jid in the bare jid bucket
    982   PresenceVector::iterator presence_pos;
    983   XmppPresenceImpl* presence;
    984   presence_pos = std::find_if(presence_vector->begin(),
    985                               presence_vector->end(),
    986                               PresencePredicate(jid));
    987 
    988   // Update/add it to the bucket
    989   if (presence_pos == presence_vector->end()) {
    990     presence = new XmppPresenceImpl();
    991     if (XMPP_RETURN_OK == presence->set_raw_xml(stanza)) {
    992       added = true;
    993       presence_vector->push_back(presence);
    994     } else {
    995       delete presence;
    996       presence = NULL;
    997     }
    998   } else {
    999     presence = *presence_pos;
   1000     presence->set_raw_xml(stanza);
   1001   }
   1002 
   1003   // now add to the comprehensive vector
   1004   if (added)
   1005     incoming_presence_vector_->push_back(presence);
   1006 
   1007   // Call back to the user with the changed presence information
   1008   if (roster_handler_)
   1009     roster_handler_->IncomingPresenceChanged(this, presence);
   1010 }
   1011 
   1012 
   1013 void
   1014 XmppRosterModuleImpl::InternalIncomingPresenceError(const Jid& jid,
   1015                                                     const XmlElement* stanza) {
   1016   if (roster_handler_)
   1017     roster_handler_->SubscriptionError(this, jid, stanza);
   1018 }
   1019 
   1020 void
   1021 XmppRosterModuleImpl::InternalRosterItems(const XmlElement* stanza) {
   1022   const XmlElement* result_data = stanza->FirstNamed(QN_ROSTER_QUERY);
   1023   if (!result_data)
   1024     return; // unknown stuff in result!
   1025 
   1026   bool all_new = contacts_->empty();
   1027 
   1028   for (const XmlElement* roster_item = result_data->FirstNamed(QN_ROSTER_ITEM);
   1029        roster_item;
   1030        roster_item = roster_item->NextNamed(QN_ROSTER_ITEM))
   1031   {
   1032     const std::string& jid_string = roster_item->Attr(QN_JID);
   1033     Jid jid(jid_string);
   1034     if (!jid.IsValid())
   1035       continue;
   1036 
   1037     // This algorithm is N^2 on the number of incoming contacts after the
   1038     // initial load. There is no way to do this faster without allowing
   1039     // duplicates, introducing more data structures or write a custom data
   1040     // structure.  We'll see if this becomes a perf problem and fix it if it
   1041     // does.
   1042     ContactVector::iterator pos = contacts_->end();
   1043 
   1044     if (!all_new) {
   1045       pos = std::find_if(contacts_->begin(),
   1046                          contacts_->end(),
   1047                          RosterPredicate(jid));
   1048     }
   1049 
   1050     if (pos != contacts_->end()) { // Update/remove a current contact
   1051       if (roster_item->Attr(QN_SUBSCRIPTION) == "remove") {
   1052         XmppRosterContact* contact = *pos;
   1053         contacts_->erase(pos);
   1054         if (roster_handler_)
   1055           roster_handler_->ContactRemoved(this, contact,
   1056             std::distance(contacts_->begin(), pos));
   1057         delete contact;
   1058       } else {
   1059         XmppRosterContact* old_contact = *pos;
   1060         *pos = new XmppRosterContactImpl();
   1061         (*pos)->SetXmlFromWire(roster_item);
   1062         if (roster_handler_)
   1063           roster_handler_->ContactChanged(this, old_contact,
   1064             std::distance(contacts_->begin(), pos));
   1065         delete old_contact;
   1066       }
   1067     } else { // Add a new contact
   1068       XmppRosterContactImpl* contact = new XmppRosterContactImpl();
   1069       contact->SetXmlFromWire(roster_item);
   1070       contacts_->push_back(contact);
   1071       if (roster_handler_ && !all_new)
   1072         roster_handler_->ContactsAdded(this, contacts_->size() - 1, 1);
   1073     }
   1074   }
   1075 
   1076   // Send a consolidated update if all contacts are new
   1077   if (roster_handler_ && all_new)
   1078     roster_handler_->ContactsAdded(this, 0, contacts_->size());
   1079 }
   1080 
   1081 }
   1082