summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'Swiften')
-rw-r--r--Swiften/JID/JID.h8
-rw-r--r--Swiften/MUC/MUC.h4
-rw-r--r--Swiften/MUC/MUCImpl.cpp41
-rw-r--r--Swiften/MUC/MUCImpl.h6
-rw-r--r--Swiften/MUC/UnitTest/MUCTest.cpp74
-rw-r--r--Swiften/MUC/UnitTest/MockMUC.h1
6 files changed, 122 insertions, 12 deletions
diff --git a/Swiften/JID/JID.h b/Swiften/JID/JID.h
index 126a7b1..aefd3df 100644
--- a/Swiften/JID/JID.h
+++ b/Swiften/JID/JID.h
@@ -102,70 +102,78 @@ namespace Swift {
*/
const std::string& getResource() const {
return resource_;
}
/**
* Is a bare JID, i.e. has no resource part.
*/
bool isBare() const {
return !hasResource_;
}
/**
* Returns the given node, escaped according to XEP-0106.
* The resulting node is a valid node for a JID, whereas the input value can contain characters
* that are not allowed.
*/
static std::string getEscapedNode(const std::string& node);
/**
* Returns the node of the current JID, unescaped according to XEP-0106.
*/
std::string getUnescapedNode() const;
/**
* Get the JID without a resource.
* @return Invalid if the original is invalid.
*/
JID toBare() const {
JID result(*this);
result.hasResource_ = false;
result.resource_ = "";
return result;
}
+ /**
+ * Get the full JID with the supplied resource.
+ */
+ JID withResource(const std::string& resource) const {
+ JID result(this->getNode(), this->getDomain(), resource);
+ return result;
+ }
+
std::string toString() const;
bool equals(const JID& o, CompareType compareType) const {
return compare(o, compareType) == 0;
}
int compare(const JID& o, CompareType compareType) const;
operator std::string() const {
return toString();
}
bool operator<(const Swift::JID& b) const {
return compare(b, Swift::JID::WithResource) < 0;
}
SWIFTEN_API friend std::ostream& operator<<(std::ostream& os, const Swift::JID& j);
friend bool operator==(const Swift::JID& a, const Swift::JID& b) {
return a.compare(b, Swift::JID::WithResource) == 0;
}
friend bool operator!=(const Swift::JID& a, const Swift::JID& b) {
return a.compare(b, Swift::JID::WithResource) != 0;
}
/**
* Returns an empty optional if the JID is invalid, and an
* optional with a value if the JID is valid.
*/
static boost::optional<JID> parse(const std::string&);
/**
* If Swiften was compiled with SWIFTEN_JID_NO_DEFAULT_IDN_CONVERTER (not default), use this method at
* the beginning of the program to set an IDN converter to use for JID IDN conversions.
diff --git a/Swiften/MUC/MUC.h b/Swiften/MUC/MUC.h
index 0dcccd9..8815489 100644
--- a/Swiften/MUC/MUC.h
+++ b/Swiften/MUC/MUC.h
@@ -1,98 +1,100 @@
/*
- * Copyright (c) 2010-2013 Kevin Smith
+ * Copyright (c) 2010-2014 Kevin Smith
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
*/
#pragma once
#include <Swiften/JID/JID.h>
#include <Swiften/Base/API.h>
#include <string>
#include <Swiften/Elements/Message.h>
#include <Swiften/Elements/Presence.h>
#include <Swiften/Elements/MUCOccupant.h>
#include <Swiften/MUC/MUCRegistry.h>
#include <Swiften/Elements/MUCOwnerPayload.h>
#include <Swiften/Elements/MUCAdminPayload.h>
#include <Swiften/Elements/Form.h>
#include <boost/shared_ptr.hpp>
#include <Swiften/Base/boost_bsignals.h>
#include <boost/signals/connection.hpp>
#include <map>
namespace Swift {
class StanzaChannel;
class IQRouter;
class DirectedPresenceSender;
class SWIFTEN_API MUC {
public:
typedef boost::shared_ptr<MUC> ref;
enum JoinResult { JoinSucceeded, JoinFailed };
enum LeavingType { LeavePart, LeaveKick, LeaveBan, LeaveDestroy, LeaveNotMember, Disconnect };
public:
virtual ~MUC();
/**
* Returns the (bare) JID of the MUC.
*/
virtual JID getJID() const = 0;
/**
* Returns if the room is unlocked and other people can join the room.
* @return True if joinable by others; false otherwise.
*/
virtual bool isUnlocked() const = 0;
virtual void joinAs(const std::string &nick) = 0;
virtual void joinWithContextSince(const std::string &nick, const boost::posix_time::ptime& since) = 0;
/*virtual void queryRoomInfo(); */
/*virtual void queryRoomItems(); */
/*virtual std::string getCurrentNick() = 0; */
virtual std::map<std::string, MUCOccupant> getOccupants() const = 0;
+ virtual void changeNickname(const std::string& newNickname) = 0;
virtual void part() = 0;
/*virtual void handleIncomingMessage(Message::ref message) = 0; */
/** Expose public so it can be called when e.g. user goes offline */
virtual void handleUserLeft(LeavingType) = 0;
/** Get occupant information*/
virtual const MUCOccupant& getOccupant(const std::string& nick) = 0;
virtual bool hasOccupant(const std::string& nick) = 0;
virtual void kickOccupant(const JID& jid) = 0;
virtual void changeOccupantRole(const JID& jid, MUCOccupant::Role role) = 0;
virtual void requestAffiliationList(MUCOccupant::Affiliation) = 0;
virtual void changeAffiliation(const JID& jid, MUCOccupant::Affiliation affiliation) = 0;
virtual void changeSubject(const std::string& subject) = 0;
virtual void requestConfigurationForm() = 0;
virtual void configureRoom(Form::ref) = 0;
virtual void cancelConfigureRoom() = 0;
virtual void destroyRoom() = 0;
/** Send an invite for the person to join the MUC */
virtual void invitePerson(const JID& person, const std::string& reason = "", bool isImpromptu = false, bool isReuseChat = false) = 0;
virtual void setCreateAsReservedIfNew() = 0;
virtual void setPassword(const boost::optional<std::string>& password) = 0;
public:
boost::signal<void (const std::string& /*nick*/)> onJoinComplete;
boost::signal<void (ErrorPayload::ref)> onJoinFailed;
boost::signal<void (ErrorPayload::ref, const JID&, MUCOccupant::Role)> onRoleChangeFailed;
boost::signal<void (ErrorPayload::ref, const JID&, MUCOccupant::Affiliation)> onAffiliationChangeFailed;
boost::signal<void (ErrorPayload::ref)> onConfigurationFailed;
boost::signal<void (ErrorPayload::ref)> onAffiliationListFailed;
boost::signal<void (Presence::ref)> onOccupantPresenceChange;
boost::signal<void (const std::string&, const MUCOccupant& /*now*/, const MUCOccupant::Role& /*old*/)> onOccupantRoleChanged;
boost::signal<void (const std::string&, const MUCOccupant::Affiliation& /*new*/, const MUCOccupant::Affiliation& /*old*/)> onOccupantAffiliationChanged;
boost::signal<void (const MUCOccupant&)> onOccupantJoined;
+ boost::signal<void (const std::string& /*oldNickname*/, const std::string& /*newNickname*/ )> onOccupantNicknameChanged;
boost::signal<void (const MUCOccupant&, LeavingType, const std::string& /*reason*/)> onOccupantLeft;
boost::signal<void (Form::ref)> onConfigurationFormReceived;
boost::signal<void (MUCOccupant::Affiliation, const std::vector<JID>&)> onAffiliationListReceived;
boost::signal<void ()> onUnlocked;
/* boost::signal<void (const MUCInfo&)> onInfoResult; */
/* boost::signal<void (const blah&)> onItemsResult; */
};
}
diff --git a/Swiften/MUC/MUCImpl.cpp b/Swiften/MUC/MUCImpl.cpp
index a1854e3..ab5faf4 100644
--- a/Swiften/MUC/MUCImpl.cpp
+++ b/Swiften/MUC/MUCImpl.cpp
@@ -1,37 +1,37 @@
/*
- * Copyright (c) 2010-2013 Kevin Smith
+ * Copyright (c) 2010-2014 Kevin Smith
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
*/
#include <Swiften/MUC/MUCImpl.h>
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/smart_ptr/make_shared.hpp>
#include <Swiften/Base/foreach.h>
#include <Swiften/Presence/DirectedPresenceSender.h>
#include <Swiften/Client/StanzaChannel.h>
#include <Swiften/Queries/IQRouter.h>
#include <Swiften/Elements/Form.h>
#include <Swiften/Elements/Message.h>
#include <Swiften/Elements/IQ.h>
#include <Swiften/Elements/MUCUserPayload.h>
#include <Swiften/Elements/MUCAdminPayload.h>
#include <Swiften/Elements/MUCPayload.h>
#include <Swiften/Elements/MUCDestroyPayload.h>
#include <Swiften/Elements/MUCInvitationPayload.h>
#include <Swiften/MUC/MUCRegistry.h>
#include <Swiften/Queries/GenericRequest.h>
namespace Swift {
typedef std::pair<std::string, MUCOccupant> StringMUCOccupantPair;
MUCImpl::MUCImpl(StanzaChannel* stanzaChannel, IQRouter* iqRouter, DirectedPresenceSender* presenceSender, const JID &muc, MUCRegistry* mucRegistry) : ownMUCJID(muc), stanzaChannel(stanzaChannel), iqRouter_(iqRouter), presenceSender(presenceSender), mucRegistry(mucRegistry), createAsReservedIfNew(false), unlocking(false), isUnlocked_(false) {
scopedConnection_ = stanzaChannel->onPresenceReceived.connect(boost::bind(&MUCImpl::handleIncomingPresence, this, _1));
}
MUCImpl::~MUCImpl()
{
@@ -58,162 +58,185 @@ void MUCImpl::setPassword(const boost::optional<std::string>& newPassword) {
* Join the MUC with context since date.
*/
void MUCImpl::joinWithContextSince(const std::string &nick, const boost::posix_time::ptime& since) {
joinSince_ = since;
internalJoin(nick);
}
std::map<std::string, MUCOccupant> MUCImpl::getOccupants() const {
return occupants;
}
void MUCImpl::internalJoin(const std::string &nick) {
//TODO: history request
joinComplete_ = false;
joinSucceeded_ = false;
mucRegistry->addMUC(getJID());
ownMUCJID = JID(ownMUCJID.getNode(), ownMUCJID.getDomain(), nick);
Presence::ref joinPresence = boost::make_shared<Presence>(*presenceSender->getLastSentUndirectedPresence());
assert(joinPresence->getType() == Presence::Available);
joinPresence->setTo(ownMUCJID);
MUCPayload::ref mucPayload = boost::make_shared<MUCPayload>();
if (joinSince_ != boost::posix_time::not_a_date_time) {
mucPayload->setSince(joinSince_);
}
if (password) {
mucPayload->setPassword(*password);
}
joinPresence->addPayload(mucPayload);
presenceSender->sendPresence(joinPresence);
}
+void MUCImpl::changeNickname(const std::string& newNickname) {
+ Presence::ref changeNicknamePresence = boost::make_shared<Presence>();
+ changeNicknamePresence->setTo(ownMUCJID.toBare().toString() + std::string("/") + newNickname);
+ presenceSender->sendPresence(changeNicknamePresence);
+}
+
void MUCImpl::part() {
presenceSender->removeDirectedPresenceReceiver(ownMUCJID, DirectedPresenceSender::AndSendPresence);
mucRegistry->removeMUC(getJID());
}
void MUCImpl::handleUserLeft(LeavingType type) {
std::map<std::string,MUCOccupant>::iterator i = occupants.find(ownMUCJID.getResource());
if (i != occupants.end()) {
MUCOccupant me = i->second;
occupants.erase(i);
onOccupantLeft(me, type, "");
}
occupants.clear();
joinComplete_ = false;
joinSucceeded_ = false;
isUnlocked_ = false;
presenceSender->removeDirectedPresenceReceiver(ownMUCJID, DirectedPresenceSender::DontSendPresence);
}
void MUCImpl::handleIncomingPresence(Presence::ref presence) {
if (!isFromMUC(presence->getFrom())) {
return;
}
MUCUserPayload::ref mucPayload;
foreach (MUCUserPayload::ref payload, presence->getPayloads<MUCUserPayload>()) {
if (!payload->getItems().empty() || !payload->getStatusCodes().empty()) {
mucPayload = payload;
}
}
// On the first incoming presence, check if our join has succeeded
// (i.e. we start getting non-error presence from the MUC) or not
if (!joinSucceeded_) {
if (presence->getType() == Presence::Error) {
std::string reason;
onJoinFailed(presence->getPayload<ErrorPayload>());
return;
}
else {
joinSucceeded_ = true;
presenceSender->addDirectedPresenceReceiver(ownMUCJID, DirectedPresenceSender::AndSendPresence);
}
}
std::string nick = presence->getFrom().getResource();
if (nick.empty()) {
return;
}
MUCOccupant::Role role(MUCOccupant::NoRole);
MUCOccupant::Affiliation affiliation(MUCOccupant::NoAffiliation);
boost::optional<JID> realJID;
if (mucPayload && mucPayload->getItems().size() > 0) {
role = mucPayload->getItems()[0].role ? mucPayload->getItems()[0].role.get() : MUCOccupant::NoRole;
affiliation = mucPayload->getItems()[0].affiliation ? mucPayload->getItems()[0].affiliation.get() : MUCOccupant::NoAffiliation;
realJID = mucPayload->getItems()[0].realJID;
}
//100 is non-anonymous
//TODO: 100 may also be specified in a <message/>
//170 is room logging to http
//TODO: Nick changes
if (presence->getType() == Presence::Unavailable) {
LeavingType type = LeavePart;
+ boost::optional<std::string> newNickname;
if (mucPayload) {
if (boost::dynamic_pointer_cast<MUCDestroyPayload>(mucPayload->getPayload())) {
type = LeaveDestroy;
}
else foreach (MUCUserPayload::StatusCode status, mucPayload->getStatusCodes()) {
if (status.code == 307) {
type = LeaveKick;
}
else if (status.code == 301) {
type = LeaveBan;
}
else if (status.code == 321) {
type = LeaveNotMember;
}
+ else if (status.code == 303) {
+ if (mucPayload->getItems().size() == 1) {
+ newNickname = mucPayload->getItems()[0].nick;
+ }
+ }
}
}
-
- if (presence->getFrom() == ownMUCJID) {
- handleUserLeft(type);
- return;
- }
- else {
+ if (newNickname) {
std::map<std::string,MUCOccupant>::iterator i = occupants.find(nick);
if (i != occupants.end()) {
- //TODO: part type
MUCOccupant occupant = i->second;
occupants.erase(i);
- onOccupantLeft(occupant, type, "");
+ occupant.setNick(newNickname.get());
+ occupants.insert(std::make_pair(newNickname.get(), occupant));
+ onOccupantNicknameChanged(nick, newNickname.get());
+ }
+ }
+ else {
+ if (presence->getFrom() == ownMUCJID) {
+ handleUserLeft(type);
+ return;
+ }
+ else {
+ std::map<std::string,MUCOccupant>::iterator i = occupants.find(nick);
+ if (i != occupants.end()) {
+ //TODO: part type
+ MUCOccupant occupant = i->second;
+ occupants.erase(i);
+ onOccupantLeft(occupant, type, "");
+ }
}
}
}
else if (presence->getType() == Presence::Available) {
std::map<std::string, MUCOccupant>::iterator it = occupants.find(nick);
MUCOccupant occupant(nick, role, affiliation);
bool isJoin = true;
if (realJID) {
occupant.setRealJID(realJID.get());
}
if (it != occupants.end()) {
isJoin = false;
MUCOccupant oldOccupant = it->second;
if (oldOccupant.getRole() != role) {
onOccupantRoleChanged(nick, occupant, oldOccupant.getRole());
}
if (oldOccupant.getAffiliation() != affiliation) {
onOccupantAffiliationChanged(nick, affiliation, oldOccupant.getAffiliation());
}
occupants.erase(it);
}
std::pair<std::map<std::string, MUCOccupant>::iterator, bool> result = occupants.insert(std::make_pair(nick, occupant));
if (isJoin) {
onOccupantJoined(result.first->second);
}
onOccupantPresenceChange(presence);
}
if (mucPayload && !joinComplete_) {
bool isLocked = false;
foreach (MUCUserPayload::StatusCode status, mucPayload->getStatusCodes()) {
if (status.code == 110) {
/* Simply knowing this is your presence is enough, 210 doesn't seem to be necessary. */
joinComplete_ = true;
if (ownMUCJID != presence->getFrom()) {
presenceSender->removeDirectedPresenceReceiver(ownMUCJID, DirectedPresenceSender::DontSendPresence);
diff --git a/Swiften/MUC/MUCImpl.h b/Swiften/MUC/MUCImpl.h
index 846ddcf..8eabb80 100644
--- a/Swiften/MUC/MUCImpl.h
+++ b/Swiften/MUC/MUCImpl.h
@@ -26,70 +26,76 @@
namespace Swift {
class StanzaChannel;
class IQRouter;
class DirectedPresenceSender;
class SWIFTEN_API MUCImpl : public MUC {
public:
typedef boost::shared_ptr<MUCImpl> ref;
public:
MUCImpl(StanzaChannel* stanzaChannel, IQRouter* iqRouter, DirectedPresenceSender* presenceSender, const JID &muc, MUCRegistry* mucRegistry);
virtual ~MUCImpl();
/**
* Returns the (bare) JID of the MUC.
*/
virtual JID getJID() const {
return ownMUCJID.toBare();
}
/**
* Returns if the room is unlocked and other people can join the room.
* @return True if joinable by others; false otherwise.
*/
virtual bool isUnlocked() const {
return isUnlocked_;
}
virtual void joinAs(const std::string &nick);
virtual void joinWithContextSince(const std::string &nick, const boost::posix_time::ptime& since);
/*virtual void queryRoomInfo(); */
/*virtual void queryRoomItems(); */
/*virtual std::string getCurrentNick(); */
virtual std::map<std::string, MUCOccupant> getOccupants() const;
+
+ /**
+ * Send a new presence to the MUC indicating a nickname change. Any custom status the user had in the is cleared.
+ * @param newNickname The nickname to change to.
+ */
+ virtual void changeNickname(const std::string& newNickname);
virtual void part();
/*virtual void handleIncomingMessage(Message::ref message); */
/** Expose public so it can be called when e.g. user goes offline */
virtual void handleUserLeft(LeavingType);
/** Get occupant information*/
virtual const MUCOccupant& getOccupant(const std::string& nick);
virtual bool hasOccupant(const std::string& nick);
virtual void kickOccupant(const JID& jid);
virtual void changeOccupantRole(const JID& jid, MUCOccupant::Role role);
virtual void requestAffiliationList(MUCOccupant::Affiliation);
virtual void changeAffiliation(const JID& jid, MUCOccupant::Affiliation affiliation);
virtual void changeSubject(const std::string& subject);
virtual void requestConfigurationForm();
virtual void configureRoom(Form::ref);
virtual void cancelConfigureRoom();
virtual void destroyRoom();
/** Send an invite for the person to join the MUC */
virtual void invitePerson(const JID& person, const std::string& reason = "", bool isImpromptu = false, bool isReuseChat = false);
virtual void setCreateAsReservedIfNew() {createAsReservedIfNew = true;}
virtual void setPassword(const boost::optional<std::string>& password);
private:
bool isFromMUC(const JID& j) const {
return ownMUCJID.equals(j, JID::WithoutResource);
}
const std::string& getOwnNick() const {
return ownMUCJID.getResource();
}
private:
void handleIncomingPresence(Presence::ref presence);
void internalJoin(const std::string& nick);
void handleCreationConfigResponse(MUCOwnerPayload::ref, ErrorPayload::ref);
void handleOccupantRoleChangeResponse(MUCAdminPayload::ref, ErrorPayload::ref, const JID&, MUCOccupant::Role);
diff --git a/Swiften/MUC/UnitTest/MUCTest.cpp b/Swiften/MUC/UnitTest/MUCTest.cpp
index d1a21b0..773edb6 100644
--- a/Swiften/MUC/UnitTest/MUCTest.cpp
+++ b/Swiften/MUC/UnitTest/MUCTest.cpp
@@ -1,78 +1,80 @@
/*
- * Copyright (c) 2010 Remko Tronçon
+ * Copyright (c) 2010-2014 Remko Tronçon
* Licensed under the GNU General Public License v3.
* See Documentation/Licenses/GPLv3.txt for more information.
*/
#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/extensions/TestFactoryRegistry.h>
#include <boost/shared_ptr.hpp>
#include <boost/smart_ptr/make_shared.hpp>
#include <boost/bind.hpp>
#include <Swiften/MUC/MUCImpl.h>
#include <Swiften/Client/DummyStanzaChannel.h>
#include <Swiften/Presence/StanzaChannelPresenceSender.h>
#include <Swiften/Presence/DirectedPresenceSender.h>
#include <Swiften/Queries/IQRouter.h>
#include <Swiften/Elements/MUCUserPayload.h>
#include <Swiften/Elements/MUCOwnerPayload.h>
#include <Swiften/Elements/VCard.h>
#include <Swiften/Elements/CapsInfo.h>
using namespace Swift;
class MUCTest : public CppUnit::TestFixture {
CPPUNIT_TEST_SUITE(MUCTest);
CPPUNIT_TEST(testJoin);
CPPUNIT_TEST(testJoin_ChangePresenceDuringJoinDoesNotSendPresenceBeforeJoinSuccess);
CPPUNIT_TEST(testJoin_ChangePresenceDuringJoinResendsPresenceAfterJoinSuccess);
CPPUNIT_TEST(testCreateInstant);
CPPUNIT_TEST(testReplicateBug);
+ CPPUNIT_TEST(testNicknameChange);
/*CPPUNIT_TEST(testJoin_Success);
CPPUNIT_TEST(testJoin_Fail);*/
CPPUNIT_TEST_SUITE_END();
public:
void setUp() {
channel = new DummyStanzaChannel();
router = new IQRouter(channel);
mucRegistry = new MUCRegistry();
stanzaChannelPresenceSender = new StanzaChannelPresenceSender(channel);
presenceSender = new DirectedPresenceSender(stanzaChannelPresenceSender);
+ nickChanges = 0;
}
void tearDown() {
delete presenceSender;
delete stanzaChannelPresenceSender;
delete mucRegistry;
delete router;
delete channel;
}
void testJoin() {
MUC::ref testling = createMUC(JID("foo@bar.com"));
testling->joinAs("Alice");
CPPUNIT_ASSERT(mucRegistry->isMUC(JID("foo@bar.com")));
Presence::ref p = channel->getStanzaAtIndex<Presence>(0);
CPPUNIT_ASSERT(p);
CPPUNIT_ASSERT_EQUAL(JID("foo@bar.com/Alice"), p->getTo());
}
void testJoin_ChangePresenceDuringJoinDoesNotSendPresenceBeforeJoinSuccess() {
MUC::ref testling = createMUC(JID("foo@bar.com"));
testling->joinAs("Alice");
presenceSender->sendPresence(Presence::create("Test"));
CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(channel->sentStanzas.size()));
}
void testJoin_ChangePresenceDuringJoinResendsPresenceAfterJoinSuccess() {
MUC::ref testling = createMUC(JID("foo@bar.com"));
testling->joinAs("Alice");
presenceSender->sendPresence(Presence::create("Test"));
receivePresence(JID("foo@bar.com/Rabbit"), "Here");
@@ -109,85 +111,153 @@ class MUCTest : public CppUnit::TestFixture {
Presence::ref initialPresence = boost::make_shared<Presence>();
initialPresence->setStatus("");
VCard::ref vcard = boost::make_shared<VCard>();
vcard->setPhoto(createByteArray("15c30080ae98ec48be94bf0e191d43edd06e500a"));
initialPresence->addPayload(vcard);
CapsInfo::ref caps = boost::make_shared<CapsInfo>();
caps->setNode("http://swift.im");
caps->setVersion("p2UP0DrcVgKM6jJqYN/B92DKK0o=");
initialPresence->addPayload(caps);
channel->sendPresence(initialPresence);
MUC::ref testling = createMUC(JID("test@rooms.swift.im"));
testling->joinAs("Test");
Presence::ref serverRespondsLocked = boost::make_shared<Presence>();
serverRespondsLocked->setFrom(JID("test@rooms.swift.im/Test"));
serverRespondsLocked->setTo(JID("test@swift.im/6913d576d55f0b67"));
serverRespondsLocked->addPayload(vcard);
serverRespondsLocked->addPayload(caps);
serverRespondsLocked->setStatus("");
MUCUserPayload::ref mucPayload(new MUCUserPayload());
MUCItem myItem;
myItem.affiliation = MUCOccupant::Owner;
myItem.role = MUCOccupant::Moderator;
mucPayload->addItem(myItem);
mucPayload->addStatusCode(MUCUserPayload::StatusCode(201));
serverRespondsLocked->addPayload(mucPayload);
channel->onPresenceReceived(serverRespondsLocked);
CPPUNIT_ASSERT_EQUAL(3, static_cast<int>(channel->sentStanzas.size()));
IQ::ref iq = channel->getStanzaAtIndex<IQ>(2);
CPPUNIT_ASSERT(iq);
CPPUNIT_ASSERT(iq->getPayload<MUCOwnerPayload>());
CPPUNIT_ASSERT(iq->getPayload<MUCOwnerPayload>()->getForm());
CPPUNIT_ASSERT_EQUAL(Form::SubmitType, iq->getPayload<MUCOwnerPayload>()->getForm()->getType());
}
+ void testNicknameChange() {
+ MUC::ref testling = createMUC(JID("foo@bar.com"));
+ // Join as Rabbit
+ testling->joinAs("Rabbit");
+
+ // Rabbit joins
+ Presence::ref rabbitJoins = boost::make_shared<Presence>();
+ rabbitJoins->setTo("test@swift.im/6913d576d55f0b67");
+ rabbitJoins->setFrom(testling->getJID().toString() + "/Rabbit");
+ channel->onPresenceReceived(rabbitJoins);
+ CPPUNIT_ASSERT_EQUAL(true, testling->hasOccupant("Rabbit"));
+
+ // Alice joins
+ Presence::ref aliceJoins = boost::make_shared<Presence>();
+ aliceJoins->setTo("test@swift.im/6913d576d55f0b67");
+ aliceJoins->setFrom(testling->getJID().toString() + "/Alice");
+ channel->onPresenceReceived(aliceJoins);
+ CPPUNIT_ASSERT_EQUAL(true, testling->hasOccupant("Alice"));
+
+ // Change nick to Dodo
+ testling->changeNickname("Dodo");
+ Presence::ref stanza = channel->getStanzaAtIndex<Presence>(1);
+ CPPUNIT_ASSERT(stanza);
+ CPPUNIT_ASSERT_EQUAL(std::string("Dodo"), stanza->getTo().getResource());
+
+ // Alice changes nick to Alice2
+ stanza = boost::make_shared<Presence>();
+ stanza->setFrom(JID("foo@bar.com/Alice"));
+ stanza->setTo(JID(router->getJID()));
+ stanza->setType(Presence::Unavailable);
+ MUCUserPayload::ref mucPayload(new MUCUserPayload());
+ MUCItem myItem;
+ myItem.affiliation = MUCOccupant::Member;
+ myItem.nick = "Alice2";
+ myItem.role = MUCOccupant::Participant;
+ mucPayload->addItem(myItem);
+ mucPayload->addStatusCode(303);
+ stanza->addPayload(mucPayload);
+ channel->onPresenceReceived(stanza);
+ CPPUNIT_ASSERT_EQUAL(1, nickChanges);
+ CPPUNIT_ASSERT_EQUAL(false, testling->hasOccupant("Alice"));
+ CPPUNIT_ASSERT_EQUAL(true, testling->hasOccupant("Alice2"));
+
+ // We (Rabbit) change nick to Robot
+ stanza = boost::make_shared<Presence>();
+ stanza->setFrom(JID("foo@bar.com/Rabbit"));
+ stanza->setTo(JID(router->getJID()));
+ stanza->setType(Presence::Unavailable);
+ mucPayload = MUCUserPayload::ref(new MUCUserPayload());
+ myItem.affiliation = MUCOccupant::Member;
+ myItem.nick = "Robot";
+ myItem.role = MUCOccupant::Participant;
+ mucPayload->addItem(myItem);
+ mucPayload->addStatusCode(303);
+ stanza->addPayload(mucPayload);
+ channel->onPresenceReceived(stanza);
+ CPPUNIT_ASSERT_EQUAL(2, nickChanges);
+ CPPUNIT_ASSERT_EQUAL(false, testling->hasOccupant("Rabbit"));
+ CPPUNIT_ASSERT_EQUAL(true, testling->hasOccupant("Robot"));
+ }
+
/*void testJoin_Success() {
MUC::ref testling = createMUC(JID("foo@bar.com"));
testling->onJoinFinished.connect(boost::bind(&MUCTest::handleJoinFinished, this, _1, _2));
testling->joinAs("Alice");
receivePresence(JID("foo@bar.com/Rabbit"), "Here");
CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(joinResults.size()));
CPPUNIT_ASSERT_EQUAL(std::string("Alice"), joinResults[0].nick);
CPPUNIT_ASSERT(joinResults[0].error);
}
void testJoin_Fail() {
//CPPUNIT_ASSERT(!mucRegistry->isMUC(JID("foo@bar.com")));
}*/
private:
MUC::ref createMUC(const JID& jid) {
- return boost::make_shared<MUCImpl>(channel, router, presenceSender, jid, mucRegistry);
+ MUC::ref muc = boost::make_shared<MUCImpl>(channel, router, presenceSender, jid, mucRegistry);
+ muc->onOccupantNicknameChanged.connect(boost::bind(&MUCTest::handleOccupantNicknameChanged, this, _1, _2));
+ return muc;
}
void handleJoinFinished(const std::string& nick, ErrorPayload::ref error) {
JoinResult r;
r.nick = nick;
r.error = error;
joinResults.push_back(r);
}
void receivePresence(const JID& jid, const std::string& status) {
Presence::ref p = Presence::create(status);
p->setFrom(jid);
//MUCUserPayload::ref mucUserPayload = boost::make_shared<MUCUserPayload>();
//mucUserPayload->addItem(item);
//p->addPayload(mucUserPayload);
channel->onPresenceReceived(p);
}
+ void handleOccupantNicknameChanged(const std::string&, const std::string&) {
+ nickChanges++;
+ }
+
private:
DummyStanzaChannel* channel;
IQRouter* router;
MUCRegistry* mucRegistry;
StanzaChannelPresenceSender* stanzaChannelPresenceSender;
DirectedPresenceSender* presenceSender;
struct JoinResult {
std::string nick;
ErrorPayload::ref error;
};
std::vector<JoinResult> joinResults;
+ int nickChanges;
};
CPPUNIT_TEST_SUITE_REGISTRATION(MUCTest);
diff --git a/Swiften/MUC/UnitTest/MockMUC.h b/Swiften/MUC/UnitTest/MockMUC.h
index 78c2fb5..8673a90 100644
--- a/Swiften/MUC/UnitTest/MockMUC.h
+++ b/Swiften/MUC/UnitTest/MockMUC.h
@@ -26,70 +26,71 @@ namespace Swift {
class StanzaChannel;
class IQRouter;
class DirectedPresenceSender;
class SWIFTEN_API MockMUC : public MUC{
public:
typedef boost::shared_ptr<MockMUC> ref;
public:
MockMUC(const JID &muc);
virtual ~MockMUC();
/**
* Cause a user to appear to have entered the room. For testing only.
*/
void insertOccupant(const MUCOccupant& occupant);
/**
* Returns the (bare) JID of the MUC.
*/
virtual JID getJID() const {
return ownMUCJID.toBare();
}
/**
* Returns if the room is unlocked and other people can join the room.
* @return True if joinable by others; false otherwise.
*/
virtual bool isUnlocked() const { return true; }
virtual void joinAs(const std::string&) {}
virtual void joinWithContextSince(const std::string&, const boost::posix_time::ptime&) {}
/*virtual void queryRoomInfo(); */
/*virtual void queryRoomItems(); */
/*virtual std::string getCurrentNick() = 0; */
virtual std::map<std::string, MUCOccupant> getOccupants() const { return occupants_; }
+ virtual void changeNickname(const std::string&) { }
virtual void part() {}
/*virtual void handleIncomingMessage(Message::ref message) = 0; */
/** Expose public so it can be called when e.g. user goes offline */
virtual void handleUserLeft(LeavingType) {}
/** Get occupant information*/
virtual const MUCOccupant& getOccupant(const std::string&);
virtual bool hasOccupant(const std::string&);
virtual void kickOccupant(const JID&) {}
virtual void changeOccupantRole(const JID&, MUCOccupant::Role);
virtual void requestAffiliationList(MUCOccupant::Affiliation) {}
virtual void changeAffiliation(const JID&, MUCOccupant::Affiliation);
virtual void changeSubject(const std::string&) {}
virtual void requestConfigurationForm() {}
virtual void configureRoom(Form::ref) {}
virtual void cancelConfigureRoom() {}
virtual void destroyRoom() {}
/** Send an invite for the person to join the MUC */
virtual void invitePerson(const JID&, const std::string&, bool, bool) {}
virtual void setCreateAsReservedIfNew() {}
virtual void setPassword(const boost::optional<std::string>&) {}
protected:
virtual bool isFromMUC(const JID& j) const {
return ownMUCJID.equals(j, JID::WithoutResource);
}
virtual const std::string& getOwnNick() const {
return ownMUCJID.getResource();
}
private:
JID ownMUCJID;
std::map<std::string, MUCOccupant> occupants_;
};
}