OLD | NEW |
| (Empty) |
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 | |
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 | |
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 | |
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 | |
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* | |
125 XmppPresence::Create() { | |
126 return new XmppPresenceImpl(); | |
127 } | |
128 | |
129 XmppPresenceImpl::XmppPresenceImpl() { | |
130 } | |
131 | |
132 const Jid | |
133 XmppPresenceImpl::jid() const { | |
134 if (!raw_xml_) | |
135 return Jid(); | |
136 | |
137 return Jid(raw_xml_->Attr(QN_FROM)); | |
138 } | |
139 | |
140 XmppPresenceAvailable | |
141 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 | |
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 | |
168 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 | |
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 | |
198 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 | |
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 | |
234 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 | |
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 | |
275 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 | |
296 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 | |
312 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* | |
325 XmppPresenceImpl::raw_xml() const { | |
326 if (!raw_xml_) | |
327 const_cast<XmppPresenceImpl*>(this)->CreateRawXmlSkeleton(); | |
328 return raw_xml_.get(); | |
329 } | |
330 | |
331 XmppReturnStatus | |
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 | |
342 XmppPresenceImpl::CreateRawXmlSkeleton() { | |
343 raw_xml_.reset(new XmlElement(QN_PRESENCE)); | |
344 } | |
345 | |
346 // XmppRosterContactImpl ------------------------------------------------------- | |
347 XmppRosterContact* | |
348 XmppRosterContact::Create() { | |
349 return new XmppRosterContactImpl(); | |
350 } | |
351 | |
352 XmppRosterContactImpl::XmppRosterContactImpl() { | |
353 ResetGroupCache(); | |
354 } | |
355 | |
356 void | |
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 | |
366 XmppRosterContactImpl::ResetGroupCache() { | |
367 group_count_ = -1; | |
368 group_index_returned_ = -1; | |
369 group_returned_ = NULL; | |
370 } | |
371 | |
372 const Jid | |
373 XmppRosterContactImpl::jid() const { | |
374 return Jid(raw_xml_->Attr(QN_JID)); | |
375 } | |
376 | |
377 XmppReturnStatus | |
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 | |
392 XmppRosterContactImpl::name() const { | |
393 return raw_xml_->Attr(QN_NAME); | |
394 } | |
395 | |
396 XmppReturnStatus | |
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 | |
410 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 | |
425 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 | |
446 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 | |
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 | |
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 | |
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* | |
538 XmppRosterContactImpl::raw_xml() const { | |
539 if (!raw_xml_) | |
540 const_cast<XmppRosterContactImpl*>(this)->CreateRawXmlSkeleton(); | |
541 return raw_xml_.get(); | |
542 } | |
543 | |
544 XmppReturnStatus | |
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 | |
560 XmppRosterContactImpl::CreateRawXmlSkeleton() { | |
561 raw_xml_.reset(new XmlElement(QN_ROSTER_ITEM)); | |
562 } | |
563 | |
564 // XmppRosterModuleImpl -------------------------------------------------------- | |
565 XmppRosterModule * | |
566 XmppRosterModule::Create() { | |
567 return new XmppRosterModuleImpl(); | |
568 } | |
569 | |
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 | |
578 XmppRosterModuleImpl::~XmppRosterModuleImpl() { | |
579 DeleteIncomingPresence(); | |
580 DeleteContacts(); | |
581 } | |
582 | |
583 XmppReturnStatus | |
584 XmppRosterModuleImpl::set_roster_handler(XmppRosterHandler * handler) { | |
585 roster_handler_ = handler; | |
586 return XMPP_RETURN_OK; | |
587 } | |
588 | |
589 XmppRosterHandler* | |
590 XmppRosterModuleImpl::roster_handler() { | |
591 return roster_handler_; | |
592 } | |
593 | |
594 XmppPresence* | |
595 XmppRosterModuleImpl::outgoing_presence() { | |
596 return &outgoing_presence_; | |
597 } | |
598 | |
599 XmppReturnStatus | |
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 | |
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 | |
644 XmppRosterModuleImpl::GetIncomingPresenceCount() { | |
645 return incoming_presence_vector_->size(); | |
646 } | |
647 | |
648 const XmppPresence* | |
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 | |
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* | |
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 | |
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 | |
698 XmppRosterModuleImpl::GetRosterContactCount() { | |
699 return contacts_->size(); | |
700 } | |
701 | |
702 const XmppRosterContact* | |
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: | |
711 explicit RosterPredicate(const Jid& jid) : jid_(jid) { | |
712 } | |
713 | |
714 bool operator() (XmppRosterContactImpl *& contact) { | |
715 return contact->jid() == jid_; | |
716 } | |
717 | |
718 private: | |
719 Jid jid_; | |
720 }; | |
721 | |
722 const XmppRosterContact* | |
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 | |
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 | |
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 | |
783 XmppRosterModuleImpl::RequestSubscription(const Jid& jid) { | |
784 return SendSubscriptionRequest(jid, "subscribe"); | |
785 } | |
786 | |
787 XmppReturnStatus | |
788 XmppRosterModuleImpl::CancelSubscription(const Jid& jid) { | |
789 return SendSubscriptionRequest(jid, "unsubscribe"); | |
790 } | |
791 | |
792 XmppReturnStatus | |
793 XmppRosterModuleImpl::ApproveSubscriber(const Jid& jid) { | |
794 return SendSubscriptionRequest(jid, "subscribed"); | |
795 } | |
796 | |
797 XmppReturnStatus | |
798 XmppRosterModuleImpl::CancelSubscriber(const Jid& jid) { | |
799 return SendSubscriptionRequest(jid, "unsubscribed"); | |
800 } | |
801 | |
802 void | |
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 | |
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 | |
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 | |
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 | |
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 | |
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: | |
935 explicit PresencePredicate(const Jid& jid) : jid_(jid) { | |
936 } | |
937 | |
938 bool operator() (XmppPresenceImpl *& contact) { | |
939 return contact->jid() == jid_; | |
940 } | |
941 | |
942 private: | |
943 Jid jid_; | |
944 }; | |
945 | |
946 void | |
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 | |
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 | |
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 } | |
OLD | NEW |