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 */ |