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