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