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