From e5aab1415d9e89392413aabe38d322a2d396b2f5 Mon Sep 17 00:00:00 2001 From: HanzZ Date: Fri, 22 Feb 2013 09:45:31 +0100 Subject: [PATCH] Libtransport: Support for bouncer mode in case of rawxml. Support for more resources in Buddy class. --- backends/skype/skypedb.cpp | 44 +++++++++++++++++- backends/skype/skypedb.h | 4 +- include/transport/buddy.h | 8 +++- include/transport/conversation.h | 11 ++++- include/transport/localbuddy.h | 8 ++-- include/transport/transport.h | 4 ++ src/buddy.cpp | 47 +++++++++++++++----- src/conversation.cpp | 19 ++++---- src/localbuddy.cpp | 9 ++++ src/networkpluginserver.cpp | 13 ++++-- src/rostermanager.cpp | 34 +++++++++----- src/tests/basictest.cpp | 3 ++ src/tests/conversationmanager.cpp | 5 +++ src/tests/localbuddy.cpp | 5 +++ src/tests/networkpluginserver.cpp | 74 +++++++++++++++++++++++++++++-- 15 files changed, 240 insertions(+), 48 deletions(-) diff --git a/backends/skype/skypedb.cpp b/backends/skype/skypedb.cpp index 4daf44ee..16c332a7 100644 --- a/backends/skype/skypedb.cpp +++ b/backends/skype/skypedb.cpp @@ -40,6 +40,10 @@ #include "malloc.h" #endif +#include "skypeplugin.h" + +#include "skypeplugin.h" + // Prepare the SQL statement #define PREP_STMT(sql, str) \ if(sqlite3_prepare_v2(db, std::string(str).c_str(), -1, &sql, NULL)) { \ @@ -104,7 +108,7 @@ bool getAvatar(const std::string &db_path, const std::string &name, std::string FINALIZE_STMT(stmt); } else { - LOG4CXX_ERROR(logger, "Can't created prepared statement"); + LOG4CXX_ERROR(logger, "Can't create prepared statement"); LOG4CXX_ERROR(logger, (sqlite3_errmsg(db) == NULL ? "" : sqlite3_errmsg(db))); } sqlite3_close(db); @@ -112,4 +116,42 @@ bool getAvatar(const std::string &db_path, const std::string &name, std::string return ret; } +bool loadBuddies(Transport::NetworkPlugin *np, const std::string &db_path) { + bool ret = false; +// sqlite3 *db; +// LOG4CXX_INFO(logger, "Opening database " << db_path); +// if (sqlite3_open(db_path.c_str(), &db)) { +// sqlite3_close(db); +// LOG4CXX_ERROR(logger, "Can't open database"); +// } +// else { +// sqlite3_stmt *stmt; +// PREP_STMT(stmt, "SELECT avatar_image FROM Contacts WHERE skypename=?"); +// if (stmt) { +// BEGIN(stmt); +// BIND_STR(stmt, name); +// if(sqlite3_step(stmt) == SQLITE_ROW) { +// int size = sqlite3_column_bytes(stmt, 0); +// const void *data = sqlite3_column_blob(stmt, 0); +// photo = std::string((const char *)data + 1, size - 1); +// ret = true; +// } +// else { +// LOG4CXX_ERROR(logger, (sqlite3_errmsg(db) == NULL ? "" : sqlite3_errmsg(db))); +// } +// +// int ret; +// while((ret = sqlite3_step(stmt)) == SQLITE_ROW) { +// } +// FINALIZE_STMT(stmt); +// } +// else { +// LOG4CXX_ERROR(logger, "Can't create prepared statement"); +// LOG4CXX_ERROR(logger, (sqlite3_errmsg(db) == NULL ? "" : sqlite3_errmsg(db))); +// } +// sqlite3_close(db); +// } + return ret; +} + } diff --git a/backends/skype/skypedb.h b/backends/skype/skypedb.h index d0ba9f27..264157ec 100644 --- a/backends/skype/skypedb.h +++ b/backends/skype/skypedb.h @@ -26,8 +26,10 @@ #include #include +class SkypePlugin; + namespace SkypeDB { bool getAvatar(const std::string &db, const std::string &name, std::string &avatar); - + bool loadBuddies(SkypePlugin *np, const std::string &db); } diff --git a/include/transport/buddy.h b/include/transport/buddy.h index 06792dc8..9d47cf6f 100644 --- a/include/transport/buddy.h +++ b/include/transport/buddy.h @@ -80,7 +80,7 @@ class Buddy { /// \param only_new if True, this function returns Presence stanza only if it's different /// than the previously generated one. /// \return Presence stanza or NULL. - Swift::Presence::ref generatePresenceStanza(int features, bool only_new = false); + std::vector &generatePresenceStanzas(int features, bool only_new = false); void setBlocked(bool block) { if (block) @@ -124,6 +124,8 @@ class Buddy { void sendPresence(); + void handleRawPresence(Swift::Presence::ref); + /// Handles VCard from legacy network and forwards it to XMPP user. /// \param id ID used in IQ-result. @@ -157,6 +159,8 @@ class Buddy { /// \return avatar hash or empty string. virtual std::string getIconHash() = 0; + virtual bool isAvailable() = 0; + /// Returns legacy name of buddy from JID. /// \param jid Jabber ID. @@ -167,10 +171,10 @@ class Buddy { protected: void generateJID(); Swift::JID m_jid; + std::vector m_presences; private: long m_id; -// Swift::Presence::ref m_lastPresence; BuddyFlag m_flags; RosterManager *m_rosterManager; Subscription m_subscription; diff --git a/include/transport/conversation.h b/include/transport/conversation.h index 268326b0..ba02f273 100644 --- a/include/transport/conversation.h +++ b/include/transport/conversation.h @@ -72,6 +72,7 @@ class Conversation { void handleMessage(boost::shared_ptr &message, const std::string &nickname = ""); void handleRawMessage(boost::shared_ptr &message); + void handleRawPresence(Swift::Presence::ref presence); /// Handles participant change in MUC. @@ -163,10 +164,16 @@ class Conversation { bool m_muc; Swift::JID m_jid; std::list m_jids; - std::map m_participants; - boost::shared_ptr m_subject; bool m_sentInitialPresence; + + // TODO: Move this to some extra class to cache the most used + // rooms across different accounts. Just now if we have 10 users + // connected to single room, we store all those things 10 times. + // It would be also great to store last 100 messages per room + // every time, so we can get history messages for IRC for example. + boost::shared_ptr m_subject; std::list > m_cachedMessages; + std::map m_participants; }; } diff --git a/include/transport/localbuddy.h b/include/transport/localbuddy.h index 69c5c153..ebe2c945 100644 --- a/include/transport/localbuddy.h +++ b/include/transport/localbuddy.h @@ -39,10 +39,10 @@ class LocalBuddy : public Buddy { std::string getName() { return m_name; } bool setName(const std::string &name); - bool getStatus(Swift::StatusShow &status, std::string &statusMessage) { - status = m_status; - statusMessage = m_statusMessage; - return true; + bool getStatus(Swift::StatusShow &status, std::string &statusMessage); + + bool isAvailable() { + return m_status.getType() != Swift::StatusShow::None; } void setStatus(const Swift::StatusShow &status, const std::string &statusMessage); diff --git a/include/transport/transport.h b/include/transport/transport.h index 51f69018..318ec673 100644 --- a/include/transport/transport.h +++ b/include/transport/transport.h @@ -155,6 +155,10 @@ namespace Transport { boost::signal)> onRawIQReceived; + bool isRawXMLEnabled() { + return m_rawXML; + } + private: void handleConnected(); void handleConnectionError(const Swift::ComponentError &error); diff --git a/src/buddy.cpp b/src/buddy.cpp index fcb6b138..00aea73c 100644 --- a/src/buddy.cpp +++ b/src/buddy.cpp @@ -40,8 +40,8 @@ Buddy::~Buddy() { } void Buddy::sendPresence() { - Swift::Presence::ref presence = generatePresenceStanza(255); - if (presence) { + std::vector &presences = generatePresenceStanzas(255); + BOOST_FOREACH(Swift::Presence::ref presence, presences) { m_rosterManager->getUser()->getComponent()->getStanzaChannel()->sendPresence(presence); } } @@ -89,17 +89,33 @@ Buddy::Subscription Buddy::getSubscription() { return m_subscription; } -Swift::Presence::ref Buddy::generatePresenceStanza(int features, bool only_new) { - std::string alias = getAlias(); - std::string name = getSafeName(); +void Buddy::handleRawPresence(Swift::Presence::ref presence) { + for (std::vector::iterator it = m_presences.begin(); it != m_presences.end(); it++) { + if ((*it)->getFrom() == presence->getFrom()) { + m_presences.erase(it); + break; + } + } + + m_presences.push_back(presence); + m_rosterManager->getUser()->getComponent()->getStanzaChannel()->sendPresence(presence); +} + +std::vector &Buddy::generatePresenceStanzas(int features, bool only_new) { + if (m_jid.getNode().empty()) { + generateJID(); + } Swift::StatusShow s; std::string statusMessage; - if (!getStatus(s, statusMessage)) - return Swift::Presence::ref(); - - if (m_jid.getNode().empty()) { - generateJID(); + if (!getStatus(s, statusMessage)) { + for (std::vector::iterator it = m_presences.begin(); it != m_presences.end(); it++) { + if ((*it)->getFrom() == m_jid) { + m_presences.erase(it); + break; + } + } + return m_presences; } Swift::Presence::ref presence = Swift::Presence::create(); @@ -128,6 +144,15 @@ Swift::Presence::ref Buddy::generatePresenceStanza(int features, bool only_new) } } + BOOST_FOREACH(Swift::Presence::ref &p, m_presences) { + if (p->getFrom() == presence->getFrom()) { + p = presence; + return m_presences; + } + } + + m_presences.push_back(presence); + // if (only_new) { // if (m_lastPresence) // m_lastPresence->setTo(Swift::JID("")); @@ -137,7 +162,7 @@ Swift::Presence::ref Buddy::generatePresenceStanza(int features, bool only_new) // m_lastPresence = presence; // } - return presence; + return m_presences; } std::string Buddy::getSafeName() { diff --git a/src/conversation.cpp b/src/conversation.cpp index 5e8aa3d1..c02396e5 100644 --- a/src/conversation.cpp +++ b/src/conversation.cpp @@ -198,10 +198,9 @@ void Conversation::handleMessage(boost::shared_ptr &message, con } 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); + for (std::map::iterator it = m_participants.begin(); it != m_participants.end(); it++) { + (*it).second->setTo(to); + m_conversationManager->getComponent()->getStanzaChannel()->sendPresence((*it).second); } } @@ -296,6 +295,12 @@ Swift::Presence::ref Conversation::generatePresence(const std::string &nick, int return presence; } +void Conversation::handleRawPresence(Swift::Presence::ref presence) { + // TODO: Detect nickname change. + m_conversationManager->getComponent()->getStanzaChannel()->sendPresence(presence); + m_participants[presence->getFrom().getResource()] = presence; +} + void Conversation::handleParticipantChanged(const std::string &nick, Conversation::ParticipantFlag flag, int status, const std::string &statusMessage, const std::string &newname) { Swift::Presence::ref presence = generatePresence(nick, flag, status, statusMessage, newname); @@ -303,11 +308,7 @@ void Conversation::handleParticipantChanged(const std::string &nick, Conversatio m_participants.erase(nick); } else { - Participant p; - p.flag = flag; - p.status = status; - p.statusMessage = statusMessage; - m_participants[nick] = p; + m_participants[nick] = presence; } diff --git a/src/localbuddy.cpp b/src/localbuddy.cpp index 47fe2b7f..ef695e17 100644 --- a/src/localbuddy.cpp +++ b/src/localbuddy.cpp @@ -102,4 +102,13 @@ void LocalBuddy::setGroups(const std::vector &groups) { } } +bool LocalBuddy::getStatus(Swift::StatusShow &status, std::string &statusMessage) { + if (getRosterManager()->getUser()->getComponent()->isRawXMLEnabled()) { + return false; + } + status = m_status; + statusMessage = m_statusMessage; + return true; +} + } diff --git a/src/networkpluginserver.cpp b/src/networkpluginserver.cpp index 4059d865..9cf79934 100644 --- a/src/networkpluginserver.cpp +++ b/src/networkpluginserver.cpp @@ -1053,10 +1053,17 @@ void NetworkPluginServer::handleElement(boost::shared_ptr elemen boost::shared_ptr presence = boost::dynamic_pointer_cast(stanza); if (presence) { - m_component->getStanzaChannel()->sendPresence(presence); if (buddy) { - buddy->m_statusMessage = presence->getStatus(); - buddy->m_status = Swift::StatusShow(presence->getShow()); + if (!buddy->isAvailable() && presence->getType() != Swift::Presence::Unavailable) { + buddy->m_status.setType(Swift::StatusShow::Online); + } + buddy->handleRawPresence(presence); + } + else if (conv) { + conv->handleRawPresence(presence); + } + else { + m_component->getStanzaChannel()->sendPresence(presence); } return; diff --git a/src/rostermanager.cpp b/src/rostermanager.cpp index f9c47e77..4074a66f 100644 --- a/src/rostermanager.cpp +++ b/src/rostermanager.cpp @@ -255,9 +255,11 @@ void RosterManager::storeBuddy(Buddy *buddy) { void RosterManager::handleBuddyRosterPushResponse(Swift::ErrorPayload::ref error, Swift::SetRosterRequest::ref request, const std::string &key) { LOG4CXX_INFO(logger, "handleBuddyRosterPushResponse called for buddy " << key); if (m_buddies[key] != NULL) { - Swift::Presence::ref presence = m_buddies[key]->generatePresenceStanza(255); - if (presence && presence->getType() == Swift::Presence::Available) { - m_component->getStanzaChannel()->sendPresence(presence); + if (m_buddies[key]->isAvailable()) { + std::vector &presences = m_buddies[key]->generatePresenceStanzas(255); + BOOST_FOREACH(Swift::Presence::ref &presence, presences) { + m_component->getStanzaChannel()->sendPresence(presence); + } } } else { @@ -440,13 +442,13 @@ void RosterManager::handleSubscription(Swift::Presence::ref presence) { Buddy *buddy = getBuddy(Buddy::JIDToLegacyName(presence->getTo())); if (buddy) { + std::vector &presences = buddy->generatePresenceStanzas(255); switch (presence->getType()) { // buddy is already there, so nothing to do, just answer case Swift::Presence::Subscribe: onBuddyAdded(buddy); response->setType(Swift::Presence::Subscribed); - currentPresence = buddy->generatePresenceStanza(255); - if (currentPresence) { + BOOST_FOREACH(Swift::Presence::ref ¤tPresence, presences) { currentPresence->setTo(presence->getFrom()); m_component->getStanzaChannel()->sendPresence(currentPresence); } @@ -587,8 +589,11 @@ void RosterManager::sendCurrentPresences(const Swift::JID &to) { if (!buddy) { continue; } - Swift::Presence::ref presence = buddy->generatePresenceStanza(255); - if (presence && presence->getType() == Swift::Presence::Available) { + if (!buddy->isAvailable()) { + continue; + } + std::vector &presences = buddy->generatePresenceStanzas(255); + BOOST_FOREACH(Swift::Presence::ref &presence, presences) { presence->setTo(to); m_component->getStanzaChannel()->sendPresence(presence); } @@ -598,8 +603,8 @@ void RosterManager::sendCurrentPresences(const Swift::JID &to) { void RosterManager::sendCurrentPresence(const Swift::JID &from, const Swift::JID &to) { Buddy *buddy = getBuddy(Buddy::JIDToLegacyName(from)); if (buddy) { - Swift::Presence::ref presence = buddy->generatePresenceStanza(255); - if (presence) { + std::vector &presences = buddy->generatePresenceStanzas(255); + BOOST_FOREACH(Swift::Presence::ref &presence, presences) { presence->setTo(to); m_component->getStanzaChannel()->sendPresence(presence); } @@ -619,11 +624,18 @@ void RosterManager::sendUnavailablePresences(const Swift::JID &to) { if (!buddy) { continue; } - Swift::Presence::ref presence = buddy->generatePresenceStanza(255); - if (presence && presence->getType() == Swift::Presence::Available) { + + if (!buddy->isAvailable()) { + continue; + } + + std::vector &presences = buddy->generatePresenceStanzas(255); + BOOST_FOREACH(Swift::Presence::ref &presence, presences) { + Swift::Presence::Type type = presence->getType(); presence->setTo(to); presence->setType(Swift::Presence::Unavailable); m_component->getStanzaChannel()->sendPresence(presence); + presence->setType(type); } } diff --git a/src/tests/basictest.cpp b/src/tests/basictest.cpp index 6f3545f6..f1df8288 100644 --- a/src/tests/basictest.cpp +++ b/src/tests/basictest.cpp @@ -241,6 +241,9 @@ void BasicTest::disconnectUser() { if (user) { user->addUserSetting("stay_connected", "0"); } + else { + return; + } received.clear(); userManager->disconnectUser("user@localhost"); dynamic_cast(factories->getTimerFactory())->setTime(100); diff --git a/src/tests/conversationmanager.cpp b/src/tests/conversationmanager.cpp index b8f2eb4e..36d90a30 100644 --- a/src/tests/conversationmanager.cpp +++ b/src/tests/conversationmanager.cpp @@ -23,6 +23,7 @@ using namespace Transport; class ConversationManagerTest : public CPPUNIT_NS :: TestFixture, public BasicTest { CPPUNIT_TEST_SUITE(ConversationManagerTest); + CPPUNIT_TEST(conversationSize); CPPUNIT_TEST(handleNormalMessages); CPPUNIT_TEST(handleNormalMessagesHeadline); CPPUNIT_TEST(handleGroupchatMessages); @@ -60,6 +61,10 @@ class ConversationManagerTest : public CPPUNIT_NS :: TestFixture, public BasicTe tearMeDown(); } + void conversationSize() { + std::cout << " = " << sizeof(Conversation) << " B"; + } + void handleMessageReceived(TestingConversation *_conv, boost::shared_ptr &_msg) { m_conv = _conv; m_msg = _msg; diff --git a/src/tests/localbuddy.cpp b/src/tests/localbuddy.cpp index 9300c290..9fe00bdc 100644 --- a/src/tests/localbuddy.cpp +++ b/src/tests/localbuddy.cpp @@ -22,6 +22,7 @@ using namespace Transport; class LocalBuddyTest : public CPPUNIT_NS :: TestFixture, public BasicTest { CPPUNIT_TEST_SUITE(LocalBuddyTest); + CPPUNIT_TEST(localBuddySize); CPPUNIT_TEST(createWithInvalidName); CPPUNIT_TEST(buddyFlagsFromJID); CPPUNIT_TEST(JIDToLegacyName); @@ -43,6 +44,10 @@ class LocalBuddyTest : public CPPUNIT_NS :: TestFixture, public BasicTest { tearMeDown(); } + void localBuddySize() { + std::cout << " = " << sizeof(LocalBuddy) << " B"; + } + void createWithInvalidName() { User *user = userManager->getUser("user@localhost"); CPPUNIT_ASSERT(user); diff --git a/src/tests/networkpluginserver.cpp b/src/tests/networkpluginserver.cpp index 3b2fd5f8..067ce32f 100644 --- a/src/tests/networkpluginserver.cpp +++ b/src/tests/networkpluginserver.cpp @@ -56,6 +56,7 @@ class NetworkPluginServerTest : public CPPUNIT_NS :: TestFixture, public BasicTe CPPUNIT_TEST(handleRawXML); CPPUNIT_TEST(benchmarkHandleBuddyChangedPayload); + CPPUNIT_TEST(benchmarkSendUnavailablePresence); CPPUNIT_TEST_SUITE_END(); public: @@ -140,6 +141,49 @@ class NetworkPluginServerTest : public CPPUNIT_NS :: TestFixture, public BasicTe std::cerr << " " << clk.elapsedTime() << " s"; } + void benchmarkSendUnavailablePresence() { + Clock clk; + std::vector lst; + for (int i = 0; i < 1000; i++) { + pbnetwork::Buddy buddy; + buddy.set_username("user@localhost"); + buddy.set_buddyname("buddy" + boost::lexical_cast(i) + "@test"); + buddy.set_status((pbnetwork::StatusType) 5); + + std::string message; + buddy.SerializeToString(&message); + lst.push_back(message); + } + + std::vector lst2; + for (int i = 0; i < 1000; i++) { + pbnetwork::Buddy buddy; + buddy.set_username("user@localhost"); + buddy.set_buddyname("buddy" + boost::lexical_cast(1000+i) + "@test"); + buddy.set_status((pbnetwork::StatusType) 2); + + std::string message; + buddy.SerializeToString(&message); + lst2.push_back(message); + } + + + for (int i = 0; i < 1000; i++) { + serv->handleBuddyChangedPayload(lst[i]); + received.clear(); + } + for (int i = 0; i < 1000; i++) { + serv->handleBuddyChangedPayload(lst2[i]); + received.clear(); + } + + User *user = userManager->getUser("user@localhost"); + clk.start(); + user->getRosterManager()->sendUnavailablePresences("user@localhost"); + clk.end(); + std::cerr << " " << clk.elapsedTime() << " s"; + } + void handleBuddyChangedPayload() { User *user = userManager->getUser("user@localhost"); @@ -198,14 +242,36 @@ class NetworkPluginServerTest : public CPPUNIT_NS :: TestFixture, public BasicTe } void handleRawXML() { + cfg->updateBackendConfig("[features]\nrawxml=1\n"); User *user = userManager->getUser("user@localhost"); + std::vector grp; + grp.push_back("group1"); + LocalBuddy *buddy = new LocalBuddy(user->getRosterManager(), -1, "buddy1@domain.tld", "Buddy 1", grp, BUDDY_JID_ESCAPING); + user->getRosterManager()->setBuddy(buddy); + received.clear(); - std::string xml = ""; - + std::string xml = ""; serv->handleRawXML(xml); - CPPUNIT_ASSERT_EQUAL(1, (int) received.size()); + + std::string xml2 = ""; + serv->handleRawXML(xml2); + + CPPUNIT_ASSERT_EQUAL(2, (int) received.size()); CPPUNIT_ASSERT(dynamic_cast(getStanza(received[0]))); - CPPUNIT_ASSERT_EQUAL(std::string("buddy1\\40domain.tld@localhost"), dynamic_cast(getStanza(received[0]))->getFrom().toString()); + CPPUNIT_ASSERT_EQUAL(std::string("buddy1\\40domain.tld@localhost/res"), dynamic_cast(getStanza(received[0]))->getFrom().toString()); + CPPUNIT_ASSERT(dynamic_cast(getStanza(received[1]))); + CPPUNIT_ASSERT_EQUAL(std::string("buddy1\\40domain.tld@localhost/res2"), dynamic_cast(getStanza(received[1]))->getFrom().toString()); + + received.clear(); + user->getRosterManager()->sendUnavailablePresences("user@localhost"); + + CPPUNIT_ASSERT_EQUAL(3, (int) received.size()); + CPPUNIT_ASSERT(dynamic_cast(getStanza(received[0]))); + CPPUNIT_ASSERT_EQUAL(std::string("buddy1\\40domain.tld@localhost/res"), dynamic_cast(getStanza(received[0]))->getFrom().toString()); + CPPUNIT_ASSERT_EQUAL(Swift::Presence::Unavailable, dynamic_cast(getStanza(received[0]))->getType()); + CPPUNIT_ASSERT(dynamic_cast(getStanza(received[1]))); + CPPUNIT_ASSERT_EQUAL(std::string("buddy1\\40domain.tld@localhost/res2"), dynamic_cast(getStanza(received[1]))->getFrom().toString()); + CPPUNIT_ASSERT_EQUAL(Swift::Presence::Unavailable, dynamic_cast(getStanza(received[1]))->getType()); } void handleMessageHeadline() {