 f875f15afb
			
		
	
	f875f15afb
	
	
	
		
			
			git-svn-id: http://webrtc.googlecode.com/svn/trunk@5897 4adac7df-926f-26a2-2b94-8c16560cd09d
		
			
				
	
	
		
			753 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			753 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|  * libjingle
 | |
|  * Copyright 2004--2005, Google Inc.
 | |
|  *
 | |
|  * Redistribution and use in source and binary forms, with or without
 | |
|  * modification, are permitted provided that the following conditions are met:
 | |
|  *
 | |
|  *  1. Redistributions of source code must retain the above copyright notice,
 | |
|  *     this list of conditions and the following disclaimer.
 | |
|  *  2. Redistributions in binary form must reproduce the above copyright notice,
 | |
|  *     this list of conditions and the following disclaimer in the documentation
 | |
|  *     and/or other materials provided with the distribution.
 | |
|  *  3. The name of the author may not be used to endorse or promote products
 | |
|  *     derived from this software without specific prior written permission.
 | |
|  *
 | |
|  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
 | |
|  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 | |
|  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
 | |
|  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | |
|  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 | |
|  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 | |
|  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 | |
|  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 | |
|  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 | |
|  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | |
|  */
 | |
| 
 | |
| #include <vector>
 | |
| #include <string>
 | |
| #include <map>
 | |
| #include <algorithm>
 | |
| #include <sstream>
 | |
| #include <iostream>
 | |
| #include "talk/base/common.h"
 | |
| #include "talk/xmpp/constants.h"
 | |
| #include "talk/xmpp/moduleimpl.h"
 | |
| #include "talk/xmpp/chatroommodule.h"
 | |
| 
 | |
