diff --git a/CMakeLists.txt b/CMakeLists.txt index aa73cafe..9b67004d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -110,6 +110,7 @@ ADD_SUBDIRECTORY(include) ADD_SUBDIRECTORY(examples) ADD_SUBDIRECTORY(spectrum) ADD_SUBDIRECTORY(backends) +ADD_SUBDIRECTORY(tests) if(DOXYGEN_FOUND) message("Docs : yes") diff --git a/backends/libircclient-qt/session.cpp b/backends/libircclient-qt/session.cpp index 64e87962..e8e5b9e8 100644 --- a/backends/libircclient-qt/session.cpp +++ b/backends/libircclient-qt/session.cpp @@ -17,6 +17,7 @@ MyIrcSession::MyIrcSession(const std::string &user, NetworkPlugin *np, QObject* { this->np = np; this->user = user; + connect(this, SIGNAL(disconnected()), SLOT(on_disconnected())); } void MyIrcSession::on_connected(){ @@ -26,6 +27,7 @@ void MyIrcSession::on_connected(){ void MyIrcSession::on_disconnected() { std::cout << "disconnected:\n"; + np->handleDisconnected(user, "", 0, ""); } void MyIrcSession::on_bufferAdded(Irc::Buffer* buffer) diff --git a/backends/libpurple/main.cpp b/backends/libpurple/main.cpp index 026d8844..b1cfa9da 100644 --- a/backends/libpurple/main.cpp +++ b/backends/libpurple/main.cpp @@ -118,7 +118,7 @@ class SpectrumNetworkPlugin : public NetworkPlugin { } } - virtual void handleVCardRequest(const std::string &user, const std::string &legacyName, unsigned int id) { + void handleVCardRequest(const std::string &user, const std::string &legacyName, unsigned int id) { PurpleAccount *account = m_sessions[user]; if (account) { serv_get_info(purple_account_get_connection(account), legacyName.c_str()); @@ -126,6 +126,18 @@ class SpectrumNetworkPlugin : public NetworkPlugin { } } + void handleBuddyUpdatedRequest(const std::string &user, const std::string &buddyName, const std::string &alias, const std::string &groups) { + PurpleAccount *account = m_sessions[user]; + if (account) { + PurpleBuddy *buddy = purple_find_buddy(account, buddyName.c_str()); + if (buddy) { + purple_blist_alias_buddy(buddy, alias.c_str()); + purple_blist_server_alias_buddy(buddy, alias.c_str()); + serv_alias_buddy(buddy); + } + } + } + std::map m_sessions; std::map m_accounts; std::map m_vcards; @@ -479,6 +491,9 @@ static PurpleCoreUiOps coreUiOps = }; static void signed_on(PurpleConnection *gc, gpointer unused) { + for (int i = 0; i<1500; i++) { + std::cout << "A\n"; + } PurpleAccount *account = purple_connection_get_account(gc); np->handleConnected(np->m_accounts[account]); } diff --git a/include/transport/networkplugin.h b/include/transport/networkplugin.h index c6125be8..2dd7e29e 100644 --- a/include/transport/networkplugin.h +++ b/include/transport/networkplugin.h @@ -64,6 +64,7 @@ class NetworkPlugin { virtual void handleVCardRequest(const std::string &/*user*/, const std::string &/*legacyName*/, unsigned int /*id*/) {} virtual void handleJoinRoomRequest(const std::string &/*user*/, const std::string &/*room*/, const std::string &/*nickname*/, const std::string &/*pasword*/) {} virtual void handleLeaveRoomRequest(const std::string &/*user*/, const std::string &/*room*/) {} + virtual void handleBuddyUpdatedRequest(const std::string &/*user*/, const std::string &/*buddyName*/, const std::string &/*alias*/, const std::string &/*groups*/) {} private: @@ -74,6 +75,7 @@ class NetworkPlugin { void handleJoinRoomPayload(const std::string &payload); void handleLeaveRoomPayload(const std::string &payload); void handleVCardPayload(const std::string &payload); + void handleBuddyChangedPayload(const std::string &payload); void handleDataRead(const Swift::SafeByteArray&); void _handleConnected(bool error); void handleDisconnected(); diff --git a/include/transport/networkpluginserver.h b/include/transport/networkpluginserver.h index c1edda8f..1d1bff73 100644 --- a/include/transport/networkpluginserver.h +++ b/include/transport/networkpluginserver.h @@ -74,6 +74,10 @@ class NetworkPluginServer { void handleUserReadyToConnect(User *user); void handleUserDestroyed(User *user); + void handleBuddyUpdated(Buddy *buddy, const Swift::RosterItemPayload &item); + void handleBuddyRemoved(Buddy *buddy); + void handleBuddyAdded(Buddy *buddy, const Swift::RosterItemPayload &item); + void handleVCardRequired(User *user, const std::string &name, unsigned int id); void send(boost::shared_ptr &, const std::string &data); diff --git a/include/transport/rostermanager.h b/include/transport/rostermanager.h index 8ff033b5..4bd8ae4b 100644 --- a/include/transport/rostermanager.h +++ b/include/transport/rostermanager.h @@ -60,6 +60,8 @@ class RosterManager { void setStorageBackend(StorageBackend *storageBackend); + void storeBuddy(Buddy *buddy); + Swift::RosterPayload::ref generateRosterPayload(); /// Returns user associated with this roster. diff --git a/include/transport/rosterresponder.h b/include/transport/rosterresponder.h index 4fd3aebc..80e61b6b 100644 --- a/include/transport/rosterresponder.h +++ b/include/transport/rosterresponder.h @@ -28,12 +28,19 @@ namespace Transport { class UserManager; +class Buddy; class RosterResponder : public Swift::Responder { public: RosterResponder(Swift::IQRouter *router, UserManager *userManager); ~RosterResponder(); + boost::signal onBuddyUpdated; + + boost::signal onBuddyRemoved; + + boost::signal onBuddyAdded; + private: virtual bool handleGetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr payload); virtual bool handleSetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr payload); diff --git a/include/transport/transport.h b/include/transport/transport.h index cd43e238..60331759 100644 --- a/include/transport/transport.h +++ b/include/transport/transport.h @@ -31,6 +31,7 @@ #include "Swiften/Network/BoostTimerFactory.h" #include "Swiften/Network/BoostIOServiceThread.h" #include "Swiften/Server/UserRegistry.h" +#include "Swiften/Base/SafeByteArray.h" #include #include "transport/config.h" #include "transport/factory.h" diff --git a/src/localbuddy.cpp b/src/localbuddy.cpp index d79e0b72..55deeec9 100644 --- a/src/localbuddy.cpp +++ b/src/localbuddy.cpp @@ -24,6 +24,7 @@ namespace Transport { LocalBuddy::LocalBuddy(RosterManager *rosterManager, long id) : Buddy(rosterManager, id) { + m_status = Swift::StatusShow::None; } LocalBuddy::~LocalBuddy() { diff --git a/src/networkplugin.cpp b/src/networkplugin.cpp index fa393588..3b4fa4f1 100644 --- a/src/networkplugin.cpp +++ b/src/networkplugin.cpp @@ -265,8 +265,18 @@ void NetworkPlugin::handleVCardPayload(const std::string &data) { handleVCardRequest(payload.username(), payload.buddyname(), payload.id()); } +void NetworkPlugin::handleBuddyChangedPayload(const std::string &data) { + pbnetwork::Buddy payload; + if (payload.ParseFromString(data) == false) { + // TODO: ERROR + return; + } + + handleBuddyUpdatedRequest(payload.username(), payload.buddyname(), payload.alias(), payload.groups()); +} + void NetworkPlugin::handleDataRead(const Swift::SafeByteArray &data) { - m_data.insert(m_data.begin(), data.begin(), data.end()); + m_data.insert(m_data.end(), data.begin(), data.end()); while (m_data.size() != 0) { unsigned int expected_size; @@ -310,6 +320,9 @@ void NetworkPlugin::handleDataRead(const Swift::SafeByteArray &data) { case pbnetwork::WrapperMessage_Type_TYPE_VCARD: handleVCardPayload(wrapper.payload()); break; + case pbnetwork::WrapperMessage_Type_TYPE_BUDDY_CHANGED: + handleBuddyChangedPayload(wrapper.payload()); + break; default: return; } diff --git a/src/networkpluginserver.cpp b/src/networkpluginserver.cpp index 78fe349b..24f8560d 100644 --- a/src/networkpluginserver.cpp +++ b/src/networkpluginserver.cpp @@ -90,7 +90,7 @@ static int exec_(const char *path, const char *host, const char *port, const cha if ( pid == 0 ) { // child process execlp(path, path, "--host", host, "--port", port, config, NULL); - exit(1); + abort(); } else if ( pid < 0 ) { // fork failed status = -1; @@ -128,6 +128,9 @@ NetworkPluginServer::NetworkPluginServer(Component *component, Config *config, U m_vcardResponder->start(); m_rosterResponder = new RosterResponder(component->getIQRouter(), userManager); + m_rosterResponder->onBuddyAdded.connect(boost::bind(&NetworkPluginServer::handleBuddyAdded, this, _1, _2)); + m_rosterResponder->onBuddyRemoved.connect(boost::bind(&NetworkPluginServer::handleBuddyRemoved, this, _1)); + m_rosterResponder->onBuddyUpdated.connect(boost::bind(&NetworkPluginServer::handleBuddyUpdated, this, _1, _2)); m_rosterResponder->start(); m_server = component->getNetworkFactories()->getConnectionFactory()->createConnectionServer(10000); @@ -195,12 +198,12 @@ void NetworkPluginServer::handleDisconnectedPayload(const std::string &data) { return; } + m_component->m_userRegistry->onPasswordInvalid(payload.user()); + User *user = m_userManager->getUser(payload.user()); if (!user) { return; } - - m_component->m_userRegistry->onPasswordInvalid(payload.user()); user->handleDisconnected(payload.message()); } @@ -322,8 +325,7 @@ void NetworkPluginServer::handleConvMessagePayload(const std::string &data, bool } void NetworkPluginServer::handleDataRead(Client *c, const Swift::SafeByteArray &data) { - c->data.insert(c->data.begin(), data.begin(), data.end()); - + c->data.insert(c->data.end(), data.begin(), data.end()); while (c->data.size() != 0) { unsigned int expected_size; @@ -339,8 +341,9 @@ void NetworkPluginServer::handleDataRead(Client *c, const Swift::SafeByteArray & pbnetwork::WrapperMessage wrapper; if (wrapper.ParseFromArray(&c->data[4], expected_size) == false) { + std::cout << "PARSING ERROR " << expected_size << "\n"; c->data.erase(c->data.begin(), c->data.begin() + 4 + expected_size); - return; + continue; } c->data.erase(c->data.begin(), c->data.begin() + 4 + expected_size); @@ -399,6 +402,10 @@ void NetworkPluginServer::pingTimeout() { void NetworkPluginServer::handleUserCreated(User *user) { Client *c = getFreeClient(); + if (!c) { + user->handleDisconnected("Internal Server Error, please reconnect."); + return; + } user->setData(c); c->users.push_back(user); @@ -513,6 +520,36 @@ void NetworkPluginServer::handleMessageReceived(NetworkConversation *conv, boost send(c->connection, message); } +void NetworkPluginServer::handleBuddyRemoved(Buddy *buddy) { + +} + +void NetworkPluginServer::handleBuddyUpdated(Buddy *b, const Swift::RosterItemPayload &item) { + User *user = b->getRosterManager()->getUser(); + + dynamic_cast(b)->setAlias(item.getName()); + user->getRosterManager()->storeBuddy(b); + + pbnetwork::Buddy buddy; + buddy.set_username(user->getJID().toBare()); + buddy.set_buddyname(b->getName()); + buddy.set_alias(b->getAlias()); + buddy.set_groups(b->getGroups().size() == 0 ? "" : b->getGroups()[0]); + buddy.set_status(Swift::StatusShow::None); + + std::string message; + buddy.SerializeToString(&message); + + WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_BUDDY_CHANGED); + + Client *c = (Client *) user->getData(); + send(c->connection, message); +} + +void NetworkPluginServer::handleBuddyAdded(Buddy *buddy, const Swift::RosterItemPayload &item) { + handleBuddyUpdated(buddy, item); +} + void NetworkPluginServer::handleVCardRequired(User *user, const std::string &name, unsigned int id) { std::cout << "VCARD REQUIRED " << name << " " << id << "\n"; pbnetwork::VCard vcard; diff --git a/src/rostermanager.cpp b/src/rostermanager.cpp index 566eb6ce..4551648d 100644 --- a/src/rostermanager.cpp +++ b/src/rostermanager.cpp @@ -119,6 +119,12 @@ void RosterManager::unsetBuddy(Buddy *buddy) { onBuddyUnset(buddy); } +void RosterManager::storeBuddy(Buddy *buddy) { + if (m_rosterStorage) { + m_rosterStorage->storeBuddy(buddy); + } +} + void RosterManager::handleBuddyRosterPushResponse(Swift::ErrorPayload::ref error, const std::string &key) { if (m_buddies[key] != NULL) { m_buddies[key]->handleBuddyChanged(); diff --git a/src/rosterresponder.cpp b/src/rosterresponder.cpp index 3c8554f3..bb544673 100644 --- a/src/rosterresponder.cpp +++ b/src/rosterresponder.cpp @@ -27,6 +27,7 @@ #include "transport/user.h" #include "transport/usermanager.h" #include "transport/rostermanager.h" +#include "transport/buddy.h" using namespace Swift; using namespace boost; @@ -67,6 +68,41 @@ bool RosterResponder::handleGetRequest(const Swift::JID& from, const Swift::JID& bool RosterResponder::handleSetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr payload) { sendResponse(from, id, boost::shared_ptr(new RosterPayload())); + + User *user = m_userManager->getUser(from.toBare().toString()); + if (!user) { + return true; + } + + if (payload->getItems().size() == 0) { + return true; + } + + Swift::RosterItemPayload item = payload->getItems()[0]; + + Buddy *buddy = user->getRosterManager()->getBuddy(Buddy::JIDToLegacyName(item.getJID())); + if (buddy) { + if (item.getSubscription() == Swift::RosterItemPayload::Remove) { + onBuddyRemoved(buddy); + } + else { + onBuddyUpdated(buddy, item); + } + } + else if (item.getSubscription() != Swift::RosterItemPayload::Remove) { + // Roster push for this new buddy is sent by RosterManager + BuddyInfo buddyInfo; + buddyInfo.id = -1; + buddyInfo.alias = item.getName(); + buddyInfo.legacyName = Buddy::JIDToLegacyName(item.getJID()); + buddyInfo.subscription = "both"; + buddyInfo.flags = 0; + + buddy = user->getComponent()->getFactory()->createBuddy(user->getRosterManager(), buddyInfo); + user->getRosterManager()->setBuddy(buddy); + onBuddyAdded(buddy, item); + } + return true; } diff --git a/src/sqlite3backend.cpp b/src/sqlite3backend.cpp index 128921a7..565a5f84 100644 --- a/src/sqlite3backend.cpp +++ b/src/sqlite3backend.cpp @@ -240,7 +240,7 @@ long SQLite3Backend::addBuddy(long userId, const BuddyInfo &buddyInfo) { BIND_INT(m_addBuddy, userId); BIND_STR(m_addBuddy, buddyInfo.legacyName); BIND_STR(m_addBuddy, buddyInfo.subscription); - BIND_STR(m_addBuddy, buddyInfo.groups[0]); // TODO: serialize groups + BIND_STR(m_addBuddy, buddyInfo.groups.size() == 0 ? "" : buddyInfo.groups[0]); // TODO: serialize groups BIND_STR(m_addBuddy, buddyInfo.alias); BIND_INT(m_addBuddy, buddyInfo.flags); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 00000000..0688a10f --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,5 @@ +ADD_SUBDIRECTORY(login) +ADD_SUBDIRECTORY(login_bad_name) +ADD_SUBDIRECTORY(login_bad_name2) + +add_custom_target(tests python runtests.py WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) diff --git a/tests/login/CMakeLists.txt b/tests/login/CMakeLists.txt new file mode 100644 index 00000000..d0468bd6 --- /dev/null +++ b/tests/login/CMakeLists.txt @@ -0,0 +1,6 @@ +FILE(GLOB SRC *.cpp) + +ADD_EXECUTABLE(login_test ${SRC}) + +TARGET_LINK_LIBRARIES(login_test transport ${SWIFTEN_LIBRARIES} -lgconf-2 -lgobject-2.0 -lglib-2.0) + diff --git a/tests/login/main.cpp b/tests/login/main.cpp new file mode 100644 index 00000000..3e55d9db --- /dev/null +++ b/tests/login/main.cpp @@ -0,0 +1,44 @@ +#include +#include + +#include +#include + +using namespace Swift; +using namespace boost; + +Client* client; + +static void handleDisconnected(const boost::optional &) { + exit(1); +} + +static void handleConnected() { + exit(0); +} + +static void handleMessageReceived(Message::ref message) { + // Echo back the incoming message + message->setTo(message->getFrom()); + message->setFrom(JID()); + client->sendMessage(message); +} + +int main(int, char **argv) { + SimpleEventLoop eventLoop; + BoostNetworkFactories networkFactories(&eventLoop); + + client = new Client(argv[1], argv[2], &networkFactories); + client->setAlwaysTrustCertificates(); + client->onConnected.connect(&handleConnected); + client->onDisconnected.connect(bind(&handleDisconnected, _1)); + client->onMessageReceived.connect(bind(&handleMessageReceived, _1)); + ClientOptions opt; + opt.allowPLAINOverNonTLS = true; + client->connect(opt); + + eventLoop.run(); + + delete client; + return 0; +} diff --git a/tests/login_bad_name/CMakeLists.txt b/tests/login_bad_name/CMakeLists.txt new file mode 100644 index 00000000..7801614f --- /dev/null +++ b/tests/login_bad_name/CMakeLists.txt @@ -0,0 +1,6 @@ +FILE(GLOB SRC *.cpp) + +ADD_EXECUTABLE(login_bad_name_test ${SRC}) + +TARGET_LINK_LIBRARIES(login_bad_name_test transport ${SWIFTEN_LIBRARIES} -lgconf-2 -lgobject-2.0 -lglib-2.0) + diff --git a/tests/login_bad_name/main.cpp b/tests/login_bad_name/main.cpp new file mode 100644 index 00000000..4df61cdb --- /dev/null +++ b/tests/login_bad_name/main.cpp @@ -0,0 +1,45 @@ +#include +#include + +#include +#include + +using namespace Swift; +using namespace boost; + +Client* client; + +static void handleDisconnected(const boost::optional &error) { + exit(error->getType() != ClientError::AuthenticationFailedError); +} + +static void handleConnected() { + exit(1); +} + +static void handleMessageReceived(Message::ref message) { + // Echo back the incoming message + message->setTo(message->getFrom()); + message->setFrom(JID()); + client->sendMessage(message); +} + +int main(int, char **argv) { + SimpleEventLoop eventLoop; + BoostNetworkFactories networkFactories(&eventLoop); + + JID jid(JID(argv[1]).getNode() + "something", JID(argv[1]).getDomain()); + client = new Client(jid, argv[2], &networkFactories); + client->setAlwaysTrustCertificates(); + client->onConnected.connect(&handleConnected); + client->onDisconnected.connect(bind(&handleDisconnected, _1)); + client->onMessageReceived.connect(bind(&handleMessageReceived, _1)); + ClientOptions opt; + opt.allowPLAINOverNonTLS = true; + client->connect(opt); + + eventLoop.run(); + + delete client; + return 0; +} diff --git a/tests/login_bad_name2/.nolibircclient-qt b/tests/login_bad_name2/.nolibircclient-qt new file mode 100644 index 00000000..e69de29b diff --git a/tests/login_bad_name2/CMakeLists.txt b/tests/login_bad_name2/CMakeLists.txt new file mode 100644 index 00000000..c4370922 --- /dev/null +++ b/tests/login_bad_name2/CMakeLists.txt @@ -0,0 +1,6 @@ +FILE(GLOB SRC *.cpp) + +ADD_EXECUTABLE(login_bad_name2_test ${SRC}) + +TARGET_LINK_LIBRARIES(login_bad_name2_test transport ${SWIFTEN_LIBRARIES} -lgconf-2 -lgobject-2.0 -lglib-2.0) + diff --git a/tests/login_bad_name2/main.cpp b/tests/login_bad_name2/main.cpp new file mode 100644 index 00000000..ad210a65 --- /dev/null +++ b/tests/login_bad_name2/main.cpp @@ -0,0 +1,45 @@ +#include +#include + +#include +#include + +using namespace Swift; +using namespace boost; + +Client* client; + +static void handleDisconnected(const boost::optional &error) { + exit(error->getType() != ClientError::AuthenticationFailedError); +} + +static void handleConnected() { + exit(1); +} + +static void handleMessageReceived(Message::ref message) { + // Echo back the incoming message + message->setTo(message->getFrom()); + message->setFrom(JID()); + client->sendMessage(message); +} + +int main(int, char **argv) { + SimpleEventLoop eventLoop; + BoostNetworkFactories networkFactories(&eventLoop); + + JID jid(std::string("something") + JID(argv[1]).getNode(), JID(argv[1]).getDomain()); + client = new Client(jid, argv[2], &networkFactories); + client->setAlwaysTrustCertificates(); + client->onConnected.connect(&handleConnected); + client->onDisconnected.connect(bind(&handleDisconnected, _1)); + client->onMessageReceived.connect(bind(&handleMessageReceived, _1)); + ClientOptions opt; + opt.allowPLAINOverNonTLS = true; + client->connect(opt); + + eventLoop.run(); + + delete client; + return 0; +} diff --git a/tests/runtests.py b/tests/runtests.py new file mode 100644 index 00000000..391a1497 --- /dev/null +++ b/tests/runtests.py @@ -0,0 +1,70 @@ +import os +import sys +from subprocess import * +import time + +def run_spectrum(backend, test): + os.system("rm test.sql") + f = open("sample.cfg", "w") + f.write("\ + [service]\n\ + jid = localhost\n\ + password = secret\n\ + server = 127.0.0.1\n\ + port = 5222\n\ + server_mode = 1\n\ + backend=../backends/%s/%s_backend\n\ + protocol=prpl-jabber\n\ +\ + [database]\n\ + database = test.sql\n\ + prefix=icq\n\ + " % (backend, backend) + ) + f.close() + p = Popen("../spectrum/src/spectrum sample.cfg > " + backend + "_" + test + ".log 2>&1", shell=True) + time.sleep(4) + return p + +def one_test_run(): + os.system("killall spectrum 2> /dev/null") + os.system("rm *.log") + + for backend in os.listdir("../backends"): + if not os.path.isdir("../backends/" + backend) or backend == "CMakeFiles": + continue + + for d in os.listdir("."): + binary = d + "/" + d + "_test" + if not os.path.exists(binary): + continue + + if os.path.exists(d + "/.no" + backend): + continue + + p = run_spectrum(backend, d) + + if backend.find("purple") >= 0: + p = Popen(binary + " pyjim%jabber.cz@localhost test", shell=True) + else: + p = Popen(binary + " testnickname%irc.freenode.net@localhost test", shell=True) + + seconds = 0 + while p.poll() is None and seconds < 20: + time.sleep(1) + seconds += 1 + + if p.returncode == 0 and seconds < 20: + print "[ PASS ]", backend, binary + + else: + if seconds == 20: + print "[ TIME ]", backend, binary + else: + print "[ FAIL ]", backend, binary + + os.system("killall spectrum 2> /dev/null") + +one_test_run() + +