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 // 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
61 static bool
PresenceShowToString(XmppPresenceShow show,const char ** output)62 PresenceShowToString(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
85 static bool
StringToSubscriptionState(const std::string & subscription,const std::string & ask,XmppSubscriptionState * state)86 StringToSubscriptionState(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
123 static bool
StringToSubscriptionRequestType(const std::string & string,XmppSubscriptionRequestType * type)124 StringToSubscriptionRequestType(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 ------------------------------------------------------
141 XmppPresence*
Create()142 XmppPresence::Create() {
143 return new XmppPresenceImpl();
144 }
145
XmppPresenceImpl()146 XmppPresenceImpl::XmppPresenceImpl() {
147 }
148
149 const Jid
jid() const150 XmppPresenceImpl::jid() const {
151 if (!raw_xml_)
152 return Jid();
153
154 return Jid(raw_xml_->Attr(QN_FROM));
155 }
156
157 XmppPresenceAvailable
available() const158 XmppPresenceImpl::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
170 XmppReturnStatus
set_available(XmppPresenceAvailable available)171 XmppPresenceImpl::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
184 XmppPresenceShow
presence_show() const185 XmppPresenceImpl::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
194 XmppReturnStatus
set_presence_show(XmppPresenceShow show)195 XmppPresenceImpl::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
214 int
priority() const215 XmppPresenceImpl::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
230 XmppReturnStatus
set_priority(int priority)231 XmppPresenceImpl::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
250 const std::string
status() const251 XmppPresenceImpl::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
276 XmppReturnStatus
set_status(const std::string & status)277 XmppPresenceImpl::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
291 XmppPresenceConnectionStatus
connection_status() const292 XmppPresenceImpl::connection_status() const {
293 if (!raw_xml_)
294 return XMPP_CONNECTION_STATUS_UNKNOWN;
295
296 XmlElement* con = raw_xml_->FirstNamed(QN_GOOGLE_PSTN_CONFERENCE_STATUS);
297 if (con) {
298 std::string status = con->Attr(QN_ATTR_STATUS);
299 if (status == STR_PSTN_CONFERENCE_STATUS_CONNECTING)
300 return XMPP_CONNECTION_STATUS_CONNECTING;
301 else if (status == STR_PSTN_CONFERENCE_STATUS_CONNECTED)
302 return XMPP_CONNECTION_STATUS_CONNECTED;
303 else if (status == STR_PSTN_CONFERENCE_STATUS_JOINING)
304 return XMPP_CONNECTION_STATUS_JOINING;
305 else if (status == STR_PSTN_CONFERENCE_STATUS_HANGUP)
306 return XMPP_CONNECTION_STATUS_HANGUP;
307 }
308
309 return XMPP_CONNECTION_STATUS_CONNECTED;
310 }
311
312 const std::string
google_user_id() const313 XmppPresenceImpl::google_user_id() const {
314 if (!raw_xml_)
315 return std::string();
316
317 XmlElement* muc_user_x = raw_xml_->FirstNamed(QN_MUC_USER_X);
318 if (muc_user_x) {
319 XmlElement* muc_user_item = muc_user_x->FirstNamed(QN_MUC_USER_ITEM);
320 if (muc_user_item) {
321 return muc_user_item->Attr(QN_GOOGLE_USER_ID);
322 }
323 }
324
325 return std::string();
326 }
327
328 const std::string
nickname() const329 XmppPresenceImpl::nickname() const {
330 if (!raw_xml_)
331 return std::string();
332
333 XmlElement* nickname = raw_xml_->FirstNamed(QN_NICKNAME);
334 if (nickname) {
335 return nickname->BodyText();
336 }
337
338 return std::string();
339 }
340
341 const XmlElement*
raw_xml() const342 XmppPresenceImpl::raw_xml() const {
343 if (!raw_xml_)
344 const_cast<XmppPresenceImpl*>(this)->CreateRawXmlSkeleton();
345 return raw_xml_.get();
346 }
347
348 XmppReturnStatus
set_raw_xml(const XmlElement * xml)349 XmppPresenceImpl::set_raw_xml(const XmlElement * xml) {
350 if (!xml ||
351 xml->Name() != QN_PRESENCE)
352 return XMPP_RETURN_BADARGUMENT;
353
354 const std::string& type = xml->Attr(QN_TYPE);
355 if (type != STR_EMPTY && type != "unavailable")
356 return XMPP_RETURN_BADARGUMENT;
357
358 raw_xml_.reset(new XmlElement(*xml));
359 return XMPP_RETURN_OK;
360 }
361
362 void
CreateRawXmlSkeleton()363 XmppPresenceImpl::CreateRawXmlSkeleton() {
364 raw_xml_.reset(new XmlElement(QN_PRESENCE));
365 }
366
367 // XmppRosterContactImpl -------------------------------------------------------
368 XmppRosterContact*
Create()369 XmppRosterContact::Create() {
370 return new XmppRosterContactImpl();
371 }
372
XmppRosterContactImpl()373 XmppRosterContactImpl::XmppRosterContactImpl() {
374 ResetGroupCache();
375 }
376
377 void
SetXmlFromWire(const XmlElement * xml)378 XmppRosterContactImpl::SetXmlFromWire(const XmlElement* xml) {
379 ResetGroupCache();
380 if (xml)
381 raw_xml_.reset(new XmlElement(*xml));
382 else
383 raw_xml_.reset(NULL);
384 }
385
386 void
ResetGroupCache()387 XmppRosterContactImpl::ResetGroupCache() {
388 group_count_ = -1;
389 group_index_returned_ = -1;
390 group_returned_ = NULL;
391 }
392
393 const Jid
jid() const394 XmppRosterContactImpl::jid() const {
395 return Jid(raw_xml_->Attr(QN_JID));
396 }
397
398 XmppReturnStatus
set_jid(const Jid & jid)399 XmppRosterContactImpl::set_jid(const Jid& jid)
400 {
401 if (!raw_xml_)
402 CreateRawXmlSkeleton();
403
404 if (!jid.IsValid())
405 return XMPP_RETURN_BADARGUMENT;
406
407 raw_xml_->SetAttr(QN_JID, jid.Str());
408
409 return XMPP_RETURN_OK;
410 }
411
412 const std::string
name() const413 XmppRosterContactImpl::name() const {
414 return raw_xml_->Attr(QN_NAME);
415 }
416
417 XmppReturnStatus
set_name(const std::string & name)418 XmppRosterContactImpl::set_name(const std::string& name) {
419 if (!raw_xml_)
420 CreateRawXmlSkeleton();
421
422 if (name == STR_EMPTY)
423 raw_xml_->ClearAttr(QN_NAME);
424 else
425 raw_xml_->SetAttr(QN_NAME, name);
426
427 return XMPP_RETURN_OK;
428 }
429
430 XmppSubscriptionState
subscription_state() const431 XmppRosterContactImpl::subscription_state() const {
432 if (!raw_xml_)
433 return XMPP_SUBSCRIPTION_NONE;
434
435 XmppSubscriptionState state = XMPP_SUBSCRIPTION_NONE;
436
437 if (StringToSubscriptionState(raw_xml_->Attr(QN_SUBSCRIPTION),
438 raw_xml_->Attr(QN_ASK),
439 &state))
440 return state;
441
442 return XMPP_SUBSCRIPTION_NONE;
443 }
444
445 size_t
GetGroupCount() const446 XmppRosterContactImpl::GetGroupCount() const {
447 if (!raw_xml_)
448 return 0;
449
450 if (-1 == group_count_) {
451 XmlElement *group_element = raw_xml_->FirstNamed(QN_ROSTER_GROUP);
452 int group_count = 0;
453 while(group_element) {
454 group_count++;
455 group_element = group_element->NextNamed(QN_ROSTER_GROUP);
456 }
457
458 ASSERT(group_count > 0); // protect the cast
459 XmppRosterContactImpl * me = const_cast<XmppRosterContactImpl*>(this);
460 me->group_count_ = group_count;
461 }
462
463 return group_count_;
464 }
465
466 const std::string
GetGroup(size_t index) const467 XmppRosterContactImpl::GetGroup(size_t index) const {
468 if (index >= GetGroupCount())
469 return STR_EMPTY;
470
471 // We cache the last group index and element that we returned. This way
472 // going through the groups in order is order n and not n^2. This could be
473 // enhanced if necessary by starting at the cached value if the index asked
474 // is after the cached one.
475 if (group_index_returned_ >= 0 &&
476 index == static_cast<size_t>(group_index_returned_) + 1)
477 {
478 XmppRosterContactImpl * me = const_cast<XmppRosterContactImpl*>(this);
479 me->group_returned_ = group_returned_->NextNamed(QN_ROSTER_GROUP);
480 ASSERT(group_returned_ != NULL);
481 me->group_index_returned_++;
482 } else if (group_index_returned_ < 0 ||
483 static_cast<size_t>(group_index_returned_) != index) {
484 XmlElement * group_element = raw_xml_->FirstNamed(QN_ROSTER_GROUP);
485 size_t group_index = 0;
486 while(group_index < index) {
487 ASSERT(group_element != NULL);
488 group_index++;
489 group_element = group_element->NextNamed(QN_ROSTER_GROUP);
490 }
491
492 XmppRosterContactImpl * me = const_cast<XmppRosterContactImpl*>(this);
493 me->group_index_returned_ = static_cast<int>(group_index);
494 me->group_returned_ = group_element;
495 }
496
497 return group_returned_->BodyText();
498 }
499
500 XmppReturnStatus
AddGroup(const std::string & group)501 XmppRosterContactImpl::AddGroup(const std::string& group) {
502 if (group == STR_EMPTY)
503 return XMPP_RETURN_BADARGUMENT;
504
505 if (!raw_xml_)
506 CreateRawXmlSkeleton();
507
508 if (FindGroup(group, NULL, NULL))
509 return XMPP_RETURN_OK;
510
511 raw_xml_->AddElement(new XmlElement(QN_ROSTER_GROUP));
512 raw_xml_->AddText(group, 1);
513 ++group_count_;
514
515 return XMPP_RETURN_OK;
516 }
517
518 XmppReturnStatus
RemoveGroup(const std::string & group)519 XmppRosterContactImpl::RemoveGroup(const std::string& group) {
520 if (group == STR_EMPTY)
521 return XMPP_RETURN_BADARGUMENT;
522
523 if (!raw_xml_)
524 return XMPP_RETURN_OK;
525
526 XmlChild * child_before;
527 if (FindGroup(group, NULL, &child_before)) {
528 raw_xml_->RemoveChildAfter(child_before);
529 ResetGroupCache();
530 }
531 return XMPP_RETURN_OK;
532 }
533
534 bool
FindGroup(const std::string & group,XmlElement ** element,XmlChild ** child_before)535 XmppRosterContactImpl::FindGroup(const std::string& group,
536 XmlElement** element,
537 XmlChild** child_before) {
538 XmlChild * prev_child = NULL;
539 XmlChild * next_child;
540 XmlChild * child;
541 for (child = raw_xml_->FirstChild(); child; child = next_child) {
542 next_child = child->NextChild();
543 if (!child->IsText() &&
544 child->AsElement()->Name() == QN_ROSTER_GROUP &&
545 child->AsElement()->BodyText() == group) {
546 if (element)
547 *element = child->AsElement();
548 if (child_before)
549 *child_before = prev_child;
550 return true;
551 }
552 prev_child = child;
553 }
554
555 return false;
556 }
557
558 const XmlElement*
raw_xml() const559 XmppRosterContactImpl::raw_xml() const {
560 if (!raw_xml_)
561 const_cast<XmppRosterContactImpl*>(this)->CreateRawXmlSkeleton();
562 return raw_xml_.get();
563 }
564
565 XmppReturnStatus
set_raw_xml(const XmlElement * xml)566 XmppRosterContactImpl::set_raw_xml(const XmlElement* xml) {
567 if (!xml ||
568 xml->Name() != QN_ROSTER_ITEM ||
569 xml->HasAttr(QN_SUBSCRIPTION) ||
570 xml->HasAttr(QN_ASK))
571 return XMPP_RETURN_BADARGUMENT;
572
573 ResetGroupCache();
574
575 raw_xml_.reset(new XmlElement(*xml));
576
577 return XMPP_RETURN_OK;
578 }
579
580 void
CreateRawXmlSkeleton()581 XmppRosterContactImpl::CreateRawXmlSkeleton() {
582 raw_xml_.reset(new XmlElement(QN_ROSTER_ITEM));
583 }
584
585 // XmppRosterModuleImpl --------------------------------------------------------
586 XmppRosterModule *
Create()587 XmppRosterModule::Create() {
588 return new XmppRosterModuleImpl();
589 }
590
XmppRosterModuleImpl()591 XmppRosterModuleImpl::XmppRosterModuleImpl() :
592 roster_handler_(NULL),
593 incoming_presence_map_(new JidPresenceVectorMap()),
594 incoming_presence_vector_(new PresenceVector()),
595 contacts_(new ContactVector()) {
596
597 }
598
~XmppRosterModuleImpl()599 XmppRosterModuleImpl::~XmppRosterModuleImpl() {
600 DeleteIncomingPresence();
601 DeleteContacts();
602 }
603
604 XmppReturnStatus
set_roster_handler(XmppRosterHandler * handler)605 XmppRosterModuleImpl::set_roster_handler(XmppRosterHandler * handler) {
606 roster_handler_ = handler;
607 return XMPP_RETURN_OK;
608 }
609
610 XmppRosterHandler*
roster_handler()611 XmppRosterModuleImpl::roster_handler() {
612 return roster_handler_;
613 }
614
615 XmppPresence*
outgoing_presence()616 XmppRosterModuleImpl::outgoing_presence() {
617 return &outgoing_presence_;
618 }
619
620 XmppReturnStatus
BroadcastPresence()621 XmppRosterModuleImpl::BroadcastPresence() {
622 // Scrub the outgoing presence
623 const XmlElement* element = outgoing_presence_.raw_xml();
624
625 ASSERT(!element->HasAttr(QN_TO) &&
626 !element->HasAttr(QN_FROM) &&
627 (element->Attr(QN_TYPE) == STR_EMPTY ||
628 element->Attr(QN_TYPE) == "unavailable"));
629
630 if (!engine())
631 return XMPP_RETURN_BADSTATE;
632
633 return engine()->SendStanza(element);
634 }
635
636 XmppReturnStatus
SendDirectedPresence(const XmppPresence * presence,const Jid & to_jid)637 XmppRosterModuleImpl::SendDirectedPresence(const XmppPresence* presence,
638 const Jid& to_jid) {
639 if (!presence)
640 return XMPP_RETURN_BADARGUMENT;
641
642 if (!engine())
643 return XMPP_RETURN_BADSTATE;
644
645 XmlElement element(*(presence->raw_xml()));
646
647 if (element.Name() != QN_PRESENCE ||
648 element.HasAttr(QN_TO) ||
649 element.HasAttr(QN_FROM))
650 return XMPP_RETURN_BADARGUMENT;
651
652 if (element.HasAttr(QN_TYPE)) {
653 if (element.Attr(QN_TYPE) != STR_EMPTY &&
654 element.Attr(QN_TYPE) != "unavailable") {
655 return XMPP_RETURN_BADARGUMENT;
656 }
657 }
658
659 element.SetAttr(QN_TO, to_jid.Str());
660
661 return engine()->SendStanza(&element);
662 }
663
664 size_t
GetIncomingPresenceCount()665 XmppRosterModuleImpl::GetIncomingPresenceCount() {
666 return incoming_presence_vector_->size();
667 }
668
669 const XmppPresence*
GetIncomingPresence(size_t index)670 XmppRosterModuleImpl::GetIncomingPresence(size_t index) {
671 if (index >= incoming_presence_vector_->size())
672 return NULL;
673 return (*incoming_presence_vector_)[index];
674 }
675
676 size_t
GetIncomingPresenceForJidCount(const Jid & jid)677 XmppRosterModuleImpl::GetIncomingPresenceForJidCount(const Jid& jid)
678 {
679 // find the vector in the map
680 JidPresenceVectorMap::iterator pos;
681 pos = incoming_presence_map_->find(jid);
682 if (pos == incoming_presence_map_->end())
683 return 0;
684
685 ASSERT(pos->second != NULL);
686
687 return pos->second->size();
688 }
689
690 const XmppPresence*
GetIncomingPresenceForJid(const Jid & jid,size_t index)691 XmppRosterModuleImpl::GetIncomingPresenceForJid(const Jid& jid,
692 size_t index) {
693 JidPresenceVectorMap::iterator pos;
694 pos = incoming_presence_map_->find(jid);
695 if (pos == incoming_presence_map_->end())
696 return NULL;
697
698 ASSERT(pos->second != NULL);
699
700 if (index >= pos->second->size())
701 return NULL;
702
703 return (*pos->second)[index];
704 }
705
706 XmppReturnStatus
RequestRosterUpdate()707 XmppRosterModuleImpl::RequestRosterUpdate() {
708 if (!engine())
709 return XMPP_RETURN_BADSTATE;
710
711 XmlElement roster_get(QN_IQ);
712 roster_get.AddAttr(QN_TYPE, "get");
713 roster_get.AddAttr(QN_ID, engine()->NextId());
714 roster_get.AddElement(new XmlElement(QN_ROSTER_QUERY, true));
715 return engine()->SendIq(&roster_get, this, NULL);
716 }
717
718 size_t
GetRosterContactCount()719 XmppRosterModuleImpl::GetRosterContactCount() {
720 return contacts_->size();
721 }
722
723 const XmppRosterContact*
GetRosterContact(size_t index)724 XmppRosterModuleImpl::GetRosterContact(size_t index) {
725 if (index >= contacts_->size())
726 return NULL;
727 return (*contacts_)[index];
728 }
729
730 class RosterPredicate {
731 public:
RosterPredicate(const Jid & jid)732 explicit RosterPredicate(const Jid& jid) : jid_(jid) {
733 }
734
operator ()(XmppRosterContactImpl * & contact)735 bool operator() (XmppRosterContactImpl *& contact) {
736 return contact->jid() == jid_;
737 }
738
739 private:
740 Jid jid_;
741 };
742
743 const XmppRosterContact*
FindRosterContact(const Jid & jid)744 XmppRosterModuleImpl::FindRosterContact(const Jid& jid) {
745 ContactVector::iterator pos;
746
747 pos = std::find_if(contacts_->begin(),
748 contacts_->end(),
749 RosterPredicate(jid));
750 if (pos == contacts_->end())
751 return NULL;
752
753 return *pos;
754 }
755
756 XmppReturnStatus
RequestRosterChange(const XmppRosterContact * contact)757 XmppRosterModuleImpl::RequestRosterChange(
758 const XmppRosterContact* contact) {
759 if (!contact)
760 return XMPP_RETURN_BADARGUMENT;
761
762 Jid jid = contact->jid();
763
764 if (!jid.IsValid())
765 return XMPP_RETURN_BADARGUMENT;
766
767 if (!engine())
768 return XMPP_RETURN_BADSTATE;
769
770 const XmlElement* contact_xml = contact->raw_xml();
771 if (contact_xml->Name() != QN_ROSTER_ITEM ||
772 contact_xml->HasAttr(QN_SUBSCRIPTION) ||
773 contact_xml->HasAttr(QN_ASK))
774 return XMPP_RETURN_BADARGUMENT;
775
776 XmlElement roster_add(QN_IQ);
777 roster_add.AddAttr(QN_TYPE, "set");
778 roster_add.AddAttr(QN_ID, engine()->NextId());
779 roster_add.AddElement(new XmlElement(QN_ROSTER_QUERY, true));
780 roster_add.AddElement(new XmlElement(*contact_xml), 1);
781
782 return engine()->SendIq(&roster_add, this, NULL);
783 }
784
785 XmppReturnStatus
RequestRosterRemove(const Jid & jid)786 XmppRosterModuleImpl::RequestRosterRemove(const Jid& jid) {
787 if (!jid.IsValid())
788 return XMPP_RETURN_BADARGUMENT;
789
790 if (!engine())
791 return XMPP_RETURN_BADSTATE;
792
793 XmlElement roster_add(QN_IQ);
794 roster_add.AddAttr(QN_TYPE, "set");
795 roster_add.AddAttr(QN_ID, engine()->NextId());
796 roster_add.AddElement(new XmlElement(QN_ROSTER_QUERY, true));
797 roster_add.AddAttr(QN_JID, jid.Str(), 1);
798 roster_add.AddAttr(QN_SUBSCRIPTION, "remove", 1);
799
800 return engine()->SendIq(&roster_add, this, NULL);
801 }
802
803 XmppReturnStatus
RequestSubscription(const Jid & jid)804 XmppRosterModuleImpl::RequestSubscription(const Jid& jid) {
805 return SendSubscriptionRequest(jid, "subscribe");
806 }
807
808 XmppReturnStatus
CancelSubscription(const Jid & jid)809 XmppRosterModuleImpl::CancelSubscription(const Jid& jid) {
810 return SendSubscriptionRequest(jid, "unsubscribe");
811 }
812
813 XmppReturnStatus
ApproveSubscriber(const Jid & jid)814 XmppRosterModuleImpl::ApproveSubscriber(const Jid& jid) {
815 return SendSubscriptionRequest(jid, "subscribed");
816 }
817
818 XmppReturnStatus
CancelSubscriber(const Jid & jid)819 XmppRosterModuleImpl::CancelSubscriber(const Jid& jid) {
820 return SendSubscriptionRequest(jid, "unsubscribed");
821 }
822
823 void
IqResponse(XmppIqCookie,const XmlElement * stanza)824 XmppRosterModuleImpl::IqResponse(XmppIqCookie, const XmlElement * stanza) {
825 // The only real Iq response that we expect to recieve are initial roster
826 // population
827 if (stanza->Attr(QN_TYPE) == "error")
828 {
829 if (roster_handler_)
830 roster_handler_->RosterError(this, stanza);
831
832 return;
833 }
834
835 ASSERT(stanza->Attr(QN_TYPE) == "result");
836
837 InternalRosterItems(stanza);
838 }
839
840 bool
HandleStanza(const XmlElement * stanza)841 XmppRosterModuleImpl::HandleStanza(const XmlElement * stanza)
842 {
843 ASSERT(engine() != NULL);
844
845 // There are two types of stanzas that we care about: presence and roster push
846 // Iqs
847 if (stanza->Name() == QN_PRESENCE) {
848 const std::string& jid_string = stanza->Attr(QN_FROM);
849 Jid jid(jid_string);
850
851 if (!jid.IsValid())
852 return false; // if the Jid isn't valid, don't process
853
854 const std::string& type = stanza->Attr(QN_TYPE);
855 XmppSubscriptionRequestType request_type;
856 if (StringToSubscriptionRequestType(type, &request_type))
857 InternalSubscriptionRequest(jid, stanza, request_type);
858 else if (type == "unavailable" || type == STR_EMPTY)
859 InternalIncomingPresence(jid, stanza);
860 else if (type == "error")
861 InternalIncomingPresenceError(jid, stanza);
862 else
863 return false;
864
865 return true;
866 } else if (stanza->Name() == QN_IQ) {
867 const XmlElement * roster_query = stanza->FirstNamed(QN_ROSTER_QUERY);
868 if (!roster_query || stanza->Attr(QN_TYPE) != "set")
869 return false;
870
871 InternalRosterItems(stanza);
872
873 // respond to the IQ
874 XmlElement result(QN_IQ);
875 result.AddAttr(QN_TYPE, "result");
876 result.AddAttr(QN_TO, stanza->Attr(QN_FROM));
877 result.AddAttr(QN_ID, stanza->Attr(QN_ID));
878
879 engine()->SendStanza(&result);
880 return true;
881 }
882
883 return false;
884 }
885
886 void
DeleteIncomingPresence()887 XmppRosterModuleImpl::DeleteIncomingPresence() {
888 // Clear out the vector of all presence notifications
889 {
890 PresenceVector::iterator pos;
891 for (pos = incoming_presence_vector_->begin();
892 pos < incoming_presence_vector_->end();
893 ++pos) {
894 XmppPresenceImpl * presence = *pos;
895 *pos = NULL;
896 delete presence;
897 }
898 incoming_presence_vector_->clear();
899 }
900
901 // Clear out all of the small presence vectors per Jid
902 {
903 JidPresenceVectorMap::iterator pos;
904 for (pos = incoming_presence_map_->begin();
905 pos != incoming_presence_map_->end();
906 ++pos) {
907 PresenceVector* presence_vector = pos->second;
908 pos->second = NULL;
909 delete presence_vector;
910 }
911 incoming_presence_map_->clear();
912 }
913 }
914
915 void
DeleteContacts()916 XmppRosterModuleImpl::DeleteContacts() {
917 ContactVector::iterator pos;
918 for (pos = contacts_->begin();
919 pos < contacts_->end();
920 ++pos) {
921 XmppRosterContact* contact = *pos;
922 *pos = NULL;
923 delete contact;
924 }
925 contacts_->clear();
926 }
927
928 XmppReturnStatus
SendSubscriptionRequest(const Jid & jid,const std::string & type)929 XmppRosterModuleImpl::SendSubscriptionRequest(const Jid& jid,
930 const std::string& type) {
931 if (!jid.IsValid())
932 return XMPP_RETURN_BADARGUMENT;
933
934 if (!engine())
935 return XMPP_RETURN_BADSTATE;
936
937 XmlElement presence_request(QN_PRESENCE);
938 presence_request.AddAttr(QN_TO, jid.Str());
939 presence_request.AddAttr(QN_TYPE, type);
940
941 return engine()->SendStanza(&presence_request);
942 }
943
944
945 void
InternalSubscriptionRequest(const Jid & jid,const XmlElement * stanza,XmppSubscriptionRequestType request_type)946 XmppRosterModuleImpl::InternalSubscriptionRequest(const Jid& jid,
947 const XmlElement* stanza,
948 XmppSubscriptionRequestType
949 request_type) {
950 if (roster_handler_)
951 roster_handler_->SubscriptionRequest(this, jid, request_type, stanza);
952 }
953
954 class PresencePredicate {
955 public:
PresencePredicate(const Jid & jid)956 explicit PresencePredicate(const Jid& jid) : jid_(jid) {
957 }
958
operator ()(XmppPresenceImpl * & contact)959 bool operator() (XmppPresenceImpl *& contact) {
960 return contact->jid() == jid_;
961 }
962
963 private:
964 Jid jid_;
965 };
966
967 void
InternalIncomingPresence(const Jid & jid,const XmlElement * stanza)968 XmppRosterModuleImpl::InternalIncomingPresence(const Jid& jid,
969 const XmlElement* stanza) {
970 bool added = false;
971 Jid bare_jid = jid.BareJid();
972
973 // First add the presence to the map
974 JidPresenceVectorMap::iterator pos;
975 pos = incoming_presence_map_->find(jid.BareJid());
976 if (pos == incoming_presence_map_->end()) {
977 // Insert a new entry into the map. Get the position of this new entry
978 pos = (incoming_presence_map_->insert(
979 std::make_pair(bare_jid, new PresenceVector()))).first;
980 }
981
982 PresenceVector * presence_vector = pos->second;
983 ASSERT(presence_vector != NULL);
984
985 // Try to find this jid in the bare jid bucket
986 PresenceVector::iterator presence_pos;
987 XmppPresenceImpl* presence;
988 presence_pos = std::find_if(presence_vector->begin(),
989 presence_vector->end(),
990 PresencePredicate(jid));
991
992 // Update/add it to the bucket
993 if (presence_pos == presence_vector->end()) {
994 presence = new XmppPresenceImpl();
995 if (XMPP_RETURN_OK == presence->set_raw_xml(stanza)) {
996 added = true;
997 presence_vector->push_back(presence);
998 } else {
999 delete presence;
1000 presence = NULL;
1001 }
1002 } else {
1003 presence = *presence_pos;
1004 presence->set_raw_xml(stanza);
1005 }
1006
1007 // now add to the comprehensive vector
1008 if (added)
1009 incoming_presence_vector_->push_back(presence);
1010
1011 // Call back to the user with the changed presence information
1012 if (roster_handler_)
1013 roster_handler_->IncomingPresenceChanged(this, presence);
1014 }
1015
1016
1017 void
InternalIncomingPresenceError(const Jid & jid,const XmlElement * stanza)1018 XmppRosterModuleImpl::InternalIncomingPresenceError(const Jid& jid,
1019 const XmlElement* stanza) {
1020 if (roster_handler_)
1021 roster_handler_->SubscriptionError(this, jid, stanza);
1022 }
1023
1024 void
InternalRosterItems(const XmlElement * stanza)1025 XmppRosterModuleImpl::InternalRosterItems(const XmlElement* stanza) {
1026 const XmlElement* result_data = stanza->FirstNamed(QN_ROSTER_QUERY);
1027 if (!result_data)
1028 return; // unknown stuff in result!
1029
1030 bool all_new = contacts_->empty();
1031
1032 for (const XmlElement* roster_item = result_data->FirstNamed(QN_ROSTER_ITEM);
1033 roster_item;
1034 roster_item = roster_item->NextNamed(QN_ROSTER_ITEM))
1035 {
1036 const std::string& jid_string = roster_item->Attr(QN_JID);
1037 Jid jid(jid_string);
1038 if (!jid.IsValid())
1039 continue;
1040
1041 // This algorithm is N^2 on the number of incoming contacts after the
1042 // initial load. There is no way to do this faster without allowing
1043 // duplicates, introducing more data structures or write a custom data
1044 // structure. We'll see if this becomes a perf problem and fix it if it
1045 // does.
1046 ContactVector::iterator pos = contacts_->end();
1047
1048 if (!all_new) {
1049 pos = std::find_if(contacts_->begin(),
1050 contacts_->end(),
1051 RosterPredicate(jid));
1052 }
1053
1054 if (pos != contacts_->end()) { // Update/remove a current contact
1055 if (roster_item->Attr(QN_SUBSCRIPTION) == "remove") {
1056 XmppRosterContact* contact = *pos;
1057 contacts_->erase(pos);
1058 if (roster_handler_)
1059 roster_handler_->ContactRemoved(this, contact,
1060 std::distance(contacts_->begin(), pos));
1061 delete contact;
1062 } else {
1063 XmppRosterContact* old_contact = *pos;
1064 *pos = new XmppRosterContactImpl();
1065 (*pos)->SetXmlFromWire(roster_item);
1066 if (roster_handler_)
1067 roster_handler_->ContactChanged(this, old_contact,
1068 std::distance(contacts_->begin(), pos));
1069 delete old_contact;
1070 }
1071 } else { // Add a new contact
1072 XmppRosterContactImpl* contact = new XmppRosterContactImpl();
1073 contact->SetXmlFromWire(roster_item);
1074 contacts_->push_back(contact);
1075 if (roster_handler_ && !all_new)
1076 roster_handler_->ContactsAdded(this, contacts_->size() - 1, 1);
1077 }
1078 }
1079
1080 // Send a consolidated update if all contacts are new
1081 if (roster_handler_ && all_new)
1082 roster_handler_->ContactsAdded(this, 0, contacts_->size());
1083 }
1084
1085 }
1086