| namespace buzz {
 | |
| 
 | |
| // forward declarations
 | |
| class XmppChatroomImpl;
 | |
| class XmppChatroomMemberImpl;
 | |
| 
 | |
| //! Module that encapsulates multiple chatrooms.
 | |
| //! Each chatroom is represented by an XmppChatroomImpl instance
 | |
| class XmppChatroomModuleImpl : public XmppChatroomModule,
 | |
|   public XmppModuleImpl, public XmppIqHandler {
 | |
| public:
 | |
|   IMPLEMENT_XMPPMODULE
 | |
| 
 | |
|    // Creates a chatroom with specified Jid
 | |
|   XmppChatroomModuleImpl();
 | |
|   ~XmppChatroomModuleImpl();
 | |
| 
 | |
|   // XmppChatroomModule
 | |
|   virtual XmppReturnStatus set_chatroom_handler(XmppChatroomHandler* handler);
 | |
|   virtual XmppChatroomHandler* chatroom_handler();
 | |
|   virtual XmppReturnStatus set_chatroom_jid(const Jid& chatroom_jid);
 | |
|   virtual const Jid& chatroom_jid() const;
 | |
|   virtual XmppReturnStatus set_nickname(const std::string& nickname);
 | |
|   virtual const std::string& nickname() const;
 | |
|   virtual const Jid member_jid() const;
 | |
|   virtual XmppReturnStatus RequestEnterChatroom(const std::string& password,
 | |
|       const std::string& client_version,
 | |
|       const std::string& locale);
 | |
|   virtual XmppReturnStatus RequestExitChatroom();
 | |
|   virtual XmppReturnStatus RequestConnectionStatusChange(
 | |
|       XmppPresenceConnectionStatus connection_status);
 | |
|   virtual size_t GetChatroomMemberCount();
 | |
|   virtual XmppReturnStatus CreateMemberEnumerator(XmppChatroomMemberEnumerator** enumerator);
 | |
|   virtual const std::string subject();
 | |
|   virtual XmppChatroomState state() { return chatroom_state_; }
 | |
|   virtual XmppReturnStatus SendMessage(const XmlElement& message);
 | |
| 
 | |
|   // XmppModule
 | |
|   virtual void IqResponse(XmppIqCookie cookie, const XmlElement * pelStanza) {UNUSED2(cookie, pelStanza);}
 | |
|   virtual bool HandleStanza(const XmlElement *);
 | |
| 
 | |
| private:
 | |
|   friend class XmppChatroomMemberEnumeratorImpl;
 | |
| 
 | |
|   XmppReturnStatus ServerChangeMyPresence(const XmlElement& presence);
 | |
|   XmppReturnStatus ClientChangeMyPresence(XmppChatroomState new_state);
 | |
|   XmppReturnStatus ChangePresence(XmppChatroomState new_state, const XmlElement* presence, bool isServer);
 | |
|   XmppReturnStatus ServerChangedOtherPresence(const XmlElement& presence_element);
 | |
|   XmppChatroomEnteredStatus GetEnterFailureFromXml(const XmlElement* presence);
 | |
|   XmppChatroomExitedStatus GetExitFailureFromXml(const XmlElement* presence);
 | |
| 
 | |
|   bool CheckEnterChatroomStateOk();
 | |
| 
 | |
|   void FireEnteredStatus(const XmlElement* presence,
 | |
|                          XmppChatroomEnteredStatus status);
 | |
|   void FireExitStatus(XmppChatroomExitedStatus status);
 | |
|   void FireMessageReceived(const XmlElement& message);
 | |
|   void FireMemberEntered(const XmppChatroomMember* entered_member);
 | |
|   void FireMemberChanged(const XmppChatroomMember* changed_member);
 | |
|   void FireMemberExited(const XmppChatroomMember* exited_member);
 | |
| 
 | |
| 
 | |
|   typedef std::map<Jid, XmppChatroomMemberImpl*> JidMemberMap;
 | |
| 
 | |
|   XmppChatroomHandler*              chatroom_handler_;
 | |
|   Jid                               chatroom_jid_;
 | |
|   std::string                       nickname_;
 | |
|   XmppChatroomState                 chatroom_state_;
 | |
|   JidMemberMap                      chatroom_jid_members_;
 | |
|   int                               chatroom_jid_members_version_;
 | |
| };
 | |
| 
 | |
| 
 | |
| class XmppChatroomMemberImpl : public XmppChatroomMember {
 | |
| public:
 | |
|   ~XmppChatroomMemberImpl() {}
 | |
|   XmppReturnStatus SetPresence(const XmppPresence* presence);
 | |
| 
 | |
|   // XmppChatroomMember
 | |
|   const Jid member_jid() const;
 | |
|   const Jid full_jid() const;
 | |
|   const std::string name() const;
 | |
|   const XmppPresence* presence() const;
 | |
| 
 | |
| private:
 | |
|   talk_base::scoped_ptr<XmppPresence>  presence_;
 | |
| };
 | |
| 
 | |
| class XmppChatroomMemberEnumeratorImpl :
 | |
|         public XmppChatroomMemberEnumerator  {
 | |
| public:
 | |
|   XmppChatroomMemberEnumeratorImpl(XmppChatroomModuleImpl::JidMemberMap* chatroom_jid_members,
 | |
|                                         int* map_version);
 | |
| 
 | |
|   // XmppChatroomMemberEnumerator
 | |
|   virtual XmppChatroomMember* current();
 | |
|   virtual bool Next();
 | |
|   virtual bool Prev();
 | |
|   virtual bool IsValid();
 | |
|   virtual bool IsBeforeBeginning();
 | |
|   virtual bool IsAfterEnd();
 | |
| 
 | |
| private:
 | |
|   XmppChatroomModuleImpl::JidMemberMap*           map_;
 | |
|   int                                             map_version_created_;
 | |
|   int*                                            map_version_;
 | |
|   XmppChatroomModuleImpl::JidMemberMap::iterator  iterator_;
 | |
|   bool                                            before_beginning_;
 | |
| };
 | |
| 
 | |
| 
 | |
| // XmppChatroomModuleImpl ------------------------------------------------
 | |
| XmppChatroomModule *
 | |
| XmppChatroomModule::Create() {
 | |
|   return new XmppChatroomModuleImpl();
 | |
| }
 | |
| 
 | |
| XmppChatroomModuleImpl::XmppChatroomModuleImpl() :
 | |
|   chatroom_handler_(NULL),
 | |
|   chatroom_jid_(STR_EMPTY),
 | |
|   chatroom_state_(XMPP_CHATROOM_STATE_NOT_IN_ROOM),
 | |
|   chatroom_jid_members_version_(0) {
 | |
| }
 | |
| 
 | |
| XmppChatroomModuleImpl::~XmppChatroomModuleImpl() {
 | |
|   JidMemberMap::iterator iterator = chatroom_jid_members_.begin();
 | |
|   while (iterator != chatroom_jid_members_.end()) {
 | |
|     delete iterator->second;
 | |
|     iterator++;
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| bool
 | |
| XmppChatroomModuleImpl::HandleStanza(const XmlElement* stanza) {
 | |
|   ASSERT(engine() != NULL);
 | |
| 
 | |
|   // we handle stanzas that are for one of our chatrooms
 | |
|   Jid from_jid = Jid(stanza->Attr(QN_FROM));
 | |
|   // see if it's one of our chatrooms
 | |
|   if (chatroom_jid_ != from_jid.BareJid()) {
 | |
|     return false; // not one of our chatrooms
 | |
|   } else {
 | |
|     // handle presence stanza
 | |
|     if (stanza->Name() == QN_PRESENCE) {
 | |
|       if (from_jid == member_jid()) {
 | |
|         ServerChangeMyPresence(*stanza);
 | |
|       } else {
 | |
|         ServerChangedOtherPresence(*stanza);
 | |
|       }
 | |
|     } else if (stanza->Name() == QN_MESSAGE) {
 | |
|       FireMessageReceived(*stanza);
 | |
|     }
 | |
|     return true;
 | |
|   }
 | |
| }
 | |
| 
 | |
| 
 | |
| XmppReturnStatus
 | |
| XmppChatroomModuleImpl::set_chatroom_handler(XmppChatroomHandler* handler) {
 | |
|   // Calling with NULL removes the handler.
 | |
|   chatroom_handler_ = handler;
 | |
|   return XMPP_RETURN_OK;
 | |
| }
 | |
| 
 | |
| 
 | |
| XmppChatroomHandler*
 | |
| XmppChatroomModuleImpl::chatroom_handler() {
 | |
|   return chatroom_handler_;
 | |
| }
 | |
| 
 | |
| XmppReturnStatus
 | |
| XmppChatroomModuleImpl::set_chatroom_jid(const Jid& chatroom_jid) {
 | |
|   if (chatroom_state_ != XMPP_CHATROOM_STATE_NOT_IN_ROOM) {
 | |
|     return XMPP_RETURN_BADSTATE; // $TODO - this isn't a bad state, it's a bad call,  diff error code?
 | |
|   }
 | |
|   if (chatroom_jid != chatroom_jid.BareJid()) {
 | |
|     // chatroom_jid must be a bare jid
 | |
|     return XMPP_RETURN_BADARGUMENT;
 | |
|   }
 | |
| 
 | |
|   chatroom_jid_ = chatroom_jid;
 | |
|   return XMPP_RETURN_OK;
 | |
| }
 | |
| 
 | |
| const Jid&
 | |
| XmppChatroomModuleImpl::chatroom_jid() const {
 | |
|   return chatroom_jid_;
 | |
| }
 | |
| 
 | |
|  XmppReturnStatus
 | |
|  XmppChatroomModuleImpl::set_nickname(const std::string& nickname) {
 | |
|   if (chatroom_state_ != XMPP_CHATROOM_STATE_NOT_IN_ROOM) {
 | |
|     return XMPP_RETURN_BADSTATE; // $TODO - this isn't a bad state, it's a bad call,  diff error code?
 | |
|   }
 | |
|   nickname_ = nickname;
 | |
|   return XMPP_RETURN_OK;
 | |
|  }
 | |
| 
 | |
|  const std::string&
 | |
|  XmppChatroomModuleImpl::nickname() const {
 | |
|   return nickname_;
 | |
|  }
 | |
| 
 | |
| const Jid
 | |
| XmppChatroomModuleImpl::member_jid() const {
 | |
|   return Jid(chatroom_jid_.node(), chatroom_jid_.domain(), nickname_);
 | |
| }
 | |
| 
 | |
| 
 | |
| bool
 | |
| XmppChatroomModuleImpl::CheckEnterChatroomStateOk() {
 | |
|   if (chatroom_jid_.IsValid() == false) {
 | |
|     ASSERT(0);
 | |
|     return false;
 | |
|   }
 | |
|   if (nickname_ == STR_EMPTY) {
 | |
|     ASSERT(0);
 | |
|     return false;
 | |
|   }
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| std::string GetAttrValueFor(XmppPresenceConnectionStatus connection_status) {
 | |
|   switch (connection_status) {
 | |
|     default:
 | |
|     case XMPP_CONNECTION_STATUS_UNKNOWN:
 | |
|       return "";
 | |
|     case XMPP_CONNECTION_STATUS_CONNECTING:
 | |
|       return STR_PSTN_CONFERENCE_STATUS_CONNECTING;
 | |
|     case XMPP_CONNECTION_STATUS_CONNECTED:
 | |
|       return STR_PSTN_CONFERENCE_STATUS_CONNECTED;
 | |
|   }
 | |
| }
 | |
| 
 | |
| XmppReturnStatus
 | |
| XmppChatroomModuleImpl::RequestEnterChatroom(
 | |
|     const std::string& password,
 | |
|     const std::string& client_version,
 | |
|     const std::string& locale) {
 | |
|   UNUSED(password);
 | |
|   if (!engine())
 | |
|     return XMPP_RETURN_BADSTATE;
 | |
| 
 | |
|   if (chatroom_state_ != XMPP_CHATROOM_STATE_NOT_IN_ROOM)
 | |
|     return XMPP_RETURN_BADSTATE; // $TODO - this isn't a bad state, it's a bad call,  diff error code?
 | |
| 
 | |
|   if (CheckEnterChatroomStateOk() == false) {
 | |
|     return XMPP_RETURN_BADSTATE;
 | |
|   }
 | |
| 
 | |
|   // entering a chatroom is a presence request to the server
 | |
|   XmlElement element(QN_PRESENCE);
 | |
|   element.AddAttr(QN_TO, member_jid().Str());
 | |
| 
 | |
|   XmlElement* muc_x = new XmlElement(QN_MUC_X);
 | |
|   element.AddElement(muc_x);
 | |
| 
 | |
|   if (!client_version.empty()) {
 | |
|     XmlElement* client_version_element = new XmlElement(QN_CLIENT_VERSION,
 | |
|                                                         false);
 | |
|     client_version_element->SetBodyText(client_version);
 | |
|     muc_x->AddElement(client_version_element);
 | |
|   }
 | |
| 
 | |
|   if (!locale.empty()) {
 | |
|     XmlElement* locale_element = new XmlElement(QN_LOCALE, false);
 | |
| 
 | |
|     locale_element->SetBodyText(locale);
 | |
|     muc_x->AddElement(locale_element);
 | |
|   }
 | |
| 
 | |
|   XmppReturnStatus status = engine()->SendStanza(&element);
 | |
|   if (status == XMPP_RETURN_OK) {
 | |
|     return ClientChangeMyPresence(XMPP_CHATROOM_STATE_REQUESTED_ENTER);
 | |
|   }
 | |
|   return status;
 | |
| }
 | |
| 
 | |
| XmppReturnStatus
 | |
| XmppChatroomModuleImpl::RequestExitChatroom() {
 | |
|   if (!engine())
 | |
|     return XMPP_RETURN_BADSTATE;
 | |
| 
 | |
|   // exiting a chatroom is a presence request to the server
 | |
|   XmlElement element(QN_PRESENCE);
 | |
|   element.AddAttr(QN_TO, member_jid().Str());
 | |
|   element.AddAttr(QN_TYPE, "unavailable");
 | |
|   XmppReturnStatus status = engine()->SendStanza(&element);
 | |
|   if (status == XMPP_RETURN_OK &&
 | |
|       chatroom_state_ == XMPP_CHATROOM_STATE_IN_ROOM) {
 | |
|     return ClientChangeMyPresence(XMPP_CHATROOM_STATE_REQUESTED_EXIT);
 | |
|   }
 | |
|   return status;
 | |
| }
 | |
| 
 | |
| XmppReturnStatus
 | |
| XmppChatroomModuleImpl::RequestConnectionStatusChange(
 | |
|     XmppPresenceConnectionStatus connection_status) {
 | |
|   if (!engine())
 | |
|     return XMPP_RETURN_BADSTATE;
 | |
| 
 | |
|   if (chatroom_state_ != XMPP_CHATROOM_STATE_IN_ROOM) {
 | |
|     // $TODO - this isn't a bad state, it's a bad call,  diff error code?
 | |
|     return XMPP_RETURN_BADSTATE;
 | |
|   }
 | |
| 
 | |
|   if (CheckEnterChatroomStateOk() == false) {
 | |
|     return XMPP_RETURN_BADSTATE;
 | |
|   }
 | |
| 
 | |
|   // entering a chatroom is a presence request to the server
 | |
|   XmlElement element(QN_PRESENCE);
 | |
|   element.AddAttr(QN_TO, member_jid().Str());
 | |
|   element.AddElement(new XmlElement(QN_MUC_X));
 | |
|   if (connection_status != XMPP_CONNECTION_STATUS_UNKNOWN) {
 | |
|     XmlElement* con_status_element =
 | |
|         new XmlElement(QN_GOOGLE_PSTN_CONFERENCE_STATUS);
 | |
|     con_status_element->AddAttr(QN_STATUS, GetAttrValueFor(connection_status));
 | |
|     element.AddElement(con_status_element);
 | |
|   }
 | |
|   XmppReturnStatus status = engine()->SendStanza(&element);
 | |
| 
 | |
|   return status;
 | |
| }
 | |
| 
 | |
| size_t
 | |
| XmppChatroomModuleImpl::GetChatroomMemberCount() {
 | |
|   return chatroom_jid_members_.size();
 | |
| }
 | |
| 
 | |
| XmppReturnStatus
 | |
| XmppChatroomModuleImpl::CreateMemberEnumerator(XmppChatroomMemberEnumerator** enumerator) {
 | |
|   *enumerator = new XmppChatroomMemberEnumeratorImpl(&chatroom_jid_members_, &chatroom_jid_members_version_);
 | |
|   return XMPP_RETURN_OK;
 | |
| }
 | |
| 
 | |
| const std::string
 | |
| XmppChatroomModuleImpl::subject() {
 | |
|   return ""; //NYI
 | |
| }
 | |
| 
 | |
| XmppReturnStatus
 | |
| XmppChatroomModuleImpl::SendMessage(const XmlElement& message) {
 | |
|   XmppReturnStatus xmpp_status = XMPP_RETURN_OK;
 | |
| 
 | |
|   // can only send a message if we're in the room
 | |
|   if (chatroom_state_ != XMPP_CHATROOM_STATE_IN_ROOM) {
 | |
|     return XMPP_RETURN_BADSTATE; // $TODO - this isn't a bad state, it's a bad call,  diff error code?
 | |
|   }
 | |
| 
 | |
|   if (message.Name() != QN_MESSAGE) {
 | |
|     IFR(XMPP_RETURN_BADARGUMENT);
 | |
|   }
 | |
| 
 | |
|   const std::string& type = message.Attr(QN_TYPE);
 | |
|   if (type != "groupchat") {
 | |
|     IFR(XMPP_RETURN_BADARGUMENT);
 | |
|   }
 | |
| 
 | |
|   if (message.HasAttr(QN_FROM)) {
 | |
|     IFR(XMPP_RETURN_BADARGUMENT);
 | |
|   }
 | |
| 
 | |
|   if (message.Attr(QN_TO) != chatroom_jid_.Str()) {
 | |
|     IFR(XMPP_RETURN_BADARGUMENT);
 | |
|   }
 | |
| 
 | |
|   IFR(engine()->SendStanza(&message));
 | |
| 
 | |
|   return xmpp_status;
 | |
| }
 | |
| 
 | |
| enum TransitionType {
 | |
|   TRANSITION_TYPE_NONE                 = 0,
 | |
|   TRANSITION_TYPE_ENTER_SUCCESS        = 1,
 | |
|   TRANSITION_TYPE_ENTER_FAILURE        = 2,
 | |
|   TRANSITION_TYPE_EXIT_VOLUNTARILY     = 3,
 | |
|   TRANSITION_TYPE_EXIT_INVOLUNTARILY   = 4,
 | |
| };
 | |
| 
 | |
| struct StateTransitionDescription {
 | |
|   XmppChatroomState old_state;
 | |
|   XmppChatroomState new_state;
 | |
|   bool              is_valid_server_transition;
 | |
|   bool              is_valid_client_transition;
 | |
|   TransitionType    transition_type;
 | |
| };
 | |
| 
 | |
| StateTransitionDescription Transitions[] = {
 | |
|   { XMPP_CHATROOM_STATE_NOT_IN_ROOM,     XMPP_CHATROOM_STATE_REQUESTED_ENTER, false, true,  TRANSITION_TYPE_NONE, },
 | |
|   { XMPP_CHATROOM_STATE_NOT_IN_ROOM,     XMPP_CHATROOM_STATE_IN_ROOM,         false, false, TRANSITION_TYPE_ENTER_SUCCESS, },
 | |
|   { XMPP_CHATROOM_STATE_NOT_IN_ROOM,     XMPP_CHATROOM_STATE_REQUESTED_EXIT,  false, false, TRANSITION_TYPE_NONE, },
 | |
|   { XMPP_CHATROOM_STATE_REQUESTED_ENTER, XMPP_CHATROOM_STATE_NOT_IN_ROOM,     true,  false, TRANSITION_TYPE_ENTER_FAILURE, },
 | |
|   { XMPP_CHATROOM_STATE_REQUESTED_ENTER, XMPP_CHATROOM_STATE_IN_ROOM,         true,  false, TRANSITION_TYPE_ENTER_SUCCESS, },
 | |
|   { XMPP_CHATROOM_STATE_REQUESTED_ENTER, XMPP_CHATROOM_STATE_REQUESTED_EXIT,  false, false, TRANSITION_TYPE_NONE, },
 | |
|   { XMPP_CHATROOM_STATE_IN_ROOM,         XMPP_CHATROOM_STATE_NOT_IN_ROOM,     true,  false, TRANSITION_TYPE_EXIT_INVOLUNTARILY,  },
 | |
|   { XMPP_CHATROOM_STATE_IN_ROOM,         XMPP_CHATROOM_STATE_REQUESTED_ENTER, false, false, TRANSITION_TYPE_NONE, },
 | |
|   { XMPP_CHATROOM_STATE_IN_ROOM,         XMPP_CHATROOM_STATE_REQUESTED_EXIT,  false, true,  TRANSITION_TYPE_NONE, },
 | |
|   { XMPP_CHATROOM_STATE_REQUESTED_EXIT,  XMPP_CHATROOM_STATE_NOT_IN_ROOM,     true,  false, TRANSITION_TYPE_EXIT_VOLUNTARILY, },
 | |
|   { XMPP_CHATROOM_STATE_REQUESTED_EXIT,  XMPP_CHATROOM_STATE_REQUESTED_ENTER, false, false, TRANSITION_TYPE_NONE, },
 | |
|   { XMPP_CHATROOM_STATE_REQUESTED_EXIT,  XMPP_CHATROOM_STATE_IN_ROOM,         false, false, TRANSITION_TYPE_NONE, },
 | |
| };
 | |
| 
 | |
| 
 | |
| 
 | |
| void
 | |
| XmppChatroomModuleImpl::FireEnteredStatus(const XmlElement* presence,
 | |
|                                           XmppChatroomEnteredStatus status) {
 | |
|   if (chatroom_handler_) {
 | |
|     talk_base::scoped_ptr<XmppPresence> xmpp_presence(XmppPresence::Create());
 | |
|     xmpp_presence->set_raw_xml(presence);
 | |
|     chatroom_handler_->ChatroomEnteredStatus(this, xmpp_presence.get(), status);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void
 | |
| XmppChatroomModuleImpl::FireExitStatus(XmppChatroomExitedStatus status) {
 | |
|   if (chatroom_handler_)
 | |
|     chatroom_handler_->ChatroomExitedStatus(this, status);
 | |
| }
 | |
| 
 | |
| void
 | |
| XmppChatroomModuleImpl::FireMessageReceived(const XmlElement& message) {
 | |
|   if (chatroom_handler_)
 | |
|     chatroom_handler_->MessageReceived(this, message);
 | |
| }
 | |
| 
 | |
| void
 | |
| XmppChatroomModuleImpl::FireMemberEntered(const XmppChatroomMember* entered_member) {
 | |
|   if (chatroom_handler_)
 | |
|     chatroom_handler_->MemberEntered(this, entered_member);
 | |
| }
 | |
| 
 | |
| void
 | |
| XmppChatroomModuleImpl::FireMemberChanged(
 | |
|     const XmppChatroomMember* changed_member) {
 | |
|   if (chatroom_handler_)
 | |
|     chatroom_handler_->MemberChanged(this, changed_member);
 | |
| }
 | |
| 
 | |
| void
 | |
| XmppChatroomModuleImpl::FireMemberExited(const XmppChatroomMember* exited_member) {
 | |
|   if (chatroom_handler_)
 | |
|     chatroom_handler_->MemberExited(this, exited_member);
 | |
| }
 | |
| 
 | |
| 
 | |
| XmppReturnStatus
 | |
| XmppChatroomModuleImpl::ServerChangedOtherPresence(const XmlElement&
 | |
|                                                    presence_element) {
 | |
|   XmppReturnStatus xmpp_status = XMPP_RETURN_OK;
 | |
|   talk_base::scoped_ptr<XmppPresence> presence(XmppPresence::Create());
 | |
|   IFR(presence->set_raw_xml(&presence_element));
 | |
| 
 | |
|   JidMemberMap::iterator pos = chatroom_jid_members_.find(presence->jid());
 | |
| 
 | |
|   if (pos == chatroom_jid_members_.end()) {
 | |
|     if (presence->available() == XMPP_PRESENCE_AVAILABLE) {
 | |
|       XmppChatroomMemberImpl* member = new XmppChatroomMemberImpl();
 | |
|       member->SetPresence(presence.get());
 | |
|       chatroom_jid_members_.insert(std::make_pair(member->member_jid(), member));
 | |
|       chatroom_jid_members_version_++;
 | |
|       FireMemberEntered(member);
 | |
|     }
 | |
|   } else {
 | |
|     XmppChatroomMemberImpl* member = pos->second;
 | |
|     if (presence->available() == XMPP_PRESENCE_AVAILABLE) {
 | |
|       member->SetPresence(presence.get());
 | |
|       chatroom_jid_members_version_++;
 | |
|       FireMemberChanged(member);
 | |
|     }
 | |
|     else if (presence->available() == XMPP_PRESENCE_UNAVAILABLE) {
 | |
|       member->SetPresence(presence.get());
 | |
|       chatroom_jid_members_.erase(pos);
 | |
|       chatroom_jid_members_version_++;
 | |
|       FireMemberExited(member);
 | |
|       delete member;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return xmpp_status;
 | |
| }
 | |
| 
 | |
| XmppReturnStatus
 | |
| XmppChatroomModuleImpl::ClientChangeMyPresence(XmppChatroomState new_state) {
 | |
|   return ChangePresence(new_state, NULL, false);
 | |
| }
 | |
| 
 | |
| XmppReturnStatus
 | |
| XmppChatroomModuleImpl::ServerChangeMyPresence(const XmlElement& presence) {
 | |
|    XmppChatroomState new_state;
 | |
| 
 | |
|    if (presence.HasAttr(QN_TYPE) == false) {
 | |
|       new_state = XMPP_CHATROOM_STATE_IN_ROOM;
 | |
|    } else {
 | |
|      new_state = XMPP_CHATROOM_STATE_NOT_IN_ROOM;
 | |
|    }
 | |
|   return ChangePresence(new_state, &presence, true);
 | |
| 
 | |
| }
 | |
| 
 | |
| XmppReturnStatus
 | |
| XmppChatroomModuleImpl::ChangePresence(XmppChatroomState new_state,
 | |
|                                        const XmlElement* presence,
 | |
|                                        bool isServer) {
 | |
|   UNUSED(presence);
 | |
| 
 | |
|   XmppChatroomState old_state = chatroom_state_;
 | |
| 
 | |
|   // do nothing if state hasn't changed
 | |
|   if (old_state == new_state)
 | |
|     return XMPP_RETURN_OK;
 | |
| 
 | |
|   // find the right transition description
 | |
|   StateTransitionDescription* transition_desc = NULL;
 | |
|   for (int i=0; i < ARRAY_SIZE(Transitions); i++) {
 | |
|     if (Transitions[i].old_state == old_state &&
 | |
|         Transitions[i].new_state == new_state) {
 | |
|         transition_desc = &Transitions[i];
 | |
|         break;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (transition_desc == NULL) {
 | |
|     ASSERT(0);
 | |
|     return XMPP_RETURN_BADSTATE;
 | |
|   }
 | |
| 
 | |
|   // we assert for any invalid transition states, and we'll
 | |
|   if (isServer) {
 | |
|     // $TODO send original stanza back to server and log an error?
 | |
|     // Disable the assert because of b/6133072
 | |
|     // ASSERT(transition_desc->is_valid_server_transition);
 | |
|     if (!transition_desc->is_valid_server_transition) {
 | |
|       return XMPP_RETURN_BADSTATE;
 | |
|     }
 | |
|   } else {
 | |
|     if (transition_desc->is_valid_client_transition == false) {
 | |
|       ASSERT(0);
 | |
|       return XMPP_RETURN_BADARGUMENT;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // set the new state and then fire any notifications to the handler
 | |
|   chatroom_state_ = new_state;
 | |
| 
 | |
|   switch (transition_desc->transition_type) {
 | |
|     case TRANSITION_TYPE_ENTER_SUCCESS:
 | |
|       FireEnteredStatus(presence, XMPP_CHATROOM_ENTERED_SUCCESS);
 | |
|       break;
 | |
|     case TRANSITION_TYPE_ENTER_FAILURE:
 | |
|       FireEnteredStatus(presence, GetEnterFailureFromXml(presence));
 | |
|       break;
 | |
|     case TRANSITION_TYPE_EXIT_INVOLUNTARILY:
 | |
|       FireExitStatus(GetExitFailureFromXml(presence));
 | |
|       break;
 | |
|     case TRANSITION_TYPE_EXIT_VOLUNTARILY:
 | |
|       FireExitStatus(XMPP_CHATROOM_EXITED_REQUESTED);
 | |
|       break;
 | |
|     case TRANSITION_TYPE_NONE:
 | |
|       break;
 | |
|   }
 | |
| 
 | |
|   return XMPP_RETURN_OK;
 | |
| }
 | |
| 
 | |
| XmppChatroomEnteredStatus
 | |
| XmppChatroomModuleImpl::GetEnterFailureFromXml(const XmlElement* presence) {
 | |
|   XmppChatroomEnteredStatus status = XMPP_CHATROOM_ENTERED_FAILURE_UNSPECIFIED;
 | |
|   const XmlElement* error = presence->FirstNamed(QN_ERROR);
 | |
|   if (error != NULL && error->HasAttr(QN_CODE)) {
 | |
|     int code = atoi(error->Attr(QN_CODE).c_str());
 | |
|     switch (code) {
 | |
|       case 401: status = XMPP_CHATROOM_ENTERED_FAILURE_PASSWORD_REQUIRED; break;
 | |
|       case 403: {
 | |
|         status = XMPP_CHATROOM_ENTERED_FAILURE_MEMBER_BANNED;
 | |
|         if (error->FirstNamed(QN_GOOGLE_SESSION_BLOCKED)) {
 | |
|           status = XMPP_CHATROOM_ENTERED_FAILURE_MEMBER_BLOCKED;
 | |
|         } else if (error->FirstNamed(QN_GOOGLE_SESSION_BLOCKING)) {
 | |
|           status = XMPP_CHATROOM_ENTERED_FAILURE_MEMBER_BLOCKING;
 | |
|         }
 | |
|         break;
 | |
|       }
 | |
|       case 405: status = XMPP_CHATROOM_ENTERED_FAILURE_ROOM_LOCKED; break;
 | |
|       case 406: status = XMPP_CHATROOM_ENTERED_FAILURE_OUTDATED_CLIENT; break;
 | |
|       case 407: status = XMPP_CHATROOM_ENTERED_FAILURE_NOT_A_MEMBER; break;
 | |
|       case 409: status = XMPP_CHATROOM_ENTERED_FAILURE_NICKNAME_CONFLICT; break;
 | |
|       // http://xmpp.org/extensions/xep-0045.html#enter-maxusers
 | |
|       case 503: status = XMPP_CHATROOM_ENTERED_FAILURE_MAX_USERS; break;
 | |
|     }
 | |
|   }
 | |
|   return status;
 | |
| }
 | |
| 
 | |
| XmppChatroomExitedStatus
 | |
| XmppChatroomModuleImpl::GetExitFailureFromXml(const XmlElement* presence) {
 | |
|   XmppChatroomExitedStatus status = XMPP_CHATROOM_EXITED_UNSPECIFIED;
 | |
|   const XmlElement* muc_user = presence->FirstNamed(QN_MUC_USER_X);
 | |
|   if (muc_user != NULL) {
 | |
|     const XmlElement* user_status = muc_user->FirstNamed(QN_MUC_USER_STATUS);
 | |
|     if (user_status != NULL && user_status->HasAttr(QN_CODE)) {
 | |
|       int code = atoi(user_status->Attr(QN_CODE).c_str());
 | |
|       switch (code) {
 | |
|         case 307: status = XMPP_CHATROOM_EXITED_KICKED; break;
 | |
|         case 322: status = XMPP_CHATROOM_EXITED_NOT_A_MEMBER; break;
 | |
|         case 332: status = XMPP_CHATROOM_EXITED_SYSTEM_SHUTDOWN; break;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   return status;
 | |
| }
 | |
| 
 | |
| XmppReturnStatus
 | |
| XmppChatroomMemberImpl::SetPresence(const XmppPresence* presence) {
 | |
|   ASSERT(presence != NULL);
 | |
| 
 | |
|   // copy presence
 | |
|   presence_.reset(XmppPresence::Create());
 | |
|   presence_->set_raw_xml(presence->raw_xml());
 | |
|   return XMPP_RETURN_OK;
 | |
| }
 | |
| 
 | |
| const Jid
 | |
| XmppChatroomMemberImpl::member_jid() const {
 | |
|   return presence_->jid();
 | |
| }
 | |
| 
 | |
| const Jid
 | |
| XmppChatroomMemberImpl::full_jid() const {
 | |
|   return Jid("");
 | |
| }
 | |
| 
 | |
| const std::string
 | |
| XmppChatroomMemberImpl::name() const {
 | |
|   return member_jid().resource();
 | |
| }
 | |
| 
 | |
| const XmppPresence*
 | |
| XmppChatroomMemberImpl::presence() const {
 | |
|   return presence_.get();
 | |
| }
 | |
| 
 | |
| 
 | |
| // XmppChatroomMemberEnumeratorImpl --------------------------------------
 | |
| XmppChatroomMemberEnumeratorImpl::XmppChatroomMemberEnumeratorImpl(
 | |
|         XmppChatroomModuleImpl::JidMemberMap* map, int* map_version) {
 | |
|   map_ = map;
 | |
|   map_version_ = map_version;
 | |
|   map_version_created_ = *map_version_;
 | |
|   iterator_ = map->begin();
 | |
|   before_beginning_ = true;
 | |
| }
 | |
| 
 | |
| XmppChatroomMember*
 | |
| XmppChatroomMemberEnumeratorImpl::current() {
 | |
|   if (IsValid() == false) {
 | |
|     return NULL;
 | |
|   } else if (IsBeforeBeginning() || IsAfterEnd()) {
 | |
|     return NULL;
 | |
|   } else {
 | |
|     return iterator_->second;
 | |
|   }
 | |
| }
 | |
| 
 | |
| bool
 | |
| XmppChatroomMemberEnumeratorImpl::Prev() {
 | |
|   if (IsValid() == false) {
 | |
|     return false;
 | |
|   } else if (IsBeforeBeginning()) {
 | |
|     return false;
 | |
|   } else if (iterator_ == map_->begin()) {
 | |
|     before_beginning_ = true;
 | |
|     return false;
 | |
|   } else {
 | |
|     iterator_--;
 | |
|     return current() != NULL;
 | |
|   }
 | |
| }
 | |
| 
 | |
| bool
 | |
| XmppChatroomMemberEnumeratorImpl::Next() {
 | |
|   if (IsValid() == false) {
 | |
|     return false;
 | |
|   } else if (IsBeforeBeginning()) {
 | |
|     before_beginning_ = false;
 | |
|     iterator_ = map_->begin();
 | |
|     return current() != NULL;
 | |
|   } else if (IsAfterEnd()) {
 | |
|     return false;
 | |
|   } else {
 | |
|     iterator_++;
 | |
|     return current() != NULL;
 | |
|   }
 | |
| }
 | |
| 
 | |
| bool
 | |
| XmppChatroomMemberEnumeratorImpl::IsValid() {
 | |
|   return map_version_created_ == *map_version_;
 | |
| }
 | |
| 
 | |
| bool
 | |
| XmppChatroomMemberEnumeratorImpl::IsBeforeBeginning() {
 | |
|   return before_beginning_;
 | |
| }
 | |
| 
 | |
| bool
 | |
| XmppChatroomMemberEnumeratorImpl::IsAfterEnd() {
 | |
|   return (iterator_ == map_->end());
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| } // namespace buzz
 |