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