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