Changeset - 56dd64999814
[Not reviewed]
0 9 3
Jan Kaluza - 14 years ago 2011-12-07 10:30:39
hanzz.k@gmail.com
Reconnect users automatically in case of spectrum2 main instance crash/restart
12 files changed with 212 insertions and 9 deletions:
0 comments (0 inline, 0 general)
ChangeLog
Show inline comments
 
new file 100644
 
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
include/transport/mysqlbackend.h
Show inline comments
 
@@ -61,48 +61,50 @@ class MySQLBackend : public StorageBackend
 
		void setUser(const UserInfo &user);
 

	
 
		/// Gets user data from database and stores them into user reference.
 
		/// \param barejid barejid of user
 
		/// \param user UserInfo object where user data will be stored
 
		/// \return true if user has been found in database
 
		bool getUser(const std::string &barejid, UserInfo &user);
 

	
 
		/// Changes users online state variable in database.
 
		/// \param id id of user - UserInfo.id
 
		/// \param online online state
 
		void setUserOnline(long id, bool online);
 

	
 
		/// 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
 
		bool removeUser(long id);
 

	
 
		/// Returns JIDs of all buddies in user's roster.
 
		/// \param id id of user - UserInfo.id
 
		/// \param roster string list used to store user's roster
 
		/// \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);
 
		void removeBuddy(long id) {}
 

	
 
		void getUserSetting(long userId, const std::string &variable, int &type, std::string &value);
 
		void updateUserSetting(long userId, const std::string &variable, const std::string &value);
 

	
 
		void beginTransaction();
 
		void commitTransaction();
 

	
 
	private:
 
		bool exec(const std::string &query);
 

	
 
		class Statement {
 
			public:
 
				Statement(MYSQL *conn, const std::string &format, const std::string &statement);
 
				~Statement();
 

	
 
				int execute();
 

	
 
				int fetch();
 

	
 
				// Pushes new data used as input for the statement.
 
@@ -127,29 +129,30 @@ class MySQLBackend : public StorageBackend
 
				std::string m_string;
 
		};
 

	
 
		MYSQL m_conn;
 
		Config *m_config;
 
		std::string m_prefix;
 

	
 
		// statements
 
// 		MYSQL_STMT *m_setUser;
 
		Statement *m_setUser;
 
		Statement *m_getUser;
 
		Statement *m_getUserSetting;
 
		Statement *m_setUserSetting;
 
		Statement *m_updateUserSetting;
 
		Statement *m_removeUser;
 
		Statement *m_removeUserBuddies;
 
		Statement *m_removeUserSettings;
 
		Statement *m_removeUserBuddiesSettings;
 
		Statement *m_addBuddy;
 
		Statement *m_updateBuddy;
 
		Statement *m_updateBuddySetting;
 
		Statement *m_getBuddies;
 
		Statement *m_getBuddiesSettings;
 
		Statement *m_setUserOnline;
 
		Statement *m_getOnlineUsers;
 
};
 

	
 
}
 

	
 
#endif
include/transport/sqlite3backend.h
Show inline comments
 
