This commit is contained in:
Vitaly Takmazov 2011-12-08 13:35:47 +04:00
commit b653edf477
31 changed files with 574 additions and 57 deletions

4
ChangeLog Normal file
View file

@ -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

View file

@ -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<std::string> groups;
groups.push_back("ZCode");
np->handleBuddyChanged(user, "zcode", "ZCode", groups, pbnetwork::STATUS_ONLINE);
// sleep(1);
// np->handleMessage(np->m_user, "zork", first_msg);
}

View file

@ -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<std::string> &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<std::string> &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);
@ -1052,7 +1053,22 @@ static std::string getIconHash(PurpleBuddy *m_buddy) {
static std::vector<std::string> getGroups(PurpleBuddy *m_buddy) {
std::vector<std::string> 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 +1121,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
);
}

View file

@ -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 <Swiften/Elements/SpectrumErrorPayload.h>
namespace Swift {
SpectrumErrorPayload::SpectrumErrorPayload(Error error) : error_(error) { }
}

View file

@ -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 <vector>
#include <string>
#include <Swiften/Elements/Payload.h>
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_;
};
}

View file

@ -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 <Swiften/Serializer/PayloadSerializers/SpectrumErrorSerializer.h>
#include <boost/shared_ptr.hpp>
#include <Swiften/Base/foreach.h>
#include <Swiften/Serializer/XML/XMLTextNode.h>
#include <Swiften/Serializer/XML/XMLRawTextNode.h>
#include <Swiften/Serializer/XML/XMLElement.h>
#include <boost/lexical_cast.hpp>
namespace Swift {
SpectrumErrorSerializer::SpectrumErrorSerializer() : GenericPayloadSerializer<SpectrumErrorPayload>() {
}
std::string SpectrumErrorSerializer::serializePayload(boost::shared_ptr<SpectrumErrorPayload> 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<std::string>(error->getError()));
return el.serialize();
}
}

View file

@ -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 <Swiften/Serializer/GenericPayloadSerializer.h>
#include <Swiften/Elements/SpectrumErrorPayload.h>
namespace Swift {
class SpectrumErrorSerializer : public GenericPayloadSerializer<SpectrumErrorPayload> {
public:
SpectrumErrorSerializer();
virtual std::string serializePayload(boost::shared_ptr<SpectrumErrorPayload>) const;
};
}

View file

@ -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<BuddyInfo> &roster);
bool getOnlineUsers(std::vector<std::string> &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;
};
}

View file

@ -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<std::string> &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<std::string> &/*groups*/) {}
virtual void handleBuddyRemovedRequest(const std::string &/*user*/, const std::string &/*buddyName*/, const std::vector<std::string> &/*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*/) {}

View file

@ -55,6 +55,7 @@ class NetworkPluginServer {
unsigned long shared;
bool acceptUsers;
bool longRun;
bool willDie;
};
NetworkPluginServer(Component *component, Config *config, UserManager *userManager, FileTransferManager *ftManager);

View file

@ -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;

View file

@ -70,6 +70,8 @@ class SQLite3Backend : public StorageBackend
/// \param online online state
void setUserOnline(long id, bool online);
bool getOnlineUsers(std::vector<std::string> &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;
};
}

View file

@ -108,6 +108,9 @@ class StorageBackend
/// getBuddies
virtual bool getBuddies(long id, std::list<BuddyInfo> &roster) = 0;
/// getOnlineUsers
virtual bool getOnlineUsers(std::vector<std::string> &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;

View file

@ -27,6 +27,7 @@
#include "Swiften/Disco/EntityCapsProvider.h"
#include "storagebackend.h"
#include <Swiften/FileTransfer/OutgoingFileTransfer.h>
#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;
@ -136,6 +137,7 @@ class User : public Swift::EntityCapsProvider {
std::map<Swift::JID, Swift::DiscoInfo::ref> m_legacyCaps;
std::vector<boost::shared_ptr<Swift::OutgoingFileTransfer> > m_filetransfers;
int m_resources;
int m_reconnectCounter;
};
}

View file

@ -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;

View file

