diff --git a/backends/libpurple/main.cpp b/backends/libpurple/main.cpp index 005430c2..81fb77c0 100644 --- a/backends/libpurple/main.cpp +++ b/backends/libpurple/main.cpp @@ -1599,7 +1599,17 @@ static void RoomlistProgress(PurpleRoomlist *list, gboolean in_progress) PurpleRoomlistRoom *room = (PurpleRoomlistRoom *)rooms->data; m_rooms.push_back(room->name); } - np->handleRoomList("", m_rooms, m_rooms); + + std::string user = ""; + if (list->account) { + user = np->m_accounts[list->account]; + } + + LOG4CXX_INFO(logger, "RoomList is fetched for user " << user); + np->handleRoomList(user, m_rooms, m_rooms); + } + else { + LOG4CXX_INFO(logger, "RoomList is still in progress"); } } diff --git a/include/transport/User.h b/include/transport/User.h index e65e6a4d..a4eda2a8 100644 --- a/include/transport/User.h +++ b/include/transport/User.h @@ -76,6 +76,8 @@ class User { UserManager *getUserManager() { return m_userManager; } virtual void disconnectUser(const std::string &error, Swift::SpectrumErrorPayload::Error e) = 0; + virtual void clearRoomList() {} + virtual void addRoomToRoomList(const std::string &handle, const std::string &name) {} virtual void requestVCard() {} void setData(void *data) { m_data = data; } diff --git a/include/transport/protocol.proto b/include/transport/protocol.proto index 48f11bf7..703738f6 100644 --- a/include/transport/protocol.proto +++ b/include/transport/protocol.proto @@ -89,6 +89,7 @@ message Room { message RoomList { repeated string room = 1; repeated string name = 2; + optional string user = 3; } enum ParticipantFlag { diff --git a/libtransport/NetworkPluginServer.cpp b/libtransport/NetworkPluginServer.cpp index 8a5242f6..78667505 100644 --- a/libtransport/NetworkPluginServer.cpp +++ b/libtransport/NetworkPluginServer.cpp @@ -994,9 +994,23 @@ void NetworkPluginServer::handleRoomListPayload(const std::string &data) { return; } - m_component->getFrontend()->clearRoomList(); - for (int i = 0; i < payload.room_size() && i < payload.name_size(); i++) { - m_component->getFrontend()->addRoomToRoomList(Swift::JID::getEscapedNode(payload.room(i)) + "@" + m_component->getJID().toString(), payload.name(i)); + if (!payload.user().empty()) { + User *user = m_userManager->getUser(payload.user()); + if (!user) { + LOG4CXX_ERROR(logger, "Room list payload received for unknown user " << payload.user()); + return; + } + + user->clearRoomList(); + for (int i = 0; i < payload.room_size() && i < payload.name_size(); i++) { + user->addRoomToRoomList(Swift::JID::getEscapedNode(payload.room(i)) + "@" + m_component->getJID().toString(), payload.name(i)); + } + } + else { + m_component->getFrontend()->clearRoomList(); + for (int i = 0; i < payload.room_size() && i < payload.name_size(); i++) { + m_component->getFrontend()->addRoomToRoomList(Swift::JID::getEscapedNode(payload.room(i)) + "@" + m_component->getJID().toString(), payload.name(i)); + } } } #if HAVE_SWIFTEN_3 diff --git a/plugin/cpp/networkplugin.cpp b/plugin/cpp/networkplugin.cpp index 8149c6f5..9d1a73e1 100644 --- a/plugin/cpp/networkplugin.cpp +++ b/plugin/cpp/networkplugin.cpp @@ -376,6 +376,8 @@ void NetworkPlugin::handleRoomList(const std::string &user, const std::list @@ -63,6 +62,7 @@ #include "Swiften/Parser/GenericPayloadParserFactory.h" #include "Swiften/Queries/IQRouter.h" #include "Swiften/Elements/RosterPayload.h" +#include "discoitemsresponder.h" #include "Swiften/Elements/InBandRegistrationPayload.h" using namespace Swift; @@ -81,6 +81,7 @@ void XMPPFrontend::init(Component *transport, Swift::EventLoop *loop, Swift::Net m_server = NULL; m_rawXML = false; m_config = transport->getConfig(); + m_userManager = NULL; m_jid = Swift::JID(CONFIG_STRING(m_config, "service.jid")); m_config->onBackendConfigUpdated.connect(boost::bind(&XMPPFrontend::handleBackendConfigChanged, this)); @@ -173,9 +174,6 @@ void XMPPFrontend::init(Component *transport, Swift::EventLoop *loop, Swift::Net m_stanzaChannel->onPresenceReceived.connect(bind(&XMPPFrontend::handleGeneralPresence, this, _1)); m_stanzaChannel->onMessageReceived.connect(bind(&XMPPFrontend::handleMessage, this, _1)); - - m_discoItemsResponder = new DiscoItemsResponder(transport); - m_discoItemsResponder->start(); } XMPPFrontend::~XMPPFrontend() { @@ -200,16 +198,16 @@ void XMPPFrontend::handleMessage(boost::shared_ptr message) { void XMPPFrontend::clearRoomList() { - m_discoItemsResponder->clearRooms(); + static_cast(m_userManager)->getDiscoItemsResponder()->clearRooms(); } void XMPPFrontend::addRoomToRoomList(const std::string &handle, const std::string &name) { - m_discoItemsResponder->addRoom(handle, name); + static_cast(m_userManager)->getDiscoItemsResponder()->addRoom(handle, name); } void XMPPFrontend::sendPresence(Swift::Presence::ref presence) { if (!presence->getFrom().getNode().empty()) { - presence->addPayload(boost::shared_ptr(new Swift::CapsInfo(m_discoItemsResponder->getBuddyCapsInfo()))); + presence->addPayload(boost::shared_ptr(new Swift::CapsInfo(static_cast(m_userManager)->getDiscoItemsResponder()->getBuddyCapsInfo()))); } m_stanzaChannel->sendPresence(presence); @@ -277,7 +275,8 @@ User *XMPPFrontend::createUser(const Swift::JID &jid, UserInfo &userInfo, Compon } UserManager *XMPPFrontend::createUserManager(Component *component, UserRegistry *userRegistry, StorageBackend *storageBackend) { - return new XMPPUserManager(component, userRegistry, storageBackend); + m_userManager = new XMPPUserManager(component, userRegistry, storageBackend); + return m_userManager; } bool XMPPFrontend::handleIQ(boost::shared_ptr iq) { diff --git a/spectrum/src/frontends/xmpp/XMPPFrontend.h b/spectrum/src/frontends/xmpp/XMPPFrontend.h index 4fc1733c..e627c163 100644 --- a/spectrum/src/frontends/xmpp/XMPPFrontend.h +++ b/spectrum/src/frontends/xmpp/XMPPFrontend.h @@ -47,7 +47,6 @@ namespace Transport { class UserRegistry; class Frontend; class Config; - class DiscoItemsResponder; class VCardResponder; class XMPPFrontend : public Frontend, Swift::IQHandler { @@ -95,10 +94,6 @@ namespace Transport { return m_rawXML; } - DiscoItemsResponder *getDiscoItemsResponder() { - return m_discoItemsResponder; - } - private: void handleConnected(); void handleConnectionError(const Swift::ComponentError &error); @@ -121,13 +116,13 @@ namespace Transport { Swift::CapsMemoryStorage *m_capsMemoryStorage; Swift::StanzaChannel *m_stanzaChannel; Swift::IQRouter *m_iqRouter; - DiscoItemsResponder *m_discoItemsResponder; VCardResponder *m_vcardResponder; Config* m_config; Swift::JID m_jid; bool m_rawXML; Component *m_transport; + UserManager *m_userManager; friend class XMPPUser; friend class UserRegistration; diff --git a/spectrum/src/frontends/xmpp/XMPPUser.cpp b/spectrum/src/frontends/xmpp/XMPPUser.cpp index ba0f113a..45a0e4e6 100644 --- a/spectrum/src/frontends/xmpp/XMPPUser.cpp +++ b/spectrum/src/frontends/xmpp/XMPPUser.cpp @@ -45,6 +45,7 @@ XMPPUser::XMPPUser(const Swift::JID &jid, UserInfo &userInfo, Component *compone m_component = component; m_userManager = userManager; m_userInfo = userInfo; + m_rooms = boost::shared_ptr(new Swift::DiscoItems()); } XMPPUser::~XMPPUser(){ @@ -98,5 +99,13 @@ void XMPPUser::requestVCard() { m_vcardRequests.push_back(request); } +void XMPPUser::clearRoomList() { + m_rooms = boost::shared_ptr(new Swift::DiscoItems()); +} + +void XMPPUser::addRoomToRoomList(const std::string &handle, const std::string &name) { + m_rooms->addItem(Swift::DiscoItems::Item(name, handle)); +} + } diff --git a/spectrum/src/frontends/xmpp/XMPPUser.h b/spectrum/src/frontends/xmpp/XMPPUser.h index 29e62432..ca76afde 100644 --- a/spectrum/src/frontends/xmpp/XMPPUser.h +++ b/spectrum/src/frontends/xmpp/XMPPUser.h @@ -30,6 +30,7 @@ #include "Swiften/Network/Timer.h" #include "Swiften/Network/Connection.h" #include "Swiften/VCards/GetVCardRequest.h" +#include "Swiften/Elements/DiscoItems.h" namespace Transport { @@ -57,6 +58,14 @@ class XMPPUser : public User { void requestVCard(); + void clearRoomList(); + + void addRoomToRoomList(const std::string &handle, const std::string &name); + + boost::shared_ptr getRoomList() { + return m_rooms; + } + private: void onConnectingTimeout(); void handleVCardReceived(boost::shared_ptr vcard, Swift::ErrorPayload::ref error, Swift::GetVCardRequest::ref request); @@ -66,6 +75,7 @@ class XMPPUser : public User { UserManager *m_userManager; UserInfo m_userInfo; std::list m_vcardRequests; + boost::shared_ptr m_rooms; }; } diff --git a/spectrum/src/frontends/xmpp/XMPPUserManager.cpp b/spectrum/src/frontends/xmpp/XMPPUserManager.cpp index 6f9c92fd..b9919fe9 100644 --- a/spectrum/src/frontends/xmpp/XMPPUserManager.cpp +++ b/spectrum/src/frontends/xmpp/XMPPUserManager.cpp @@ -32,6 +32,7 @@ #include "adhocmanager.h" #include "settingsadhoccommand.h" #include "RosterResponder.h" +#include "discoitemsresponder.h" #include "Swiften/Server/ServerStanzaChannel.h" #include "Swiften/Elements/StreamError.h" @@ -82,7 +83,10 @@ XMPPUserManager::XMPPUserManager(Component *component, UserRegistry *userRegistr m_rosterResponder = new RosterResponder(frontend->getIQRouter(), this); m_rosterResponder->start(); - m_adHocManager = new AdHocManager(component, frontend->getDiscoItemsResponder(), this, storageBackend); + m_discoItemsResponder = new DiscoItemsResponder(component, this); + m_discoItemsResponder->start(); + + m_adHocManager = new AdHocManager(component, m_discoItemsResponder, this, storageBackend); m_adHocManager->start(); SettingsAdHocCommandFactory *m_settings = new SettingsAdHocCommandFactory(); @@ -111,6 +115,9 @@ XMPPUserManager::~XMPPUserManager() { m_rosterResponder->stop(); delete m_rosterResponder; + + m_discoItemsResponder->stop(); + delete m_discoItemsResponder; } void XMPPUserManager::sendVCard(unsigned int id, Swift::VCard::ref vcard) { diff --git a/spectrum/src/frontends/xmpp/XMPPUserManager.h b/spectrum/src/frontends/xmpp/XMPPUserManager.h index 9ba994ac..315e9e1f 100644 --- a/spectrum/src/frontends/xmpp/XMPPUserManager.h +++ b/spectrum/src/frontends/xmpp/XMPPUserManager.h @@ -40,6 +40,7 @@ class GatewayResponder; class AdHocManager; class SettingsAdHocCommandFactory; class RosterResponder; +class DiscoItemsResponder; class XMPPUserManager : public UserManager { public: @@ -51,6 +52,10 @@ class XMPPUserManager : public UserManager { UserRegistration *getUserRegistration(); + DiscoItemsResponder *getDiscoItemsResponder() { + return m_discoItemsResponder; + } + private: void handleVCardRequired(User *, const std::string &name, unsigned int id); void handleVCardUpdated(User *, boost::shared_ptr vcard); @@ -63,6 +68,7 @@ class XMPPUserManager : public UserManager { AdHocManager *m_adHocManager; SettingsAdHocCommandFactory *m_settings; RosterResponder *m_rosterResponder; + DiscoItemsResponder *m_discoItemsResponder; }; } diff --git a/spectrum/src/frontends/xmpp/discoinforesponder.cpp b/spectrum/src/frontends/xmpp/discoinforesponder.cpp index 616b4162..6acabf48 100644 --- a/spectrum/src/frontends/xmpp/discoinforesponder.cpp +++ b/spectrum/src/frontends/xmpp/discoinforesponder.cpp @@ -30,6 +30,8 @@ #include "transport/Logging.h" #include "Swiften/Disco/CapsInfoGenerator.h" #include "XMPPFrontend.h" +#include "transport/UserManager.h" +#include "XMPPUser.h" using namespace Swift; using namespace boost; @@ -38,7 +40,7 @@ DEFINE_LOGGER(logger, "DiscoInfoResponder"); namespace Transport { -DiscoInfoResponder::DiscoInfoResponder(Swift::IQRouter *router, Config *config) : Swift::GetResponder(router) { +DiscoInfoResponder::DiscoInfoResponder(Swift::IQRouter *router, Config *config, UserManager *userManager) : Swift::GetResponder(router) { m_config = config; m_config->onBackendConfigUpdated.connect(boost::bind(&DiscoInfoResponder::updateFeatures, this)); m_buddyInfo = NULL; @@ -50,6 +52,7 @@ DiscoInfoResponder::DiscoInfoResponder(Swift::IQRouter *router, Config *config) #endif updateFeatures(); + m_userManager = userManager; } DiscoInfoResponder::~DiscoInfoResponder() { @@ -148,21 +151,39 @@ bool DiscoInfoResponder::handleGetRequest(const Swift::JID& from, const Swift::J res->setNode(info->getNode()); sendResponse(from, id, res); } + return true; } + // disco#info for room - else if (m_rooms.find(to.toBare().toString()) != m_rooms.end()) { + if (m_rooms.find(to.toBare().toString()) != m_rooms.end()) { boost::shared_ptr res(new DiscoInfo()); res->addIdentity(DiscoInfo::Identity(m_rooms[to.toBare().toString()], "conference", "text")); res->addFeature("http://jabber.org/protocol/muc"); res->setNode(info->getNode()); sendResponse(from, to, id, res); + return true; } + + // disco#info for per-user rooms (like Skype/Facebook groupchats) + XMPPUser *user = static_cast(m_userManager->getUser(from.toBare().toString())); + if (user) { + BOOST_FOREACH(const DiscoItems::Item &item, user->getRoomList()->getItems()) { + LOG4CXX_INFO(logger, "XXX " << item.getNode() << " " << to.toBare().toString()); + if (item.getJID().toString() == to.toBare().toString()) { + boost::shared_ptr res(new DiscoInfo()); + res->addIdentity(DiscoInfo::Identity(item.getName(), "conference", "text")); + res->addFeature("http://jabber.org/protocol/muc"); + res->setNode(info->getNode()); + sendResponse(from, to, id, res); + return true; + } + } + } + // disco#info for buddy - else { - boost::shared_ptr res(new DiscoInfo(*m_buddyInfo)); - res->setNode(info->getNode()); - sendResponse(from, to, id, res); - } + boost::shared_ptr res(new DiscoInfo(*m_buddyInfo)); + res->setNode(info->getNode()); + sendResponse(from, to, id, res); return true; } diff --git a/spectrum/src/frontends/xmpp/discoinforesponder.h b/spectrum/src/frontends/xmpp/discoinforesponder.h index 5d93e6aa..459d7440 100644 --- a/spectrum/src/frontends/xmpp/discoinforesponder.h +++ b/spectrum/src/frontends/xmpp/discoinforesponder.h @@ -37,10 +37,11 @@ namespace Transport { class Config; +class UserManager; class DiscoInfoResponder : public Swift::GetResponder { public: - DiscoInfoResponder(Swift::IQRouter *router, Config *config); + DiscoInfoResponder(Swift::IQRouter *router, Config *config, UserManager *userManager); ~DiscoInfoResponder(); void setTransportFeatures(std::list &features); @@ -70,6 +71,7 @@ class DiscoInfoResponder : public Swift::GetResponder { #if HAVE_SWIFTEN_3 boost::shared_ptr crypto; #endif + UserManager *m_userManager; }; } diff --git a/spectrum/src/frontends/xmpp/discoitemsresponder.cpp b/spectrum/src/frontends/xmpp/discoitemsresponder.cpp index c53e41a6..c5a2a924 100644 --- a/spectrum/src/frontends/xmpp/discoitemsresponder.cpp +++ b/spectrum/src/frontends/xmpp/discoitemsresponder.cpp @@ -29,6 +29,8 @@ #include "discoinforesponder.h" #include "XMPPFrontend.h" #include "transport/Frontend.h" +#include "transport/UserManager.h" +#include "XMPPUser.h" using namespace Swift; using namespace boost; @@ -37,14 +39,16 @@ namespace Transport { DEFINE_LOGGER(logger, "DiscoItemsResponder"); -DiscoItemsResponder::DiscoItemsResponder(Component *component) : Swift::GetResponder(static_cast(component->getFrontend())->getIQRouter()) { +DiscoItemsResponder::DiscoItemsResponder(Component *component, UserManager *userManager) : Swift::GetResponder(static_cast(component->getFrontend())->getIQRouter()) { m_component = component; m_commands = boost::shared_ptr(new DiscoItems()); m_commands->setNode("http://jabber.org/protocol/commands"); m_rooms = boost::shared_ptr(new DiscoItems()); - m_discoInfoResponder = new DiscoInfoResponder(static_cast(component->getFrontend())->getIQRouter(), component->getConfig()); + m_discoInfoResponder = new DiscoInfoResponder(static_cast(component->getFrontend())->getIQRouter(), component->getConfig(), userManager); m_discoInfoResponder->start(); + + m_userManager = userManager; } DiscoItemsResponder::~DiscoItemsResponder() { @@ -80,7 +84,21 @@ bool DiscoItemsResponder::handleGetRequest(const Swift::JID& from, const Swift:: sendResponse(from, id, m_commands); } else if (to.getNode().empty()) { - sendResponse(from, id, m_rooms); + XMPPUser *user = static_cast(m_userManager->getUser(from.toBare().toString())); + if (!user) { + sendResponse(from, id, m_rooms); + return true; + } + + boost::shared_ptr rooms = boost::shared_ptr(new DiscoItems()); + BOOST_FOREACH(const DiscoItems::Item &item, m_rooms->getItems()) { + rooms->addItem(item); + } + BOOST_FOREACH(const DiscoItems::Item &item, user->getRoomList()->getItems()) { + rooms->addItem(item); + } + + sendResponse(from, id, rooms); } else { sendResponse(from, id, boost::shared_ptr(new DiscoItems())); diff --git a/spectrum/src/frontends/xmpp/discoitemsresponder.h b/spectrum/src/frontends/xmpp/discoitemsresponder.h index ff12cdcf..cb828f3f 100644 --- a/spectrum/src/frontends/xmpp/discoitemsresponder.h +++ b/spectrum/src/frontends/xmpp/discoitemsresponder.h @@ -29,10 +29,11 @@ namespace Transport { class Component; class DiscoInfoResponder; +class UserManager; class DiscoItemsResponder : public Swift::GetResponder { public: - DiscoItemsResponder(Component *component); + DiscoItemsResponder(Component *component, UserManager *userManager); ~DiscoItemsResponder(); Swift::CapsInfo &getBuddyCapsInfo(); @@ -52,6 +53,7 @@ class DiscoItemsResponder : public Swift::GetResponder { boost::shared_ptr m_commands; boost::shared_ptr m_rooms; DiscoInfoResponder *m_discoInfoResponder; + UserManager *m_userManager; }; } diff --git a/tests/libtransport/basictest.cpp b/tests/libtransport/basictest.cpp index db352217..2ec25a70 100644 --- a/tests/libtransport/basictest.cpp +++ b/tests/libtransport/basictest.cpp @@ -55,7 +55,7 @@ void BasicTest::setMeUp (void) { userManager = frontend->createUserManager(component, userRegistry, storage); - itemsResponder = frontend->getDiscoItemsResponder(); + itemsResponder = static_cast(userManager)->getDiscoItemsResponder(); payloadSerializers = new Swift::FullPayloadSerializerCollection(); payloadParserFactories = new Swift::FullPayloadParserFactoryCollection(); diff --git a/tests/libtransport/discoitemsresponder.cpp b/tests/libtransport/discoitemsresponder.cpp index b27aa67f..a93c2a65 100644 --- a/tests/libtransport/discoitemsresponder.cpp +++ b/tests/libtransport/discoitemsresponder.cpp @@ -15,7 +15,9 @@ using namespace Transport; class DiscoItemsResponderTest : public CPPUNIT_NS :: TestFixture, public BasicTest { CPPUNIT_TEST_SUITE(DiscoItemsResponderTest); CPPUNIT_TEST(roomList); + CPPUNIT_TEST(roomListUser); CPPUNIT_TEST(roomInfo); + CPPUNIT_TEST(roomInfoUser); CPPUNIT_TEST(roomListEscaping); CPPUNIT_TEST(roomInfoEscaping); CPPUNIT_TEST(clearRooms); @@ -50,6 +52,50 @@ class DiscoItemsResponderTest : public CPPUNIT_NS :: TestFixture, public BasicTe CPPUNIT_ASSERT_EQUAL(std::string("#room"), getStanza(received[0])->getPayload()->getItems()[0].getName()); } + void roomListUser() { + connectUser(); + User *user = userManager->getUser("user@localhost"); + user->addRoomToRoomList("#room2@localhost", "#room2"); + itemsResponder->addRoom("#room@localhost", "#room"); + + boost::shared_ptr payload(new Swift::DiscoItems()); + boost::shared_ptr iq = Swift::IQ::createRequest(Swift::IQ::Get, Swift::JID("localhost"), "id", payload); + iq->setFrom("user@localhost"); + injectIQ(iq); + loop->processEvents(); + + CPPUNIT_ASSERT_EQUAL(1, (int) received.size()); + CPPUNIT_ASSERT(dynamic_cast(getStanza(received[0]))); + CPPUNIT_ASSERT_EQUAL(Swift::IQ::Result, dynamic_cast(getStanza(received[0]))->getType()); + CPPUNIT_ASSERT(getStanza(received[0])->getPayload()); + CPPUNIT_ASSERT_EQUAL(std::string("#room@localhost"), getStanza(received[0])->getPayload()->getItems()[0].getJID().toString()); + CPPUNIT_ASSERT_EQUAL(std::string("#room"), getStanza(received[0])->getPayload()->getItems()[0].getName()); + CPPUNIT_ASSERT_EQUAL(std::string("#room2@localhost"), getStanza(received[0])->getPayload()->getItems()[1].getJID().toString()); + CPPUNIT_ASSERT_EQUAL(std::string("#room2"), getStanza(received[0])->getPayload()->getItems()[1].getName()); + } + + void roomInfoUser() { + connectUser(); + User *user = userManager->getUser("user@localhost"); + user->addRoomToRoomList("#room2@localhost", "#room2"); + itemsResponder->addRoom("#room@localhost", "#room"); + + boost::shared_ptr payload(new Swift::DiscoInfo()); + boost::shared_ptr iq = Swift::IQ::createRequest(Swift::IQ::Get, Swift::JID("localhost"), "id", payload); + iq->setFrom("user@localhost"); + iq->setTo("#room2@localhost"); + injectIQ(iq); + loop->processEvents(); + + CPPUNIT_ASSERT_EQUAL(1, (int) received.size()); + CPPUNIT_ASSERT(dynamic_cast(getStanza(received[0]))); + CPPUNIT_ASSERT_EQUAL(Swift::IQ::Result, dynamic_cast(getStanza(received[0]))->getType()); + CPPUNIT_ASSERT(getStanza(received[0])->getPayload()); + CPPUNIT_ASSERT_EQUAL(std::string("#room2"), getStanza(received[0])->getPayload()->getIdentities()[0].getName()); + CPPUNIT_ASSERT_EQUAL(std::string("conference"), getStanza(received[0])->getPayload()->getIdentities()[0].getCategory()); + CPPUNIT_ASSERT_EQUAL(std::string("text"), getStanza(received[0])->getPayload()->getIdentities()[0].getType()); + } + void roomInfo() { itemsResponder->addRoom("#room@localhost", "#room");