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