diff --git a/include/transport/conversation.h b/include/transport/conversation.h index 7e477ebd..e137b566 100644 --- a/include/transport/conversation.h +++ b/include/transport/conversation.h @@ -34,6 +34,12 @@ class ConversationManager; /// Represents one XMPP-Legacy network conversation. class Conversation { public: + typedef struct _Participant { + int flag; + int status; + std::string statusMessage; + } Participant; + /// Type of participants in MUC rooms. enum ParticipantFlag {None, Moderator}; @@ -128,6 +134,11 @@ class Conversation { void destroyRoom(); + void sendParticipants(const Swift::JID &to); + + private: + Swift::Presence::ref generatePresence(const std::string &nick, int flag, int status, const std::string &statusMessage, const std::string &newname = ""); + private: ConversationManager *m_conversationManager; std::string m_legacyName; @@ -136,6 +147,7 @@ class Conversation { bool m_muc; Swift::JID m_jid; std::list m_jids; + std::map m_participants; }; } diff --git a/src/conversation.cpp b/src/conversation.cpp index 770a219e..f6105461 100644 --- a/src/conversation.cpp +++ b/src/conversation.cpp @@ -123,7 +123,15 @@ void Conversation::handleMessage(boost::shared_ptr &message, con } } -void Conversation::handleParticipantChanged(const std::string &nick, int flag, int status, const std::string &statusMessage, const std::string &newname) { +void Conversation::sendParticipants(const Swift::JID &to) { + for (std::map::iterator it = m_participants.begin(); it != m_participants.end(); it++) { + Swift::Presence::ref presence = generatePresence(it->first, it->second.flag, it->second.status, it->second.statusMessage, ""); + presence->setTo(to); + m_conversationManager->getComponent()->getStanzaChannel()->sendPresence(presence); + } +} + +Swift::Presence::ref Conversation::generatePresence(const std::string &nick, int flag, int status, const std::string &statusMessage, const std::string &newname) { std::string nickname = nick; Swift::Presence::ref presence = Swift::Presence::create(); std::string legacyName = m_legacyName; @@ -173,8 +181,25 @@ void Conversation::handleParticipantChanged(const std::string &nick, int flag, i } p->addItem(item); - presence->addPayload(boost::shared_ptr(p)); + return presence; +} + +void Conversation::handleParticipantChanged(const std::string &nick, int flag, int status, const std::string &statusMessage, const std::string &newname) { + Swift::Presence::ref presence = generatePresence(nick, flag, status, statusMessage, newname); + + if (presence->getType() == Swift::Presence::Unavailable) { + m_participants.erase(nick); + } + else { + Participant p; + p.flag = flag; + p.status = status; + p.statusMessage = statusMessage; + m_participants[nick] = p; + } + + BOOST_FOREACH(const Swift::JID &jid, m_jids) { presence->setTo(jid); m_conversationManager->getComponent()->getStanzaChannel()->sendPresence(presence); diff --git a/src/tests/conversationmanager.cpp b/src/tests/conversationmanager.cpp index a9ff8011..a997f6d5 100644 --- a/src/tests/conversationmanager.cpp +++ b/src/tests/conversationmanager.cpp @@ -25,8 +25,10 @@ class ConversationManagerTest : public CPPUNIT_NS :: TestFixture, public BasicTe CPPUNIT_TEST_SUITE(ConversationManagerTest); CPPUNIT_TEST(handleNormalMessages); CPPUNIT_TEST(handleGroupchatMessages); + CPPUNIT_TEST(handleGroupchatMessagesTwoResources); CPPUNIT_TEST(handleChatstateMessages); CPPUNIT_TEST(handleParticipantChanged); + CPPUNIT_TEST(handleParticipantChangedTwoResources); CPPUNIT_TEST(handlePMFromXMPP); CPPUNIT_TEST(handleGroupchatRemoved); CPPUNIT_TEST_SUITE_END(); @@ -191,6 +193,48 @@ class ConversationManagerTest : public CPPUNIT_NS :: TestFixture, public BasicTe CPPUNIT_ASSERT_EQUAL(std::string("response!"), m_msg->getBody()); } + void handleGroupchatMessagesTwoResources() { + connectSecondResource(); + received2.clear(); + User *user = userManager->getUser("user@localhost"); + TestingConversation *conv = new TestingConversation(user->getConversationManager(), "#room", true); + user->getConversationManager()->addConversation(conv); + conv->onMessageToSend.connect(boost::bind(&ConversationManagerTest::handleMessageReceived, this, _1, _2)); + conv->setNickname("nickname"); + conv->addJID("user@localhost/resource"); + conv->addJID("user@localhost/resource2"); + + // reset resources should not touch this resource + user->getConversationManager()->resetResources(); + + boost::shared_ptr msg(new Swift::Message()); + msg->setBody("hi there!"); + + // Forward it + conv->handleMessage(msg, "anotheruser"); + + loop->processEvents(); + CPPUNIT_ASSERT_EQUAL(1, (int) received2.size()); + CPPUNIT_ASSERT(dynamic_cast(getStanza(received2[0]))); + CPPUNIT_ASSERT_EQUAL(std::string("hi there!"), dynamic_cast(getStanza(received2[0]))->getBody()); + CPPUNIT_ASSERT_EQUAL(std::string("user@localhost/resource2"), dynamic_cast(getStanza(received2[0]))->getTo().toString()); + CPPUNIT_ASSERT_EQUAL(std::string("#room@localhost/anotheruser"), dynamic_cast(getStanza(received2[0]))->getFrom().toString()); + + received.clear(); + + // send response + msg->setFrom("user@localhost/resource2"); + msg->setTo("#room@localhost"); + msg->setBody("response!"); + msg->setType(Swift::Message::Groupchat); + injectMessage(msg); + loop->processEvents(); + + CPPUNIT_ASSERT_EQUAL(0, (int) received.size()); + CPPUNIT_ASSERT(m_msg); + CPPUNIT_ASSERT_EQUAL(std::string("response!"), m_msg->getBody()); + } + void handleParticipantChanged() { User *user = userManager->getUser("user@localhost"); TestingConversation *conv = new TestingConversation(user->getConversationManager(), "#room", true); @@ -246,6 +290,31 @@ class ConversationManagerTest : public CPPUNIT_NS :: TestFixture, public BasicTe CPPUNIT_ASSERT_EQUAL(303, getStanza(received[0])->getPayload()->getStatusCodes()[0].code); } + void handleParticipantChangedTwoResources() { + connectSecondResource(); + received2.clear(); + User *user = userManager->getUser("user@localhost"); + TestingConversation *conv = new TestingConversation(user->getConversationManager(), "#room", true); + + conv->onMessageToSend.connect(boost::bind(&ConversationManagerTest::handleMessageReceived, this, _1, _2)); + conv->setNickname("nickname"); + conv->addJID("user@localhost/resource"); + conv->addJID("user@localhost/resource2"); + + // normal presence + conv->handleParticipantChanged("anotheruser", 0, Swift::StatusShow::Away, "my status message"); + loop->processEvents(); + + CPPUNIT_ASSERT_EQUAL(1, (int) received2.size()); + CPPUNIT_ASSERT(dynamic_cast(getStanza(received2[0]))); + CPPUNIT_ASSERT_EQUAL(Swift::StatusShow::Away, dynamic_cast(getStanza(received2[0]))->getShow()); + CPPUNIT_ASSERT_EQUAL(std::string("user@localhost/resource2"), dynamic_cast(getStanza(received2[0]))->getTo().toString()); + CPPUNIT_ASSERT_EQUAL(std::string("#room@localhost/anotheruser"), dynamic_cast(getStanza(received2[0]))->getFrom().toString()); + CPPUNIT_ASSERT(getStanza(received2[0])->getPayload()); + CPPUNIT_ASSERT_EQUAL(Swift::MUCOccupant::Member, *getStanza(received2[0])->getPayload()->getItems()[0].affiliation); + CPPUNIT_ASSERT_EQUAL(Swift::MUCOccupant::Participant, *getStanza(received2[0])->getPayload()->getItems()[0].role); + } + void handlePMFromXMPP() { User *user = userManager->getUser("user@localhost"); TestingConversation *conv = new TestingConversation(user->getConversationManager(), "#room", true); diff --git a/src/tests/user.cpp b/src/tests/user.cpp index ac92dc64..7b761ca9 100644 --- a/src/tests/user.cpp +++ b/src/tests/user.cpp @@ -159,7 +159,15 @@ class UserTest : public CPPUNIT_NS :: TestFixture, public BasicTest { void handlePresenceJoinRoomTwoResources() { handlePresenceJoinRoom(); + User *user = userManager->getUser("user@localhost"); + + // Add 1 participant + Conversation *conv = user->getConversationManager()->getConversation("#room"); + conv->handleParticipantChanged("anotheruser", 0, Swift::StatusShow::Away, "my status message"); + + // Connect 2nd resource connectSecondResource(); + received2.clear(); Swift::Presence::ref response = Swift::Presence::create(); response->setTo("#room@localhost/hanzz"); response->setFrom("user@localhost/resource2"); @@ -173,6 +181,16 @@ class UserTest : public CPPUNIT_NS :: TestFixture, public BasicTest { CPPUNIT_ASSERT_EQUAL(std::string(""), room); CPPUNIT_ASSERT_EQUAL(std::string(""), roomNickname); CPPUNIT_ASSERT_EQUAL(std::string(""), roomPassword); + + dumpReceived(); + CPPUNIT_ASSERT_EQUAL(2, (int) received2.size()); + CPPUNIT_ASSERT(dynamic_cast(getStanza(received2[1]))); + CPPUNIT_ASSERT_EQUAL(Swift::StatusShow::Away, dynamic_cast(getStanza(received2[1]))->getShow()); + CPPUNIT_ASSERT_EQUAL(std::string("user@localhost/resource2"), dynamic_cast(getStanza(received2[1]))->getTo().toString()); + CPPUNIT_ASSERT_EQUAL(std::string("#room@localhost/anotheruser"), dynamic_cast(getStanza(received2[1]))->getFrom().toString()); + CPPUNIT_ASSERT(getStanza(received2[1])->getPayload()); + CPPUNIT_ASSERT_EQUAL(Swift::MUCOccupant::Member, *getStanza(received2[1])->getPayload()->getItems()[0].affiliation); + CPPUNIT_ASSERT_EQUAL(Swift::MUCOccupant::Participant, *getStanza(received2[1])->getPayload()->getItems()[0].role); } void handlePresenceLeaveRoom() { diff --git a/src/user.cpp b/src/user.cpp index 82095c5a..ac37f799 100644 --- a/src/user.cpp +++ b/src/user.cpp @@ -249,6 +249,7 @@ void User::handlePresence(Swift::Presence::ref presence) { } else { conv->addJID(presence->getFrom()); + conv->sendParticipants(presence->getFrom()); } return; }