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 <algorithm>
29#include <iostream>
30#include <map>
31#include <sstream>
32#include <string>
33#include <vector>
34#include "talk/xmpp/constants.h"
35#include "talk/xmpp/rostermoduleimpl.h"
36#include "webrtc/base/common.h"
37#include "webrtc/base/stringencode.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 (!rtc::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 (rtc::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_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
312const std::string
313XmppPresenceImpl::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
328const std::string
329XmppPresenceImpl::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
341const XmlElement*
342XmppPresenceImpl::raw_xml() const {
343  if (!raw_xml_)
344    const_cast<XmppPresenceImpl*>(this)->CreateRawXmlSkeleton();
345  return raw_xml_.get();
346}
347
348XmppReturnStatus
349XmppPresenceImpl::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
358void
359XmppPresenceImpl::CreateRawXmlSkeleton() {
360  raw_xml_.reset(new XmlElement(QN_PRESENCE));
361}
362
363// XmppRosterContactImpl -------------------------------------------------------
364XmppRosterContact*
365XmppRosterContact::Create() {
366  return new XmppRosterContactImpl();
367}
368
369XmppRosterContactImpl::XmppRosterContactImpl() {
370  ResetGroupCache();
371}
372
373void
374XmppRosterContactImpl::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
382void
383XmppRosterContactImpl::ResetGroupCache() {
384  group_count_ = -1;
385  group_index_returned_ = -1;
386  group_returned_ = NULL;
387}
388
389const Jid
390XmppRosterContactImpl::jid() const {
391  return Jid(raw_xml_->Attr(QN_JID));
392}
393
394XmppReturnStatus
395XmppRosterContactImpl::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
408const std::string
409XmppRosterContactImpl::name() const {
410  return raw_xml_->Attr(QN_NAME);
411}
412
413XmppReturnStatus
414XmppRosterContactImpl::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
426XmppSubscriptionState
427XmppRosterContactImpl::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
441size_t
442XmppRosterContactImpl::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
462const std::string
463XmppRosterContactImpl::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
496XmppReturnStatus
497XmppRosterContactImpl::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
514XmppReturnStatus
515XmppRosterContactImpl::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
530bool
531XmppRosterContactImpl::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
554const XmlElement*
555XmppRosterContactImpl::raw_xml() const {
556  if (!raw_xml_)
557    const_cast<XmppRosterContactImpl*>(this)->CreateRawXmlSkeleton();
558  return raw_xml_.get();
559}
560
561XmppReturnStatus
562XmppRosterContactImpl::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
576void
577XmppRosterContactImpl::CreateRawXmlSkeleton() {
578  raw_xml_.reset(new XmlElement(QN_ROSTER_ITEM));
579}
580
581// XmppRosterModuleImpl --------------------------------------------------------
582XmppRosterModule *
583XmppRosterModule::Create() {
584  return new XmppRosterModuleImpl();
585}
586
587XmppRosterModuleImpl::XmppRosterModuleImpl() :
588  roster_handler_(NULL),
589  incoming_presence_map_(new JidPresenceVectorMap()),
590  incoming_presence_vector_(new PresenceVector()),
591  contacts_(new ContactVector()) {
592
593}
594
595XmppRosterModuleImpl::~XmppRosterModuleImpl() {
596  DeleteIncomingPresence();
597  DeleteContacts();
598}
599
600XmppReturnStatus
601XmppRosterModuleImpl::set_roster_handler(XmppRosterHandler * handler) {
602  roster_handler_ = handler;
603  return XMPP_RETURN_OK;
604}
605
606XmppRosterHandler*
607XmppRosterModuleImpl::roster_handler() {
608  return roster_handler_;
609}
610
611XmppPresence*
612XmppRosterModuleImpl::outgoing_presence() {
613  return &outgoing_presence_;
614}
615
616XmppReturnStatus
617XmppRosterModuleImpl::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
632XmppReturnStatus
633XmppRosterModuleImpl::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
660size_t
661XmppRosterModuleImpl::GetIncomingPresenceCount() {
662  return incoming_presence_vector_->size();
663}
664
665const XmppPresence*
666XmppRosterModuleImpl::GetIncomingPresence(size_t index) {
667  if (index >= incoming_presence_vector_->size())
668    return NULL;
669  return (*incoming_presence_vector_)[index];
670}
671
672size_t
673XmppRosterModuleImpl::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
686const XmppPresence*
687XmppRosterModuleImpl::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
702XmppReturnStatus
703XmppRosterModuleImpl::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
714size_t
715XmppRosterModuleImpl::GetRosterContactCount() {
716  return contacts_->size();
717}
718
719const XmppRosterContact*
720XmppRosterModuleImpl::GetRosterContact(size_t index) {
721  if (index >= contacts_->size())
722    return NULL;
723  return (*contacts_)[index];
724}
725
726class RosterPredicate {
727public:
728  explicit RosterPredicate(const Jid& jid) : jid_(jid) {
729  }
730
731  bool operator() (XmppRosterContactImpl *& contact) {
732    return contact->jid() == jid_;
733  }
734
735private:
736  Jid jid_;
737};
738
739const XmppRosterContact*
740XmppRosterModuleImpl::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
752XmppReturnStatus
753XmppRosterModuleImpl::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
781XmppReturnStatus
782XmppRosterModuleImpl::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
799XmppReturnStatus
800XmppRosterModuleImpl::RequestSubscription(const Jid& jid) {
801  return SendSubscriptionRequest(jid, "subscribe");
802}
803
804XmppReturnStatus
805XmppRosterModuleImpl::CancelSubscription(const Jid& jid) {
806  return SendSubscriptionRequest(jid, "unsubscribe");
807}
808
809XmppReturnStatus
810XmppRosterModuleImpl::ApproveSubscriber(const Jid& jid) {
811  return SendSubscriptionRequest(jid, "subscribed");
812}
813
814XmppReturnStatus
815XmppRosterModuleImpl::CancelSubscriber(const Jid& jid) {
816  return SendSubscriptionRequest(jid, "unsubscribed");
817}
818
819void
820XmppRosterModuleImpl::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
836bool
837XmppRosterModuleImpl::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
882void
883XmppRosterModuleImpl::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
911void
912XmppRosterModuleImpl::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
924XmppReturnStatus
925XmppRosterModuleImpl::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
941void
942XmppRosterModuleImpl::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
950class PresencePredicate {
951public:
952  explicit PresencePredicate(const Jid& jid) : jid_(jid) {
953  }
954
955  bool operator() (XmppPresenceImpl *& contact) {
956    return contact->jid() == jid_;
957  }
958
959private:
960  Jid jid_;
961};
962
963void
964XmppRosterModuleImpl::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
1013void
1014XmppRosterModuleImpl::InternalIncomingPresenceError(const Jid& jid,
1015                                                    const XmlElement* stanza) {
1016  if (roster_handler_)
1017    roster_handler_->SubscriptionError(this, jid, stanza);
1018}
1019
1020void
1021XmppRosterModuleImpl::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}
1082