Home | History | Annotate | Download | only in xmpp
      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();
     57   virtual XmppChatroomState state() { return chatroom_state_; }
     58   virtual XmppReturnStatus SendMessage(const XmlElement& message);
     59 
     60   // XmppModule
     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:
     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 *
    136 XmppChatroomModule::Create() {
    137   return new XmppChatroomModuleImpl();
    138 }
    139 
    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 
    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
    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
    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*
    190 XmppChatroomModuleImpl::chatroom_handler() {
    191   return chatroom_handler_;
    192 }
    193 
    194 XmppReturnStatus
    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&
    209 XmppChatroomModuleImpl::chatroom_jid() const {
    210   return chatroom_jid_;
    211 }
    212 
    213  XmppReturnStatus
    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&
    223  XmppChatroomModuleImpl::nickname() const {
    224   return nickname_;
    225  }
    226 
    227 const Jid
    228 XmppChatroomModuleImpl::member_jid() const {
    229   return Jid(chatroom_jid_.node(), chatroom_jid_.domain(), nickname_);
    230 }
    231 
    232 
    233 bool
    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 
    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
    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
    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
    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
    350 XmppChatroomModuleImpl::GetChatroomMemberCount() {
    351   return chatroom_jid_members_.size();
    352 }
    353 
    354 XmppReturnStatus
    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
    361 XmppChatroomModuleImpl::subject() {
    362   return ""; //NYI
    363 }
    364 
    365 XmppReturnStatus
    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
    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
    440 XmppChatroomModuleImpl::FireExitStatus(XmppChatroomExitedStatus status) {
    441   if (chatroom_handler_)
    442     chatroom_handler_->ChatroomExitedStatus(this, status);
    443 }
    444 
    445 void
    446 XmppChatroomModuleImpl::FireMessageReceived(const XmlElement& message) {
    447   if (chatroom_handler_)
    448     chatroom_handler_->MessageReceived(this, message);
    449 }
    450 
    451 void
    452 XmppChatroomModuleImpl::FireMemberEntered(const XmppChatroomMember* entered_member) {
    453   if (chatroom_handler_)
    454     chatroom_handler_->MemberEntered(this, entered_member);
    455 }
    456 
    457 void
    458 XmppChatroomModuleImpl::FireMemberChanged(
    459     const XmppChatroomMember* changed_member) {
    460   if (chatroom_handler_)
    461     chatroom_handler_->MemberChanged(this, changed_member);
    462 }
    463 
    464 void
    465 XmppChatroomModuleImpl::FireMemberExited(const XmppChatroomMember* exited_member) {
    466   if (chatroom_handler_)
    467     chatroom_handler_->MemberExited(this, exited_member);
    468 }
    469 
    470 
    471 XmppReturnStatus
    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
    508 XmppChatroomModuleImpl::ClientChangeMyPresence(XmppChatroomState new_state) {
    509   return ChangePresence(new_state, NULL, false);
    510 }
    511 
    512 XmppReturnStatus
    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
    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
    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
    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
    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
    647 XmppChatroomMemberImpl::member_jid() const {
    648   return presence_->jid();
    649 }
    650 
    651 const Jid
    652 XmppChatroomMemberImpl::full_jid() const {
    653   return Jid("");
    654 }
    655 
    656 const std::string
    657 XmppChatroomMemberImpl::name() const {
    658   return member_jid().resource();
    659 }
    660 
    661 const XmppPresence*
    662 XmppChatroomMemberImpl::presence() const {
    663   return presence_.get();
    664 }
    665 
    666 
    667 // XmppChatroomMemberEnumeratorImpl --------------------------------------
    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*
    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
    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
    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
    720 XmppChatroomMemberEnumeratorImpl::IsValid() {
    721   return map_version_created_ == *map_version_;
    722 }
    723 
    724 bool
    725 XmppChatroomMemberEnumeratorImpl::IsBeforeBeginning() {
    726   return before_beginning_;
    727 }
    728 
    729 bool
    730 XmppChatroomMemberEnumeratorImpl::IsAfterEnd() {
    731   return (iterator_ == map_->end());
    732 }
    733 
    734 
    735 
    736 } // namespace buzz
    737