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/chatroommodule.h"
18 #include "webrtc/libjingle/xmpp/constants.h"
19 #include "webrtc/libjingle/xmpp/moduleimpl.h"
20 #include "webrtc/base/arraysize.h"
21 #include "webrtc/base/common.h"
22
23 namespace buzz {
24
25 // forward declarations
26 class XmppChatroomImpl;
27 class XmppChatroomMemberImpl;
28
29 //! Module that encapsulates multiple chatrooms.
30 //! Each chatroom is represented by an XmppChatroomImpl instance
31 class XmppChatroomModuleImpl : public XmppChatroomModule,
32 public XmppModuleImpl, public XmppIqHandler {
33 public:
34 IMPLEMENT_XMPPMODULE
35
36 // Creates a chatroom with specified Jid
37 XmppChatroomModuleImpl();
38 ~XmppChatroomModuleImpl();
39
40 // XmppChatroomModule
41 virtual XmppReturnStatus set_chatroom_handler(XmppChatroomHandler* handler);
42 virtual XmppChatroomHandler* chatroom_handler();
43 virtual XmppReturnStatus set_chatroom_jid(const Jid& chatroom_jid);
44 virtual const Jid& chatroom_jid() const;
45 virtual XmppReturnStatus set_nickname(const std::string& nickname);
46 virtual const std::string& nickname() const;
47 virtual const Jid member_jid() const;
48 virtual XmppReturnStatus RequestEnterChatroom(const std::string& password,
49 const std::string& client_version,
50 const std::string& locale);
51 virtual XmppReturnStatus RequestExitChatroom();
52 virtual XmppReturnStatus RequestConnectionStatusChange(
53 XmppPresenceConnectionStatus connection_status);
54 virtual size_t GetChatroomMemberCount();
55 virtual XmppReturnStatus CreateMemberEnumerator(XmppChatroomMemberEnumerator** enumerator);
56 virtual const std::string subject();
state()57 virtual XmppChatroomState state() { return chatroom_state_; }
58 virtual XmppReturnStatus SendMessage(const XmlElement& message);
59
60 // XmppModule
IqResponse(XmppIqCookie cookie,const XmlElement * pelStanza)61 virtual void IqResponse(XmppIqCookie cookie, const XmlElement * pelStanza) {RTC_UNUSED2(cookie, pelStanza);}
62 virtual bool HandleStanza(const XmlElement *);
63
64 private:
65 friend class XmppChatroomMemberEnumeratorImpl;
66
67 XmppReturnStatus ServerChangeMyPresence(const XmlElement& presence);
68 XmppReturnStatus ClientChangeMyPresence(XmppChatroomState new_state);
69 XmppReturnStatus ChangePresence(XmppChatroomState new_state, const XmlElement* presence, bool isServer);
70 XmppReturnStatus ServerChangedOtherPresence(const XmlElement& presence_element);
71 XmppChatroomEnteredStatus GetEnterFailureFromXml(const XmlElement* presence);
72 XmppChatroomExitedStatus GetExitFailureFromXml(const XmlElement* presence);
73
74 bool CheckEnterChatroomStateOk();
75
76 void FireEnteredStatus(const XmlElement* presence,
77 XmppChatroomEnteredStatus status);
78 void FireExitStatus(XmppChatroomExitedStatus status);
79 void FireMessageReceived(const XmlElement& message);
80 void FireMemberEntered(const XmppChatroomMember* entered_member);
81 void FireMemberChanged(const XmppChatroomMember* changed_member);
82 void FireMemberExited(const XmppChatroomMember* exited_member);
83
84
85 typedef std::map<Jid, XmppChatroomMemberImpl*> JidMemberMap;
86
87 XmppChatroomHandler* chatroom_handler_;
88 Jid chatroom_jid_;
89 std::string nickname_;
90 XmppChatroomState chatroom_state_;
91 JidMemberMap chatroom_jid_members_;
92 int chatroom_jid_members_version_;
93 };
94
95
96 class XmppChatroomMemberImpl : public XmppChatroomMember {
97 public:
~XmppChatroomMemberImpl()98 ~XmppChatroomMemberImpl() {}
99 XmppReturnStatus SetPresence(const XmppPresence* presence);
100
101 // XmppChatroomMember
102 const Jid member_jid() const;
103 const Jid full_jid() const;
104 const std::string name() const;
105 const XmppPresence* presence() const;
106
107 private:
108 rtc::scoped_ptr<XmppPresence> presence_;
109 };
110
111 class XmppChatroomMemberEnumeratorImpl :
112 public XmppChatroomMemberEnumerator {
113 public:
114 XmppChatroomMemberEnumeratorImpl(XmppChatroomModuleImpl::JidMemberMap* chatroom_jid_members,
115 int* map_version);
116
117 // XmppChatroomMemberEnumerator
118 virtual XmppChatroomMember* current();
119 virtual bool Next();
120 virtual bool Prev();
121 virtual bool IsValid();
122 virtual bool IsBeforeBeginning();
123 virtual bool IsAfterEnd();
124
125 private:
126 XmppChatroomModuleImpl::JidMemberMap* map_;
127 int map_version_created_;
128 int* map_version_;
129 XmppChatroomModuleImpl::JidMemberMap::iterator iterator_;
130 bool before_beginning_;
131 };
132
133
134 // XmppChatroomModuleImpl ------------------------------------------------
135 XmppChatroomModule *
Create()136 XmppChatroomModule::Create() {
137 return new XmppChatroomModuleImpl();
138 }
139
XmppChatroomModuleImpl()140 XmppChatroomModuleImpl::XmppChatroomModuleImpl() :
141 chatroom_handler_(NULL),
142 chatroom_jid_(STR_EMPTY),
143 chatroom_state_(XMPP_CHATROOM_STATE_NOT_IN_ROOM),
144 chatroom_jid_members_version_(0) {
145 }
146
~XmppChatroomModuleImpl()147 XmppChatroomModuleImpl::~XmppChatroomModuleImpl() {
148 JidMemberMap::iterator iterator = chatroom_jid_members_.begin();
149 while (iterator != chatroom_jid_members_.end()) {
150 delete iterator->second;
151 iterator++;
152 }
153 }
154
155
156 bool
HandleStanza(const XmlElement * stanza)157 XmppChatroomModuleImpl::HandleStanza(const XmlElement* stanza) {
158 ASSERT(engine() != NULL);
159
160 // we handle stanzas that are for one of our chatrooms
161 Jid from_jid = Jid(stanza->Attr(QN_FROM));
162 // see if it's one of our chatrooms
163 if (chatroom_jid_ != from_jid.BareJid()) {
164 return false; // not one of our chatrooms
165 } else {
166 // handle presence stanza
167 if (stanza->Name() == QN_PRESENCE) {
168 if (from_jid == member_jid()) {
169 ServerChangeMyPresence(*stanza);
170 } else {
171 ServerChangedOtherPresence(*stanza);
172 }
173 } else if (stanza->Name() == QN_MESSAGE) {
174 FireMessageReceived(*stanza);
175 }
176 return true;
177 }
178 }
179
180
181 XmppReturnStatus
set_chatroom_handler(XmppChatroomHandler * handler)182 XmppChatroomModuleImpl::set_chatroom_handler(XmppChatroomHandler* handler) {
183 // Calling with NULL removes the handler.
184 chatroom_handler_ = handler;
185 return XMPP_RETURN_OK;
186 }
187
188
189 XmppChatroomHandler*
chatroom_handler()190 XmppChatroomModuleImpl::chatroom_handler() {
191 return chatroom_handler_;
192 }
193
194 XmppReturnStatus
set_chatroom_jid(const Jid & chatroom_jid)195 XmppChatroomModuleImpl::set_chatroom_jid(const Jid& chatroom_jid) {
196 if (chatroom_state_ != XMPP_CHATROOM_STATE_NOT_IN_ROOM) {
197 return XMPP_RETURN_BADSTATE; // $TODO - this isn't a bad state, it's a bad call, diff error code?
198 }
199 if (chatroom_jid != chatroom_jid.BareJid()) {
200 // chatroom_jid must be a bare jid
201 return XMPP_RETURN_BADARGUMENT;
202 }
203
204 chatroom_jid_ = chatroom_jid;
205 return XMPP_RETURN_OK;
206 }
207
208 const Jid&
chatroom_jid() const209 XmppChatroomModuleImpl::chatroom_jid() const {
210 return chatroom_jid_;
211 }
212
213 XmppReturnStatus
set_nickname(const std::string & nickname)214 XmppChatroomModuleImpl::set_nickname(const std::string& nickname) {
215 if (chatroom_state_ != XMPP_CHATROOM_STATE_NOT_IN_ROOM) {
216 return XMPP_RETURN_BADSTATE; // $TODO - this isn't a bad state, it's a bad call, diff error code?
217 }
218 nickname_ = nickname;
219 return XMPP_RETURN_OK;
220 }
221
222 const std::string&
nickname() const223 XmppChatroomModuleImpl::nickname() const {
224 return nickname_;
225 }
226
227 const Jid
member_jid() const228 XmppChatroomModuleImpl::member_jid() const {
229 return Jid(chatroom_jid_.node(), chatroom_jid_.domain(), nickname_);
230 }
231
232
233 bool
CheckEnterChatroomStateOk()234 XmppChatroomModuleImpl::CheckEnterChatroomStateOk() {
235 if (chatroom_jid_.IsValid() == false) {
236 ASSERT(0);
237 return false;
238 }
239 if (nickname_ == STR_EMPTY) {
240 ASSERT(0);
241 return false;
242 }
243 return true;
244 }
245
GetAttrValueFor(XmppPresenceConnectionStatus connection_status)246 std::string GetAttrValueFor(XmppPresenceConnectionStatus connection_status) {
247 switch (connection_status) {
248 default:
249 case XMPP_CONNECTION_STATUS_UNKNOWN:
250 return "";
251 case XMPP_CONNECTION_STATUS_CONNECTING:
252 return STR_PSTN_CONFERENCE_STATUS_CONNECTING;
253 case XMPP_CONNECTION_STATUS_CONNECTED:
254 return STR_PSTN_CONFERENCE_STATUS_CONNECTED;
255 }
256 }
257
258 XmppReturnStatus
RequestEnterChatroom(const std::string & password,const std::string & client_version,const std::string & locale)259 XmppChatroomModuleImpl::RequestEnterChatroom(
260 const std::string& password,
261 const std::string& client_version,
262 const std::string& locale) {
263 RTC_UNUSED(password);
264 if (!engine())
265 return XMPP_RETURN_BADSTATE;
266
267 if (chatroom_state_ != XMPP_CHATROOM_STATE_NOT_IN_ROOM)
268 return XMPP_RETURN_BADSTATE; // $TODO - this isn't a bad state, it's a bad call, diff error code?
269
270 if (CheckEnterChatroomStateOk() == false) {
271 return XMPP_RETURN_BADSTATE;
272 }
273
274 // entering a chatroom is a presence request to the server
275 XmlElement element(QN_PRESENCE);
276 element.AddAttr(QN_TO, member_jid().Str());
277
278 XmlElement* muc_x = new XmlElement(QN_MUC_X);
279 element.AddElement(muc_x);
280
281 if (!client_version.empty()) {
282 XmlElement* client_version_element = new XmlElement(QN_CLIENT_VERSION,
283 false);
284 client_version_element->SetBodyText(client_version);
285 muc_x->AddElement(client_version_element);
286 }
287
288 if (!locale.empty()) {
289 XmlElement* locale_element = new XmlElement(QN_LOCALE, false);
290
291 locale_element->SetBodyText(locale);
292 muc_x->AddElement(locale_element);
293 }
294
295 XmppReturnStatus status = engine()->SendStanza(&element);
296 if (status == XMPP_RETURN_OK) {
297 return ClientChangeMyPresence(XMPP_CHATROOM_STATE_REQUESTED_ENTER);
298 }
299 return status;
300 }
301
302 XmppReturnStatus
RequestExitChatroom()303 XmppChatroomModuleImpl::RequestExitChatroom() {
304 if (!engine())
305 return XMPP_RETURN_BADSTATE;
306
307 // exiting a chatroom is a presence request to the server
308 XmlElement element(QN_PRESENCE);
309 element.AddAttr(QN_TO, member_jid().Str());
310 element.AddAttr(QN_TYPE, "unavailable");
311 XmppReturnStatus status = engine()->SendStanza(&element);
312 if (status == XMPP_RETURN_OK &&
313 chatroom_state_ == XMPP_CHATROOM_STATE_IN_ROOM) {
314 return ClientChangeMyPresence(XMPP_CHATROOM_STATE_REQUESTED_EXIT);
315 }
316 return status;
317 }
318
319 XmppReturnStatus
RequestConnectionStatusChange(XmppPresenceConnectionStatus connection_status)320 XmppChatroomModuleImpl::RequestConnectionStatusChange(
321 XmppPresenceConnectionStatus connection_status) {
322 if (!engine())
323 return XMPP_RETURN_BADSTATE;
324
325 if (chatroom_state_ != XMPP_CHATROOM_STATE_IN_ROOM) {
326 // $TODO - this isn't a bad state, it's a bad call, diff error code?
327 return XMPP_RETURN_BADSTATE;
328 }
329
330 if (CheckEnterChatroomStateOk() == false) {
331 return XMPP_RETURN_BADSTATE;
332 }
333
334 // entering a chatroom is a presence request to the server
335 XmlElement element(QN_PRESENCE);
336 element.AddAttr(QN_TO, member_jid().Str());
337 element.AddElement(new XmlElement(QN_MUC_X));
338 if (connection_status != XMPP_CONNECTION_STATUS_UNKNOWN) {
339 XmlElement* con_status_element =
340 new XmlElement(QN_GOOGLE_PSTN_CONFERENCE_STATUS);
341 con_status_element->AddAttr(QN_STATUS, GetAttrValueFor(connection_status));
342 element.AddElement(con_status_element);
343 }
344 XmppReturnStatus status = engine()->SendStanza(&element);
345
346 return status;
347 }
348
349 size_t
GetChatroomMemberCount()350 XmppChatroomModuleImpl::GetChatroomMemberCount() {
351 return chatroom_jid_members_.size();
352 }
353
354 XmppReturnStatus
CreateMemberEnumerator(XmppChatroomMemberEnumerator ** enumerator)355 XmppChatroomModuleImpl::CreateMemberEnumerator(XmppChatroomMemberEnumerator** enumerator) {
356 *enumerator = new XmppChatroomMemberEnumeratorImpl(&chatroom_jid_members_, &chatroom_jid_members_version_);
357 return XMPP_RETURN_OK;
358 }
359
360 const std::string
subject()361 XmppChatroomModuleImpl::subject() {
362 return ""; //NYI
363 }
364
365 XmppReturnStatus
SendMessage(const XmlElement & message)366 XmppChatroomModuleImpl::SendMessage(const XmlElement& message) {
367 XmppReturnStatus xmpp_status = XMPP_RETURN_OK;
368
369 // can only send a message if we're in the room
370 if (chatroom_state_ != XMPP_CHATROOM_STATE_IN_ROOM) {
371 return XMPP_RETURN_BADSTATE; // $TODO - this isn't a bad state, it's a bad call, diff error code?
372 }
373
374 if (message.Name() != QN_MESSAGE) {
375 IFR(XMPP_RETURN_BADARGUMENT);
376 }
377
378 const std::string& type = message.Attr(QN_TYPE);
379 if (type != "groupchat") {
380 IFR(XMPP_RETURN_BADARGUMENT);
381 }
382
383 if (message.HasAttr(QN_FROM)) {
384 IFR(XMPP_RETURN_BADARGUMENT);
385 }
386
387 if (message.Attr(QN_TO) != chatroom_jid_.Str()) {
388 IFR(XMPP_RETURN_BADARGUMENT);
389 }
390
391 IFR(engine()->SendStanza(&message));
392
393 return xmpp_status;
394 }
395
396 enum TransitionType {
397 TRANSITION_TYPE_NONE = 0,
398 TRANSITION_TYPE_ENTER_SUCCESS = 1,
399 TRANSITION_TYPE_ENTER_FAILURE = 2,
400 TRANSITION_TYPE_EXIT_VOLUNTARILY = 3,
401 TRANSITION_TYPE_EXIT_INVOLUNTARILY = 4,
402 };
403
404 struct StateTransitionDescription {
405 XmppChatroomState old_state;
406 XmppChatroomState new_state;
407 bool is_valid_server_transition;
408 bool is_valid_client_transition;
409 TransitionType transition_type;
410 };
411
412 StateTransitionDescription Transitions[] = {
413 { XMPP_CHATROOM_STATE_NOT_IN_ROOM, XMPP_CHATROOM_STATE_REQUESTED_ENTER, false, true, TRANSITION_TYPE_NONE, },
414 { XMPP_CHATROOM_STATE_NOT_IN_ROOM, XMPP_CHATROOM_STATE_IN_ROOM, false, false, TRANSITION_TYPE_ENTER_SUCCESS, },
415 { XMPP_CHATROOM_STATE_NOT_IN_ROOM, XMPP_CHATROOM_STATE_REQUESTED_EXIT, false, false, TRANSITION_TYPE_NONE, },
416 { XMPP_CHATROOM_STATE_REQUESTED_ENTER, XMPP_CHATROOM_STATE_NOT_IN_ROOM, true, false, TRANSITION_TYPE_ENTER_FAILURE, },
417 { XMPP_CHATROOM_STATE_REQUESTED_ENTER, XMPP_CHATROOM_STATE_IN_ROOM, true, false, TRANSITION_TYPE_ENTER_SUCCESS, },
418 { XMPP_CHATROOM_STATE_REQUESTED_ENTER, XMPP_CHATROOM_STATE_REQUESTED_EXIT, false, false, TRANSITION_TYPE_NONE, },
419 { XMPP_CHATROOM_STATE_IN_ROOM, XMPP_CHATROOM_STATE_NOT_IN_ROOM, true, false, TRANSITION_TYPE_EXIT_INVOLUNTARILY, },
420 { XMPP_CHATROOM_STATE_IN_ROOM, XMPP_CHATROOM_STATE_REQUESTED_ENTER, false, false, TRANSITION_TYPE_NONE, },
421 { XMPP_CHATROOM_STATE_IN_ROOM, XMPP_CHATROOM_STATE_REQUESTED_EXIT, false, true, TRANSITION_TYPE_NONE, },
422 { XMPP_CHATROOM_STATE_REQUESTED_EXIT, XMPP_CHATROOM_STATE_NOT_IN_ROOM, true, false, TRANSITION_TYPE_EXIT_VOLUNTARILY, },
423 { XMPP_CHATROOM_STATE_REQUESTED_EXIT, XMPP_CHATROOM_STATE_REQUESTED_ENTER, false, false, TRANSITION_TYPE_NONE, },
424 { XMPP_CHATROOM_STATE_REQUESTED_EXIT, XMPP_CHATROOM_STATE_IN_ROOM, false, false, TRANSITION_TYPE_NONE, },
425 };
426
427
428
429 void
FireEnteredStatus(const XmlElement * presence,XmppChatroomEnteredStatus status)430 XmppChatroomModuleImpl::FireEnteredStatus(const XmlElement* presence,
431 XmppChatroomEnteredStatus status) {
432 if (chatroom_handler_) {
433 rtc::scoped_ptr<XmppPresence> xmpp_presence(XmppPresence::Create());
434 xmpp_presence->set_raw_xml(presence);
435 chatroom_handler_->ChatroomEnteredStatus(this, xmpp_presence.get(), status);
436 }
437 }
438
439 void
FireExitStatus(XmppChatroomExitedStatus status)440 XmppChatroomModuleImpl::FireExitStatus(XmppChatroomExitedStatus status) {
441 if (chatroom_handler_)
442 chatroom_handler_->ChatroomExitedStatus(this, status);
443 }
444
445 void
FireMessageReceived(const XmlElement & message)446 XmppChatroomModuleImpl::FireMessageReceived(const XmlElement& message) {
447 if (chatroom_handler_)
448 chatroom_handler_->MessageReceived(this, message);
449 }
450
451 void
FireMemberEntered(const XmppChatroomMember * entered_member)452 XmppChatroomModuleImpl::FireMemberEntered(const XmppChatroomMember* entered_member) {
453 if (chatroom_handler_)
454 chatroom_handler_->MemberEntered(this, entered_member);
455 }
456
457 void
FireMemberChanged(const XmppChatroomMember * changed_member)458 XmppChatroomModuleImpl::FireMemberChanged(
459 const XmppChatroomMember* changed_member) {
460 if (chatroom_handler_)
461 chatroom_handler_->MemberChanged(this, changed_member);
462 }
463
464 void
FireMemberExited(const XmppChatroomMember * exited_member)465 XmppChatroomModuleImpl::FireMemberExited(const XmppChatroomMember* exited_member) {
466 if (chatroom_handler_)
467 chatroom_handler_->MemberExited(this, exited_member);
468 }
469
470
471 XmppReturnStatus
ServerChangedOtherPresence(const XmlElement & presence_element)472 XmppChatroomModuleImpl::ServerChangedOtherPresence(const XmlElement&
473 presence_element) {
474 XmppReturnStatus xmpp_status = XMPP_RETURN_OK;
475 rtc::scoped_ptr<XmppPresence> presence(XmppPresence::Create());
476 IFR(presence->set_raw_xml(&presence_element));
477
478 JidMemberMap::iterator pos = chatroom_jid_members_.find(presence->jid());
479
480 if (pos == chatroom_jid_members_.end()) {
481 if (presence->available() == XMPP_PRESENCE_AVAILABLE) {
482 XmppChatroomMemberImpl* member = new XmppChatroomMemberImpl();
483 member->SetPresence(presence.get());
484 chatroom_jid_members_.insert(std::make_pair(member->member_jid(), member));
485 chatroom_jid_members_version_++;
486 FireMemberEntered(member);
487 }
488 } else {
489 XmppChatroomMemberImpl* member = pos->second;
490 if (presence->available() == XMPP_PRESENCE_AVAILABLE) {
491 member->SetPresence(presence.get());
492 chatroom_jid_members_version_++;
493 FireMemberChanged(member);
494 }
495 else if (presence->available() == XMPP_PRESENCE_UNAVAILABLE) {
496 member->SetPresence(presence.get());
497 chatroom_jid_members_.erase(pos);
498 chatroom_jid_members_version_++;
499 FireMemberExited(member);
500 delete member;
501 }
502 }
503
504 return xmpp_status;
505 }
506
507 XmppReturnStatus
ClientChangeMyPresence(XmppChatroomState new_state)508 XmppChatroomModuleImpl::ClientChangeMyPresence(XmppChatroomState new_state) {
509 return ChangePresence(new_state, NULL, false);
510 }
511
512 XmppReturnStatus
ServerChangeMyPresence(const XmlElement & presence)513 XmppChatroomModuleImpl::ServerChangeMyPresence(const XmlElement& presence) {
514 XmppChatroomState new_state;
515
516 if (presence.HasAttr(QN_TYPE) == false) {
517 new_state = XMPP_CHATROOM_STATE_IN_ROOM;
518 } else {
519 new_state = XMPP_CHATROOM_STATE_NOT_IN_ROOM;
520 }
521 return ChangePresence(new_state, &presence, true);
522
523 }
524
525 XmppReturnStatus
ChangePresence(XmppChatroomState new_state,const XmlElement * presence,bool isServer)526 XmppChatroomModuleImpl::ChangePresence(XmppChatroomState new_state,
527 const XmlElement* presence,
528 bool isServer) {
529 RTC_UNUSED(presence);
530
531 XmppChatroomState old_state = chatroom_state_;
532
533 // do nothing if state hasn't changed
534 if (old_state == new_state)
535 return XMPP_RETURN_OK;
536
537 // find the right transition description
538 StateTransitionDescription* transition_desc = NULL;
539 for (size_t i = 0; i < arraysize(Transitions); i++) {
540 if (Transitions[i].old_state == old_state &&
541 Transitions[i].new_state == new_state) {
542 transition_desc = &Transitions[i];
543 break;
544 }
545 }
546
547 if (transition_desc == NULL) {
548 ASSERT(0);
549 return XMPP_RETURN_BADSTATE;
550 }
551
552 // we assert for any invalid transition states, and we'll
553 if (isServer) {
554 // $TODO send original stanza back to server and log an error?
555 // Disable the assert because of b/6133072
556 // ASSERT(transition_desc->is_valid_server_transition);
557 if (!transition_desc->is_valid_server_transition) {
558 return XMPP_RETURN_BADSTATE;
559 }
560 } else {
561 if (transition_desc->is_valid_client_transition == false) {
562 ASSERT(0);
563 return XMPP_RETURN_BADARGUMENT;
564 }
565 }
566
567 // set the new state and then fire any notifications to the handler
568 chatroom_state_ = new_state;
569
570 switch (transition_desc->transition_type) {
571 case TRANSITION_TYPE_ENTER_SUCCESS:
572 FireEnteredStatus(presence, XMPP_CHATROOM_ENTERED_SUCCESS);
573 break;
574 case TRANSITION_TYPE_ENTER_FAILURE:
575 FireEnteredStatus(presence, GetEnterFailureFromXml(presence));
576 break;
577 case TRANSITION_TYPE_EXIT_INVOLUNTARILY:
578 FireExitStatus(GetExitFailureFromXml(presence));
579 break;
580 case TRANSITION_TYPE_EXIT_VOLUNTARILY:
581 FireExitStatus(XMPP_CHATROOM_EXITED_REQUESTED);
582 break;
583 case TRANSITION_TYPE_NONE:
584 break;
585 }
586
587 return XMPP_RETURN_OK;
588 }
589
590 XmppChatroomEnteredStatus
GetEnterFailureFromXml(const XmlElement * presence)591 XmppChatroomModuleImpl::GetEnterFailureFromXml(const XmlElement* presence) {
592 XmppChatroomEnteredStatus status = XMPP_CHATROOM_ENTERED_FAILURE_UNSPECIFIED;
593 const XmlElement* error = presence->FirstNamed(QN_ERROR);
594 if (error != NULL && error->HasAttr(QN_CODE)) {
595 int code = atoi(error->Attr(QN_CODE).c_str());
596 switch (code) {
597 case 401: status = XMPP_CHATROOM_ENTERED_FAILURE_PASSWORD_REQUIRED; break;
598 case 403: {
599 status = XMPP_CHATROOM_ENTERED_FAILURE_MEMBER_BANNED;
600 if (error->FirstNamed(QN_GOOGLE_SESSION_BLOCKED)) {
601 status = XMPP_CHATROOM_ENTERED_FAILURE_MEMBER_BLOCKED;
602 } else if (error->FirstNamed(QN_GOOGLE_SESSION_BLOCKING)) {
603 status = XMPP_CHATROOM_ENTERED_FAILURE_MEMBER_BLOCKING;
604 }
605 break;
606 }
607 case 405: status = XMPP_CHATROOM_ENTERED_FAILURE_ROOM_LOCKED; break;
608 case 406: status = XMPP_CHATROOM_ENTERED_FAILURE_OUTDATED_CLIENT; break;
609 case 407: status = XMPP_CHATROOM_ENTERED_FAILURE_NOT_A_MEMBER; break;
610 case 409: status = XMPP_CHATROOM_ENTERED_FAILURE_NICKNAME_CONFLICT; break;
611 // http://xmpp.org/extensions/xep-0045.html#enter-maxusers
612 case 503: status = XMPP_CHATROOM_ENTERED_FAILURE_MAX_USERS; break;
613 }
614 }
615 return status;
616 }
617
618 XmppChatroomExitedStatus
GetExitFailureFromXml(const XmlElement * presence)619 XmppChatroomModuleImpl::GetExitFailureFromXml(const XmlElement* presence) {
620 XmppChatroomExitedStatus status = XMPP_CHATROOM_EXITED_UNSPECIFIED;
621 const XmlElement* muc_user = presence->FirstNamed(QN_MUC_USER_X);
622 if (muc_user != NULL) {
623 const XmlElement* user_status = muc_user->FirstNamed(QN_MUC_USER_STATUS);
624 if (user_status != NULL && user_status->HasAttr(QN_CODE)) {
625 int code = atoi(user_status->Attr(QN_CODE).c_str());
626 switch (code) {
627 case 307: status = XMPP_CHATROOM_EXITED_KICKED; break;
628 case 322: status = XMPP_CHATROOM_EXITED_NOT_A_MEMBER; break;
629 case 332: status = XMPP_CHATROOM_EXITED_SYSTEM_SHUTDOWN; break;
630 }
631 }
632 }
633 return status;
634 }
635
636 XmppReturnStatus
SetPresence(const XmppPresence * presence)637 XmppChatroomMemberImpl::SetPresence(const XmppPresence* presence) {
638 ASSERT(presence != NULL);
639
640 // copy presence
641 presence_.reset(XmppPresence::Create());
642 presence_->set_raw_xml(presence->raw_xml());
643 return XMPP_RETURN_OK;
644 }
645
646 const Jid
member_jid() const647 XmppChatroomMemberImpl::member_jid() const {
648 return presence_->jid();
649 }
650
651 const Jid
full_jid() const652 XmppChatroomMemberImpl::full_jid() const {
653 return Jid("");
654 }
655
656 const std::string
name() const657 XmppChatroomMemberImpl::name() const {
658 return member_jid().resource();
659 }
660
661 const XmppPresence*
presence() const662 XmppChatroomMemberImpl::presence() const {
663 return presence_.get();
664 }
665
666
667 // XmppChatroomMemberEnumeratorImpl --------------------------------------
XmppChatroomMemberEnumeratorImpl(XmppChatroomModuleImpl::JidMemberMap * map,int * map_version)668 XmppChatroomMemberEnumeratorImpl::XmppChatroomMemberEnumeratorImpl(
669 XmppChatroomModuleImpl::JidMemberMap* map, int* map_version) {
670 map_ = map;
671 map_version_ = map_version;
672 map_version_created_ = *map_version_;
673 iterator_ = map->begin();
674 before_beginning_ = true;
675 }
676
677 XmppChatroomMember*
current()678 XmppChatroomMemberEnumeratorImpl::current() {
679 if (IsValid() == false) {
680 return NULL;
681 } else if (IsBeforeBeginning() || IsAfterEnd()) {
682 return NULL;
683 } else {
684 return iterator_->second;
685 }
686 }
687
688 bool
Prev()689 XmppChatroomMemberEnumeratorImpl::Prev() {
690 if (IsValid() == false) {
691 return false;
692 } else if (IsBeforeBeginning()) {
693 return false;
694 } else if (iterator_ == map_->begin()) {
695 before_beginning_ = true;
696 return false;
697 } else {
698 iterator_--;
699 return current() != NULL;
700 }
701 }
702
703 bool
Next()704 XmppChatroomMemberEnumeratorImpl::Next() {
705 if (IsValid() == false) {
706 return false;
707 } else if (IsBeforeBeginning()) {
708 before_beginning_ = false;
709 iterator_ = map_->begin();
710 return current() != NULL;
711 } else if (IsAfterEnd()) {
712 return false;
713 } else {
714 iterator_++;
715 return current() != NULL;
716 }
717 }
718
719 bool
IsValid()720 XmppChatroomMemberEnumeratorImpl::IsValid() {
721 return map_version_created_ == *map_version_;
722 }
723
724 bool
IsBeforeBeginning()725 XmppChatroomMemberEnumeratorImpl::IsBeforeBeginning() {
726 return before_beginning_;
727 }
728
729 bool
IsAfterEnd()730 XmppChatroomMemberEnumeratorImpl::IsAfterEnd() {
731 return (iterator_ == map_->end());
732 }
733
734
735
736 } // namespace buzz
737