@@ -49,74 +49,77 @@ class SQLite3Backend : public StorageBackend
 
		/// \return true if database is opened successfully.
 
		bool connect();
 

	
 
		/// Creates database structure.
 
		/// \see connect()
 
		/// \return true if database structure has been created successfully. Note that it returns True also if database structure
 
		/// already exists.
 
		bool createDatabase();
 

	
 
		/// Stores user into database.
 
		/// \param user user struct containing all information about user which have to be stored
 
		void setUser(const UserInfo &user);
 

	
 
		/// Gets user data from database and stores them into user reference.
 
		/// \param barejid barejid of user
 
		/// \param user UserInfo object where user data will be stored
 
		/// \return true if user has been found in database
 
		bool getUser(const std::string &barejid, UserInfo &user);
 

	
 
		/// Changes users online state variable in database.
 
		/// \param id id of user - UserInfo.id
 
		/// \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
 
		bool removeUser(long id);
 

	
 
		/// Returns JIDs of all buddies in user's roster.
 
		/// \param id id of user - UserInfo.id
 
		/// \param roster string list used to store user's roster
 
		/// \return true if user has been found in database and roster has been fetched
 
		bool getBuddies(long id, std::list<BuddyInfo> &roster);
 

	
 
		long addBuddy(long userId, const BuddyInfo &buddyInfo);
 

	
 
		void updateBuddy(long userId, const BuddyInfo &buddyInfo);
 
		void removeBuddy(long id) {}
 

	
 
		void getUserSetting(long userId, const std::string &variable, int &type, std::string &value);
 
		void updateUserSetting(long userId, const std::string &variable, const std::string &value);
 

	
 
		void beginTransaction();
 
		void commitTransaction();
 

	
 
	private:
 
		bool exec(const std::string &query);
 

	
 
		sqlite3 *m_db;
 
		Config *m_config;
 
		std::string m_prefix;
 

	
 
		// statements
 
		sqlite3_stmt *m_setUser;
 
		sqlite3_stmt *m_getUser;
 
		sqlite3_stmt *m_getUserSetting;
 
		sqlite3_stmt *m_setUserSetting;
 
		sqlite3_stmt *m_updateUserSetting;
 
		sqlite3_stmt *m_removeUser;
 
		sqlite3_stmt *m_removeUserBuddies;
 
		sqlite3_stmt *m_removeUserSettings;
 
		sqlite3_stmt *m_removeUserBuddiesSettings;
 
		sqlite3_stmt *m_addBuddy;
 
		sqlite3_stmt *m_updateBuddy;
 
		sqlite3_stmt *m_updateBuddySetting;
 
		sqlite3_stmt *m_getBuddies;
 
		sqlite3_stmt *m_getBuddiesSettings;
 
		sqlite3_stmt *m_setUserOnline;
 
		sqlite3_stmt *m_getOnlineUsers;
 
};
 

	
 
}
 

	
 
#endif
include/transport/storagebackend.h
Show inline comments
 
@@ -87,40 +87,43 @@ class StorageBackend
 
		/// Virtual desctructor.
 
		virtual ~StorageBackend() {}
 

	
 
		/// connect
 
		virtual bool connect() = 0;
 

	
 
		/// createDatabase
 
		virtual bool createDatabase() = 0;
 

	
 
		/// setUser
 
		virtual void setUser(const UserInfo &user) = 0;
 

	
 
		/// getuser
 
		virtual bool getUser(const std::string &barejid, UserInfo &user) = 0;
 

	
 
		/// setUserOnline
 
		virtual void setUserOnline(long id, bool online) = 0;
 

	
 
		/// removeUser
 
		virtual bool removeUser(long id) = 0;
 

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

	
 
		virtual void getUserSetting(long userId, const std::string &variable, int &type, std::string &value) = 0;
 
		virtual void updateUserSetting(long userId, const std::string &variable, const std::string &value) = 0;
 

	
 
		virtual void beginTransaction() = 0;
 
		virtual void commitTransaction() = 0;
 

	
 
		/// onStorageError
 
// 		boost::signal<void (const std::string &statement, const std::string &error)> onStorageError;
 

	
 
};
 

	
 
}
include/transport/usermanager.h
Show inline comments
 
