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