• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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   // 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
PresenceShowToString(XmppPresenceShow show,const char ** output)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
StringToSubscriptionState(const std::string & subscription,const std::string & ask,XmppSubscriptionState * state)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
StringToSubscriptionRequestType(const std::string & string,XmppSubscriptionRequestType * type)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*
Create()142 XmppPresence::Create() {
143   return new XmppPresenceImpl();
144 }
145 
XmppPresenceImpl()146 XmppPresenceImpl::XmppPresenceImpl() {
147 }
148 
149 const Jid
jid() const150 XmppPresenceImpl::jid() const {
151   if (!raw_xml_)
152     return Jid();
153 
154   return Jid(raw_xml_->Attr(QN_FROM));
155 }
156 
157 XmppPresenceAvailable
available() const158 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
set_available(XmppPresenceAvailable available)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
presence_show() const185 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
set_presence_show(XmppPresenceShow show)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
priority() const215 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
set_priority(int priority)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
status() const251 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
set_status(const std::string & status)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
connection_status() const292 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
google_user_id() const313 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
nickname() const329 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*
raw_xml() const342 XmppPresenceImpl::raw_xml() const {
343   if (!raw_xml_)
344     const_cast<XmppPresenceImpl*>(this)->CreateRawXmlSkeleton();
345   return raw_xml_.get();
346 }
347 
348 XmppReturnStatus
set_raw_xml(const XmlElement * xml)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
CreateRawXmlSkeleton()363 XmppPresenceImpl::CreateRawXmlSkeleton() {
364   raw_xml_.reset(new XmlElement(QN_PRESENCE));
365 }
366 
367 // XmppRosterContactImpl -------------------------------------------------------
368 XmppRosterContact*
Create()369 XmppRosterContact::Create() {
370   return new XmppRosterContactImpl();
371 }
372 
XmppRosterContactImpl()373 XmppRosterContactImpl::XmppRosterContactImpl() {
374   ResetGroupCache();
375 }
376 
377 void
SetXmlFromWire(const XmlElement * xml)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
ResetGroupCache()387 XmppRosterContactImpl::ResetGroupCache() {
388   group_count_ = -1;
389   group_index_returned_ = -1;
390   group_returned_ = NULL;
391 }
392 
393 const Jid
jid() const394 XmppRosterContactImpl::jid() const {
395   return Jid(raw_xml_->Attr(QN_JID));
396 }
397 
398 XmppReturnStatus
set_jid(const Jid & jid)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
name() const413 XmppRosterContactImpl::name() const {
414   return raw_xml_->Attr(QN_NAME);
415 }
416 
417 XmppReturnStatus
set_name(const std::string & name)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
subscription_state() const431 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
GetGroupCount() const446 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
GetGroup(size_t index) const467 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
AddGroup(const std::string & group)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
RemoveGroup(const std::string & group)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
FindGroup(const std::string & group,XmlElement ** element,XmlChild ** child_before)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*
raw_xml() const559 XmppRosterContactImpl::raw_xml() const {
560   if (!raw_xml_)
561     const_cast<XmppRosterContactImpl*>(this)->CreateRawXmlSkeleton();
562   return raw_xml_.get();
563 }
564 
565 XmppReturnStatus
set_raw_xml(const XmlElement * xml)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
CreateRawXmlSkeleton()581 XmppRosterContactImpl::CreateRawXmlSkeleton() {
582   raw_xml_.reset(new XmlElement(QN_ROSTER_ITEM));
583 }
584 
585 // XmppRosterModuleImpl --------------------------------------------------------
586 XmppRosterModule *
Create()587 XmppRosterModule::Create() {
588   return new XmppRosterModuleImpl();
589 }
590 
XmppRosterModuleImpl()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 
~XmppRosterModuleImpl()599 XmppRosterModuleImpl::~XmppRosterModuleImpl() {
600   DeleteIncomingPresence();
601   DeleteContacts();
602 }
603 
604 XmppReturnStatus
set_roster_handler(XmppRosterHandler * handler)605 XmppRosterModuleImpl::set_roster_handler(XmppRosterHandler * handler) {
606   roster_handler_ = handler;
607   return XMPP_RETURN_OK;
608 }
609 
610 XmppRosterHandler*
roster_handler()611 XmppRosterModuleImpl::roster_handler() {
612   return roster_handler_;
613 }
614 
615 XmppPresence*
outgoing_presence()616 XmppRosterModuleImpl::outgoing_presence() {
617   return &outgoing_presence_;
618 }
619 
620 XmppReturnStatus
BroadcastPresence()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
SendDirectedPresence(const XmppPresence * presence,const Jid & to_jid)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
GetIncomingPresenceCount()665 XmppRosterModuleImpl::GetIncomingPresenceCount() {
666   return incoming_presence_vector_->size();
667 }
668 
669 const XmppPresence*
GetIncomingPresence(size_t index)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
GetIncomingPresenceForJidCount(const Jid & jid)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*
GetIncomingPresenceForJid(const Jid & jid,size_t index)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
RequestRosterUpdate()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
GetRosterContactCount()719 XmppRosterModuleImpl::GetRosterContactCount() {
720   return contacts_->size();
721 }
722 
723 const XmppRosterContact*
GetRosterContact(size_t index)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:
RosterPredicate(const Jid & jid)732   explicit RosterPredicate(const Jid& jid) : jid_(jid) {
733   }
734 
operator ()(XmppRosterContactImpl * & contact)735   bool operator() (XmppRosterContactImpl *& contact) {
736     return contact->jid() == jid_;
737   }
738 
739 private:
740   Jid jid_;
741 };
742 
743 const XmppRosterContact*
FindRosterContact(const Jid & jid)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
RequestRosterChange(const XmppRosterContact * contact)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
RequestRosterRemove(const Jid & jid)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
RequestSubscription(const Jid & jid)804 XmppRosterModuleImpl::RequestSubscription(const Jid& jid) {
805   return SendSubscriptionRequest(jid, "subscribe");
806 }
807 
808 XmppReturnStatus
CancelSubscription(const Jid & jid)809 XmppRosterModuleImpl::CancelSubscription(const Jid& jid) {
810   return SendSubscriptionRequest(jid, "unsubscribe");
811 }
812 
813 XmppReturnStatus
ApproveSubscriber(const Jid & jid)814 XmppRosterModuleImpl::ApproveSubscriber(const Jid& jid) {
815   return SendSubscriptionRequest(jid, "subscribed");
816 }
817 
818 XmppReturnStatus
CancelSubscriber(const Jid & jid)819 XmppRosterModuleImpl::CancelSubscriber(const Jid& jid) {
820   return SendSubscriptionRequest(jid, "unsubscribed");
821 }
822 
823 void
IqResponse(XmppIqCookie,const XmlElement * stanza)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
HandleStanza(const XmlElement * stanza)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
DeleteIncomingPresence()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
DeleteContacts()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
SendSubscriptionRequest(const Jid & jid,const std::string & type)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
InternalSubscriptionRequest(const Jid & jid,const XmlElement * stanza,XmppSubscriptionRequestType request_type)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:
PresencePredicate(const Jid & jid)956   explicit PresencePredicate(const Jid& jid) : jid_(jid) {
957   }
958 
operator ()(XmppPresenceImpl * & contact)959   bool operator() (XmppPresenceImpl *& contact) {
960     return contact->jid() == jid_;
961   }
962 
963 private:
964   Jid jid_;
965 };
966 
967 void
InternalIncomingPresence(const Jid & jid,const XmlElement * stanza)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
InternalIncomingPresenceError(const Jid & jid,const XmlElement * stanza)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
InternalRosterItems(const XmlElement * stanza)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