a09a99950e
git-svn-id: http://webrtc.googlecode.com/svn/trunk@6891 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 <algorithm>
|
|
#include <iostream>
|
|
#include <map>
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <vector>
|
|
#include "talk/xmpp/chatroommodule.h"
|
|
#include "talk/xmpp/constants.h"
|
|
#include "talk/xmpp/moduleimpl.h"
|
|
#include "webrtc/base/common.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) {RTC_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:
|
|
rtc::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) {
|
|
RTC_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_) {
|
|
rtc::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;
|
|
rtc::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) {
|
|
RTC_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
|