• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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