Home | History | Annotate | Download | only in xmpp
      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();
     73   virtual XmppChatroomState state() { return chatroom_state_; }
     74   virtual XmppReturnStatus SendMessage(const XmlElement& message);
     75 
     76   // XmppModule
     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:
    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 *
    152 XmppChatroomModule::Create() {
    153   return new XmppChatroomModuleImpl();
    154 }
    155 
    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 
    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
    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
    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*
    206 XmppChatroomModuleImpl::chatroom_handler() {
    207   return chatroom_handler_;
    208 }
    209 
    210 XmppReturnStatus
    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&
    225 XmppChatroomModuleImpl::chatroom_jid() const {
    226   return chatroom_jid_;
    227 }
    228 
    229  XmppReturnStatus
    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&
    239  XmppChatroomModuleImpl::nickname() const {
    240   return nickname_;
    241  }
    242 
    243 const Jid
    244 XmppChatroomModuleImpl::member_jid() const {
    245   return Jid(chatroom_jid_.node(), chatroom_jid_.domain(), nickname_);
    246 }
    247 
    248 
    249 bool
    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 
    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
    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
    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
    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
    370 XmppChatroomModuleImpl::GetChatroomMemberCount() {
    371   return chatroom_jid_members_.size();
    372 }
    373 
    374 XmppReturnStatus
    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
    381 XmppChatroomModuleImpl::subject() {
    382   return ""; //NYI
    383 }
    384 
    385 XmppReturnStatus
    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
    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
    460 XmppChatroomModuleImpl::FireExitStatus(XmppChatroomExitedStatus status) {
    461   if (chatroom_handler_)
    462     chatroom_handler_->ChatroomExitedStatus(this, status);
    463 }
    464 
    465 void
    466 XmppChatroomModuleImpl::FireMessageReceived(const XmlElement& message) {
    467   if (chatroom_handler_)
    468     chatroom_handler_->MessageReceived(this, message);
    469 }
    470 
    471 void
    472 XmppChatroomModuleImpl::FireMemberEntered(const XmppChatroomMember* entered_member) {
    473   if (chatroom_handler_)
    474     chatroom_handler_->MemberEntered(this, entered_member);
    475 }
    476 
    477 void
    478 XmppChatroomModuleImpl::FireMemberChanged(
    479     const XmppChatroomMember* changed_member) {
    480   if (chatroom_handler_)
    481     chatroom_handler_->MemberChanged(this, changed_member);
    482 }
    483 
    484 void
    485 XmppChatroomModuleImpl::FireMemberExited(const XmppChatroomMember* exited_member) {
    486   if (chatroom_handler_)
    487     chatroom_handler_->MemberExited(this, exited_member);
    488 }
    489 
    490 
    491 XmppReturnStatus
    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
    527 XmppChatroomModuleImpl::ClientChangeMyPresence(XmppChatroomState new_state) {
    528   return ChangePresence(new_state, NULL, false);
    529 }
    530 
    531 XmppReturnStatus
    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
    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
    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
    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
    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
    666 XmppChatroomMemberImpl::member_jid() const {
    667   return presence_->jid();
    668 }
    669 
    670 const Jid
    671 XmppChatroomMemberImpl::full_jid() const {
    672   return Jid("");
    673 }
    674 
    675 const std::string
    676 XmppChatroomMemberImpl::name() const {
    677   return member_jid().resource();
    678 }
    679 
    680 const XmppPresence*
    681 XmppChatroomMemberImpl::presence() const {
    682   return presence_.get();
    683 }
    684 
    685 
    686 // XmppChatroomMemberEnumeratorImpl --------------------------------------
    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*
    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
    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
    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
    739 XmppChatroomMemberEnumeratorImpl::IsValid() {
    740   return map_version_created_ == *map_version_;
    741 }
    742 
    743 bool
    744 XmppChatroomMemberEnumeratorImpl::IsBeforeBeginning() {
    745   return before_beginning_;
    746 }
    747 
    748 bool
    749 XmppChatroomMemberEnumeratorImpl::IsAfterEnd() {
    750   return (iterator_ == map_->end());
    751 }
    752 
    753 
    754 
    755 } // namespace buzz
    756