From 8faecb5664c8ba8a81b361b4d6b7ba2b27303d50 Mon Sep 17 00:00:00 2001 From: Jan Kaluza Date: Mon, 28 Nov 2011 17:34:45 +0100 Subject: [PATCH 01/13] SpectrumErrorPayload --- .../Swiften/Elements/SpectrumErrorPayload.cpp | 13 +++++ .../Swiften/Elements/SpectrumErrorPayload.h | 50 ++++++++++++++++++ .../SpectrumErrorSerializer.cpp | 51 +++++++++++++++++++ .../SpectrumErrorSerializer.h | 19 +++++++ include/transport/user.h | 3 +- src/networkpluginserver.cpp | 3 +- src/transport.cpp | 3 ++ src/user.cpp | 4 +- 8 files changed, 143 insertions(+), 3 deletions(-) create mode 100644 include/Swiften/Elements/SpectrumErrorPayload.cpp create mode 100644 include/Swiften/Elements/SpectrumErrorPayload.h create mode 100644 include/Swiften/Serializer/PayloadSerializers/SpectrumErrorSerializer.cpp create mode 100644 include/Swiften/Serializer/PayloadSerializers/SpectrumErrorSerializer.h diff --git a/include/Swiften/Elements/SpectrumErrorPayload.cpp b/include/Swiften/Elements/SpectrumErrorPayload.cpp new file mode 100644 index 00000000..9661f63e --- /dev/null +++ b/include/Swiften/Elements/SpectrumErrorPayload.cpp @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2011 Jan Kaluza + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include + +namespace Swift { + +SpectrumErrorPayload::SpectrumErrorPayload(Error error) : error_(error) { } + +} diff --git a/include/Swiften/Elements/SpectrumErrorPayload.h b/include/Swiften/Elements/SpectrumErrorPayload.h new file mode 100644 index 00000000..b2b402ff --- /dev/null +++ b/include/Swiften/Elements/SpectrumErrorPayload.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2011 Jan Kaluza + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include + +#include +#include + +namespace Swift { + class SpectrumErrorPayload : public Payload { + public: + enum Error { + CONNECTION_ERROR_NETWORK_ERROR = 0, + CONNECTION_ERROR_INVALID_USERNAME = 1, + CONNECTION_ERROR_AUTHENTICATION_FAILED = 2, + CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE = 3, + CONNECTION_ERROR_NO_SSL_SUPPORT = 4, + CONNECTION_ERROR_ENCRYPTION_ERROR = 5, + CONNECTION_ERROR_NAME_IN_USE = 6, + CONNECTION_ERROR_INVALID_SETTINGS = 7, + CONNECTION_ERROR_CERT_NOT_PROVIDED = 8, + CONNECTION_ERROR_CERT_UNTRUSTED = 9, + CONNECTION_ERROR_CERT_EXPIRED = 10, + CONNECTION_ERROR_CERT_NOT_ACTIVATED = 11, + CONNECTION_ERROR_CERT_HOSTNAME_MISMATCH = 12, + CONNECTION_ERROR_CERT_FINGERPRINT_MISMATCH = 13, + CONNECTION_ERROR_CERT_SELF_SIGNED = 14, + CONNECTION_ERROR_CERT_OTHER_ERROR = 15, + CONNECTION_ERROR_OTHER_ERROR = 16, + }; + + SpectrumErrorPayload(Error error = CONNECTION_ERROR_OTHER_ERROR); + + Error getError() const { + return error_; + } + + void setError(Error error) { + error_ = error; + } + + private: + Error error_; + }; +} diff --git a/include/Swiften/Serializer/PayloadSerializers/SpectrumErrorSerializer.cpp b/include/Swiften/Serializer/PayloadSerializers/SpectrumErrorSerializer.cpp new file mode 100644 index 00000000..552e1936 --- /dev/null +++ b/include/Swiften/Serializer/PayloadSerializers/SpectrumErrorSerializer.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2011 Jan Kaluza + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include + +#include + +#include +#include +#include +#include +#include + +namespace Swift { + +SpectrumErrorSerializer::SpectrumErrorSerializer() : GenericPayloadSerializer() { +} + +std::string SpectrumErrorSerializer::serializePayload(boost::shared_ptr error) const { + std::string data; + switch (error->getError()) { + case SpectrumErrorPayload::CONNECTION_ERROR_NETWORK_ERROR: data = "CONNECTION_ERROR_NETWORK_ERROR"; break; + case SpectrumErrorPayload::CONNECTION_ERROR_INVALID_USERNAME: data = "CONNECTION_ERROR_INVALID_USERNAME"; break; + case SpectrumErrorPayload::CONNECTION_ERROR_AUTHENTICATION_FAILED: data = "CONNECTION_ERROR_AUTHENTICATION_FAILED"; break; + case SpectrumErrorPayload::CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE: data = "CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE"; break; + case SpectrumErrorPayload::CONNECTION_ERROR_NO_SSL_SUPPORT: data = "CONNECTION_ERROR_NO_SSL_SUPPORT"; break; + case SpectrumErrorPayload::CONNECTION_ERROR_ENCRYPTION_ERROR: data = "CONNECTION_ERROR_ENCRYPTION_ERROR"; break; + case SpectrumErrorPayload::CONNECTION_ERROR_NAME_IN_USE: data = "CONNECTION_ERROR_NAME_IN_USE"; break; + case SpectrumErrorPayload::CONNECTION_ERROR_INVALID_SETTINGS: data = "CONNECTION_ERROR_INVALID_SETTINGS"; break; + case SpectrumErrorPayload::CONNECTION_ERROR_CERT_NOT_PROVIDED: data = "CONNECTION_ERROR_CERT_NOT_PROVIDED"; break; + case SpectrumErrorPayload::CONNECTION_ERROR_CERT_UNTRUSTED: data = "CONNECTION_ERROR_CERT_UNTRUSTED"; break; + case SpectrumErrorPayload::CONNECTION_ERROR_CERT_EXPIRED: data = "CONNECTION_ERROR_NETWORK_ERROR"; break; + case SpectrumErrorPayload::CONNECTION_ERROR_CERT_NOT_ACTIVATED: data = "CONNECTION_ERROR_NETWORK_ERROR"; break; + case SpectrumErrorPayload::CONNECTION_ERROR_CERT_HOSTNAME_MISMATCH: data = "CONNECTION_ERROR_NETWORK_ERROR"; break; + case SpectrumErrorPayload::CONNECTION_ERROR_CERT_FINGERPRINT_MISMATCH: data = "CONNECTION_ERROR_NETWORK_ERROR"; break; + case SpectrumErrorPayload::CONNECTION_ERROR_CERT_SELF_SIGNED: data = "CONNECTION_ERROR_NETWORK_ERROR"; break; + case SpectrumErrorPayload::CONNECTION_ERROR_CERT_OTHER_ERROR: data = "CONNECTION_ERROR_NETWORK_ERROR"; break; + case SpectrumErrorPayload::CONNECTION_ERROR_OTHER_ERROR: data = "CONNECTION_ERROR_NETWORK_ERROR"; break; + } + + XMLElement el("spectrumerror", "http://spectrum.im/error", data); + + el.setAttribute("error", boost::lexical_cast(error->getError())); + + return el.serialize(); +} + +} diff --git a/include/Swiften/Serializer/PayloadSerializers/SpectrumErrorSerializer.h b/include/Swiften/Serializer/PayloadSerializers/SpectrumErrorSerializer.h new file mode 100644 index 00000000..112e79e2 --- /dev/null +++ b/include/Swiften/Serializer/PayloadSerializers/SpectrumErrorSerializer.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2011 Jan Kaluza + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include +#include + +namespace Swift { + class SpectrumErrorSerializer : public GenericPayloadSerializer { + public: + SpectrumErrorSerializer(); + + virtual std::string serializePayload(boost::shared_ptr) const; + }; +} diff --git a/include/transport/user.h b/include/transport/user.h index 952bb3b4..76fe48bc 100644 --- a/include/transport/user.h +++ b/include/transport/user.h @@ -27,6 +27,7 @@ #include "Swiften/Disco/EntityCapsProvider.h" #include "storagebackend.h" #include +#include "Swiften/Elements/SpectrumErrorPayload.h" namespace Transport { @@ -93,7 +94,7 @@ class User : public Swift::EntityCapsProvider { /// \return language const char *getLang() { return "en"; } - void handleDisconnected(const std::string &error); + void handleDisconnected(const std::string &error, Swift::SpectrumErrorPayload::Error e = Swift::SpectrumErrorPayload::CONNECTION_ERROR_OTHER_ERROR); bool isReadyToConnect() { return m_readyForConnect; diff --git a/src/networkpluginserver.cpp b/src/networkpluginserver.cpp index b284390c..a99c51de 100644 --- a/src/networkpluginserver.cpp +++ b/src/networkpluginserver.cpp @@ -39,6 +39,7 @@ #include "Swiften/Elements/AttentionPayload.h" #include "Swiften/Elements/XHTMLIMPayload.h" #include "Swiften/Elements/InvisiblePayload.h" +#include "Swiften/Elements/SpectrumErrorPayload.h" #include "transport/protocol.pb.h" #include "log4cxx/logger.h" @@ -387,7 +388,7 @@ void NetworkPluginServer::handleDisconnectedPayload(const std::string &data) { if (!user) { return; } - user->handleDisconnected(payload.message()); + user->handleDisconnected(payload.message(), (Swift::SpectrumErrorPayload::Error) payload.error()); } void NetworkPluginServer::handleVCardPayload(const std::string &data) { diff --git a/src/transport.cpp b/src/transport.cpp index 7d3677f1..90e28deb 100644 --- a/src/transport.cpp +++ b/src/transport.cpp @@ -36,6 +36,7 @@ #include "Swiften/Serializer/PayloadSerializers/XHTMLIMSerializer.h" #include "Swiften/Parser/PayloadParsers/StatsParser.h" #include "Swiften/Serializer/PayloadSerializers/StatsSerializer.h" +#include "Swiften/Serializer/PayloadSerializers/SpectrumErrorSerializer.h" #include "transport/BlockParser.h" #include "transport/BlockSerializer.h" #include "Swiften/Parser/PayloadParsers/InvisibleParser.h" @@ -99,6 +100,7 @@ Component::Component(Swift::EventLoop *loop, Swift::NetworkFactories *factories, m_server->addPayloadSerializer(new Transport::BlockSerializer()); m_server->addPayloadSerializer(new Swift::InvisibleSerializer()); m_server->addPayloadSerializer(new Swift::StatsSerializer()); + m_server->addPayloadSerializer(new Swift::SpectrumErrorSerializer()); m_server->onDataRead.connect(boost::bind(&Component::handleDataRead, this, _1)); m_server->onDataWritten.connect(boost::bind(&Component::handleDataWritten, this, _1)); @@ -124,6 +126,7 @@ Component::Component(Swift::EventLoop *loop, Swift::NetworkFactories *factories, m_component->addPayloadSerializer(new Transport::BlockSerializer()); m_component->addPayloadSerializer(new Swift::InvisibleSerializer()); m_component->addPayloadSerializer(new Swift::StatsSerializer()); + m_component->addPayloadSerializer(new Swift::SpectrumErrorSerializer()); m_stanzaChannel = m_component->getStanzaChannel(); m_iqRouter = m_component->getIQRouter(); diff --git a/src/user.cpp b/src/user.cpp index 9b1d7fd6..7d9ec3ab 100644 --- a/src/user.cpp +++ b/src/user.cpp @@ -28,6 +28,7 @@ #include "Swiften/Server/ServerStanzaChannel.h" #include "Swiften/Elements/StreamError.h" #include "Swiften/Elements/MUCPayload.h" +#include "Swiften/Elements/SpectrumErrorPayload.h" #include "log4cxx/logger.h" #include #include @@ -315,7 +316,7 @@ void User::setIgnoreDisconnect(bool ignoreDisconnect) { LOG4CXX_INFO(logger, m_jid.toString() << ": Setting ignoreDisconnect=" << m_ignoreDisconnect); } -void User::handleDisconnected(const std::string &error) { +void User::handleDisconnected(const std::string &error, Swift::SpectrumErrorPayload::Error e) { if (m_ignoreDisconnect) { LOG4CXX_INFO(logger, m_jid.toString() << ": Disconnecting from legacy network ignored (probably moving between backends)"); return; @@ -333,6 +334,7 @@ void User::handleDisconnected(const std::string &error) { msg->setBody(error); msg->setTo(m_jid.toBare()); msg->setFrom(m_component->getJID()); + msg->addPayload(boost::make_shared(e)); m_component->getStanzaChannel()->sendMessage(msg); // In server mode, server finishes the session and pass unavailable session to userManager if we're connected to legacy network, From c48fa3e77d4034fdff256b2b26dd44c061e9c4cb Mon Sep 17 00:00:00 2001 From: Jan Kaluza Date: Wed, 30 Nov 2011 11:36:41 +0100 Subject: [PATCH 02/13] Allow change the legacy name in registration for MySQL --- src/mysqlbackend.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mysqlbackend.cpp b/src/mysqlbackend.cpp index 60ac8b47..70ffca45 100644 --- a/src/mysqlbackend.cpp +++ b/src/mysqlbackend.cpp @@ -320,7 +320,7 @@ bool MySQLBackend::connect() { createDatabase(); - m_setUser = new Statement(&m_conn, "sssssbs", "INSERT INTO " + m_prefix + "users (jid, uin, password, language, encoding, last_login, vip) VALUES (?, ?, ?, ?, ?, NOW(), ?) ON DUPLICATE KEY UPDATE password=?"); + m_setUser = new Statement(&m_conn, "sssssbs", "INSERT INTO " + m_prefix + "users (jid, uin, password, language, encoding, last_login, vip) VALUES (?, ?, ?, ?, ?, NOW(), ?) ON DUPLICATE KEY UPDATE uin=?, password=?"); m_getUser = new Statement(&m_conn, "s|isssssb", "SELECT id, jid, uin, password, encoding, language, vip FROM " + m_prefix + "users WHERE jid=?"); m_removeUser = new Statement(&m_conn, "i", "DELETE FROM " + m_prefix + "users WHERE id=?"); @@ -414,7 +414,7 @@ void MySQLBackend::setUser(const UserInfo &user) { if (!CONFIG_STRING(m_config, "database.encryption_key").empty()) { encrypted = Util::encryptPassword(encrypted, CONFIG_STRING(m_config, "database.encryption_key")); } - *m_setUser << user.jid << user.uin << encrypted << user.language << user.encoding << user.vip << user.password; + *m_setUser << user.jid << user.uin << encrypted << user.language << user.encoding << user.vip << user.uin << user.password; EXEC(m_setUser, setUser(user)); } From 6b81ff06d6c506ffc6a60ccfa9c2ae00a04dddb2 Mon Sep 17 00:00:00 2001 From: HanzZ Date: Wed, 30 Nov 2011 21:43:12 +0100 Subject: [PATCH 03/13] Support for buddies on more groups --- backends/frotz/main.cpp | 4 +++- backends/libpurple/main.cpp | 19 +++++++++++++++++-- include/transport/networkplugin.h | 6 +++--- include/transport/protocol.proto | 2 +- include/transport/util.h | 4 ++++ plugin/src/networkplugin.cpp | 19 +++++++++++++++---- spectrum/src/sample.cfg | 4 ++-- src/mysqlbackend.cpp | 15 +++++++++------ src/networkpluginserver.cpp | 20 ++++++++++++++------ src/sqlite3backend.cpp | 8 +++++--- src/util.cpp | 25 +++++++++++++++++++++++++ 11 files changed, 98 insertions(+), 28 deletions(-) diff --git a/backends/frotz/main.cpp b/backends/frotz/main.cpp index b1e433a4..7a13290d 100644 --- a/backends/frotz/main.cpp +++ b/backends/frotz/main.cpp @@ -171,7 +171,9 @@ class FrotzNetworkPlugin : public NetworkPlugin { void handleLoginRequest(const std::string &user, const std::string &legacyName, const std::string &password) { np->handleConnected(user); - np->handleBuddyChanged(user, "zcode", "ZCode", "ZCode", pbnetwork::STATUS_ONLINE); + std::vector groups; + groups.push_back("ZCode"); + np->handleBuddyChanged(user, "zcode", "ZCode", groups, pbnetwork::STATUS_ONLINE); // sleep(1); // np->handleMessage(np->m_user, "zork", first_msg); } diff --git a/backends/libpurple/main.cpp b/backends/libpurple/main.cpp index 65d719e3..a8622562 100644 --- a/backends/libpurple/main.cpp +++ b/backends/libpurple/main.cpp @@ -1052,7 +1052,22 @@ static std::string getIconHash(PurpleBuddy *m_buddy) { static std::vector getGroups(PurpleBuddy *m_buddy) { std::vector groups; - groups.push_back((purple_buddy_get_group(m_buddy) && purple_group_get_name(purple_buddy_get_group(m_buddy))) ? std::string(purple_group_get_name(purple_buddy_get_group(m_buddy))) : std::string("Buddies")); + if (purple_buddy_get_name(m_buddy)) { + GSList *buddies = purple_find_buddies(purple_buddy_get_account(m_buddy), purple_buddy_get_name(m_buddy)); + while(buddies) { + PurpleGroup *g = purple_buddy_get_group((PurpleBuddy *) buddies->data); + buddies = g_slist_delete_link(buddies, buddies); + + if(g && purple_group_get_name(g)) { + groups.push_back(purple_group_get_name(g)); + } + } + } + + if (groups.empty()) { + groups.push_back("Buddies"); + } + return groups; } @@ -1105,7 +1120,7 @@ static void buddyListNewNode(PurpleBlistNode *node) { } } - np->handleBuddyChanged(np->m_accounts[account], purple_buddy_get_name(buddy), getAlias(buddy), getGroups(buddy)[0], status, message, getIconHash(buddy), + np->handleBuddyChanged(np->m_accounts[account], purple_buddy_get_name(buddy), getAlias(buddy), getGroups(buddy), status, message, getIconHash(buddy), blocked ); } diff --git a/include/transport/networkplugin.h b/include/transport/networkplugin.h index 8bfbf221..bba9c013 100644 --- a/include/transport/networkplugin.h +++ b/include/transport/networkplugin.h @@ -53,7 +53,7 @@ class NetworkPlugin { /// \param iconHash MD5 hash of buddy icon. Empty if none buddy icon. /// \param blocked True if this buddy is blocked in privacy lists in legacy network. void handleBuddyChanged(const std::string &user, const std::string &buddyName, const std::string &alias, - const std::string &groups, pbnetwork::StatusType status, const std::string &statusMessage = "", const std::string &iconHash = "", + const std::vector &groups, pbnetwork::StatusType status, const std::string &statusMessage = "", const std::string &iconHash = "", bool blocked = false ); @@ -197,8 +197,8 @@ class NetworkPlugin { 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 handleStatusChangeRequest(const std::string &/*user*/, int status, const std::string &statusMessage) {} - virtual void handleBuddyUpdatedRequest(const std::string &/*user*/, const std::string &/*buddyName*/, const std::string &/*alias*/, const std::string &/*groups*/) {} - virtual void handleBuddyRemovedRequest(const std::string &/*user*/, const std::string &/*buddyName*/, const std::string &/*groups*/) {} + virtual void handleBuddyUpdatedRequest(const std::string &/*user*/, const std::string &/*buddyName*/, const std::string &/*alias*/, const std::vector &/*groups*/) {} + virtual void handleBuddyRemovedRequest(const std::string &/*user*/, const std::string &/*buddyName*/, const std::vector &/*groups*/) {} virtual void handleBuddyBlockToggled(const std::string &/*user*/, const std::string &/*buddyName*/, bool /*blocked*/) {} virtual void handleTypingRequest(const std::string &/*user*/, const std::string &/*buddyName*/) {} diff --git a/include/transport/protocol.proto b/include/transport/protocol.proto index 336915a0..9a296392 100644 --- a/include/transport/protocol.proto +++ b/include/transport/protocol.proto @@ -55,7 +55,7 @@ message Buddy { required string userName = 1; required string buddyName = 2; optional string alias = 3; - optional string groups = 4; + repeated string group = 4; optional StatusType status = 5; optional string statusMessage = 6; optional string iconHash = 7; diff --git a/include/transport/util.h b/include/transport/util.h index 79d52a47..fe171115 100644 --- a/include/transport/util.h +++ b/include/transport/util.h @@ -36,6 +36,10 @@ std::string encryptPassword(const std::string &password, const std::string &key) std::string decryptPassword(std::string &encrypted, const std::string &key); +std::string serializeGroups(const std::vector &groups); + +std::vector deserializeGroups(std::string &groups); + } } diff --git a/plugin/src/networkplugin.cpp b/plugin/src/networkplugin.cpp index fb5d58fb..9348eb11 100644 --- a/plugin/src/networkplugin.cpp +++ b/plugin/src/networkplugin.cpp @@ -115,12 +115,14 @@ void NetworkPlugin::handleSubject(const std::string &user, const std::string &le } void NetworkPlugin::handleBuddyChanged(const std::string &user, const std::string &buddyName, const std::string &alias, - const std::string &groups, pbnetwork::StatusType status, const std::string &statusMessage, const std::string &iconHash, bool blocked) { + const std::vector &groups, pbnetwork::StatusType status, const std::string &statusMessage, const std::string &iconHash, bool blocked) { pbnetwork::Buddy buddy; buddy.set_username(user); buddy.set_buddyname(buddyName); buddy.set_alias(alias); - buddy.set_groups(groups); + for (std::vector::const_iterator it = groups.begin(); it != groups.end(); it++) { + buddy.add_group(*it); + } buddy.set_status((pbnetwork::StatusType) status); buddy.set_statusmessage(statusMessage); buddy.set_iconhash(iconHash); @@ -424,7 +426,11 @@ void NetworkPlugin::handleBuddyChangedPayload(const std::string &data) { handleBuddyBlockToggled(payload.username(), payload.buddyname(), payload.blocked()); } else { - handleBuddyUpdatedRequest(payload.username(), payload.buddyname(), payload.alias(), payload.groups()); + std::vector groups; + for (int i = 0; i < payload.group_size(); i++) { + groups.push_back(payload.group(i)); + } + handleBuddyUpdatedRequest(payload.username(), payload.buddyname(), payload.alias(), groups); } } @@ -435,7 +441,12 @@ void NetworkPlugin::handleBuddyRemovedPayload(const std::string &data) { return; } - handleBuddyRemovedRequest(payload.username(), payload.buddyname(), payload.groups()); + std::vector groups; + for (int i = 0; i < payload.group_size(); i++) { + groups.push_back(payload.group(i)); + } + + handleBuddyRemovedRequest(payload.username(), payload.buddyname(), groups); } void NetworkPlugin::handleChatStatePayload(const std::string &data, int type) { diff --git a/spectrum/src/sample.cfg b/spectrum/src/sample.cfg index 3fdca1d1..fcb060d3 100644 --- a/spectrum/src/sample.cfg +++ b/spectrum/src/sample.cfg @@ -13,10 +13,10 @@ 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=/home/hanzz/code/libtransport/backends/libpurple/spectrum2_libpurple_backend +backend=/home/hanzz/code/libtransport/backends/libpurple/spectrum2_libpurple_backend #backend=/usr/bin/mono /home/hanzz/code/networkplugin-csharp/msnp-sharp-backend/bin/Debug/msnp-sharp-backend.exe #backend=/home/hanzz/code/libtransport/backends/frotz/spectrum2_frotz_backend -backend=/home/hanzz/code/libtransport/backends/libircclient-qt/spectrum2_libircclient-qt_backend +#backend=/home/hanzz/code/libtransport/backends/libircclient-qt/spectrum2_libircclient-qt_backend #protocol=prpl-msn protocol=any #protocol=prpl-icq diff --git a/src/mysqlbackend.cpp b/src/mysqlbackend.cpp index 70ffca45..62dcde06 100644 --- a/src/mysqlbackend.cpp +++ b/src/mysqlbackend.cpp @@ -320,7 +320,7 @@ bool MySQLBackend::connect() { createDatabase(); - m_setUser = new Statement(&m_conn, "sssssbs", "INSERT INTO " + m_prefix + "users (jid, uin, password, language, encoding, last_login, vip) VALUES (?, ?, ?, ?, ?, NOW(), ?) ON DUPLICATE KEY UPDATE uin=?, password=?"); + m_setUser = new Statement(&m_conn, "sssssbss", "INSERT INTO " + m_prefix + "users (jid, uin, password, language, encoding, last_login, vip) VALUES (?, ?, ?, ?, ?, NOW(), ?) ON DUPLICATE KEY UPDATE uin=?, password=?"); m_getUser = new Statement(&m_conn, "s|isssssb", "SELECT id, jid, uin, password, encoding, language, vip FROM " + m_prefix + "users WHERE jid=?"); m_removeUser = new Statement(&m_conn, "i", "DELETE FROM " + m_prefix + "users WHERE id=?"); @@ -414,7 +414,7 @@ void MySQLBackend::setUser(const UserInfo &user) { if (!CONFIG_STRING(m_config, "database.encryption_key").empty()) { encrypted = Util::encryptPassword(encrypted, CONFIG_STRING(m_config, "database.encryption_key")); } - *m_setUser << user.jid << user.uin << encrypted << user.language << user.encoding << user.vip << user.uin << user.password; + *m_setUser << user.jid << user.uin << encrypted << user.language << user.encoding << user.vip << user.uin << encrypted; EXEC(m_setUser, setUser(user)); } @@ -444,8 +444,9 @@ void MySQLBackend::setUserOnline(long id, bool online) { long MySQLBackend::addBuddy(long userId, const BuddyInfo &buddyInfo) { // "INSERT INTO " + m_prefix + "buddies (user_id, uin, subscription, groups, nickname, flags) VALUES (?, ?, ?, ?, ?, ?)" + std::string groups = Util::serializeGroups(buddyInfo.groups); *m_addBuddy << userId << buddyInfo.legacyName << buddyInfo.subscription; - *m_addBuddy << (buddyInfo.groups.size() == 0 ? "" : buddyInfo.groups[0]); + *m_addBuddy << groups; *m_addBuddy << buddyInfo.alias << buddyInfo.flags; EXEC(m_addBuddy, addBuddy(userId, buddyInfo)); @@ -463,7 +464,8 @@ long MySQLBackend::addBuddy(long userId, const BuddyInfo &buddyInfo) { void MySQLBackend::updateBuddy(long userId, const BuddyInfo &buddyInfo) { // "UPDATE " + m_prefix + "buddies SET groups=?, nickname=?, flags=?, subscription=? WHERE user_id=? AND uin=?" - *m_updateBuddy << (buddyInfo.groups.size() == 0 ? "" : buddyInfo.groups[0]); + std::string groups = Util::serializeGroups(buddyInfo.groups); + *m_updateBuddy << groups; *m_updateBuddy << buddyInfo.alias << buddyInfo.flags << buddyInfo.subscription; *m_updateBuddy << userId << buddyInfo.legacyName; @@ -491,8 +493,9 @@ bool MySQLBackend::getBuddies(long id, std::list &roster) { std::string group; *m_getBuddies >> b.id >> b.legacyName >> b.subscription >> b.alias >> group >> b.flags; - if (!group.empty()) - b.groups.push_back(group); + if (!group.empty()) { + b.groups = Util::deserializeGroups(group); + } roster.push_back(b); } diff --git a/src/networkpluginserver.cpp b/src/networkpluginserver.cpp index a99c51de..64dc9f54 100644 --- a/src/networkpluginserver.cpp +++ b/src/networkpluginserver.cpp @@ -198,9 +198,11 @@ static void handleBuddyPayload(LocalBuddy *buddy, const pbnetwork::Buddy &payloa } // Change groups if it's not empty. The same as above... - if (!payload.groups().empty()) { - std::vector groups; - groups.push_back(payload.groups()); + std::vector groups; + for (int i = 0; i < payload.group_size(); i++) { + groups.push_back(payload.group(i)); + } + if (!groups.empty()) { buddy->setGroups(groups); } @@ -1163,7 +1165,9 @@ void NetworkPluginServer::handleBuddyRemoved(Buddy *b) { 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]); + BOOST_FOREACH(const std::string &g, b->getGroups()) { + buddy.add_group(g); + } buddy.set_status(pbnetwork::STATUS_NONE); std::string message; @@ -1189,7 +1193,9 @@ void NetworkPluginServer::handleBuddyUpdated(Buddy *b, const Swift::RosterItemPa 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]); + BOOST_FOREACH(const std::string &g, b->getGroups()) { + buddy.add_group(g); + } buddy.set_status(pbnetwork::STATUS_NONE); std::string message; @@ -1215,7 +1221,9 @@ void NetworkPluginServer::handleBlockToggled(Buddy *b) { 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]); + BOOST_FOREACH(const std::string &g, b->getGroups()) { + buddy.add_group(g); + } buddy.set_status(pbnetwork::STATUS_NONE); buddy.set_blocked(!b->isBlocked()); diff --git a/src/sqlite3backend.cpp b/src/sqlite3backend.cpp index 7a4f444b..4313fd67 100644 --- a/src/sqlite3backend.cpp +++ b/src/sqlite3backend.cpp @@ -21,6 +21,7 @@ #ifdef WITH_SQLITE #include "transport/sqlite3backend.h" +#include "transport/util.h" #include #include "log4cxx/logger.h" @@ -254,7 +255,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.size() == 0 ? "" : buddyInfo.groups[0]); // TODO: serialize groups + BIND_STR(m_addBuddy, Util::serializeGroups(buddyInfo.groups)); BIND_STR(m_addBuddy, buddyInfo.alias); BIND_INT(m_addBuddy, buddyInfo.flags); @@ -280,7 +281,7 @@ long SQLite3Backend::addBuddy(long userId, const BuddyInfo &buddyInfo) { void SQLite3Backend::updateBuddy(long userId, const BuddyInfo &buddyInfo) { // UPDATE " + m_prefix + "buddies SET groups=?, nickname=?, flags=?, subscription=? WHERE user_id=? AND uin=? BEGIN(m_updateBuddy); - BIND_STR(m_updateBuddy, buddyInfo.groups.size() == 0 ? "" : buddyInfo.groups[0]); // TODO: serialize groups + BIND_STR(m_updateBuddy, Util::serializeGroups(buddyInfo.groups)); BIND_STR(m_updateBuddy, buddyInfo.alias); BIND_INT(m_updateBuddy, buddyInfo.flags); BIND_STR(m_updateBuddy, buddyInfo.subscription); @@ -321,7 +322,8 @@ bool SQLite3Backend::getBuddies(long id, std::list &roster) { b.legacyName = GET_STR(m_getBuddies); b.subscription = GET_STR(m_getBuddies); b.alias = GET_STR(m_getBuddies); - b.groups.push_back(GET_STR(m_getBuddies)); + std::string groups = GET_STR(m_getBuddies); + b.groups = Util::deserializeGroups(groups); b.flags = GET_INT(m_getBuddies); if (buddy_id == b.id) { diff --git a/src/util.cpp b/src/util.cpp index 0a5f3a04..7053ea06 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -24,6 +24,7 @@ #include #include #include +#include using namespace boost::filesystem; @@ -103,6 +104,30 @@ std::string decryptPassword(std::string &encrypted, const std::string &key) { return password; } +std::string serializeGroups(const std::vector &groups) { + std::string ret; + BOOST_FOREACH(const std::string &group, groups) { + ret += group + "\n"; + } + if (!ret.empty()) { + ret.erase(ret.end() - 1); + } + return ret; +} + +std::vector deserializeGroups(std::string &groups) { + std::vector ret; + if (groups.empty()) { + return ret; + } + + boost::split(ret, groups, boost::is_any_of("\n")); + if (ret.back().empty()) { + ret.erase(ret.end() - 1); + } + return ret; +} + } } From f5a1aa3f391421f5c285702ae67e65217185f527 Mon Sep 17 00:00:00 2001 From: HanzZ Date: Sat, 3 Dec 2011 21:38:02 +0100 Subject: [PATCH 04/13] Working spectrum2 status --- spectrum_manager/src/main.cpp | 63 +++++++++++++++++++++++++++++++++-- 1 file changed, 61 insertions(+), 2 deletions(-) diff --git a/spectrum_manager/src/main.cpp b/spectrum_manager/src/main.cpp index 227dc835..840e43e1 100644 --- a/spectrum_manager/src/main.cpp +++ b/spectrum_manager/src/main.cpp @@ -234,7 +234,7 @@ static void stop_all_instances(ManagerConfig *config) { } } -void ask_local_servers(ManagerConfig *config, Swift::BoostNetworkFactories &networkFactories, const std::string &message) { +static void show_status(ManagerConfig *config) { path p(CONFIG_STRING(config, "service.config_directory")); try { @@ -256,8 +256,63 @@ void ask_local_servers(ManagerConfig *config, Swift::BoostNetworkFactories &netw std::cerr << "Can't load config file " << itr->path().string() << ". Skipping...\n"; } + std::vector vhosts; + if (CONFIG_HAS_KEY(&cfg, "vhosts.vhost")) + vhosts = CONFIG_VECTOR(&cfg, "vhosts.vhost"); + vhosts.push_back(CONFIG_STRING(&cfg, "service.jid")); + + BOOST_FOREACH(std::string &vhost, vhosts) { + Config vhostCfg; + if (vhostCfg.load(itr->path().string(), vhost) == false) { + std::cerr << "Can't load config file " << itr->path().string() << ". Skipping...\n"; + continue; + } + + int pid = isRunning(CONFIG_STRING(&vhostCfg, "service.pidfile")); + if (pid) { + std::cout << itr->path() << ": " << vhost << " Running\n"; + } + else { + std::cout << itr->path() << ": " << vhost << " Stopped\n"; + } + } + } + } + } + catch (const filesystem_error& ex) { + std::cerr << "boost filesystem error\n"; + exit(5); + } +} + +static void ask_local_servers(ManagerConfig *config, Swift::BoostNetworkFactories &networkFactories, const std::string &message) { + path p(CONFIG_STRING(config, "service.config_directory")); + + try { + if (!exists(p)) { + std::cerr << "Config directory " << CONFIG_STRING(config, "service.config_directory") << " does not exist\n"; + exit(6); + } + + if (!is_directory(p)) { + std::cerr << "Config directory " << CONFIG_STRING(config, "service.config_directory") << " does not exist\n"; + exit(7); + } + + directory_iterator end_itr; + for (directory_iterator itr(p); itr != end_itr; ++itr) { + if (is_regular(itr->path()) && extension(itr->path()) == ".cfg") { + Config cfg; + if (cfg.load(itr->path().string()) == false) { + std::cerr << "Can't load config file " << itr->path().string() << ". Skipping...\n"; + } + + if (CONFIG_STRING(&cfg, "service.admin_jid").empty() || CONFIG_STRING(&cfg, "service.admin_password").empty()) { + std::cerr << itr->path().string() << ": service.admin_jid or service.admin_password empty. This server can't be queried over XMPP.\n"; + } + finished++; - Swift::Client *client = new Swift::Client(CONFIG_STRING(&cfg, "service.admin_username"), CONFIG_STRING(&cfg, "service.admin_password"), &networkFactories); + Swift::Client *client = new Swift::Client(CONFIG_STRING(&cfg, "service.admin_jid"), CONFIG_STRING(&cfg, "service.admin_password"), &networkFactories); client->setAlwaysTrustCertificates(); client->onConnected.connect(boost::bind(&handleConnected, client, CONFIG_STRING(&cfg, "service.jid"))); client->onDisconnected.connect(bind(&handleDisconnected, client, _1, CONFIG_STRING(&cfg, "service.jid"))); @@ -285,6 +340,7 @@ int main(int argc, char **argv) boost::program_options::options_description desc("Usage: spectrum [OPTIONS] \nCommands:\n" " start - start all local Spectrum2 instances\n" " stop - stop all local Spectrum2 instances\n" + " status - status of local Spectrum2 instances\n" " - send command to all local + remote Spectrum2 instances and print output\n" "Allowed options"); desc.add_options() @@ -333,6 +389,9 @@ int main(int argc, char **argv) else if (command == "stop") { stop_all_instances(&config); } + else if (command == "status") { + show_status(&config); + } else { Swift::SimpleEventLoop eventLoop; Swift::BoostNetworkFactories networkFactories(&eventLoop); From d4217644d05b1f9cfa8796a8b4bc41ab2f00e331 Mon Sep 17 00:00:00 2001 From: HanzZ Date: Sat, 3 Dec 2011 21:49:00 +0100 Subject: [PATCH 05/13] Return exit code from spectrum2_manager status --- spectrum_manager/src/main.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/spectrum_manager/src/main.cpp b/spectrum_manager/src/main.cpp index 840e43e1..c22569d0 100644 --- a/spectrum_manager/src/main.cpp +++ b/spectrum_manager/src/main.cpp @@ -234,7 +234,8 @@ static void stop_all_instances(ManagerConfig *config) { } } -static void show_status(ManagerConfig *config) { +static int show_status(ManagerConfig *config) { + int ret = 0; path p(CONFIG_STRING(config, "service.config_directory")); try { @@ -273,6 +274,7 @@ static void show_status(ManagerConfig *config) { std::cout << itr->path() << ": " << vhost << " Running\n"; } else { + ret = 3; std::cout << itr->path() << ": " << vhost << " Stopped\n"; } } @@ -283,6 +285,7 @@ static void show_status(ManagerConfig *config) { std::cerr << "boost filesystem error\n"; exit(5); } + return ret; } static void ask_local_servers(ManagerConfig *config, Swift::BoostNetworkFactories &networkFactories, const std::string &message) { @@ -390,7 +393,7 @@ int main(int argc, char **argv) stop_all_instances(&config); } else if (command == "status") { - show_status(&config); + return show_status(&config); } else { Swift::SimpleEventLoop eventLoop; From b1e661969b890b4a818994807ca672b4f1f0307f Mon Sep 17 00:00:00 2001 From: Jan Kaluza Date: Mon, 5 Dec 2011 13:25:06 +0100 Subject: [PATCH 06/13] Link spectrum2 only against swiften libraries to preserve linkage against boost-mt and boost --- spectrum/src/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spectrum/src/CMakeLists.txt b/spectrum/src/CMakeLists.txt index 6c20b757..e84cbbb1 100644 --- a/spectrum/src/CMakeLists.txt +++ b/spectrum/src/CMakeLists.txt @@ -6,7 +6,7 @@ ADD_EXECUTABLE(spectrum2 ${SRC}) ADD_DEPENDENCIES(spectrum2 spectrum2_libpurple_backend) ADD_DEPENDENCIES(spectrum2 spectrum2_libircclient-qt_backend) -target_link_libraries(spectrum2 transport ${Boost_LIBRARIES} ${SWIFTEN_LIBRARY} ${LOG4CXX_LIBRARIES}) +target_link_libraries(spectrum2 transport ${SWIFTEN_LIBRARY} ${LOG4CXX_LIBRARIES}) INSTALL(TARGETS spectrum2 RUNTIME DESTINATION bin) From 80e10aad92e553ff5f0084af7507d12635973a39 Mon Sep 17 00:00:00 2001 From: HanzZ Date: Tue, 6 Dec 2011 17:29:10 +0100 Subject: [PATCH 07/13] Changed main.cpp to fit more-groups changes --- backends/libpurple/main.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/backends/libpurple/main.cpp b/backends/libpurple/main.cpp index a8622562..2c04df27 100644 --- a/backends/libpurple/main.cpp +++ b/backends/libpurple/main.cpp @@ -69,7 +69,7 @@ class SpectrumNetworkPlugin; GKeyFile *keyfile; SpectrumNetworkPlugin *np; -std::string replaceAll( +static std::string replaceAll( std::string result, const std::string& replaceWhat, const std::string& replaceWithWhat) @@ -808,7 +808,7 @@ class SpectrumNetworkPlugin : public NetworkPlugin { } } - void handleBuddyRemovedRequest(const std::string &user, const std::string &buddyName, const std::string &groups) { + void handleBuddyRemovedRequest(const std::string &user, const std::string &buddyName, const std::vector &groups) { PurpleAccount *account = m_sessions[user]; if (account) { if (m_authRequests.find(user + buddyName) != m_authRequests.end()) { @@ -823,9 +823,10 @@ class SpectrumNetworkPlugin : public NetworkPlugin { } } - void handleBuddyUpdatedRequest(const std::string &user, const std::string &buddyName, const std::string &alias, const std::string &groups) { + void handleBuddyUpdatedRequest(const std::string &user, const std::string &buddyName, const std::string &alias, const std::vector &groups_) { PurpleAccount *account = m_sessions[user]; if (account) { + std::string groups = groups_.empty() ? "" : groups_[0]; if (m_authRequests.find(user + buddyName) != m_authRequests.end()) { m_authRequests[user + buddyName]->authorize_cb(m_authRequests[user + buddyName]->user_data); From 56dd6499981437f1aa0bbe597f98b194eab14970 Mon Sep 17 00:00:00 2001 From: Jan Kaluza Date: Wed, 7 Dec 2011 10:30:39 +0100 Subject: [PATCH 08/13] Reconnect users automatically in case of spectrum2 main instance crash/restart --- ChangeLog | 4 ++ include/transport/mysqlbackend.h | 3 + include/transport/sqlite3backend.h | 3 + include/transport/storagebackend.h | 3 + include/transport/usermanager.h | 4 +- include/transport/usersreconnecter.h | 56 +++++++++++++++++ spectrum/src/main.cpp | 14 ++++- src/mysqlbackend.cpp | 16 +++++ src/sqlite3backend.cpp | 19 ++++++ src/transport.cpp | 2 + src/usermanager.cpp | 8 +-- src/usersreconnecter.cpp | 89 ++++++++++++++++++++++++++++ 12 files changed, 212 insertions(+), 9 deletions(-) create mode 100644 ChangeLog create mode 100644 include/transport/usersreconnecter.h create mode 100644 src/usersreconnecter.cpp diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 00000000..3372f46a --- /dev/null +++ b/ChangeLog @@ -0,0 +1,4 @@ +version 2.0.0 alpha (2011-12-06): + General: + * First Spectrum 2.0.0 alpha release, check more on + http://spectrum.im/projects/spectrum/wiki/Spectrum_200_alpha diff --git a/include/transport/mysqlbackend.h b/include/transport/mysqlbackend.h index b35a345d..47c8757b 100644 --- a/include/transport/mysqlbackend.h +++ b/include/transport/mysqlbackend.h @@ -82,6 +82,8 @@ class MySQLBackend : public StorageBackend /// \return true if user has been found in database and roster has been fetched bool getBuddies(long id, std::list &roster); + bool getOnlineUsers(std::vector &users); + long addBuddy(long userId, const BuddyInfo &buddyInfo); void updateBuddy(long userId, const BuddyInfo &buddyInfo); @@ -148,6 +150,7 @@ class MySQLBackend : public StorageBackend Statement *m_getBuddies; Statement *m_getBuddiesSettings; Statement *m_setUserOnline; + Statement *m_getOnlineUsers; }; } diff --git a/include/transport/sqlite3backend.h b/include/transport/sqlite3backend.h index 91275d69..5d7372bf 100644 --- a/include/transport/sqlite3backend.h +++ b/include/transport/sqlite3backend.h @@ -70,6 +70,8 @@ class SQLite3Backend : public StorageBackend /// \param online online state void setUserOnline(long id, bool online); + bool getOnlineUsers(std::vector &users); + /// Removes user and all connected data from database. /// \param id id of user - UserInfo.id /// \return true if user has been found in database and removed @@ -115,6 +117,7 @@ class SQLite3Backend : public StorageBackend sqlite3_stmt *m_getBuddies; sqlite3_stmt *m_getBuddiesSettings; sqlite3_stmt *m_setUserOnline; + sqlite3_stmt *m_getOnlineUsers; }; } diff --git a/include/transport/storagebackend.h b/include/transport/storagebackend.h index 190ec03b..6bac4aa7 100644 --- a/include/transport/storagebackend.h +++ b/include/transport/storagebackend.h @@ -108,6 +108,9 @@ class StorageBackend /// getBuddies virtual bool getBuddies(long id, std::list &roster) = 0; + /// getOnlineUsers + virtual bool getOnlineUsers(std::vector &users) = 0; + virtual long addBuddy(long userId, const BuddyInfo &buddyInfo) = 0; virtual void updateBuddy(long userId, const BuddyInfo &buddyInfo) = 0; virtual void removeBuddy(long id) = 0; diff --git a/include/transport/usermanager.h b/include/transport/usermanager.h index cd02aade..290c4235 100644 --- a/include/transport/usermanager.h +++ b/include/transport/usermanager.h @@ -78,9 +78,9 @@ class UserManager : public Swift::EntityCapsProvider { /// Removes user. This function disconnects user and safely removes /// User class. This does *not* remove user from StorageBackend. /// \param user User class to remove - void removeUser(User *user); + void removeUser(User *user, bool onUserBehalf = true); - void removeAllUsers(); + void removeAllUsers(bool onUserBehalf = true); Swift::DiscoInfo::ref getCaps(const Swift::JID&) const; diff --git a/include/transport/usersreconnecter.h b/include/transport/usersreconnecter.h new file mode 100644 index 00000000..52497e64 --- /dev/null +++ b/include/transport/usersreconnecter.h @@ -0,0 +1,56 @@ +/** + * XMPP - libpurple transport + * + * Copyright (C) 2009, Jan Kaluza + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#pragma once + +#include +#include +#include +#include "Swiften/Swiften.h" + +namespace Transport { + +class StorageBackend; +class Component; + +/// Tries to reconnect users who have been online before crash/restart. +class UsersReconnecter { + public: + /// Creates new UsersReconnecter. + /// \param component Transport instance associated with this roster. + /// \param storageBackend StorageBackend from which the users will be fetched. + UsersReconnecter(Component *component, StorageBackend *storageBackend); + + /// Destructor. + virtual ~UsersReconnecter(); + + void reconnectNextUser(); + + private: + void handleConnected(); + + Component *m_component; + StorageBackend *m_storageBackend; + bool m_started; + std::vector m_users; + Swift::Timer::ref m_nextUserTimer; +}; + +} diff --git a/spectrum/src/main.cpp b/spectrum/src/main.cpp index fb52de18..d4ca6f4e 100644 --- a/spectrum/src/main.cpp +++ b/spectrum/src/main.cpp @@ -9,6 +9,7 @@ #include "transport/networkpluginserver.h" #include "transport/admininterface.h" #include "transport/statsresponder.h" +#include "transport/usersreconnecter.h" #include "transport/util.h" #include "Swiften/EventLoop/SimpleEventLoop.h" #include @@ -42,7 +43,7 @@ Component *component_ = NULL; UserManager *userManager_ = NULL; static void stop_spectrum() { - userManager_->removeAllUsers(); + userManager_->removeAllUsers(false); component_->stop(); eventLoop_->stop(); } @@ -369,13 +370,15 @@ int main(int argc, char **argv) UserManager userManager(&transport, &userRegistry, storageBackend); userManager_ = &userManager; + UserRegistration *userRegistration = NULL; + UsersReconnecter *usersReconnecter = NULL; if (storageBackend) { userRegistration = new UserRegistration(&transport, &userManager, storageBackend); userRegistration->start(); -// logger.setUserRegistration(&userRegistration); + + usersReconnecter = new UsersReconnecter(&transport, storageBackend); } -// logger.setUserManager(&userManager); FileTransferManager ftManager(&transport, &userManager); @@ -393,6 +396,11 @@ int main(int argc, char **argv) userRegistration->stop(); delete userRegistration; } + + if (usersReconnecter) { + delete usersReconnecter; + } + delete storageBackend; delete factories; } diff --git a/src/mysqlbackend.cpp b/src/mysqlbackend.cpp index 62dcde06..4bc2eec4 100644 --- a/src/mysqlbackend.cpp +++ b/src/mysqlbackend.cpp @@ -300,6 +300,7 @@ void MySQLBackend::disconnect() { delete m_updateUserSetting; delete m_updateBuddySetting; delete m_setUserOnline; + delete m_getOnlineUsers; mysql_close(&m_conn); } @@ -339,6 +340,7 @@ bool MySQLBackend::connect() { m_updateUserSetting = new Statement(&m_conn, "sis", "UPDATE " + m_prefix + "users_settings SET value=? WHERE user_id=? AND var=?"); m_setUserOnline = new Statement(&m_conn, "bi", "UPDATE " + m_prefix + "users SET online=?, last_login=NOW() WHERE id=?"); + m_getOnlineUsers = new Statement(&m_conn, "|s", "SELECT jid FROM " + m_prefix + "users WHERE online=1"); return true; } @@ -442,6 +444,20 @@ void MySQLBackend::setUserOnline(long id, bool online) { EXEC(m_setUserOnline, setUserOnline(id, online)); } +bool MySQLBackend::getOnlineUsers(std::vector &users) { + EXEC(m_getOnlineUsers, getOnlineUsers(users)); + if (!exec_ok) + return false; + + std::string jid; + while (m_getOnlineUsers->fetch() == 0) { + *m_getOnlineUsers >> jid; + users.push_back(jid); + } + + return true; +} + long MySQLBackend::addBuddy(long userId, const BuddyInfo &buddyInfo) { // "INSERT INTO " + m_prefix + "buddies (user_id, uin, subscription, groups, nickname, flags) VALUES (?, ?, ?, ?, ?, ?)" std::string groups = Util::serializeGroups(buddyInfo.groups); diff --git a/src/sqlite3backend.cpp b/src/sqlite3backend.cpp index 4313fd67..ad03bf23 100644 --- a/src/sqlite3backend.cpp +++ b/src/sqlite3backend.cpp @@ -99,6 +99,7 @@ SQLite3Backend::~SQLite3Backend(){ FINALIZE_STMT(m_updateUserSetting); FINALIZE_STMT(m_updateBuddySetting); FINALIZE_STMT(m_setUserOnline); + FINALIZE_STMT(m_getOnlineUsers); sqlite3_close(m_db); } } @@ -132,6 +133,7 @@ bool SQLite3Backend::connect() { PREP_STMT(m_updateUserSetting, "UPDATE " + m_prefix + "users_settings SET value=? WHERE user_id=? AND var=?"); PREP_STMT(m_setUserOnline, "UPDATE " + m_prefix + "users SET online=?, last_login=DATETIME('NOW') WHERE id=?"); + PREP_STMT(m_getOnlineUsers, "SELECT jid FROM " + m_prefix + "users WHERE online=1"); return true; } @@ -249,6 +251,23 @@ void SQLite3Backend::setUserOnline(long id, bool online) { EXECUTE_STATEMENT(m_setUserOnline, "setUserOnline query"); } +bool SQLite3Backend::getOnlineUsers(std::vector &users) { + sqlite3_reset(m_getOnlineUsers); + + int ret; + while((ret = sqlite3_step(m_getOnlineUsers)) == SQLITE_ROW) { + std::string jid = (const char *) sqlite3_column_text(m_getOnlineUsers, 0); + users.push_back(jid); + } + + if (ret != SQLITE_DONE) { + LOG4CXX_ERROR(logger, "getOnlineUsers query"<< (sqlite3_errmsg(m_db) == NULL ? "" : sqlite3_errmsg(m_db))); + return false; + } + + return true; +} + long SQLite3Backend::addBuddy(long userId, const BuddyInfo &buddyInfo) { // "INSERT INTO " + m_prefix + "buddies (user_id, uin, subscription, groups, nickname, flags) VALUES (?, ?, ?, ?, ?, ?)" BEGIN(m_addBuddy); diff --git a/src/transport.cpp b/src/transport.cpp index 90e28deb..ddcda381 100644 --- a/src/transport.cpp +++ b/src/transport.cpp @@ -197,6 +197,8 @@ void Component::start() { else if (m_server) { LOG4CXX_INFO(logger, "Starting component in server mode on port " << CONFIG_INT(m_config, "service.port")); m_server->start(); + // We're connected right here, because we're in server mode... + handleConnected(); } } diff --git a/src/usermanager.cpp b/src/usermanager.cpp index f4d7867e..ccb036f4 100644 --- a/src/usermanager.cpp +++ b/src/usermanager.cpp @@ -101,7 +101,7 @@ Swift::DiscoInfo::ref UserManager::getCaps(const Swift::JID &jid) const { return user->getCaps(jid); } -void UserManager::removeUser(User *user) { +void UserManager::removeUser(User *user, bool onUserBehalf) { m_users.erase(user->getJID().toBare().toString()); if (m_cachedUser == user) m_cachedUser = NULL; @@ -110,7 +110,7 @@ void UserManager::removeUser(User *user) { disconnectUser(user->getJID()); } - if (m_storageBackend) { + if (m_storageBackend && onUserBehalf) { m_storageBackend->setUserOnline(user->getUserInfo().id, false); } @@ -122,9 +122,9 @@ void UserManager::removeUser(User *user) { // VALGRIND_DO_LEAK_CHECK; } -void UserManager::removeAllUsers() { +void UserManager::removeAllUsers(bool onUserBehalf) { while(m_users.begin() != m_users.end()) { - removeUser((*m_users.begin()).second); + removeUser((*m_users.begin()).second, onUserBehalf); } } diff --git a/src/usersreconnecter.cpp b/src/usersreconnecter.cpp new file mode 100644 index 00000000..0ba66afb --- /dev/null +++ b/src/usersreconnecter.cpp @@ -0,0 +1,89 @@ +/** + * XMPP - libpurple transport + * + * Copyright (C) 2009, Jan Kaluza + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#include "transport/usersreconnecter.h" + +#include +#include +#include "Swiften/Queries/IQRouter.h" +#include "Swiften/Swiften.h" +#include "transport/storagebackend.h" +#include "transport/transport.h" +#include "log4cxx/logger.h" + +using namespace log4cxx; + +using namespace Swift; +using namespace boost; + +namespace Transport { + +static LoggerPtr logger = Logger::getLogger("UserReconnecter"); + +UsersReconnecter::UsersReconnecter(Component *component, StorageBackend *storageBackend) { + m_component = component; + m_storageBackend = storageBackend; + m_started = false; + + m_nextUserTimer = m_component->getNetworkFactories()->getTimerFactory()->createTimer(1000); + m_nextUserTimer->onTick.connect(boost::bind(&UsersReconnecter::reconnectNextUser, this)); + + m_component->onConnected.connect(bind(&UsersReconnecter::handleConnected, this)); +} + +UsersReconnecter::~UsersReconnecter() { + m_component->onConnected.disconnect(bind(&UsersReconnecter::handleConnected, this)); + m_nextUserTimer->stop(); + m_nextUserTimer->onTick.disconnect(boost::bind(&UsersReconnecter::reconnectNextUser, this)); +} + +void UsersReconnecter::reconnectNextUser() { + if (m_users.empty()) { + LOG4CXX_INFO(logger, "All users reconnected, stopping UserReconnecter."); + return; + } + + std::string user = m_users.back(); + m_users.pop_back(); + + LOG4CXX_INFO(logger, "Sending probe presence to " << user); + Swift::Presence::ref response = Swift::Presence::create(); + response->setTo(user); + response->setFrom(m_component->getJID()); + response->setType(Swift::Presence::Probe); + + m_component->getStanzaChannel()->sendPresence(response); + m_nextUserTimer->start(); +} + +void UsersReconnecter::handleConnected() { + if (m_started) + return; + + LOG4CXX_INFO(logger, "Starting UserReconnecter."); + m_started = true; + + m_storageBackend->getOnlineUsers(m_users); + + reconnectNextUser(); +} + + +} From 3fc191ceb46bb597a0fe09a3688cf1f68442c1af Mon Sep 17 00:00:00 2001 From: HanzZ Date: Wed, 7 Dec 2011 17:38:00 +0100 Subject: [PATCH 09/13] Comment out database stuff --- spectrum/src/sample2.cfg | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/spectrum/src/sample2.cfg b/spectrum/src/sample2.cfg index 0482e4f8..ae1f0d73 100644 --- a/spectrum/src/sample2.cfg +++ b/spectrum/src/sample2.cfg @@ -74,20 +74,20 @@ type = none # For SQLite3: Full path to database # For MySQL: name of database -# database = /var/lib/spectrum2/$jid/database.sql -database = jabber_transport +# default database = /var/lib/spectrum2/$jid/database.sql +#database = jabber_transport # Server. -server = localhost +#server = localhost # Port. -port = 0 +#port = 0 # User. -user = spectrum +#user = spectrum # Paasword. -password = secret +#password = secret # Prefix used for tables -prefix = jabber_ +#prefix = jabber_ From 97a6609c8731a8bbc6ef3a9e39dbb789b0c8ff69 Mon Sep 17 00:00:00 2001 From: HanzZ Date: Wed, 7 Dec 2011 18:20:28 +0100 Subject: [PATCH 10/13] maybe working auto-reconnect --- include/Swiften/Elements/SpectrumErrorPayload.h | 2 +- include/transport/networkpluginserver.h | 1 + src/networkpluginserver.cpp | 13 +++++++++++-- src/user.cpp | 9 +++++++++ 4 files changed, 22 insertions(+), 3 deletions(-) diff --git a/include/Swiften/Elements/SpectrumErrorPayload.h b/include/Swiften/Elements/SpectrumErrorPayload.h index b2b402ff..0fe3cc47 100644 --- a/include/Swiften/Elements/SpectrumErrorPayload.h +++ b/include/Swiften/Elements/SpectrumErrorPayload.h @@ -31,7 +31,7 @@ namespace Swift { CONNECTION_ERROR_CERT_FINGERPRINT_MISMATCH = 13, CONNECTION_ERROR_CERT_SELF_SIGNED = 14, CONNECTION_ERROR_CERT_OTHER_ERROR = 15, - CONNECTION_ERROR_OTHER_ERROR = 16, + CONNECTION_ERROR_OTHER_ERROR = 16 }; SpectrumErrorPayload(Error error = CONNECTION_ERROR_OTHER_ERROR); diff --git a/include/transport/networkpluginserver.h b/include/transport/networkpluginserver.h index c04ae834..671c8ba4 100644 --- a/include/transport/networkpluginserver.h +++ b/include/transport/networkpluginserver.h @@ -55,6 +55,7 @@ class NetworkPluginServer { unsigned long shared; bool acceptUsers; bool longRun; + bool willDie; }; NetworkPluginServer(Component *component, Config *config, UserManager *userManager, FileTransferManager *ftManager); diff --git a/src/networkpluginserver.cpp b/src/networkpluginserver.cpp index 64dc9f54..feade943 100644 --- a/src/networkpluginserver.cpp +++ b/src/networkpluginserver.cpp @@ -288,6 +288,7 @@ void NetworkPluginServer::handleNewClientConnection(boost::shared_ptrres = 0; client->init_res = 0; client->shared = 0; + client->willDie = 0; // Backend does not accept new clients automatically if it's long-running client->acceptUsers = !m_isNextLongRun; client->longRun = m_isNextLongRun; @@ -336,6 +337,9 @@ void NetworkPluginServer::handleNewClientConnection(boost::shared_ptrconnection && (*it)->longRun == longRun) { + if ((*it)->willDie == false && (*it)->acceptUsers == acceptUsers && (*it)->users.size() < CONFIG_INT(m_config, "service.users_per_backend") && (*it)->connection && (*it)->longRun == longRun) { c = *it; // if we're not reusing all backends and backend is full, stop accepting new users on this backend if (!CONFIG_BOOL(m_config, "service.reuse_old_backends")) { diff --git a/src/user.cpp b/src/user.cpp index 7d9ec3ab..3f113f8c 100644 --- a/src/user.cpp +++ b/src/user.cpp @@ -322,6 +322,15 @@ void User::handleDisconnected(const std::string &error, Swift::SpectrumErrorPayl return; } + if (e == Swift::SpectrumErrorPayload::CONNECTION_ERROR_OTHER_ERROR || e == Swift::SpectrumErrorPayload::CONNECTION_ERROR_NETWORK_ERROR) { + LOG4CXX_INFO(logger, m_jid.toString() << ": Disconnecting from legacy network, trying to reconnect automatically."); + // Simulate destruction/resurrection :) + // TODO: If this stops working, create onReconnect signal + m_userManager->onUserDestroyed(this); + m_userManager->onUserCreated(this); + return; + } + if (error.empty()) { LOG4CXX_INFO(logger, m_jid.toString() << ": Disconnected from legacy network"); } From 79d0e907bb274ace2389215930fa7fe451ea178c Mon Sep 17 00:00:00 2001 From: HanzZ Date: Wed, 7 Dec 2011 18:41:16 +0100 Subject: [PATCH 11/13] Allow only 3 reconnections in a row, then disconnect user --- include/transport/user.h | 1 + src/user.cpp | 17 +++++++++++------ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/include/transport/user.h b/include/transport/user.h index 76fe48bc..de134634 100644 --- a/include/transport/user.h +++ b/include/transport/user.h @@ -137,6 +137,7 @@ class User : public Swift::EntityCapsProvider { std::map m_legacyCaps; std::vector > m_filetransfers; int m_resources; + int m_reconnectCounter; }; } diff --git a/src/user.cpp b/src/user.cpp index 3f113f8c..61740525 100644 --- a/src/user.cpp +++ b/src/user.cpp @@ -56,6 +56,7 @@ User::User(const Swift::JID &jid, UserInfo &userInfo, Component *component, User m_readyForConnect = false; m_ignoreDisconnect = false; m_resources = 0; + m_reconnectCounter = 0; m_reconnectTimer = m_component->getNetworkFactories()->getTimerFactory()->createTimer(10000); m_reconnectTimer->onTick.connect(boost::bind(&User::onConnectingTimeout, this)); @@ -170,6 +171,7 @@ void User::sendCurrentPresence() { void User::setConnected(bool connected) { m_connected = connected; + m_reconnectCounter = 0; setIgnoreDisconnect(false); updateLastActivity(); @@ -323,12 +325,15 @@ void User::handleDisconnected(const std::string &error, Swift::SpectrumErrorPayl } if (e == Swift::SpectrumErrorPayload::CONNECTION_ERROR_OTHER_ERROR || e == Swift::SpectrumErrorPayload::CONNECTION_ERROR_NETWORK_ERROR) { - LOG4CXX_INFO(logger, m_jid.toString() << ": Disconnecting from legacy network, trying to reconnect automatically."); - // Simulate destruction/resurrection :) - // TODO: If this stops working, create onReconnect signal - m_userManager->onUserDestroyed(this); - m_userManager->onUserCreated(this); - return; + if (m_reconnectCounter < 3) { + m_reconnectCounter++; + LOG4CXX_INFO(logger, m_jid.toString() << ": Disconnecting from legacy network for, trying to reconnect automatically."); + // Simulate destruction/resurrection :) + // TODO: If this stops working, create onReconnect signal + m_userManager->onUserDestroyed(this); + m_userManager->onUserCreated(this); + return; + } } if (error.empty()) { From f18e3570a8a12d959ae059246d232603b31c2e67 Mon Sep 17 00:00:00 2001 From: HanzZ Date: Wed, 7 Dec 2011 20:08:01 +0100 Subject: [PATCH 12/13] Advice to check the log to find out reason when we can't connect the DB --- spectrum/src/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spectrum/src/main.cpp b/spectrum/src/main.cpp index d4ca6f4e..39a47758 100644 --- a/spectrum/src/main.cpp +++ b/spectrum/src/main.cpp @@ -353,7 +353,7 @@ int main(int argc, char **argv) if (CONFIG_STRING(&config, "database.type") == "sqlite3") { storageBackend = new SQLite3Backend(&config); if (!storageBackend->connect()) { - std::cerr << "Can't connect to database.\n"; + std::cerr << "Can't connect to database. Check the log to find out the reason.\n"; return -1; } } @@ -362,7 +362,7 @@ int main(int argc, char **argv) if (CONFIG_STRING(&config, "database.type") == "mysql") { storageBackend = new MySQLBackend(&config); if (!storageBackend->connect()) { - std::cerr << "Can't connect to database.\n"; + std::cerr << "Can't connect to database. Check the log to find out the reason.\n"; return -1; } } From 93418187e2a748deaf8508d4ef257ba08f556881 Mon Sep 17 00:00:00 2001 From: HanzZ Date: Wed, 7 Dec 2011 20:40:32 +0100 Subject: [PATCH 13/13] Check properly database type and show proper messages on error --- spectrum/src/main.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/spectrum/src/main.cpp b/spectrum/src/main.cpp index 39a47758..a300a709 100644 --- a/spectrum/src/main.cpp +++ b/spectrum/src/main.cpp @@ -357,7 +357,13 @@ int main(int argc, char **argv) return -1; } } +#else + if (CONFIG_STRING(&config, "database.type") == "sqlite3") { + std::cerr << "Spectrum2 is not compiled with mysql backend.\n"; + return -2; + } #endif + #ifdef WITH_MYSQL if (CONFIG_STRING(&config, "database.type") == "mysql") { storageBackend = new MySQLBackend(&config); @@ -366,8 +372,18 @@ int main(int argc, char **argv) return -1; } } +#else + if (CONFIG_STRING(&config, "database.type") == "mysql") { + std::cerr << "Spectrum2 is not compiled with mysql backend.\n"; + return -2; + } #endif + if (CONFIG_STRING(&config, "database.type") != "mysql" && CONFIG_STRING(&config, "database.type") != "sqlite3") { + std::cerr << "Unknown storage backend " << CONFIG_STRING(&config, "database.type") << "\n"; + return -2; + } + UserManager userManager(&transport, &userRegistry, storageBackend); userManager_ = &userManager;