From 3201977efb6ad2672a1a6baa034fa0db493c746f Mon Sep 17 00:00:00 2001 From: Jan Kaluza Date: Thu, 18 Oct 2012 09:04:20 +0200 Subject: [PATCH] Get room list on IRC and show rooms in service discovery --- backends/libcommuni/session.cpp | 17 ++++ backends/libcommuni/session.h | 2 + include/transport/discoitemsresponder.h | 4 + include/transport/transport.h | 16 ---- include/transport/user.h | 2 + include/transport/usermanager.h | 6 +- spectrum/src/main.cpp | 8 +- spectrum/src/sample.cfg | 5 +- src/buddy.cpp | 4 +- src/config.cpp | 1 + src/discoinforesponder.cpp | 26 +++++- src/discoinforesponder.h | 4 + src/discoitemsresponder.cpp | 16 +++- src/networkpluginserver.cpp | 2 +- src/tests/basictest.cpp | 8 +- src/tests/discoitemsresponder.cpp | 100 ++++++++++++++++++++++++ src/transport.cpp | 18 +---- src/usermanager.cpp | 4 +- 18 files changed, 192 insertions(+), 51 deletions(-) create mode 100644 src/tests/discoitemsresponder.cpp diff --git a/backends/libcommuni/session.cpp b/backends/libcommuni/session.cpp index 6abdb4db..7b86f99f 100644 --- a/backends/libcommuni/session.cpp +++ b/backends/libcommuni/session.cpp @@ -23,6 +23,8 @@ DEFINE_LOGGER(logger, "IRCSession"); +static bool sentList; + MyIrcSession::MyIrcSession(const std::string &user, IRCNetworkPlugin *np, const std::string &suffix, QObject* parent) : IrcSession(parent) { this->np = np; @@ -40,6 +42,10 @@ void MyIrcSession::on_connected() { m_connected = true; if (suffix.empty()) { np->handleConnected(user); + if (!sentList) { + sendCommand(IrcCommand::createList("")); + sentList = true; + } } for(AutoJoinMap::iterator it = m_autoJoin.begin(); it != m_autoJoin.end(); it++) { @@ -208,6 +214,17 @@ void MyIrcSession::on_numericMessageReceived(IrcMessage *message) { case 432: np->handleDisconnected(user, pbnetwork::CONNECTION_ERROR_INVALID_USERNAME, "Erroneous Nickname"); break; + case 321: + m_rooms.clear(); + m_names.clear(); + break; + case 322: + m_rooms.push_back(TO_UTF8(m->parameters().value(1))); + m_names.push_back(TO_UTF8(m->parameters().value(1))); + break; + case 323: + np->handleRoomList("", m_rooms, m_names); + break; default: break; } diff --git a/backends/libcommuni/session.h b/backends/libcommuni/session.h index 507feb93..e8a262ac 100644 --- a/backends/libcommuni/session.h +++ b/backends/libcommuni/session.h @@ -85,6 +85,8 @@ protected: AutoJoinMap m_autoJoin; std::string m_topicData; bool m_connected; + std::list m_rooms; + std::list m_names; }; //class MyIrcBuffer : public Irc::Buffer diff --git a/include/transport/discoitemsresponder.h b/include/transport/discoitemsresponder.h index 2558b8b6..9b17e832 100644 --- a/include/transport/discoitemsresponder.h +++ b/include/transport/discoitemsresponder.h @@ -28,12 +28,15 @@ namespace Transport { class Component; +class DiscoInfoResponder; class DiscoItemsResponder : public Swift::GetResponder { public: DiscoItemsResponder(Component *component); ~DiscoItemsResponder(); + Swift::CapsInfo &getBuddyCapsInfo(); + void addAdHocCommand(const std::string &node, const std::string &name); // void removeAdHocCommand(const std::string &node); @@ -48,6 +51,7 @@ class DiscoItemsResponder : public Swift::GetResponder { Component *m_component; boost::shared_ptr m_commands; boost::shared_ptr m_rooms; + DiscoInfoResponder *m_discoInfoResponder; }; } \ No newline at end of file diff --git a/include/transport/transport.h b/include/transport/transport.h index e6f0610d..c69ca396 100644 --- a/include/transport/transport.h +++ b/include/transport/transport.h @@ -47,7 +47,6 @@ namespace Transport { // } SpectrumImportantFeatures; // class StorageBackend; - class DiscoInfoResponder; class Factory; class UserRegistry; @@ -81,8 +80,6 @@ namespace Transport { /// \return Swift::StanzaChannel associated with this Transport::Component. Swift::StanzaChannel *getStanzaChannel(); - Swift::CapsInfo &getBuddyCapsInfo(); - /// Returns Swift::IQRouter associated with this Component. /// \return Swift::IQRouter associated with this Component. @@ -104,18 +101,6 @@ namespace Transport { void start(); void stop(); - /// Sets disco#info features which are sent as answer to disco#info IQ-get. - - /// This sets features of transport contact (For example "j2j.domain.tld"). - /// \param features list of features as sent in disco#info response - void setTransportFeatures(std::list &features); - - /// Sets disco#info features which are sent as answer to disco#info IQ-get. - - /// This sets features of legacy network buddies (For example "me\40gmail.com@j2j.domain.tld"). - /// \param features list of features as sent in disco#info response - void setBuddyFeatures(std::list &features); - /// Returns Jabber ID of this transport. /// \return Jabber ID of this transport @@ -186,7 +171,6 @@ namespace Transport { Transport::UserRegistry *m_userRegistry; StorageBackend *m_storageBackend; - DiscoInfoResponder *m_discoInfoResponder; int m_reconnectCount; Config* m_config; std::string m_protocol; diff --git a/include/transport/user.h b/include/transport/user.h index e89ba70b..47a1206c 100644 --- a/include/transport/user.h +++ b/include/transport/user.h @@ -71,6 +71,8 @@ class User : public Swift::EntityCapsProvider { Component *getComponent() { return m_component; } + UserManager *getUserManager() { return m_userManager; } + void setData(void *data) { m_data = data; } void *getData() { return m_data; } diff --git a/include/transport/usermanager.h b/include/transport/usermanager.h index 2601be21..683d2f05 100644 --- a/include/transport/usermanager.h +++ b/include/transport/usermanager.h @@ -32,6 +32,7 @@ class Component; class StorageBackend; class StorageResponder; class RosterResponder; +class DiscoItemsResponder; /// Manages online XMPP Users. @@ -55,7 +56,7 @@ class UserManager : public Swift::EntityCapsProvider { /// Creates new UserManager. /// \param component Component which's presence will be handled /// \param storageBackend Storage backend used to fetch UserInfos - UserManager(Component *component, UserRegistry *userRegistry, StorageBackend *storageBackend = NULL); + UserManager(Component *component, UserRegistry *userRegistry, DiscoItemsResponder *discoItemsResponder, StorageBackend *storageBackend = NULL); /// Destroys UserManager. ~UserManager(); @@ -84,6 +85,8 @@ class UserManager : public Swift::EntityCapsProvider { Swift::DiscoInfo::ref getCaps(const Swift::JID&) const; + DiscoItemsResponder *getDiscoResponder() { return m_discoItemsResponder; } + /// Called when new User class is created. /// \param user newly created User class boost::signal onUserCreated; @@ -143,6 +146,7 @@ class UserManager : public Swift::EntityCapsProvider { Swift::Timer::ref m_removeTimer; unsigned long m_sentToXMPP; unsigned long m_sentToBackend; + DiscoItemsResponder *m_discoItemsResponder; friend class RosterResponder; }; diff --git a/spectrum/src/main.cpp b/spectrum/src/main.cpp index b966014e..587df261 100644 --- a/spectrum/src/main.cpp +++ b/spectrum/src/main.cpp @@ -384,7 +384,10 @@ int main(int argc, char **argv) Logging::redirect_stderr(); - UserManager userManager(&transport, &userRegistry, storageBackend); + DiscoItemsResponder discoItemsResponder(&transport); + discoItemsResponder.start(); + + UserManager userManager(&transport, &userRegistry, &discoItemsResponder, storageBackend); userManager_ = &userManager; UserRegistration *userRegistration = NULL; @@ -398,9 +401,6 @@ int main(int argc, char **argv) FileTransferManager ftManager(&transport, &userManager); - DiscoItemsResponder discoItemsResponder(&transport); - discoItemsResponder.start(); - NetworkPluginServer plugin(&transport, &config, &userManager, &ftManager, &discoItemsResponder); AdminInterface adminInterface(&transport, &userManager, &plugin, storageBackend, userRegistration); diff --git a/spectrum/src/sample.cfg b/spectrum/src/sample.cfg index 389c4872..361ee530 100644 --- a/spectrum/src/sample.cfg +++ b/spectrum/src/sample.cfg @@ -13,15 +13,16 @@ admin_password=test #cert=server.pfx #patch to PKCS#12 certificate #cert_password=test #password to that certificate if any users_per_backend=10 -backend=../..//backends/libpurple/spectrum2_libpurple_backend +#backend=../..//backends/libpurple/spectrum2_libpurple_backend #backend=../../backends/twitter/spectrum2_twitter_backend -#backend=/home/hanzz/code/libtransport/backends/libpurple/spectrum2_communi_backend +backend=/home/hanzz/code/libtransport/backends/libcommuni/spectrum2_libcommuni_backend protocol=prpl-icq #protocol=prpl-msn #protocol=any #protocol=prpl-icq working_dir=./ portfile=$jid.port +irc_server=irc.freenode.org [backend] #default_avatar=catmelonhead.jpg diff --git a/src/buddy.cpp b/src/buddy.cpp index 3a32c558..07d1a76c 100644 --- a/src/buddy.cpp +++ b/src/buddy.cpp @@ -23,6 +23,8 @@ #include "transport/user.h" #include "transport/transport.h" #include "transport/BlockPayload.h" +#include "transport/usermanager.h" +#include "transport/discoitemsresponder.h" namespace Transport { @@ -106,7 +108,7 @@ Swift::Presence::ref Buddy::generatePresenceStanza(int features, bool only_new) if (presence->getType() != Swift::Presence::Unavailable) { // caps - presence->addPayload(boost::shared_ptr(new Swift::CapsInfo(m_rosterManager->getUser()->getComponent()->getBuddyCapsInfo()))); + presence->addPayload(boost::shared_ptr(new Swift::CapsInfo(m_rosterManager->getUser()->getUserManager()->getDiscoResponder()->getBuddyCapsInfo()))); // if (features & 0/*TRANSPORT_FEATURE_AVATARS*/) { presence->addPayload(boost::shared_ptr(new Swift::VCardUpdate (getIconHash()))); diff --git a/src/config.cpp b/src/config.cpp index ce49f6d6..cda6af24 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -97,6 +97,7 @@ bool Config::load(std::istream &ifs, boost::program_options::options_description ("service.more_resources", value()->default_value(false), "Allow more resources to be connected in server mode at the same time.") ("service.enable_privacy_lists", value()->default_value(true), "") ("service.enable_xhtml", value()->default_value(true), "") + ("service.max_room_list_size", value()->default_value(100), "") ("vhosts.vhost", value >()->multitoken(), "") ("identity.name", value()->default_value("Spectrum 2 Transport"), "Name showed in service discovery.") ("identity.category", value()->default_value("gateway"), "Disco#info identity category. 'gateway' by default.") diff --git a/src/discoinforesponder.cpp b/src/discoinforesponder.cpp index 6d701088..0620b8c5 100644 --- a/src/discoinforesponder.cpp +++ b/src/discoinforesponder.cpp @@ -22,15 +22,19 @@ #include #include +#include #include "Swiften/Disco/DiscoInfoResponder.h" #include "Swiften/Queries/IQRouter.h" #include "Swiften/Elements/DiscoInfo.h" #include "Swiften/Swiften.h" #include "transport/config.h" +#include "transport/logging.h" using namespace Swift; using namespace boost; +DEFINE_LOGGER(logger, "DiscoInfoResponder"); + namespace Transport { DiscoInfoResponder::DiscoInfoResponder(Swift::IQRouter *router, Config *config) : Swift::GetResponder(router) { @@ -80,19 +84,37 @@ void DiscoInfoResponder::setBuddyFeatures(std::list &f) { onBuddyCapsInfoChanged(m_capsInfo); } +void DiscoInfoResponder::addRoom(const std::string &jid, const std::string &name) { + std::string j = jid; + boost::algorithm::to_lower(j); + m_rooms[j] = name; +} + +void DiscoInfoResponder::clearRooms() { + m_rooms.clear(); +} + bool DiscoInfoResponder::handleGetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr info) { if (!info->getNode().empty()) { sendError(from, id, ErrorPayload::ItemNotFound, ErrorPayload::Cancel); return true; } - // presence for transport + + // disco#info for transport if (to.getNode().empty()) { boost::shared_ptr res(new DiscoInfo(m_transportInfo)); res->setNode(info->getNode()); sendResponse(from, id, res); } - // presence for buddy + // disco#info for room + else 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->setNode(info->getNode()); + sendResponse(from, to, id, res); + } + // disco#info for buddy else { boost::shared_ptr res(new DiscoInfo(m_buddyInfo)); res->setNode(info->getNode()); diff --git a/src/discoinforesponder.h b/src/discoinforesponder.h index 7e374702..fadf0586 100644 --- a/src/discoinforesponder.h +++ b/src/discoinforesponder.h @@ -38,6 +38,9 @@ class DiscoInfoResponder : public Swift::GetResponder { void setTransportFeatures(std::list &features); void setBuddyFeatures(std::list &features); + void addRoom(const std::string &jid, const std::string &name); + void clearRooms(); + boost::signal onBuddyCapsInfoChanged; Swift::CapsInfo &getBuddyCapsInfo() { @@ -51,6 +54,7 @@ class DiscoInfoResponder : public Swift::GetResponder { Swift::DiscoInfo m_buddyInfo; Config *m_config; Swift::CapsInfo m_capsInfo; + std::map m_rooms; }; } \ No newline at end of file diff --git a/src/discoitemsresponder.cpp b/src/discoitemsresponder.cpp index ec5417f3..acec5822 100644 --- a/src/discoitemsresponder.cpp +++ b/src/discoitemsresponder.cpp @@ -26,6 +26,7 @@ #include "Swiften/Swiften.h" #include "transport/transport.h" #include "transport/logging.h" +#include "discoinforesponder.h" using namespace Swift; using namespace boost; @@ -40,10 +41,12 @@ DiscoItemsResponder::DiscoItemsResponder(Component *component) : Swift::GetRespo m_commands->setNode("http://jabber.org/protocol/commands"); m_rooms = boost::shared_ptr(new DiscoItems()); + m_discoInfoResponder = new DiscoInfoResponder(component->getIQRouter(), component->getConfig()); + m_discoInfoResponder->start(); } DiscoItemsResponder::~DiscoItemsResponder() { - + delete m_discoInfoResponder; } void DiscoItemsResponder::addAdHocCommand(const std::string &node, const std::string &name) { @@ -51,11 +54,20 @@ void DiscoItemsResponder::addAdHocCommand(const std::string &node, const std::st } void DiscoItemsResponder::addRoom(const std::string &node, const std::string &name) { - m_rooms->addItem(DiscoItems::Item(name, m_component->getJID(), node)); + if (m_rooms->getItems().size() > CONFIG_INT(m_component->getConfig(), "service.max_room_list_size")) { + return; + } + m_rooms->addItem(DiscoItems::Item(name, node)); + m_discoInfoResponder->addRoom(node, name); } void DiscoItemsResponder::clearRooms() { m_rooms = boost::shared_ptr(new DiscoItems()); + m_discoInfoResponder->clearRooms(); +} + +Swift::CapsInfo &DiscoItemsResponder::getBuddyCapsInfo() { + return m_discoInfoResponder->getBuddyCapsInfo(); } diff --git a/src/networkpluginserver.cpp b/src/networkpluginserver.cpp index 13138055..177469f8 100644 --- a/src/networkpluginserver.cpp +++ b/src/networkpluginserver.cpp @@ -861,7 +861,7 @@ void NetworkPluginServer::handleRoomListPayload(const std::string &data) { m_discoItemsResponder->clearRooms(); for (int i = 0; i < payload.room_size() && i < payload.name_size(); i++) { - m_discoItemsResponder->addRoom(payload.room(i), payload.name(i)); + m_discoItemsResponder->addRoom(Swift::JID::getEscapedNode(payload.room(i)) + "@" + m_component->getJID().toString(), payload.name(i)); } } diff --git a/src/tests/basictest.cpp b/src/tests/basictest.cpp index 9a893e7d..7c3ef14c 100644 --- a/src/tests/basictest.cpp +++ b/src/tests/basictest.cpp @@ -57,14 +57,14 @@ void BasicTest::setMeUp (void) { component = new Component(loop, factories, cfg, factory, userRegistry); component->start(); - userManager = new UserManager(component, userRegistry, storage); + itemsResponder = new DiscoItemsResponder(component); + itemsResponder->start(); + + userManager = new UserManager(component, userRegistry, itemsResponder, storage); userRegistration = new UserRegistration(component, userManager, storage); userRegistration->start(); - itemsResponder = new DiscoItemsResponder(component); - itemsResponder->start(); - payloadSerializers = new Swift::FullPayloadSerializerCollection(); payloadParserFactories = new Swift::FullPayloadParserFactoryCollection(); diff --git a/src/tests/discoitemsresponder.cpp b/src/tests/discoitemsresponder.cpp new file mode 100644 index 00000000..6032cacb --- /dev/null +++ b/src/tests/discoitemsresponder.cpp @@ -0,0 +1,100 @@ +#include "transport/userregistry.h" +#include "transport/userregistration.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/localbuddy.h" +#include "transport/settingsadhoccommand.h" +#include "transport/adhocmanager.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 DiscoItemsResponderTest : public CPPUNIT_NS :: TestFixture, public BasicTest { + CPPUNIT_TEST_SUITE(DiscoItemsResponderTest); + CPPUNIT_TEST(roomList); + CPPUNIT_TEST(roomInfo); + CPPUNIT_TEST(clearRooms); + CPPUNIT_TEST_SUITE_END(); + + public: + + void setUp (void) { + setMeUp(); + } + + void tearDown (void) { + received.clear(); + tearMeDown(); + } + + void roomList() { + 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()); + } + + void roomInfo() { + 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("#room@localhost"); + injectIQ(iq); + loop->processEvents(); + + dumpReceived(); + 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"), 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 clearRooms() { + itemsResponder->addRoom("#room@localhost", "#room"); + itemsResponder->clearRooms(); + + 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(getStanza(received[0])->getPayload()->getItems().empty()); + } + +}; + +CPPUNIT_TEST_SUITE_REGISTRATION (DiscoItemsResponderTest); diff --git a/src/transport.cpp b/src/transport.cpp index 99ef21d9..95ca0319 100644 --- a/src/transport.cpp +++ b/src/transport.cpp @@ -26,7 +26,6 @@ #include "transport/factory.h" #include "transport/userregistry.h" #include "transport/logging.h" -#include "discoinforesponder.h" #include "storageparser.h" #ifdef _WIN32 #include @@ -158,8 +157,7 @@ Component::Component(Swift::EventLoop *loop, Swift::NetworkFactories *factories, m_presenceOracle = new Transport::PresenceOracle(m_stanzaChannel); m_presenceOracle->onPresenceChange.connect(bind(&Component::handlePresence, this, _1)); - m_discoInfoResponder = new DiscoInfoResponder(m_iqRouter, m_config); - m_discoInfoResponder->start(); + // // m_registerHandler = new SpectrumRegisterHandler(m_component); @@ -171,7 +169,6 @@ Component::~Component() { delete m_entityCapsManager; delete m_capsManager; delete m_capsMemoryStorage; - delete m_discoInfoResponder; if (m_component) delete m_component; if (m_server) { @@ -188,19 +185,6 @@ Transport::PresenceOracle *Component::getPresenceOracle() { return m_presenceOracle; } -void Component::setTransportFeatures(std::list &features) { - m_discoInfoResponder->setTransportFeatures(features); -} - -Swift::CapsInfo &Component::getBuddyCapsInfo() { - return m_discoInfoResponder->getBuddyCapsInfo(); -} - -void Component::setBuddyFeatures(std::list &features) { - // TODO: handle caps change - m_discoInfoResponder->setBuddyFeatures(features); -} - void Component::start() { if (m_component && !m_component->isAvailable()) { LOG4CXX_INFO(logger, "Connecting XMPP server " << CONFIG_STRING(m_config, "service.server") << " port " << CONFIG_INT(m_config, "service.port")); diff --git a/src/usermanager.cpp b/src/usermanager.cpp index 98c75dcf..a24161f2 100644 --- a/src/usermanager.cpp +++ b/src/usermanager.cpp @@ -26,6 +26,7 @@ #include "transport/rostermanager.h" #include "transport/userregistry.h" #include "transport/logging.h" +#include "transport/discoitemsresponder.h" #include "storageresponder.h" #include "Swiften/Swiften.h" @@ -40,7 +41,7 @@ namespace Transport { DEFINE_LOGGER(logger, "UserManager"); -UserManager::UserManager(Component *component, UserRegistry *userRegistry, StorageBackend *storageBackend) { +UserManager::UserManager(Component *component, UserRegistry *userRegistry, DiscoItemsResponder *discoItemsResponder, StorageBackend *storageBackend) { m_cachedUser = NULL; m_onlineBuddies = 0; m_sentToXMPP = 0; @@ -49,6 +50,7 @@ UserManager::UserManager(Component *component, UserRegistry *userRegistry, Stora m_storageBackend = storageBackend; m_storageResponder = NULL; m_userRegistry = userRegistry; + m_discoItemsResponder = discoItemsResponder; if (m_storageBackend) { m_storageResponder = new StorageResponder(component->getIQRouter(), m_storageBackend, this);