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