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