diff --git a/include/transport/conversationmanager.h b/include/transport/conversationmanager.h index 17ca1467..44c81888 100644 --- a/include/transport/conversationmanager.h +++ b/include/transport/conversationmanager.h @@ -57,11 +57,7 @@ class ConversationManager { /// \param name legacy network name. /// \return Conversation or NULL. - Conversation *getConversation(const std::string &name) { - if (m_convs.find(name) == m_convs.end()) - return NULL; - return m_convs[name]; - } + Conversation *getConversation(const std::string &name); /// Adds new Conversation to the manager. diff --git a/src/conversation.cpp b/src/conversation.cpp index f599c852..7aca1f4c 100644 --- a/src/conversation.cpp +++ b/src/conversation.cpp @@ -30,7 +30,7 @@ namespace Transport { Conversation::Conversation(ConversationManager *conversationManager, const std::string &legacyName, bool isMUC) : m_conversationManager(conversationManager) { m_legacyName = legacyName; - m_conversationManager->addConversation(this); +// m_conversationManager->addConversation(this); m_muc = isMUC; m_jid = m_conversationManager->getUser()->getJID().toBare(); } diff --git a/src/conversationmanager.cpp b/src/conversationmanager.cpp index 6a5d7d16..58d88c24 100644 --- a/src/conversationmanager.cpp +++ b/src/conversationmanager.cpp @@ -46,6 +46,25 @@ ConversationManager::~ConversationManager() { } } +Conversation *ConversationManager::getConversation(const std::string &name) { + if (m_convs.find(name) != m_convs.end()) + return m_convs[name]; + + if (name.find("/") == std::string::npos) { + return NULL; + } + + // handle PMs + std::string room = name.substr(0, name.find("/")); + std::string nick = name.substr(name.find("/") + 1); + + if (getConversation(room) == NULL) { + return NULL; + } + + return getConversation(nick); +} + void ConversationManager::addConversation(Conversation *conv) { m_convs[conv->getLegacyName()] = conv; LOG4CXX_INFO(logger, m_user->getJID().toString() << ": Adding conversation " << conv->getLegacyName()); @@ -82,16 +101,18 @@ void ConversationManager::handleMessageReceived(Swift::Message::ref message) { // create conversation if it does not exist. if (!m_convs[name]) { - m_convs[name] = m_component->getFactory()->createConversation(this, name); + Conversation *conv = m_component->getFactory()->createConversation(this, name); + addConversation(conv); } // if it exists and it's MUC, but this message is PM, get PM conversation or create new one. else if (m_convs[name]->isMUC() && message->getType() != Swift::Message::Groupchat) { std::string room_name = name; - name = message->getTo().getResource(); - if (!m_convs[name]) { - m_convs[name] = m_component->getFactory()->createConversation(this, name); - m_convs[name]->setRoom(room_name); - m_convs[name]->setNickname(name); + name = room_name + "/" + message->getTo().getResource(); + if (m_convs.find(name) == m_convs.end()) { + Conversation *conv = m_component->getFactory()->createConversation(this, message->getTo().getResource()); + conv->setRoom(room_name); + conv->setNickname(name); + addConversation(conv); } } diff --git a/src/networkpluginserver.cpp b/src/networkpluginserver.cpp index 214bf7d7..82ede6e8 100644 --- a/src/networkpluginserver.cpp +++ b/src/networkpluginserver.cpp @@ -609,6 +609,7 @@ void NetworkPluginServer::handleConvMessagePayload(const std::string &data, bool NetworkConversation *conv = (NetworkConversation *) user->getConversationManager()->getConversation(payload.buddyname()); if (!conv) { conv = new NetworkConversation(user->getConversationManager(), payload.buddyname()); + user->getConversationManager()->addConversation(conv); conv->onMessageToSend.connect(boost::bind(&NetworkPluginServer::handleMessageReceived, this, _1, _2)); } @@ -636,6 +637,7 @@ void NetworkPluginServer::handleAttentionPayload(const std::string &data) { NetworkConversation *conv = (NetworkConversation *) user->getConversationManager()->getConversation(payload.buddyname()); if (!conv) { conv = new NetworkConversation(user->getConversationManager(), payload.buddyname()); + user->getConversationManager()->addConversation(conv); conv->onMessageToSend.connect(boost::bind(&NetworkPluginServer::handleMessageReceived, this, _1, _2)); } @@ -1157,6 +1159,7 @@ void NetworkPluginServer::handleRoomJoined(User *user, const Swift::JID &who, co send(c->connection, message); NetworkConversation *conv = new NetworkConversation(user->getConversationManager(), r, true); + user->getConversationManager()->addConversation(conv); conv->onMessageToSend.connect(boost::bind(&NetworkPluginServer::handleMessageReceived, this, _1, _2)); conv->setNickname(nickname); conv->setJID(who); diff --git a/src/tests/basictest.cpp b/src/tests/basictest.cpp index f88fc34e..e9779f9c 100644 --- a/src/tests/basictest.cpp +++ b/src/tests/basictest.cpp @@ -146,6 +146,10 @@ void BasicTest::injectIQ(boost::shared_ptr iq) { dynamic_cast(component->getStanzaChannel())->onIQReceived(iq); } +void BasicTest::injectMessage(boost::shared_ptr msg) { + dynamic_cast(component->getStanzaChannel())->onMessageReceived(msg); +} + Swift::Stanza *BasicTest::getStanza(boost::shared_ptr element) { Swift::Stanza *stanza = dynamic_cast(element.get()); CPPUNIT_ASSERT(stanza); diff --git a/src/tests/basictest.h b/src/tests/basictest.h index fe754bd9..ee4415cd 100644 --- a/src/tests/basictest.h +++ b/src/tests/basictest.h @@ -55,8 +55,10 @@ class TestingConversation : public Conversation { // Called when there's new message to legacy network from XMPP network void sendMessage(boost::shared_ptr &message) { - + onMessageToSend(this, message); } + + boost::signal &)> onMessageToSend; }; class TestingFactory : public Factory { @@ -66,10 +68,15 @@ class TestingFactory : public Factory { // Creates new conversation (NetworkConversation in this case) Conversation *createConversation(ConversationManager *conversationManager, const std::string &legacyName) { - Conversation *nc = new TestingConversation(conversationManager, legacyName); + TestingConversation *nc = new TestingConversation(conversationManager, legacyName); + nc->onMessageToSend.connect(boost::bind(&TestingFactory::handleMessageToSend, this, _1, _2)); return nc; } + void handleMessageToSend(TestingConversation *_conv, boost::shared_ptr &_msg) { + onMessageToSend(_conv, _msg); + } + // Creates new LocalBuddy Buddy *createBuddy(RosterManager *rosterManager, const BuddyInfo &buddyInfo) { LocalBuddy *buddy = new LocalBuddy(rosterManager, buddyInfo.id, buddyInfo.legacyName, buddyInfo.alias, buddyInfo.groups, (BuddyFlag) buddyInfo.flags); @@ -82,6 +89,8 @@ class TestingFactory : public Factory { buddy->setIconHash(buddyInfo.settings.find("icon_hash")->second.s); return buddy; } + + boost::signal &)> onMessageToSend; }; class TestingStorageBackend : public StorageBackend { @@ -205,6 +214,7 @@ class BasicTest : public Swift::XMPPParserClient { void injectPresence(boost::shared_ptr &response); void injectIQ(boost::shared_ptr iq); + void injectMessage(boost::shared_ptr msg); void dumpReceived(); diff --git a/src/tests/conversationmanager.cpp b/src/tests/conversationmanager.cpp new file mode 100644 index 00000000..7ff2b3f4 --- /dev/null +++ b/src/tests/conversationmanager.cpp @@ -0,0 +1,252 @@ +#include "transport/userregistry.h" +#include "transport/config.h" +#include "transport/storagebackend.h" +#include "transport/user.h" +#include "transport/transport.h" +#include "transport/conversation.h" +#include "transport/usermanager.h" +#include "transport/conversationmanager.h" +#include "transport/localbuddy.h" +#include +#include +#include +#include +#include +#include +#include +#include "Swiften/Server/ServerStanzaChannel.h" +#include "Swiften/Server/ServerFromClientSession.h" +#include "Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.h" +#include "basictest.h" + +using namespace Transport; + +class ConversationManagerTest : public CPPUNIT_NS :: TestFixture, public BasicTest { + CPPUNIT_TEST_SUITE(ConversationManagerTest); + CPPUNIT_TEST(handleNormalMessages); + CPPUNIT_TEST(handleGroupchatMessages); + CPPUNIT_TEST(handleParticipantChanged); + CPPUNIT_TEST(handlePMFromXMPP); + CPPUNIT_TEST(handlePMFromLegacy); + CPPUNIT_TEST_SUITE_END(); + + public: + TestingConversation *m_conv; + boost::shared_ptr m_msg; + + void setUp (void) { + m_conv = NULL; + m_msg.reset(); + setMeUp(); + connectUser(); + add2Buddies(); + factory->onMessageToSend.connect(boost::bind(&ConversationManagerTest::handleMessageReceived, this, _1, _2)); + received.clear(); + } + + void tearDown (void) { + received.clear(); + disconnectUser(); + factory->onMessageToSend.disconnect(boost::bind(&ConversationManagerTest::handleMessageReceived, this, _1, _2)); + tearMeDown(); + } + + void handleMessageReceived(TestingConversation *_conv, boost::shared_ptr &_msg) { + m_conv = _conv; + m_msg = _msg; + } + + void handleNormalMessages() { + User *user = userManager->getUser("user@localhost"); + + TestingConversation *conv = new TestingConversation(user->getConversationManager(), "buddy1"); + user->getConversationManager()->addConversation(conv); + conv->onMessageToSend.connect(boost::bind(&ConversationManagerTest::handleMessageReceived, this, _1, _2)); + + boost::shared_ptr msg(new Swift::Message()); + msg->setBody("hi there!"); + + // Forward it + conv->handleMessage(msg); + loop->processEvents(); + + CPPUNIT_ASSERT_EQUAL(1, (int) received.size()); + CPPUNIT_ASSERT(dynamic_cast(getStanza(received[0]))); + CPPUNIT_ASSERT_EQUAL(std::string("hi there!"), dynamic_cast(getStanza(received[0]))->getBody()); + CPPUNIT_ASSERT_EQUAL(std::string("user@localhost"), dynamic_cast(getStanza(received[0]))->getTo().toString()); + CPPUNIT_ASSERT_EQUAL(std::string("buddy1@localhost/bot"), dynamic_cast(getStanza(received[0]))->getFrom().toString()); + + received.clear(); + + // send response + msg->setFrom("user@localhost/resource"); + msg->setTo("buddy1@localhost/bot"); + msg->setBody("response!"); + injectMessage(msg); + loop->processEvents(); + + CPPUNIT_ASSERT_EQUAL(0, (int) received.size()); + CPPUNIT_ASSERT(m_msg); + CPPUNIT_ASSERT_EQUAL(std::string("response!"), m_msg->getBody()); + + // send another message from legacy network, should be sent to user@localhost/resource now + boost::shared_ptr msg2(new Swift::Message()); + msg2->setBody("hi there!"); + + // Forward it + conv->handleMessage(msg2); + loop->processEvents(); + + CPPUNIT_ASSERT_EQUAL(1, (int) received.size()); + CPPUNIT_ASSERT(dynamic_cast(getStanza(received[0]))); + CPPUNIT_ASSERT_EQUAL(std::string("hi there!"), dynamic_cast(getStanza(received[0]))->getBody()); + CPPUNIT_ASSERT_EQUAL(std::string("user@localhost/resource"), dynamic_cast(getStanza(received[0]))->getTo().toString()); + CPPUNIT_ASSERT_EQUAL(std::string("buddy1@localhost/bot"), dynamic_cast(getStanza(received[0]))->getFrom().toString()); + + received.clear(); + + // and now to bare JID again... + user->getConversationManager()->resetResources(); + conv->handleMessage(msg2); + loop->processEvents(); + + CPPUNIT_ASSERT_EQUAL(1, (int) received.size()); + CPPUNIT_ASSERT(dynamic_cast(getStanza(received[0]))); + CPPUNIT_ASSERT_EQUAL(std::string("hi there!"), dynamic_cast(getStanza(received[0]))->getBody()); + CPPUNIT_ASSERT_EQUAL(std::string("user@localhost"), dynamic_cast(getStanza(received[0]))->getTo().toString()); + CPPUNIT_ASSERT_EQUAL(std::string("buddy1@localhost/bot"), dynamic_cast(getStanza(received[0]))->getFrom().toString()); + + received.clear(); + } + + void handleGroupchatMessages() { + 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->setJID("user@localhost/resource"); + + // 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) received.size()); + CPPUNIT_ASSERT(dynamic_cast(getStanza(received[0]))); + CPPUNIT_ASSERT_EQUAL(std::string("hi there!"), dynamic_cast(getStanza(received[0]))->getBody()); + CPPUNIT_ASSERT_EQUAL(std::string("user@localhost/resource"), dynamic_cast(getStanza(received[0]))->getTo().toString()); + CPPUNIT_ASSERT_EQUAL(std::string("#room@localhost/anotheruser"), dynamic_cast(getStanza(received[0]))->getFrom().toString()); + + received.clear(); + + // send response + msg->setFrom("user@localhost/resource"); + 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); + + conv->onMessageToSend.connect(boost::bind(&ConversationManagerTest::handleMessageReceived, this, _1, _2)); + conv->setNickname("nickname"); + conv->setJID("user@localhost/resource"); + + // normal presence + conv->handleParticipantChanged("anotheruser", 0, Swift::StatusShow::Away, "my status message"); + loop->processEvents(); + + CPPUNIT_ASSERT_EQUAL(1, (int) received.size()); + CPPUNIT_ASSERT(dynamic_cast(getStanza(received[0]))); + CPPUNIT_ASSERT_EQUAL(Swift::StatusShow::Away, dynamic_cast(getStanza(received[0]))->getShow()); + CPPUNIT_ASSERT_EQUAL(std::string("user@localhost/resource"), dynamic_cast(getStanza(received[0]))->getTo().toString()); + CPPUNIT_ASSERT_EQUAL(std::string("#room@localhost/anotheruser"), dynamic_cast(getStanza(received[0]))->getFrom().toString()); + CPPUNIT_ASSERT(getStanza(received[0])->getPayload()); + CPPUNIT_ASSERT_EQUAL(Swift::MUCOccupant::Member, *getStanza(received[0])->getPayload()->getItems()[0].affiliation); + CPPUNIT_ASSERT_EQUAL(Swift::MUCOccupant::Participant, *getStanza(received[0])->getPayload()->getItems()[0].role); + + received.clear(); + + // this user presence - status code 110 + conv->handleParticipantChanged("nickname", 1, Swift::StatusShow::Away, "my status message"); + loop->processEvents(); + + CPPUNIT_ASSERT_EQUAL(1, (int) received.size()); + CPPUNIT_ASSERT(dynamic_cast(getStanza(received[0]))); + CPPUNIT_ASSERT_EQUAL(Swift::StatusShow::Away, dynamic_cast(getStanza(received[0]))->getShow()); + CPPUNIT_ASSERT_EQUAL(std::string("user@localhost/resource"), dynamic_cast(getStanza(received[0]))->getTo().toString()); + CPPUNIT_ASSERT_EQUAL(std::string("#room@localhost/nickname"), dynamic_cast(getStanza(received[0]))->getFrom().toString()); + CPPUNIT_ASSERT(getStanza(received[0])->getPayload()); + CPPUNIT_ASSERT_EQUAL(Swift::MUCOccupant::Admin, *getStanza(received[0])->getPayload()->getItems()[0].affiliation); + CPPUNIT_ASSERT_EQUAL(Swift::MUCOccupant::Moderator, *getStanza(received[0])->getPayload()->getItems()[0].role); + CPPUNIT_ASSERT_EQUAL(110, getStanza(received[0])->getPayload()->getStatusCodes()[0].code); + + received.clear(); + + // renamed - status code 303 + conv->handleParticipantChanged("anotheruser", 1, Swift::StatusShow::Away, "my status message", "hanzz"); + loop->processEvents(); + + CPPUNIT_ASSERT_EQUAL(2, (int) received.size()); + CPPUNIT_ASSERT(dynamic_cast(getStanza(received[0]))); + CPPUNIT_ASSERT_EQUAL(Swift::Presence::Unavailable, dynamic_cast(getStanza(received[0]))->getType()); + CPPUNIT_ASSERT_EQUAL(std::string("user@localhost/resource"), dynamic_cast(getStanza(received[0]))->getTo().toString()); + CPPUNIT_ASSERT_EQUAL(std::string("#room@localhost/anotheruser"), dynamic_cast(getStanza(received[0]))->getFrom().toString()); + CPPUNIT_ASSERT(getStanza(received[0])->getPayload()); + CPPUNIT_ASSERT_EQUAL(Swift::MUCOccupant::Admin, *getStanza(received[0])->getPayload()->getItems()[0].affiliation); + CPPUNIT_ASSERT_EQUAL(Swift::MUCOccupant::Moderator, *getStanza(received[0])->getPayload()->getItems()[0].role); + CPPUNIT_ASSERT_EQUAL(std::string("hanzz"), *getStanza(received[0])->getPayload()->getItems()[0].nick); + CPPUNIT_ASSERT_EQUAL(303, getStanza(received[0])->getPayload()->getStatusCodes()[0].code); + } + + void handlePMFromXMPP() { + 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->setJID("user@localhost/resource"); + + conv->handleParticipantChanged("anotheruser", 0, Swift::StatusShow::Away, "my status message"); + loop->processEvents(); + + received.clear(); + boost::shared_ptr msg(new Swift::Message()); + msg->setBody("hi there!"); + msg->setFrom("user@localhost/resource"); + msg->setTo("#room@localhost/anotheruser"); + msg->setBody("hi there!"); + injectMessage(msg); + loop->processEvents(); + + CPPUNIT_ASSERT_EQUAL(0, (int) received.size()); + CPPUNIT_ASSERT(m_msg); + CPPUNIT_ASSERT_EQUAL(std::string("hi there!"), m_msg->getBody()); + CPPUNIT_ASSERT_EQUAL(std::string("#room/anotheruser"), m_conv->getLegacyName()); + + Conversation *pmconv = user->getConversationManager()->getConversation("#room/anotheruser"); + + boost::shared_ptr msg2(new Swift::Message()); + msg2->setBody("response!"); + + pmconv->handleMessage(msg2); + dumpReceived(); + } + +}; + +CPPUNIT_TEST_SUITE_REGISTRATION (ConversationManagerTest); diff --git a/src/tests/user.cpp b/src/tests/user.cpp index 2aac1d55..cd5044bf 100644 --- a/src/tests/user.cpp +++ b/src/tests/user.cpp @@ -138,6 +138,7 @@ class UserTest : public CPPUNIT_NS :: TestFixture, public BasicTest { // simulate that backend joined the room TestingConversation *conv = new TestingConversation(user->getConversationManager(), "#room", true); + user->getConversationManager()->addConversation(conv); received.clear(); injectPresence(response);