@ -0,0 +1,56 @@
/**
* XMPP - libpurple transport
*
* Copyright (C) 2009, Jan Kaluza <hanzz@soc.pidgin.im>
*
* 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 <string>
#include <algorithm>
#include <vector>
#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<std::string> m_users;
Swift::Timer::ref m_nextUserTimer;
};
}

View file

@ -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<std::string> &groups);
std::vector<std::string> deserializeGroups(std::string &groups);
}
}

View file

@ -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<std::string> &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<std::string>::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<std::string> 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<std::string> 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) {

View file

@ -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)

View file

@ -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 <boost/filesystem.hpp>
@ -42,7 +43,7 @@ Component *component_ = NULL;
UserManager *userManager_ = NULL;
static void stop_spectrum() {
userManager_->removeAllUsers();
userManager_->removeAllUsers(false);
component_->stop();
eventLoop_->stop();
}
@ -352,30 +353,48 @@ 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;
}
}
#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);
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;
}
}
#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;
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 +412,11 @@ int main(int argc, char **argv)
userRegistration->stop();
delete userRegistration;
}
if (usersReconnecter) {
delete usersReconnecter;
}
delete storageBackend;
delete factories;
}

View file

@ -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

View file

@ -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_

View file

@ -234,7 +234,8 @@ static void stop_all_instances(ManagerConfig *config) {
}
}
void ask_local_servers(ManagerConfig *config, Swift::BoostNetworkFactories &networkFactories, const std::string &message) {
static int show_status(ManagerConfig *config) {
int ret = 0;
path p(CONFIG_STRING(config, "service.config_directory"));
try {
@ -256,8 +257,65 @@ void ask_local_servers(ManagerConfig *config, Swift::BoostNetworkFactories &netw
std::cerr << "Can't load config file " << itr->path().string() << ". Skipping...\n";
}
std::vector<std::string> 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 {
ret = 3;
std::cout << itr->path() << ": " << vhost << " Stopped\n";
}
}
}
}
}
catch (const filesystem_error& ex) {
std::cerr << "boost filesystem error\n";
exit(5);
}
return ret;
}
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 +343,7 @@ int main(int argc, char **argv)
boost::program_options::options_description desc("Usage: spectrum [OPTIONS] <COMMAND>\nCommands:\n"
" start - start all local Spectrum2 instances\n"
" stop - stop all local Spectrum2 instances\n"
" status - status of local Spectrum2 instances\n"
" <other> - send command to all local + remote Spectrum2 instances and print output\n"
"Allowed options");
desc.add_options()
@ -333,6 +392,9 @@ int main(int argc, char **argv)
else if (command == "stop") {
stop_all_instances(&config);
}
else if (command == "status") {
return show_status(&config);
}
else {
Swift::SimpleEventLoop eventLoop;
Swift::BoostNetworkFactories networkFactories(&eventLoop);

View file

@ -300,6 +300,7 @@ void MySQLBackend::disconnect() {
delete m_updateUserSetting;
delete m_updateBuddySetting;
delete m_setUserOnline;
delete m_getOnlineUsers;
mysql_close(&m_conn);
}
@ -320,7 +321,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, "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=?");
@ -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;
}
@ -414,7 +416,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 << encrypted;
EXEC(m_setUser, setUser(user));
}
@ -442,10 +444,25 @@ void MySQLBackend::setUserOnline(long id, bool online) {
EXEC(m_setUserOnline, setUserOnline(id, online));
}
bool MySQLBackend::getOnlineUsers(std::vector<std::string> &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);
*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 +480,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 +509,9 @@ bool MySQLBackend::getBuddies(long id, std::list<BuddyInfo> &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);
}

View file

@ -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"
@ -197,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<std::string> groups;
groups.push_back(payload.groups());
std::vector<std::string> groups;
for (int i = 0; i < payload.group_size(); i++) {
groups.push_back(payload.group(i));
}
if (!groups.empty()) {
buddy->setGroups(groups);
}
@ -285,6 +288,7 @@ void NetworkPluginServer::handleNewClientConnection(boost::shared_ptr<Swift::Con
client->res = 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;
@ -333,6 +337,9 @@ void NetworkPluginServer::handleNewClientConnection(boost::shared_ptr<Swift::Con
void NetworkPluginServer::handleSessionFinished(Backend *c) {
LOG4CXX_INFO(logger, "Backend " << c << " disconnected. Current backend count=" << (m_clients.size() - 1));
// This backend will do, so we can't reconnect users to it in User::handleDisconnected call
c->willDie = true;
// If there are users associated with this backend, it must have crashed, so print error output
// and disconnect users
for (std::list<User *>::const_iterator it = c->users.begin(); it != c->users.end(); it++) {
@ -387,7 +394,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) {
@ -932,7 +939,7 @@ void NetworkPluginServer::handleUserCreated(User *user) {
user->setData(c);
c->users.push_back(user);
// UserInfo userInfo = user->getUserInfo();
// Don't forget to disconnect these in handleUserDestroyed!!!
user->onReadyToConnect.connect(boost::bind(&NetworkPluginServer::handleUserReadyToConnect, this, user));
user->onPresenceChanged.connect(boost::bind(&NetworkPluginServer::handleUserPresenceChanged, this, user, _1));
user->onRoomJoined.connect(boost::bind(&NetworkPluginServer::handleRoomJoined, this, user, _1, _2, _3, _4));
@ -1051,6 +1058,11 @@ void NetworkPluginServer::handleUserDestroyed(User *user) {
m_waitingUsers.remove(user);
UserInfo userInfo = user->getUserInfo();
user->onReadyToConnect.disconnect(boost::bind(&NetworkPluginServer::handleUserReadyToConnect, this, user));
user->onPresenceChanged.disconnect(boost::bind(&NetworkPluginServer::handleUserPresenceChanged, this, user, _1));
user->onRoomJoined.disconnect(boost::bind(&NetworkPluginServer::handleRoomJoined, this, user, _1, _2, _3, _4));
user->onRoomLeft.disconnect(boost::bind(&NetworkPluginServer::handleRoomLeft, this, user, _1));
pbnetwork::Logout logout;
logout.set_user(user->getJID().toBare());
logout.set_legacyname(userInfo.uin);
@ -1162,7 +1174,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;
@ -1188,7 +1202,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;
@ -1214,7 +1230,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());
@ -1343,7 +1361,7 @@ NetworkPluginServer::Backend *NetworkPluginServer::getFreeClient(bool acceptUser
// Check all backends and find free one
for (std::list<Backend *>::const_iterator it = m_clients.begin(); it != m_clients.end(); it++) {
if ((*it)->acceptUsers == acceptUsers && (*it)->users.size() < CONFIG_INT(m_config, "service.users_per_backend") && (*it)->connection && (*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")) {

View file

@ -21,6 +21,7 @@
#ifdef WITH_SQLITE
#include "transport/sqlite3backend.h"
#include "transport/util.h"
#include <boost/bind.hpp>
#include "log4cxx/logger.h"
@ -98,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);
}
}
@ -131,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;
}
@ -248,13 +251,30 @@ void SQLite3Backend::setUserOnline(long id, bool online) {
EXECUTE_STATEMENT(m_setUserOnline, "setUserOnline query");
}
bool SQLite3Backend::getOnlineUsers(std::vector<std::string> &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);
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 +300,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 +341,8 @@ bool SQLite3Backend::getBuddies(long id, std::list<BuddyInfo> &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) {

View file

@ -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();
@ -194,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();
}
}

View file

@ -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 <boost/foreach.hpp>
#include <stdio.h>
@ -55,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));
@ -169,6 +171,7 @@ void User::sendCurrentPresence() {
void User::setConnected(bool connected) {
m_connected = connected;
m_reconnectCounter = 0;
setIgnoreDisconnect(false);
updateLastActivity();
@ -315,12 +318,24 @@ 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;
}
if (e == Swift::SpectrumErrorPayload::CONNECTION_ERROR_OTHER_ERROR || e == Swift::SpectrumErrorPayload::CONNECTION_ERROR_NETWORK_ERROR) {
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()) {
LOG4CXX_INFO(logger, m_jid.toString() << ": Disconnected from legacy network");
}
@ -333,6 +348,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<Swift::SpectrumErrorPayload>(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,

View file

@ -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);
}
}

89
src/usersreconnecter.cpp Normal file
View file

@ -0,0 +1,89 @@
/**
* XMPP - libpurple transport
*
* Copyright (C) 2009, Jan Kaluza <hanzz@soc.pidgin.im>
*
* 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 <iostream>
#include <boost/bind.hpp>
#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();
}
}

View file

@ -24,6 +24,7 @@
#include <iterator>
#include <algorithm>
#include <boost/filesystem.hpp>
#include <boost/algorithm/string.hpp>
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<std::string> &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<std::string> deserializeGroups(std::string &groups) {
std::vector<std::string> 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;
}
}
}