1 /*
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
22 namespace buzz {
23
24 // enum prase and persist helpers ----------------------------------------------
25 static bool
StringToPresenceShow(const std::string & input,XmppPresenceShow * show)26 StringToPresenceShow(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
44 static bool
PresenceShowToString(XmppPresenceShow show,const char ** output)45 PresenceShowToString(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
68 static bool
StringToSubscriptionState(const std::string & subscription,const std::string & ask,XmppSubscriptionState * state)69 StringToSubscriptionState(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
106 static bool
StringToSubscriptionRequestType(const std::string & string,XmppSubscriptionRequestType * type)107 StringToSubscriptionRequestType(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 ------------------------------------------------------
124 XmppPresence*
Create()125 XmppPresence::Create() {
126 return new XmppPresenceImpl();
127 }
128
XmppPresenceImpl()129 XmppPresenceImpl::XmppPresenceImpl() {
130 }
131
132 const Jid
jid() const133 XmppPresenceImpl::jid() const {
134 if (!raw_xml_)
135 return Jid();
136
137 return Jid(raw_xml_->Attr(QN_FROM));
138 }
139
140 XmppPresenceAvailable
available() const141 XmppPresenceImpl::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
153 XmppReturnStatus
set_available(XmppPresenceAvailable available)154 XmppPresenceImpl::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
167 XmppPresenceShow
presence_show() const168 XmppPresenceImpl::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
177 XmppReturnStatus
set_presence_show(XmppPresenceShow show)178 XmppPresenceImpl::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
197 int
priority() const198 XmppPresenceImpl::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
213 XmppReturnStatus
set_priority(int priority)214 XmppPresenceImpl::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
233 const std::string
status() const234 XmppPresenceImpl::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
259 XmppReturnStatus
set_status(const std::string & status)260 XmppPresenceImpl::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
274 XmppPresenceConnectionStatus
connection_status() const275 XmppPresenceImpl::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
295 const std::string
google_user_id() const296 XmppPresenceImpl::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
311 const std::string
nickname() const312 XmppPresenceImpl::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
324 const XmlElement*
raw_xml() const325 XmppPresenceImpl::raw_xml() const {
326 if (!raw_xml_)
327 const_cast<XmppPresenceImpl*>(this)->CreateRawXmlSkeleton();
328 return raw_xml_.get();
329 }
330
331 XmppReturnStatus
set_raw_xml(const XmlElement * xml)332 XmppPresenceImpl::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
341 void
CreateRawXmlSkeleton()342 XmppPresenceImpl::CreateRawXmlSkeleton() {
343 raw_xml_.reset(new XmlElement(QN_PRESENCE));
344 }
345
346 // XmppRosterContactImpl -------------------------------------------------------
347 XmppRosterContact*
Create()348 XmppRosterContact::Create() {
349 return new XmppRosterContactImpl();
350 }
351
XmppRosterContactImpl()352 XmppRosterContactImpl::XmppRosterContactImpl() {
353 ResetGroupCache();
354 }
355
356 void
SetXmlFromWire(const XmlElement * xml)357 XmppRosterContactImpl::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
365 void
ResetGroupCache()366 XmppRosterContactImpl::ResetGroupCache() {
367 group_count_ = -1;
368 group_index_returned_ = -1;
369 group_returned_ = NULL;
370 }
371
372 const Jid
jid() const373 XmppRosterContactImpl::jid() const {
374 return Jid(raw_xml_->Attr(QN_JID));
375 }
376
377 XmppReturnStatus
set_jid(const Jid & jid)378 XmppRosterContactImpl::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
391 const std::string
name() const392 XmppRosterContactImpl::name() const {
393 return raw_xml_->Attr(QN_NAME);
394 }
395
396 XmppReturnStatus
set_name(const std::string & name)397 XmppRosterContactImpl::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
409 XmppSubscriptionState
subscription_state() const410 XmppRosterContactImpl::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
424 size_t
GetGroupCount() const425 XmppRosterContactImpl::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
445 const std::string
GetGroup(size_t index) const446 XmppRosterContactImpl::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
479 XmppReturnStatus
AddGroup(const std::string & group)480 XmppRosterContactImpl::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
497 XmppReturnStatus
RemoveGroup(const std::string & group)498 XmppRosterContactImpl::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
513 bool
FindGroup(const std::string & group,XmlElement ** element,XmlChild ** child_before)514 XmppRosterContactImpl::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
537 const XmlElement*
raw_xml() const538 XmppRosterContactImpl::raw_xml() const {
539 if (!raw_xml_)
540 const_cast<XmppRosterContactImpl*>(this)->CreateRawXmlSkeleton();
541 return raw_xml_.get();
542 }
543
544 XmppReturnStatus
set_raw_xml(const XmlElement * xml)545 XmppRosterContactImpl::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
559 void
CreateRawXmlSkeleton()560 XmppRosterContactImpl::CreateRawXmlSkeleton() {
561 raw_xml_.reset(new XmlElement(QN_ROSTER_ITEM));
562 }
563
564 // XmppRosterModuleImpl --------------------------------------------------------
565 XmppRosterModule *
Create()566 XmppRosterModule::Create() {
567 return new XmppRosterModuleImpl();
568 }
569
XmppRosterModuleImpl()570 XmppRosterModuleImpl::XmppRosterModuleImpl() :
571 roster_handler_(NULL),
572 incoming_presence_map_(new JidPresenceVectorMap()),
573 incoming_presence_vector_(new PresenceVector()),
574 contacts_(new ContactVector()) {
575
576 }
577
~XmppRosterModuleImpl()578 XmppRosterModuleImpl::~XmppRosterModuleImpl() {
579 DeleteIncomingPresence();
580 DeleteContacts();
581 }
582
583 XmppReturnStatus
set_roster_handler(XmppRosterHandler * handler)584 XmppRosterModuleImpl::set_roster_handler(XmppRosterHandler * handler) {
585 roster_handler_ = handler;
586 return XMPP_RETURN_OK;
587 }
588
589 XmppRosterHandler*
roster_handler()590 XmppRosterModuleImpl::roster_handler() {
591 return roster_handler_;
592 }
593
594 XmppPresence*
outgoing_presence()595 XmppRosterModuleImpl::outgoing_presence() {
596 return &outgoing_presence_;
597 }
598
599 XmppReturnStatus
BroadcastPresence()600 XmppRosterModuleImpl::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
615 XmppReturnStatus
SendDirectedPresence(const XmppPresence * presence,const Jid & to_jid)616 XmppRosterModuleImpl::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
643 size_t
GetIncomingPresenceCount()644 XmppRosterModuleImpl::GetIncomingPresenceCount() {
645 return incoming_presence_vector_->size();
646 }
647
648 const XmppPresence*
GetIncomingPresence(size_t index)649 XmppRosterModuleImpl::GetIncomingPresence(size_t index) {
650 if (index >= incoming_presence_vector_->size())
651 return NULL;
652 return (*incoming_presence_vector_)[index];
653 }
654
655 size_t
GetIncomingPresenceForJidCount(const Jid & jid)656 XmppRosterModuleImpl::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
669 const XmppPresence*
GetIncomingPresenceForJid(const Jid & jid,size_t index)670 XmppRosterModuleImpl::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
685 XmppReturnStatus
RequestRosterUpdate()686 XmppRosterModuleImpl::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
697 size_t
GetRosterContactCount()698 XmppRosterModuleImpl::GetRosterContactCount() {
699 return contacts_->size();
700 }
701
702 const XmppRosterContact*
GetRosterContact(size_t index)703 XmppRosterModuleImpl::GetRosterContact(size_t index) {
704 if (index >= contacts_->size())
705 return NULL;
706 return (*contacts_)[index];
707 }
708
709 class RosterPredicate {
710 public:
RosterPredicate(const Jid & jid)711 explicit RosterPredicate(const Jid& jid) : jid_(jid) {
712 }
713
operator ()(XmppRosterContactImpl * & contact)714 bool operator() (XmppRosterContactImpl *& contact) {
715 return contact->jid() == jid_;
716 }
717
718 private:
719 Jid jid_;
720 };
721
722 const XmppRosterContact*
FindRosterContact(const Jid & jid)723 XmppRosterModuleImpl::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
735 XmppReturnStatus
RequestRosterChange(const XmppRosterContact * contact)736 XmppRosterModuleImpl::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
764 XmppReturnStatus
RequestRosterRemove(const Jid & jid)765 XmppRosterModuleImpl::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
782 XmppReturnStatus
RequestSubscription(const Jid & jid)783 XmppRosterModuleImpl::RequestSubscription(const Jid& jid) {
784 return SendSubscriptionRequest(jid, "subscribe");
785 }
786
787 XmppReturnStatus
CancelSubscription(const Jid & jid)788 XmppRosterModuleImpl::CancelSubscription(const Jid& jid) {
789 return SendSubscriptionRequest(jid, "unsubscribe");
790 }
791
792 XmppReturnStatus
ApproveSubscriber(const Jid & jid)793 XmppRosterModuleImpl::ApproveSubscriber(const Jid& jid) {
794 return SendSubscriptionRequest(jid, "subscribed");
795 }
796
797 XmppReturnStatus
CancelSubscriber(const Jid & jid)798 XmppRosterModuleImpl::CancelSubscriber(const Jid& jid) {
799 return SendSubscriptionRequest(jid, "unsubscribed");
800 }
801
802 void
IqResponse(XmppIqCookie,const XmlElement * stanza)803 XmppRosterModuleImpl::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
819 bool
HandleStanza(const XmlElement * stanza)820 XmppRosterModuleImpl::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
865 void
DeleteIncomingPresence()866 XmppRosterModuleImpl::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
894 void
DeleteContacts()895 XmppRosterModuleImpl::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
907 XmppReturnStatus
SendSubscriptionRequest(const Jid & jid,const std::string & type)908 XmppRosterModuleImpl::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
924 void
InternalSubscriptionRequest(const Jid & jid,const XmlElement * stanza,XmppSubscriptionRequestType request_type)925 XmppRosterModuleImpl::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
933 class PresencePredicate {
934 public:
PresencePredicate(const Jid & jid)935 explicit PresencePredicate(const Jid& jid) : jid_(jid) {
936 }
937
operator ()(XmppPresenceImpl * & contact)938 bool operator() (XmppPresenceImpl *& contact) {
939 return contact->jid() == jid_;
940 }
941
942 private:
943 Jid jid_;
944 };
945
946 void
InternalIncomingPresence(const Jid & jid,const XmlElement * stanza)947 XmppRosterModuleImpl::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
996 void
InternalIncomingPresenceError(const Jid & jid,const XmlElement * stanza)997 XmppRosterModuleImpl::InternalIncomingPresenceError(const Jid& jid,
998 const XmlElement* stanza) {
999 if (roster_handler_)
1000 roster_handler_->SubscriptionError(this, jid, stanza);
1001 }
1002
1003 void
InternalRosterItems(const XmlElement * stanza)1004 XmppRosterModuleImpl::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 }
1065