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