diff options
| -rw-r--r-- | Swift/Controllers/Chat/ChatsManager.cpp | 24 | ||||
| -rw-r--r-- | Swift/Controllers/Chat/ChatsManager.h | 1 | ||||
| -rw-r--r-- | Swift/Controllers/Chat/MUCController.cpp | 45 | ||||
| -rw-r--r-- | Swift/Controllers/Chat/MUCController.h | 3 | ||||
| -rw-r--r-- | Swiften/JID/JID.h | 8 | ||||
| -rw-r--r-- | Swiften/MUC/MUC.h | 4 | ||||
| -rw-r--r-- | Swiften/MUC/MUCImpl.cpp | 41 | ||||
| -rw-r--r-- | Swiften/MUC/MUCImpl.h | 6 | ||||
| -rw-r--r-- | Swiften/MUC/UnitTest/MUCTest.cpp | 74 | ||||
| -rw-r--r-- | Swiften/MUC/UnitTest/MockMUC.h | 1 | 
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 @@ -824,6 +824,7 @@ MUC::ref ChatsManager::handleJoinMUCRequest(const JID &mucJID, const boost::opti  		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); @@ -837,6 +838,29 @@ 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)); 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 @@ -101,6 +101,7 @@ namespace Swift {  			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); 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 @@ -103,6 +103,7 @@ MUCController::MUCController (  	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)); @@ -483,7 +484,7 @@ std::string MUCController::roleToSortName(MUCOccupant::Role role) {  }  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) { @@ -691,6 +692,44 @@ void MUCController::handleOccupantLeft(const MUCOccupant& occupant, MUC::Leaving  	}  } +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)); @@ -818,6 +857,10 @@ std::string MUCController::generateJoinPartString(const std::vector<NickJoinPart  	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);  } 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 @@ -55,11 +55,13 @@ namespace Swift {  			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; @@ -86,6 +88,7 @@ namespace Swift {  			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); 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 @@ -134,6 +134,14 @@ namespace Swift {  				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 { 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,5 +1,5 @@  /* - * 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.   */ @@ -55,6 +55,7 @@ namespace Swift {  			/*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 */ @@ -87,6 +88,7 @@ namespace Swift {  			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; 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,5 +1,5 @@  /* - * 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.   */ @@ -90,6 +90,12 @@ void MUCImpl::internalJoin(const std::string &nick) {  	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()); @@ -154,6 +160,7 @@ void MUCImpl::handleIncomingPresence(Presence::ref presence) {  	//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; @@ -168,20 +175,36 @@ void MUCImpl::handleIncomingPresence(Presence::ref presence) {  				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, ""); +				}  			}  		}  	}  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 @@ -58,6 +58,12 @@ namespace Swift {  			/*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 */ 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,5 +1,5 @@  /* - * 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.   */ @@ -30,6 +30,7 @@ class MUCTest : public CppUnit::TestFixture {  		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(); @@ -41,6 +42,7 @@ class MUCTest : public CppUnit::TestFixture {  			mucRegistry = new MUCRegistry();  			stanzaChannelPresenceSender = new StanzaChannelPresenceSender(channel);  			presenceSender = new DirectedPresenceSender(stanzaChannelPresenceSender); +			nickChanges = 0;  		}  		void tearDown() { @@ -141,6 +143,67 @@ class MUCTest : public CppUnit::TestFixture {  			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)); @@ -158,7 +221,9 @@ class MUCTest : public CppUnit::TestFixture {  	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) { @@ -177,6 +242,10 @@ class MUCTest : public CppUnit::TestFixture {  			channel->onPresenceReceived(p);  		} +		void handleOccupantNicknameChanged(const std::string&, const std::string&) { +			nickChanges++; +		} +  	private:  		DummyStanzaChannel* channel;  		IQRouter* router; @@ -188,6 +257,7 @@ class MUCTest : public CppUnit::TestFixture {  			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 @@ -58,6 +58,7 @@ namespace Swift {  			/*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 */ | 
 Swift
 Swift