@@ -57,51 +57,51 @@ class UserManager : public Swift::EntityCapsProvider {
 
		/// \param storageBackend Storage backend used to fetch UserInfos
 
		UserManager(Component *component, UserRegistry *userRegistry, StorageBackend *storageBackend = NULL);
 

	
 
		/// Destroys UserManager.
 
		~UserManager();
 

	
 
		/// Returns user according to his bare JID.
 
		/// \param barejid bare JID of user
 
		/// \return User class associated with this user
 
		User *getUser(const std::string &barejid);
 

	
 
		/// Returns map with all connected users.
 
		/// \return All connected users.
 
		const std::map<std::string, User *> &getUsers() {
 
			return m_users;
 
		}
 

	
 
		/// Returns number of online users.
 
		/// \return number of online users
 
		int getUserCount();
 

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

	
 
		/// Called when new User class is created.
 
		/// \param user newly created User class
 
		boost::signal<void (User *user)> onUserCreated;
 

	
 
		/// Called when User class is going to be removed
 
		/// \param user removed User class
 
		boost::signal<void (User *user)> onUserDestroyed;
 

	
 
		/// Returns true if user is connected.
 
		/// \return True if user is connected.
 
		bool isUserConnected(const std::string &barejid) const {
 
			return m_users.find(barejid) != m_users.end();
 
		}
 

	
 
		/// Returns pointer to UserRegistry.
 
		/// \return Pointer to UserRegistry.
 
		UserRegistry *getUserRegistry() {
 
			return m_userRegistry;
 
		}
 

	
 
		/// Connects user manually.
include/transport/usersreconnecter.h
Show inline comments
 
new file 100644
 
/**
 
 * 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;
 
};
 

	
 
}
spectrum/src/main.cpp
Show inline comments
 
#include "transport/config.h"
 
#include "transport/transport.h"
 
#include "transport/filetransfermanager.h"
 
#include "transport/usermanager.h"
 
#include "transport/logger.h"
 
#include "transport/sqlite3backend.h"
 
#include "transport/mysqlbackend.h"
 
#include "transport/userregistration.h"
 
#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>
 
#include <boost/algorithm/string.hpp>
 
#ifndef WIN32
 
#include "sys/signal.h"
 
#include <pwd.h>
 
#include <grp.h>
 
#include <sys/resource.h>
 
#include "libgen.h"
 
#else
 
#include <windows.h>
 
#endif
 
#include "log4cxx/logger.h"
 
#include "log4cxx/consoleappender.h"
 
#include "log4cxx/patternlayout.h"
 
#include "log4cxx/propertyconfigurator.h"
 
#include "log4cxx/helpers/properties.h"
 
#include "log4cxx/helpers/transcoder.h"
 
#include "log4cxx/helpers/fileinputstream.h"
 
#include <sys/stat.h>
 

	
 
using namespace log4cxx;
 

	
 
using namespace Transport;
 

	
 
static LoggerPtr logger = log4cxx::Logger::getLogger("Spectrum");
 

	
 
Swift::SimpleEventLoop *eventLoop_ = NULL;
 
Component *component_ = NULL;
 
UserManager *userManager_ = NULL;
 

	
 
static void stop_spectrum() {
 
	userManager_->removeAllUsers();
 
	userManager_->removeAllUsers(false);
 
	component_->stop();
 
	eventLoop_->stop();
 
}
 

	
 
static void spectrum_sigint_handler(int sig) {
 
	eventLoop_->postEvent(&stop_spectrum);
 
}
 

	
 
static void spectrum_sigterm_handler(int sig) {
 
	eventLoop_->postEvent(&stop_spectrum);
 
}
 

	
 
static void removeOldIcons(std::string iconDir) {
 
	std::vector<std::string> dirs;
 
	dirs.push_back(iconDir);
 

	
 
	boost::thread thread(boost::bind(Util::removeEverythingOlderThan, dirs, time(NULL) - 3600*24*14));
 
}
 

	
 
#ifndef WIN32
 
static void daemonize(const char *cwd, const char *lock_file) {
 
	pid_t pid, sid;
 
	FILE* lock_file_f;
 
	char process_pid[20];
 
@@ -348,51 +349,58 @@ int main(int argc, char **argv)
 

	
 
	StorageBackend *storageBackend = NULL;
 

	
 
#ifdef WITH_SQLITE
 
	if (CONFIG_STRING(&config, "database.type") == "sqlite3") {
 
		storageBackend = new SQLite3Backend(&config);
 
		if (!storageBackend->connect()) {
 
			std::cerr << "Can't connect to database.\n";
 
			return -1;
 
		}
 
	}
 
#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";
 
			return -1;
 
		}
 
	}
 
#endif
 

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

	
 
	NetworkPluginServer plugin(&transport, &config, &userManager, &ftManager);
 

	
 
	AdminInterface adminInterface(&transport, &userManager, &plugin, storageBackend);
 
	StatsResponder statsResponder(&transport, &userManager, &plugin, storageBackend);
 
	statsResponder.start();
 

	
 
	eventLoop_ = &eventLoop;
 

	
 
	eventLoop.run();
 

	
 
	if (userRegistration) {
 
		userRegistration->stop();
 
		delete userRegistration;
 
	}
 

	
 
	if (usersReconnecter) {
 
		delete usersReconnecter;
 
	}
 

	
 
	delete storageBackend;
 
	delete factories;
 
}
src/mysqlbackend.cpp
Show inline comments
 
@@ -279,87 +279,89 @@ MySQLBackend::MySQLBackend(Config *config) {
 
	mysql_options(&m_conn, MYSQL_OPT_RECONNECT, &my_true);
 
}
 

	
 
MySQLBackend::~MySQLBackend(){
 
	disconnect();
 
}
 

	
 
void MySQLBackend::disconnect() {
 
	LOG4CXX_INFO(logger, "Disconnecting");
 
	delete m_setUser;
 
	delete m_getUser;
 
	delete m_removeUser;
 
	delete m_removeUserBuddies;
 
	delete m_removeUserSettings;
 
	delete m_removeUserBuddiesSettings;
 
	delete m_addBuddy;
 
	delete m_updateBuddy;
 
	delete m_getBuddies;
 
	delete m_getBuddiesSettings;
 
	delete m_getUserSetting;
 
	delete m_setUserSetting;
 
	delete m_updateUserSetting;
 
	delete m_updateBuddySetting;
 
	delete m_setUserOnline;
 
	delete m_getOnlineUsers;
 
	mysql_close(&m_conn);
 
}
 

	
 
bool MySQLBackend::connect() {
 
	LOG4CXX_INFO(logger, "Connecting MySQL server " << CONFIG_STRING(m_config, "database.server") << ", user " <<
 
		CONFIG_STRING(m_config, "database.user") << ", database " << CONFIG_STRING(m_config, "database.database") <<
 
		", port " << CONFIG_INT(m_config, "database.port")
 
	);
 

	
 
	if (!mysql_real_connect(&m_conn, CONFIG_STRING(m_config, "database.server").c_str(),
 
					   CONFIG_STRING(m_config, "database.user").c_str(),
 
					   CONFIG_STRING(m_config, "database.password").c_str(),
 
					   CONFIG_STRING(m_config, "database.database").c_str(),
 
					   CONFIG_INT(m_config, "database.port"), NULL, 0)) {
 
		LOG4CXX_ERROR(logger, "Can't connect database: " << mysql_error(&m_conn));
 
		return false;
 
	}
 

	
 
	createDatabase();
 

	
 
	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=?");
 
	m_removeUserBuddies = new Statement(&m_conn, "i", "DELETE FROM " + m_prefix + "buddies WHERE user_id=?");
 
	m_removeUserSettings = new Statement(&m_conn, "i", "DELETE FROM " + m_prefix + "users_settings WHERE user_id=?");
 
	m_removeUserBuddiesSettings = new Statement(&m_conn, "i", "DELETE FROM " + m_prefix + "buddies_settings WHERE user_id=?");
 

	
 
	m_addBuddy = new Statement(&m_conn, "issssi", "INSERT INTO " + m_prefix + "buddies (user_id, uin, subscription, groups, nickname, flags) VALUES (?, ?, ?, ?, ?, ?)");
 
	m_updateBuddy = new Statement(&m_conn, "ssisis", "UPDATE " + m_prefix + "buddies SET groups=?, nickname=?, flags=?, subscription=? WHERE user_id=? AND uin=?");
 
	m_getBuddies = new Statement(&m_conn, "i|issssi", "SELECT id, uin, subscription, nickname, groups, flags FROM " + m_prefix + "buddies WHERE user_id=? ORDER BY id ASC");
 
	m_getBuddiesSettings = new Statement(&m_conn, "i|iiss", "SELECT buddy_id, type, var, value FROM " + m_prefix + "buddies_settings WHERE user_id=? ORDER BY buddy_id ASC");
 
	m_updateBuddySetting = new Statement(&m_conn, "iisiss", "INSERT INTO " + m_prefix + "buddies_settings (user_id, buddy_id, var, type, value) VALUES (?, ?, ?, ?, ?) ON DUPLICATE KEY UPDATE value=?");
 
	
 
	m_getUserSetting = new Statement(&m_conn, "is|is", "SELECT type, value FROM " + m_prefix + "users_settings WHERE user_id=? AND var=?");
 
	m_setUserSetting = new Statement(&m_conn, "isis", "INSERT INTO " + m_prefix + "users_settings (user_id, var, type, value) VALUES (?,?,?,?)");
 
	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;
 
}
 

	
 
bool MySQLBackend::createDatabase() {
 
	int not_exist = exec("CREATE TABLE IF NOT EXISTS `" + m_prefix + "buddies` ("
 
							"`id` int(10) unsigned NOT NULL auto_increment,"
 
							"`user_id` int(10) unsigned NOT NULL,"
 
							"`uin` varchar(255) collate utf8_bin NOT NULL,"
 
							"`subscription` enum('to','from','both','ask','none') collate utf8_bin NOT NULL,"
 
							"`nickname` varchar(255) collate utf8_bin NOT NULL,"
 
							"`groups` varchar(255) collate utf8_bin NOT NULL,"
 
							"`flags` smallint(4) NOT NULL DEFAULT '0',"
 
							"PRIMARY KEY (`id`),"
 
							"UNIQUE KEY `user_id` (`user_id`,`uin`)"
 
						") ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;");
 

	
 
	if (not_exist) {
 
		exec("CREATE TABLE IF NOT EXISTS `" + m_prefix + "buddies_settings` ("
 
				"`user_id` int(10) unsigned NOT NULL,"
 
				"`buddy_id` int(10) unsigned NOT NULL,"
 
				"`var` varchar(50) collate utf8_bin NOT NULL,"
 
				"`type` smallint(4) unsigned NOT NULL,"
 
				"`value` varchar(255) collate utf8_bin NOT NULL,"
 
@@ -421,48 +423,62 @@ void MySQLBackend::setUser(const UserInfo &user) {
 
bool MySQLBackend::getUser(const std::string &barejid, UserInfo &user) {
 
	*m_getUser << barejid;
 
	EXEC(m_getUser, getUser(barejid, user));
 
	if (!exec_ok)
 
		return false;
 

	
 
	int ret = false;
 
	while (m_getUser->fetch() == 0) {
 
		ret = true;
 
		*m_getUser >> user.id >> user.jid >> user.uin >> user.password >> user.encoding >> user.language >> user.vip;
 

	
 
		if (!CONFIG_STRING(m_config, "database.encryption_key").empty()) {
 
			user.password = Util::decryptPassword(user.password, CONFIG_STRING(m_config, "database.encryption_key"));
 
		}
 
	}
 

	
 
	return ret;
 
}
 

	
 
void MySQLBackend::setUserOnline(long id, bool online) {
 
	*m_setUserOnline << online << id;
 
	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 << groups;
 
	*m_addBuddy << buddyInfo.alias << buddyInfo.flags;
 

	
 
	EXEC(m_addBuddy, addBuddy(userId, buddyInfo));
 

	
 
	long id = (long) mysql_insert_id(&m_conn);
 

	
 
// 	INSERT OR REPLACE INTO " + m_prefix + "buddies_settings (user_id, buddy_id, var, type, value) VALUES (?, ?, ?, ?, ?)
 
	if (!buddyInfo.settings.find("icon_hash")->second.s.empty()) {
 
		*m_updateBuddySetting << userId << id << buddyInfo.settings.find("icon_hash")->first << (int) TYPE_STRING << buddyInfo.settings.find("icon_hash")->second.s << buddyInfo.settings.find("icon_hash")->second.s;
 
		EXEC(m_updateBuddySetting, addBuddy(userId, buddyInfo));
 
	}
 

	
 
	return id;
 
}
 

	
 
void MySQLBackend::updateBuddy(long userId, const BuddyInfo &buddyInfo) {
 
// 	"UPDATE " + m_prefix + "buddies SET groups=?, nickname=?, flags=?, subscription=? WHERE user_id=? AND uin=?"
 
	std::string groups = Util::serializeGroups(buddyInfo.groups);
 
	*m_updateBuddy << groups;
src/sqlite3backend.cpp
Show inline comments
 
@@ -78,81 +78,83 @@ SQLite3Backend::~SQLite3Backend(){
 
		// Would be nice to use this:
 
		//
 
		//   sqlite3_stmt *pStmt;
 
		//   while((pStmt = sqlite3_next_stmt(db, 0)) != 0 ) {
 
		//    sqlite3_finalize(pStmt);
 
		//   }
 
		//
 
		// But requires SQLite3 >= 3.6.0 beta
 
	
 
		FINALIZE_STMT(m_setUser);
 
		FINALIZE_STMT(m_getUser);
 
		FINALIZE_STMT(m_removeUser);
 
		FINALIZE_STMT(m_removeUserBuddies);
 
		FINALIZE_STMT(m_removeUserSettings);
 
		FINALIZE_STMT(m_removeUserBuddiesSettings);
 
		FINALIZE_STMT(m_addBuddy);
 
		FINALIZE_STMT(m_updateBuddy);
 
		FINALIZE_STMT(m_getBuddies);
 
		FINALIZE_STMT(m_getBuddiesSettings);
 
		FINALIZE_STMT(m_getUserSetting);
 
		FINALIZE_STMT(m_setUserSetting);
 
		FINALIZE_STMT(m_updateUserSetting);
 
		FINALIZE_STMT(m_updateBuddySetting);
 
		FINALIZE_STMT(m_setUserOnline);
 
		FINALIZE_STMT(m_getOnlineUsers);
 
		sqlite3_close(m_db);
 
	}
 
}
 

	
 
bool SQLite3Backend::connect() {
 
	LOG4CXX_INFO(logger, "Opening database " << CONFIG_STRING(m_config, "database.database"));
 
	if (sqlite3_open(CONFIG_STRING(m_config, "database.database").c_str(), &m_db)) {
 
		sqlite3_close(m_db);
 
		return false;
 
	}
 

	
 
	if (createDatabase() == false)
 
		return false;
 

	
 
	PREP_STMT(m_setUser, "INSERT OR REPLACE INTO " + m_prefix + "users (jid, uin, password, language, encoding, last_login, vip) VALUES (?, ?, ?, ?, ?, DATETIME('NOW'), ?)");
 
	PREP_STMT(m_getUser, "SELECT id, jid, uin, password, encoding, language, vip FROM " + m_prefix + "users WHERE jid=?");
 

	
 
	PREP_STMT(m_removeUser, "DELETE FROM " + m_prefix + "users WHERE id=?");
 
	PREP_STMT(m_removeUserBuddies, "DELETE FROM " + m_prefix + "buddies WHERE user_id=?");
 
	PREP_STMT(m_removeUserSettings, "DELETE FROM " + m_prefix + "users_settings WHERE user_id=?");
 
	PREP_STMT(m_removeUserBuddiesSettings, "DELETE FROM " + m_prefix + "buddies_settings WHERE user_id=?");
 

	
 
	PREP_STMT(m_addBuddy, "INSERT INTO " + m_prefix + "buddies (user_id, uin, subscription, groups, nickname, flags) VALUES (?, ?, ?, ?, ?, ?)");
 
	PREP_STMT(m_updateBuddy, "UPDATE " + m_prefix + "buddies SET groups=?, nickname=?, flags=?, subscription=? WHERE user_id=? AND uin=?");
 
	PREP_STMT(m_getBuddies, "SELECT id, uin, subscription, nickname, groups, flags FROM " + m_prefix + "buddies WHERE user_id=? ORDER BY id ASC");
 
	PREP_STMT(m_getBuddiesSettings, "SELECT buddy_id, type, var, value FROM " + m_prefix + "buddies_settings WHERE user_id=? ORDER BY buddy_id ASC");
 
	PREP_STMT(m_updateBuddySetting, "INSERT OR REPLACE INTO " + m_prefix + "buddies_settings (user_id, buddy_id, var, type, value) VALUES (?, ?, ?, ?, ?)");
 
	
 
	PREP_STMT(m_getUserSetting, "SELECT type, value FROM " + m_prefix + "users_settings WHERE user_id=? AND var=?");
 
	PREP_STMT(m_setUserSetting, "INSERT INTO " + m_prefix + "users_settings (user_id, var, type, value) VALUES (?,?,?,?)");
 
	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;
 
}
 

	
 
bool SQLite3Backend::createDatabase() {
 
	int not_exist = exec("CREATE TABLE " + m_prefix + "buddies ("
 
				"  id INTEGER PRIMARY KEY NOT NULL,"
 
				"  user_id int(10) NOT NULL,"
 
				"  uin varchar(255) NOT NULL,"
 
				"  subscription varchar(20) NOT NULL,"
 
				"  nickname varchar(255) NOT NULL,"
 
				"  groups varchar(255) NOT NULL,"
 
				"  flags int(4) NOT NULL DEFAULT '0'"
 
				");");
 

	
 
	if (not_exist) {
 
		exec("CREATE UNIQUE INDEX IF NOT EXISTS user_id ON " + m_prefix + "buddies (user_id, uin);");
 

	
 
		exec("CREATE TABLE IF NOT EXISTS " + m_prefix + "buddies_settings ("
 
					"  user_id int(10) NOT NULL,"
 
					"  buddy_id int(10) NOT NULL,"
 
					"  var varchar(50) NOT NULL,"
 
					"  type int(4) NOT NULL,"
 
					"  value varchar(255) NOT NULL,"
 
@@ -228,48 +230,65 @@ bool SQLite3Backend::getUser(const std::string &barejid, UserInfo &user) {
 
		user.id = sqlite3_column_int(m_getUser, 0);
 
		user.jid = (const char *) sqlite3_column_text(m_getUser, 1);
 
		user.uin = (const char *) sqlite3_column_text(m_getUser, 2);
 
		user.password = (const char *) sqlite3_column_text(m_getUser, 3);
 
		user.encoding = (const char *) sqlite3_column_text(m_getUser, 4);
 
		user.language = (const char *) sqlite3_column_text(m_getUser, 5);
 
		user.vip = sqlite3_column_int(m_getUser, 6) != 0;
 
		return true;
 
	}
 

	
 
	if (ret != SQLITE_DONE) {
 
		LOG4CXX_ERROR(logger, "getUser query"<< (sqlite3_errmsg(m_db) == NULL ? "" : sqlite3_errmsg(m_db)));
 
	}
 

	
 
	return false;
 
}
 

	
 
void SQLite3Backend::setUserOnline(long id, bool online) {
 
	BEGIN(m_setUserOnline);
 
	BIND_INT(m_setUserOnline, (int)online);
 
	BIND_INT(m_setUserOnline, id);
 
	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, Util::serializeGroups(buddyInfo.groups));
 
	BIND_STR(m_addBuddy, buddyInfo.alias);
 
	BIND_INT(m_addBuddy, buddyInfo.flags);
 

	
 
	if(sqlite3_step(m_addBuddy) != SQLITE_DONE) {
 
		LOG4CXX_ERROR(logger, "addBuddy query"<< (sqlite3_errmsg(m_db) == NULL ? "" : sqlite3_errmsg(m_db)));
 
		return -1;
 
	}
 

	
 
	long id = (long) sqlite3_last_insert_rowid(m_db);
 

	
 
// 	INSERT OR REPLACE INTO " + m_prefix + "buddies_settings (user_id, buddy_id, var, type, value) VALUES (?, ?, ?, ?, ?)
 
	BEGIN(m_updateBuddySetting);
 
	BIND_INT(m_updateBuddySetting, userId);
 
	BIND_INT(m_updateBuddySetting, id);
 
	BIND_STR(m_updateBuddySetting, buddyInfo.settings.find("icon_hash")->first);
 
	BIND_INT(m_updateBuddySetting, TYPE_STRING);
 
	BIND_STR(m_updateBuddySetting, buddyInfo.settings.find("icon_hash")->second.s);
src/transport.cpp
Show inline comments
 
@@ -176,48 +176,50 @@ Swift::PresenceOracle *Component::getPresenceOracle() {
 

	
 
void Component::setTransportFeatures(std::list<std::string> &features) {
 
	m_discoInfoResponder->setTransportFeatures(features);
 
}
 

	
 
Swift::CapsInfo &Component::getBuddyCapsInfo() {
 
		return m_discoInfoResponder->getBuddyCapsInfo();
 
}
 

	
 
void Component::setBuddyFeatures(std::list<std::string> &features) {
 
	// TODO: handle caps change
 
	m_discoInfoResponder->setBuddyFeatures(features);
 
}
 

	
 
void Component::start() {
 
	if (m_component && !m_component->isAvailable()) {
 
		LOG4CXX_INFO(logger, "Connecting XMPP server " << CONFIG_STRING(m_config, "service.server") << " port " << CONFIG_INT(m_config, "service.port"));
 
		m_reconnectCount++;
 
		m_component->connect(CONFIG_STRING(m_config, "service.server"), CONFIG_INT(m_config, "service.port"));
 
		m_reconnectTimer->stop();
 
	}
 
	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();
 
	}
 
}
 

	
 
void Component::stop() {
 
	if (m_component) {
 
		m_reconnectCount = 0;
 
		// TODO: Call this once swiften will fix assert(!session_);
 
// 		m_component->disconnect();
 
		m_reconnectTimer->stop();
 
	}
 
	else if (m_server) {
 
		LOG4CXX_INFO(logger, "Stopping component in server mode on port " << CONFIG_INT(m_config, "service.port"));
 
		m_server->stop();
 
	}
 
}
 

	
 
void Component::handleConnected() {
 
	onConnected();
 
	m_reconnectCount = 0;
 
}
 

	
 
void Component::handleConnectionError(const ComponentError &error) {
 
	onConnectionError(error);
 
// 	if (m_reconnectCount == 2)
src/usermanager.cpp
Show inline comments
 
@@ -80,72 +80,72 @@ void UserManager::addUser(User *user) {
 

	
 
User *UserManager::getUser(const std::string &barejid){
 
	if (m_cachedUser && barejid == m_cachedUser->getJID().toBare().toString()) {
 
		return m_cachedUser;
 
	}
 

	
 
	if (m_users.find(barejid) != m_users.end()) {
 
		User *user = m_users[barejid];
 
		m_cachedUser = user;
 
		return user;
 
	}
 
	return NULL;
 
}
 

	
 
Swift::DiscoInfo::ref UserManager::getCaps(const Swift::JID &jid) const {
 
	std::map<std::string, User *>::const_iterator it = m_users.find(jid.toBare().toString());
 
	if (it == m_users.end()) {
 
		return Swift::DiscoInfo::ref();
 
	}
 

	
 
	User *user = it->second;
 
	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;
 

	
 
	if (m_component->inServerMode()) {
 
		disconnectUser(user->getJID());
 
	}
 

	
 
	if (m_storageBackend) {
 
	if (m_storageBackend && onUserBehalf) {
 
		m_storageBackend->setUserOnline(user->getUserInfo().id, false);
 
	}
 

	
 
	onUserDestroyed(user);
 
	delete user;
 
#ifndef WIN32
 
	malloc_trim(0);
 
#endif
 
// 	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);
 
	}
 
}
 

	
 
int UserManager::getUserCount() {
 
	return m_users.size();
 
}
 

	
 
void UserManager::handleDiscoInfo(const Swift::JID& jid, boost::shared_ptr<Swift::DiscoInfo> info) {
 
	User *user = getUser(jid.toBare().toString());
 
	if (!user) {
 
		return;
 
	}
 

	
 
	user->handleDiscoInfo(jid, info);
 
}
 

	
 
void UserManager::handlePresence(Swift::Presence::ref presence) {
 
	std::string barejid = presence->getTo().toBare().toString();
 
	std::string userkey = presence->getFrom().toBare().toString();
 

	
 
	User *user = getUser(userkey);
 
	// Create user class if it's not there
 
	if (!user) {
 
		// Admin user is not legacy network user, so do not create User class instance for him
src/usersreconnecter.cpp
Show inline comments
 
new file 100644
 
/**
 
 * 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();
 
}
 

	
 

	
 
}
0 comments (0 inline, 0 general)