summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Swift/Controllers/Chat/ChatsManager.cpp24
-rw-r--r--Swift/Controllers/Chat/ChatsManager.h1
-rw-r--r--Swift/Controllers/Chat/MUCController.cpp45
-rw-r--r--Swift/Controllers/Chat/MUCController.h3
-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
10 files changed, 194 insertions, 13 deletions
diff --git a/Swift/Controllers/Chat/ChatsManager.cpp b/Swift/Controllers/Chat/ChatsManager.cpp
index c180024..32e25a2 100644
--- a/Swift/Controllers/Chat/ChatsManager.cpp
+++ b/Swift/Controllers/Chat/ChatsManager.cpp
@@ -818,31 +818,55 @@ MUC::ref ChatsManager::handleJoinMUCRequest(const JID &mucJID, const boost::opti
* controllers' dtor, which are deleted in ChatManager's dtor. The adapters
* are also deleted there.*/
chatWindowFactoryAdapters_[controller] = chatWindowFactoryAdapter;
}
mucControllers_[mucJID] = controller;
controller->setAvailableServerFeatures(serverDiscoInfo_);
controller->onUserLeft.connect(boost::bind(&ChatsManager::handleUserLeftMUC, this, controller));
controller->onUserJoined.connect(boost::bind(&ChatsManager::handleChatActivity, this, mucJID.toBare(), "", true));
+ controller->onUserNicknameChanged.connect(boost::bind(&ChatsManager::handleUserNicknameChanged, this, controller, _1, _2));
controller->onActivity.connect(boost::bind(&ChatsManager::handleChatActivity, this, mucJID.toBare(), _1, true));
controller->onUnreadCountChanged.connect(boost::bind(&ChatsManager::handleUnreadCountChanged, this, controller));
handleChatActivity(mucJID.toBare(), "", true);
}
mucControllers_[mucJID]->showChatWindow();
return muc;
}
void ChatsManager::handleSearchMUCRequest() {
mucSearchController_->openSearchWindow();
}
+void ChatsManager::handleUserNicknameChanged(MUCController* mucController, const std::string& oldNickname, const std::string& newNickname) {
+ JID oldMUCChatJID = mucController->getToJID().withResource(oldNickname);
+ JID newMUCChatJID = mucController->getToJID().withResource(newNickname);
+
+ SWIFT_LOG(debug) << "nickname change in " << mucController->getToJID().toString() << " from " << oldNickname << " to " << newNickname << std::endl;
+
+ // get current chat controller
+ ChatController *chatController = getChatControllerIfExists(oldMUCChatJID);
+ if (chatController) {
+ // adjust chat controller
+ chatController->setToJID(newMUCChatJID);
+ nickResolver_->onNickChanged(newMUCChatJID, oldNickname);
+ chatControllers_.erase(oldMUCChatJID);
+ chatControllers_[newMUCChatJID] = chatController;
+
+ chatController->onActivity.disconnect(boost::bind(&ChatsManager::handleChatActivity, this, oldMUCChatJID, _1, false));
+ chatController->onActivity.connect(boost::bind(&ChatsManager::handleChatActivity, this, newMUCChatJID, _1, false));
+ /*for (std::list<ChatListWindow::Chat>::iterator i = recentChats_.begin(); i != recentChats_.end(); i++) {
+ if (i->jid ==
+ }*/
+ }
+}
+
void ChatsManager::handleIncomingMessage(boost::shared_ptr<Message> message) {
JID jid = message->getFrom();
boost::shared_ptr<MessageEvent> event(new MessageEvent(message));
bool isInvite = !!message->getPayload<MUCInvitationPayload>();
bool isMediatedInvite = (message->getPayload<MUCUserPayload>() && message->getPayload<MUCUserPayload>()->getInvite());
if (isMediatedInvite) {
jid = (*message->getPayload<MUCUserPayload>()->getInvite()).from;
}
if (!event->isReadable() && !message->getPayload<ChatState>() && !message->getPayload<DeliveryReceipt>() && !message->getPayload<DeliveryReceiptRequest>() && !isInvite && !isMediatedInvite && !message->hasSubject()) {
diff --git a/Swift/Controllers/Chat/ChatsManager.h b/Swift/Controllers/Chat/ChatsManager.h
index 575b3cb..82daf67 100644
--- a/Swift/Controllers/Chat/ChatsManager.h
+++ b/Swift/Controllers/Chat/ChatsManager.h
@@ -95,18 +95,19 @@ namespace Swift {
MUC::ref handleJoinMUCRequest(const JID& muc, const boost::optional<std::string>& password, const boost::optional<std::string>& nick, bool addAutoJoin, bool createAsReservedIfNew, bool isImpromptu, ChatWindow* reuseChatwindow = 0);
void handleSearchMUCRequest();
void handleMUCSelectedAfterSearch(const JID&);
void rebindControllerJID(const JID& from, const JID& to);
void handlePresenceChange(boost::shared_ptr<Presence> newPresence);
void handleUIEvent(boost::shared_ptr<UIEvent> event);
void handleMUCBookmarkAdded(const MUCBookmark& bookmark);
void handleMUCBookmarkRemoved(const MUCBookmark& bookmark);
void handleUserLeftMUC(MUCController* mucController);
+ void handleUserNicknameChanged(MUCController* mucController, const std::string& oldNickname, const std::string& newNickname);
void handleBookmarksReady();
void handleChatActivity(const JID& jid, const std::string& activity, bool isMUC);
void handleChatClosed(const JID& jid);
void handleNewFileTransferController(FileTransferController*);
void handleWhiteboardSessionRequest(const JID& contact, bool senderIsSelf);
void handleWhiteboardStateChange(const JID& contact, const ChatWindow::WhiteboardSessionState state);
boost::optional<ChatListWindow::Chat> removeExistingChat(const ChatListWindow::Chat& chat);
void cleanupPrivateMessageRecents();
void appendRecent(const ChatListWindow::Chat& chat);
diff --git a/Swift/Controllers/Chat/MUCController.cpp b/Swift/Controllers/Chat/MUCController.cpp
index b467227..fe90c60 100644
--- a/Swift/Controllers/Chat/MUCController.cpp
+++ b/Swift/Controllers/Chat/MUCController.cpp
@@ -97,18 +97,19 @@ MUCController::MUCController (
chatWindow_->onConfigureRequest.connect(boost::bind(&MUCController::handleConfigureRequest, this, _1));
chatWindow_->onConfigurationFormCancelled.connect(boost::bind(&MUCController::handleConfigurationCancelled, this));
chatWindow_->onDestroyRequest.connect(boost::bind(&MUCController::handleDestroyRoomRequest, this));
chatWindow_->onInviteToChat.connect(boost::bind(&MUCController::handleInvitePersonToThisMUCRequest, this, _1));
chatWindow_->onGetAffiliationsRequest.connect(boost::bind(&MUCController::handleGetAffiliationsRequest, this));
chatWindow_->onChangeAffiliationsRequest.connect(boost::bind(&MUCController::handleChangeAffiliationsRequest, this, _1));
muc_->onJoinComplete.connect(boost::bind(&MUCController::handleJoinComplete, this, _1));
muc_->onJoinFailed.connect(boost::bind(&MUCController::handleJoinFailed, this, _1));
muc_->onOccupantJoined.connect(boost::bind(&MUCController::handleOccupantJoined, this, _1));
+ muc_->onOccupantNicknameChanged.connect(boost::bind(&MUCController::handleOccupantNicknameChanged, this, _1, _2));
muc_->onOccupantPresenceChange.connect(boost::bind(&MUCController::handleOccupantPresenceChange, this, _1));
muc_->onOccupantLeft.connect(boost::bind(&MUCController::handleOccupantLeft, this, _1, _2, _3));
muc_->onRoleChangeFailed.connect(boost::bind(&MUCController::handleOccupantRoleChangeFailed, this, _1, _2, _3));
muc_->onAffiliationListReceived.connect(boost::bind(&MUCController::handleAffiliationListReceived, this, _1, _2));
muc_->onConfigurationFailed.connect(boost::bind(&MUCController::handleConfigurationFailed, this, _1));
muc_->onConfigurationFormReceived.connect(boost::bind(&MUCController::handleConfigurationFormReceived, this, _1));
highlighter_->setMode(isImpromptu_ ? Highlighter::ChatMode : Highlighter::MUCMode);
highlighter_->setNick(nick_);
if (timerFactory) {
@@ -477,19 +478,19 @@ std::string MUCController::roleToSortName(MUCOccupant::Role role) {
case MUCOccupant::Participant: return "2";
case MUCOccupant::Visitor: return "3";
case MUCOccupant::NoRole: return "4";
}
assert(false);
return "5";
}
JID MUCController::nickToJID(const std::string& nick) {
- return JID(toJID_.getNode(), toJID_.getDomain(), nick);
+ return muc_->getJID().withResource(nick);
}
bool MUCController::messageTargetsMe(boost::shared_ptr<Message> message) {
std::string stringRegexp(".*\\b" + boost::to_lower_copy(nick_) + "\\b.*");
boost::regex myRegexp(stringRegexp);
return boost::regex_match(boost::to_lower_copy(message->getBody()), myRegexp);
}
void MUCController::preHandleIncomingMessage(boost::shared_ptr<MessageEvent> messageEvent) {
@@ -685,18 +686,56 @@ void MUCController::handleOccupantLeft(const MUCOccupant& occupant, MUC::Leaving
if (clearAfter) {
clearPresenceQueue();
}
if (isImpromptu_) {
setImpromptuWindowTitle();
}
}
+void MUCController::handleOccupantNicknameChanged(const std::string& oldNickname, const std::string& newNickname) {
+ addPresenceMessage(generateNicknameChangeString(oldNickname, newNickname));
+ JID oldJID = muc_->getJID().withResource(oldNickname);
+ JID newJID = muc_->getJID().withResource(newNickname);
+
+ // adjust occupants
+ currentOccupants_.erase(oldNickname);
+ currentOccupants_.insert(newNickname);
+
+ // adjust completer
+ completer_->removeWord(oldNickname);
+ completer_->addWord(newNickname);
+
+ // update contact
+ roster_->removeContact(oldJID);
+ MUCOccupant occupant = muc_->getOccupant(newNickname);
+
+ JID realJID;
+ if (occupant.getRealJID()) {
+ realJID = occupant.getRealJID().get();
+ }
+ MUCOccupant::Role role = MUCOccupant::Participant;
+ MUCOccupant::Affiliation affiliation = MUCOccupant::NoAffiliation;
+ if (!isImpromptu_) {
+ role = occupant.getRole();
+ affiliation = occupant.getAffiliation();
+ }
+ std::string groupName(roleToGroupName(role));
+ roster_->addContact(newJID, realJID, newNickname, groupName, avatarManager_->getAvatarPath(newJID));
+ roster_->applyOnItems(SetMUC(newJID, role, affiliation));
+ if (avatarManager_ != NULL) {
+ handleAvatarChanged(newJID);
+ }
+
+ clearPresenceQueue();
+ onUserNicknameChanged(oldNickname, newNickname);
+}
+
void MUCController::handleOccupantPresenceChange(boost::shared_ptr<Presence> presence) {
receivedActivity();
roster_->applyOnItems(SetPresence(presence, JID::WithResource));
}
bool MUCController::isIncomingMessageFromMe(boost::shared_ptr<Message> message) {
JID from = message->getFrom();
return nick_ == from.getResource();
}
@@ -812,18 +851,22 @@ std::string MUCController::generateJoinPartString(const std::vector<NickJoinPart
} else {
result += QT_TRANSLATE_NOOP("", " and ");
}
}
result += eventStrings[populatedEvents[i]];
}
return result;
}
+std::string MUCController::generateNicknameChangeString(const std::string& oldNickname, const std::string& newNickname) {
+ return str(boost::format(QT_TRANSLATE_NOOP("", "%1% is now known as %2%.")) % oldNickname % newNickname);
+}
+
void MUCController::handleChangeSubjectRequest(const std::string& subject) {
muc_->changeSubject(subject);
}
void MUCController::handleBookmarkRequest() {
const JID jid = muc_->getJID();
MUCBookmark bookmark(jid, jid.toBare().toString());
bookmark.setPassword(password_);
bookmark.setNick(nick_);
diff --git a/Swift/Controllers/Chat/MUCController.h b/Swift/Controllers/Chat/MUCController.h
index b5b5837..2d732a1 100644
--- a/Swift/Controllers/Chat/MUCController.h
+++ b/Swift/Controllers/Chat/MUCController.h
@@ -49,23 +49,25 @@ namespace Swift {
};
class MUCController : public ChatControllerBase {
public:
MUCController(const JID& self, MUC::ref muc, const boost::optional<std::string>& password, const std::string &nick, StanzaChannel* stanzaChannel, IQRouter* iqRouter, ChatWindowFactory* chatWindowFactory, PresenceOracle* presenceOracle, AvatarManager* avatarManager, UIEventStream* events, bool useDelayForLatency, TimerFactory* timerFactory, EventController* eventController, EntityCapsProvider* entityCapsProvider, XMPPRoster* roster, HistoryController* historyController, MUCRegistry* mucRegistry, HighlightManager* highlightManager, boost::shared_ptr<ChatMessageParser> chatMessageParser, bool isImpromptu, AutoAcceptMUCInviteDecider* autoAcceptMUCInviteDecider, VCardManager* vcardManager);
virtual ~MUCController();
boost::signal<void ()> onUserLeft;
boost::signal<void ()> onUserJoined;
boost::signal<void ()> onImpromptuConfigCompleted;
+ boost::signal<void (const std::string&, const std::string& )> onUserNicknameChanged;
virtual void setOnline(bool online);
void rejoin();
static void appendToJoinParts(std::vector<NickJoinPart>& joinParts, const NickJoinPart& newEvent);
static std::string generateJoinPartString(const std::vector<NickJoinPart>& joinParts, bool isImpromptu);
static std::string concatenateListOfNames(const std::vector<NickJoinPart>& joinParts);
+ static std::string generateNicknameChangeString(const std::string& oldNickname, const std::string& newNickname);
bool isJoined();
const std::string& getNick();
const boost::optional<std::string> getPassword() const;
bool isImpromptu() const;
std::map<std::string, JID> getParticipantJIDs() const;
void sendInvites(const std::vector<JID>& jids, const std::string& reason) const;
protected:
void preSendMessageRequest(boost::shared_ptr<Message> message);
@@ -80,18 +82,19 @@ namespace Swift {
private:
void setAvailableRoomActions(const MUCOccupant::Affiliation& affiliation, const MUCOccupant::Role& role);
void clearPresenceQueue();
void addPresenceMessage(const std::string& message);
void handleWindowOccupantSelectionChanged(ContactRosterItem* item);
void handleActionRequestedOnOccupant(ChatWindow::OccupantAction, ContactRosterItem* item);
void handleWindowClosed();
void handleAvatarChanged(const JID& jid);
void handleOccupantJoined(const MUCOccupant& occupant);
+ void handleOccupantNicknameChanged(const std::string& oldNickname, const std::string& newNickname);
void handleOccupantLeft(const MUCOccupant& occupant, MUC::LeavingType type, const std::string& reason);
void handleOccupantPresenceChange(boost::shared_ptr<Presence> presence);
void handleOccupantRoleChanged(const std::string& nick, const MUCOccupant& occupant,const MUCOccupant::Role& oldRole);
void handleOccupantAffiliationChanged(const std::string& nick, const MUCOccupant::Affiliation& affiliation,const MUCOccupant::Affiliation& oldAffiliation);
void handleJoinComplete(const std::string& nick);
void handleJoinFailed(boost::shared_ptr<ErrorPayload> error);
void handleJoinTimeoutTick();
void handleChangeSubjectRequest(const std::string&);
void handleBookmarkRequest();
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
@@ -128,18 +128,26 @@ namespace Swift {
* @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 {
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,11 +1,11 @@
/*
- * 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>
@@ -49,18 +49,19 @@ namespace Swift {
*/
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;
@@ -81,18 +82,19 @@ namespace Swift {
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,11 +1,11 @@
/*
- * 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>
@@ -84,18 +84,24 @@ void MUCImpl::internalJoin(const std::string &nick) {
}
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;
@@ -148,46 +154,63 @@ void MUCImpl::handleIncomingPresence(Presence::ref presence) {
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());
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
@@ -52,18 +52,24 @@ namespace Swift {
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);
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,11 +1,11 @@
/*
- * 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>
@@ -24,29 +24,31 @@
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;
}
@@ -135,59 +137,127 @@ class MUCTest : public CppUnit::TestFixture {
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
@@ -52,18 +52,19 @@ namespace Swift {
*/
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);