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