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