Changeset - f47187d464dc
[Not reviewed]
0 2 0
HanzZ - 14 years ago 2011-08-13 02:03:49
hanzz.k@gmail.com
Probably working invisible state, but I'm lazy to test it after midnight...
2 files changed with 14 insertions and 1 deletions:
0 comments (0 inline, 0 general)
backends/libpurple/main.cpp
Show inline comments
 
@@ -122,384 +122,387 @@ static void * requestInput(const char *title, const char *primary,const char *se
 
}
 

	
 
static void *requestAction(const char *title, const char *primary, const char *secondary, int default_action, PurpleAccount *account, const char *who,PurpleConversation *conv, void *user_data, size_t action_count, va_list actions){
 
	std::string t(title ? title : "NULL");
 
	if (t == "SSL Certificate Verification") {
 
		LOG4CXX_INFO(logger,  "accepting SSL certificate");
 
		va_arg(actions, char *);
 
		((PurpleRequestActionCb) va_arg(actions, GCallback)) (user_data, 2);
 
	}
 
	else {
 
		if (title) {
 
			std::string headerString(title);
 
			LOG4CXX_INFO(logger,  "header string: " << headerString);
 
			if (headerString == "SSL Certificate Verification") {
 
				va_arg(actions, char *);
 
				((PurpleRequestActionCb) va_arg(actions, GCallback)) (user_data, 2);
 
			}
 
		}
 
	}
 
	return NULL;
 
}
 

	
 
static std::string getAlias(PurpleBuddy *m_buddy) {
 
	std::string alias;
 
	PurpleContact *contact = PURPLE_CONTACT(PURPLE_BLIST_NODE(m_buddy)->parent);
 
	if (contact && contact->alias) {
 
		alias = contact->alias;
 
	}
 
	else if (purple_buddy_get_alias(m_buddy)) {
 
		alias = (std::string) purple_buddy_get_alias(m_buddy);
 
	}
 
	else {
 
		alias = (std::string) purple_buddy_get_server_alias(m_buddy);
 
	}
 
	return alias;
 
}
 

	
 
class SpectrumNetworkPlugin : public NetworkPlugin {
 
	public:
 
		SpectrumEventLoop *m_loop;
 
		SpectrumNetworkPlugin(Config *config, SpectrumEventLoop *loop, const std::string &host, int port) : NetworkPlugin(loop, host, port) {
 
			this->config = config;
 
			m_loop = loop;
 
		}
 

	
 
		void handleExit() {
 
			m_loop->stop();
 
		}
 

	
 
		void handleLoginRequest(const std::string &user, const std::string &legacyName, const std::string &password) {
 
			PurpleAccount *account = NULL;
 
			
 
			std::string name = legacyName;
 
			std::string protocol = CONFIG_STRING(config, "service.protocol");
 
			if (CONFIG_STRING(config, "service.protocol") == "any") {
 
				protocol = name.substr(0, name.find("."));
 
				name = name.substr(name.find(".") + 1);
 
			}
 

	
 
			LOG4CXX_INFO(logger,  "Creating account with name '" << name.c_str() << "' and protocol '" << protocol << "'");
 
			if (purple_accounts_find(name.c_str(), protocol.c_str()) != NULL){
 
// 				Log(user, "this account already exists");
 
				account = purple_accounts_find(name.c_str(), protocol.c_str());
 
// 				User *u = (User *) account->ui_data;
 
// 				if (u && u != user) {
 
// 					Log(userInfo.jid, "This account is already connected by another jid " << user->getJID());
 
// 					return;
 
// 				}
 
			}
 
			else {
 
// 				Log(user, "creating new account");
 
				account = purple_account_new(name.c_str(), protocol.c_str());
 

	
 
				purple_accounts_add(account);
 
			}
 

	
 
			m_sessions[user] = account;
 

	
 
			// Default avatar
 
			char* contents;
 
			gsize length;
 
			gboolean ret = false;
 
			if (!CONFIG_STRING(config, "backend.avatars_directory").empty()) {
 
				std::string f = CONFIG_STRING(config, "backend.avatars_directory") + "/" + legacyName;
 
				ret = g_file_get_contents (f.c_str(), &contents, &length, NULL);
 
			}
 

	
 
			if (!CONFIG_STRING(config, "backend.default_avatar").empty() && !ret) {
 
				ret = g_file_get_contents (CONFIG_STRING(config, "backend.default_avatar").c_str(),
 
											&contents, &length, NULL);
 
			}
 

	
 
			if (ret) {
 
				purple_buddy_icons_set_account_icon(account, (guchar *) contents, length);
 
			}
 

	
 
			purple_account_set_password(account, password.c_str());
 
			purple_account_set_enabled(account, "spectrum", TRUE);
 
			purple_account_set_bool(account, "custom_smileys", FALSE);
 

	
 
			purple_account_set_privacy_type(account, PURPLE_PRIVACY_DENY_USERS);
 

	
 
			const PurpleStatusType *status_type = purple_account_get_status_type_with_primitive(account, PURPLE_STATUS_AVAILABLE);
 
			if (status_type != NULL) {
 
				purple_account_set_status(account, purple_status_type_get_id(status_type), TRUE, NULL);
 
			}
 
			m_accounts[account] = user;
 
		}
 

	
 
		void handleLogoutRequest(const std::string &user, const std::string &legacyName) {
 
			PurpleAccount *account = m_sessions[user];
 
			if (account) {
 
// 				VALGRIND_DO_LEAK_CHECK;
 
				m_sessions[user] = NULL;
 
				purple_account_set_enabled(account, "spectrum", FALSE);
 

	
 
				// Remove conversations.
 
				// This has to be called before m_account->ui_data = NULL;, because it uses
 
				// ui_data to call SpectrumMessageHandler::purpleConversationDestroyed() callback.
 
				GList *iter;
 
				for (iter = purple_get_conversations(); iter; ) {
 
					PurpleConversation *conv = (PurpleConversation*) iter->data;
 
					iter = iter->next;
 
					if (purple_conversation_get_account(conv) == account)
 
						purple_conversation_destroy(conv);
 
				}
 

	
 
				g_free(account->ui_data);
 
				account->ui_data = NULL;
 
				m_accounts.erase(account);
 

	
 
				purple_notify_close_with_handle(account);
 
				purple_request_close_with_handle(account);
 

	
 
				purple_accounts_remove(account);
 

	
 
				GSList *buddies = purple_find_buddies(account, NULL);
 
				while(buddies) {
 
					PurpleBuddy *b = (PurpleBuddy *) buddies->data;
 
					purple_blist_remove_buddy(b);
 
					buddies = g_slist_delete_link(buddies, buddies);
 
				}
 

	
 
				/* Remove any open conversation for this account */
 
				for (GList *it = purple_get_conversations(); it; ) {
 
					PurpleConversation *conv = (PurpleConversation *) it->data;
 
					it = it->next;
 
					if (purple_conversation_get_account(conv) == account)
 
						purple_conversation_destroy(conv);
 
				}
 

	
 
				/* Remove this account's pounces */
 
					// purple_pounce_destroy_all_by_account(account);
 

	
 
				/* This will cause the deletion of an old buddy icon. */
 
				purple_buddy_icons_set_account_icon(account, NULL, 0);
 

	
 
				purple_account_destroy(account);
 
				// force returning of memory chunks allocated by libxml2 to kernel
 
				malloc_trim(0);
 
// 				VALGRIND_DO_LEAK_CHECK;
 
			}
 
		}
 

	
 
		void handleStatusChangeRequest(const std::string &user, int status, const std::string &statusMessage) {
 
			PurpleAccount *account = m_sessions[user];
 
			if (account) {
 
				int st;
 
				switch(status) {
 
					case Swift::StatusShow::Away: {
 
						st = PURPLE_STATUS_AWAY;
 
						if (!purple_account_get_status_type_with_primitive(account, PURPLE_STATUS_AWAY))
 
							st = PURPLE_STATUS_EXTENDED_AWAY;
 
						else
 
							st = PURPLE_STATUS_AWAY;
 
						break;
 
					}
 
					case Swift::StatusShow::DND: {
 
						st = PURPLE_STATUS_UNAVAILABLE;
 
						break;
 
					}
 
					case Swift::StatusShow::XA: {
 
						if (!purple_account_get_status_type_with_primitive(account, PURPLE_STATUS_EXTENDED_AWAY))
 
							st = PURPLE_STATUS_AWAY;
 
						else
 
							st = PURPLE_STATUS_EXTENDED_AWAY;
 
						break;
 
					}
 
					case Swift::StatusShow::None: {
 
						st = PURPLE_STATUS_OFFLINE;
 
						break;
 
					}
 
					case 255:
 
						st = PURPLE_STATUS_INVISIBLE;
 
						break;
 
					default:
 
						st = PURPLE_STATUS_AVAILABLE;
 
						break;
 
				}
 
				gchar *_markup = purple_markup_escape_text(statusMessage.c_str(), -1);
 
				std::string markup(_markup);
 
				g_free(_markup);
 

	
 
				// we are already connected so we have to change status
 
				const PurpleStatusType *status_type = purple_account_get_status_type_with_primitive(account, (PurpleStatusPrimitive) st);
 
				if (status_type != NULL) {
 
					// send presence to legacy network
 
					if (!markup.empty()) {
 
						purple_account_set_status(account, purple_status_type_get_id(status_type), TRUE, "message", markup.c_str(), NULL);
 
					}
 
					else {
 
						purple_account_set_status(account, purple_status_type_get_id(status_type), TRUE, NULL);
 
					}
 
				}
 
			}
 
		}
 

	
 
		void handleMessageSendRequest(const std::string &user, const std::string &legacyName, const std::string &message, const std::string &xhtml) {
 
			PurpleAccount *account = m_sessions[user];
 
			if (account) {
 
				PurpleConversation *conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, legacyName.c_str(), account);
 
				if (!conv) {
 
					conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, legacyName.c_str());
 
				}
 
				if (xhtml.empty()) {
 
					gchar *_markup = purple_markup_escape_text(message.c_str(), -1);
 
					purple_conv_im_send(PURPLE_CONV_IM(conv), _markup);
 
					g_free(_markup);
 
				}
 
				else {
 
					purple_conv_im_send(PURPLE_CONV_IM(conv), xhtml.c_str());
 
				}
 
			}
 
		}
 

	
 
		void handleVCardRequest(const std::string &user, const std::string &legacyName, unsigned int id) {
 
			PurpleAccount *account = m_sessions[user];
 
			if (account) {
 
				std::string name = legacyName;
 
				if (CONFIG_STRING(config, "service.protocol") == "any" && legacyName.find("prpl-") == 0) {
 
					name = name.substr(name.find(".") + 1);
 
				}
 
				serv_get_info(purple_account_get_connection(account), name.c_str());
 
				m_vcards[user + name] = id;
 
			}
 
		}
 

	
 
		void handleVCardUpdatedRequest(const std::string &user, const std::string &p) {
 
			PurpleAccount *account = m_sessions[user];
 
			if (account) {
 
				gssize size = p.size();
 
				// this will be freed by libpurple
 
				guchar *photo = (guchar *) g_malloc(size * sizeof(guchar));
 
				memcpy(photo, p.c_str(), size);
 

	
 
#ifdef WITH_IMAGEMAGICK
 
				if (size != 0) {
 
					PurplePlugin *plugin = purple_find_prpl(Transport::instance()->protocol()->protocol().c_str());
 
					PurplePluginProtocolInfo *prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(plugin);
 
					if (prpl_info->icon_spec.format == NULL) {
 
						g_free(photo);
 
						return;
 
					}
 
					try {
 
						Magick::Blob blob(photo, size);
 
						g_free(photo);
 
						photo = NULL;
 
						Magick::Image img(blob);
 

	
 
						std::string format;
 
						gchar **strlist = g_strsplit(prpl_info->icon_spec.format, ",", 10);
 
						for (gchar **f = strlist; *f != NULL; f++) {
 
							// jpeg is the best
 
							if (strcmp(*f, "jpeg") == 0 || strcmp(*f, "jpg") == 0) {
 
								format = "jpg";
 
							}
 
							// png is number two
 
							else if (strcmp(*f, "png") == 0 && format != "jpg") {
 
								format = *f;
 
							}
 
							// gif is alright if there's not jpeg or png
 
							else if (strcmp(*f, "gif") == 0 && format != "jpg" && format != "png") {
 
								format = *f;
 
							}
 
							else if (format.empty()) {
 
								format = *f;
 
							}
 
						}
 
						g_strfreev(strlist);
 
						img.magick(format);
 

	
 
						int width, height;
 
						if (CONFIG().protocol == "icq") {
 
							width = 48;
 
							height = 48;
 
						}
 
						else {
 
							purple_buddy_icon_get_scale_size(&prpl_info->icon_spec, &width, &height);
 
						}
 

	
 
						if (img.size().width() != width || img.size().height() != height) {
 
							Magick::Geometry g = Magick::Geometry(width,height);
 
							g.aspect(CONFIG().protocol == "icq");
 
							img.scale(g);
 
						}
 

	
 
						Magick::Blob output;
 
						img.write(&output);
 
						size = output.length();
 
						// this will be freed by libpurple
 
						photo = (guchar *) g_malloc(size * sizeof(guchar));
 
						memcpy(photo, output.data(), size);
 
					}
 
					catch ( Magick::Exception & error) {
 
						Log("handleVCard","Caught Magick++ exception: " << error.what());
 
					} catch(...) {   // catch all other exceptions
 
					}
 
				}
 
#endif /* WITH_IMAGEMAGICK */
 

	
 
				if (!photo)
 
					return;
 
				purple_buddy_icons_set_account_icon(account, photo, size);
 
			}
 
		}
 

	
 
		void handleBuddyRemovedRequest(const std::string &user, const std::string &buddyName, const std::string &groups) {
 
			PurpleAccount *account = m_sessions[user];
 
			if (account) {
 
				if (m_authRequests.find(user + buddyName) != m_authRequests.end()) {
 
					m_authRequests[user + buddyName]->deny_cb(m_authRequests[user + buddyName]->user_data);
 
					m_authRequests.erase(user + buddyName);
 
				}
 
				PurpleBuddy *buddy = purple_find_buddy(account, buddyName.c_str());
 
				if (buddy) {
 
					purple_account_remove_buddy(account, buddy, purple_buddy_get_group(buddy));
 
					purple_blist_remove_buddy(buddy);
 
				}
 
			}
 
		}
 

	
 
		void handleBuddyUpdatedRequest(const std::string &user, const std::string &buddyName, const std::string &alias, const std::string &groups) {
 
			PurpleAccount *account = m_sessions[user];
 
			if (account) {
 

	
 
				if (m_authRequests.find(user + buddyName) != m_authRequests.end()) {
 
					m_authRequests[user + buddyName]->authorize_cb(m_authRequests[user + buddyName]->user_data);
 
					m_authRequests.erase(user + buddyName);
 
				}
 

	
 
				PurpleBuddy *buddy = purple_find_buddy(account, buddyName.c_str());
 
				if (buddy) {
 
					if (getAlias(buddy) != alias) {
 
						purple_blist_alias_buddy(buddy, alias.c_str());
 
						purple_blist_server_alias_buddy(buddy, alias.c_str());
 
						serv_alias_buddy(buddy);
 
					}
 

	
 
					PurpleGroup *group = purple_find_group(groups.c_str());
 
					if (!group) {
 
						group = purple_group_new(groups.c_str());
 
					}
 
					purple_blist_add_contact(purple_buddy_get_contact(buddy), group ,NULL);
 
				}
 
				else {
 
					PurpleBuddy *buddy = purple_buddy_new(account, buddyName.c_str(), alias.c_str());
 

	
 
					// Add newly created buddy to legacy network roster.
 
					PurpleGroup *group = purple_find_group(groups.c_str());
 
					if (!group) {
 
						group = purple_group_new(groups.c_str());
 
					}
 
					purple_blist_add_buddy(buddy, NULL, group ,NULL);
 
					purple_account_add_buddy(account, buddy);
 
				}
 
			}
 
		}
 

	
 
		void handleBuddyBlockToggled(const std::string &user, const std::string &buddyName, bool blocked) {
 
			PurpleAccount *account = m_sessions[user];
 
			if (account) {
 
				if (blocked) {
 
					purple_privacy_deny(account, buddyName.c_str(), FALSE, FALSE);
 
				}
 
				else {
 
					purple_privacy_allow(account, buddyName.c_str(), FALSE, FALSE);
 
				}
src/networkpluginserver.cpp
Show inline comments
 
/**
 
 * libtransport -- C++ library for easy XMPP Transports development
 
 *
 
 * Copyright (C) 2011, Jan Kaluza <hanzz.k@gmail.com>
 
 *
 
 * 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/networkpluginserver.h"
 
#include "transport/user.h"
 
#include "transport/transport.h"
 
#include "transport/storagebackend.h"
 
#include "transport/rostermanager.h"
 
#include "transport/usermanager.h"
 
#include "transport/conversationmanager.h"
 
#include "transport/localbuddy.h"
 
#include "transport/config.h"
 
#include "transport/conversation.h"
 
#include "transport/vcardresponder.h"
 
#include "transport/rosterresponder.h"
 
#include "blockresponder.h"
 
#include "Swiften/Swiften.h"
 
#include "Swiften/Server/ServerStanzaChannel.h"
 
#include "Swiften/Elements/StreamError.h"
 
#include "Swiften/Network/BoostConnectionServer.h"
 
#include "Swiften/Elements/AttentionPayload.h"
 
#include "Swiften/Elements/XHTMLIMPayload.h"
 
#include "Swiften/Elements/InvisiblePayload.h"
 
#include "pbnetwork.pb.h"
 
#include "sys/wait.h"
 
#include "sys/signal.h"
 
#include "log4cxx/logger.h"
 
#include "popt.h"
 

	
 
using namespace log4cxx;
 

	
 
namespace Transport {
 

	
 
static unsigned long backend_id;
 

	
 
static LoggerPtr logger = Logger::getLogger("NetworkPluginServer");
 

	
 
class NetworkConversation : public Conversation {
 
	public:
 
		NetworkConversation(ConversationManager *conversationManager, const std::string &legacyName, bool muc = false) : Conversation(conversationManager, legacyName, muc) {
 
		}
 

	
 
		// Called when there's new message to legacy network from XMPP network
 
		void sendMessage(boost::shared_ptr<Swift::Message> &message) {
 
			onMessageToSend(this, message);
 
		}
 

	
 
		boost::signal<void (NetworkConversation *, boost::shared_ptr<Swift::Message> &)> onMessageToSend;
 
};
 

	
 
class NetworkFactory : public Factory {
 
	public:
 
		NetworkFactory(NetworkPluginServer *nps) {
 
			m_nps = nps;
 
		}
 

	
 
		// Creates new conversation (NetworkConversation in this case)
 
		Conversation *createConversation(ConversationManager *conversationManager, const std::string &legacyName) {
 
			NetworkConversation *nc = new NetworkConversation(conversationManager, legacyName);
 
			nc->onMessageToSend.connect(boost::bind(&NetworkPluginServer::handleMessageReceived, m_nps, _1, _2));
 
			return nc;
 
		}
 

	
 
		// Creates new LocalBuddy
 
		Buddy *createBuddy(RosterManager *rosterManager, const BuddyInfo &buddyInfo) {
 
			LocalBuddy *buddy = new LocalBuddy(rosterManager, buddyInfo.id);
 
			buddy->setAlias(buddyInfo.alias);
 
			buddy->setName(buddyInfo.legacyName);
 
			buddy->setSubscription(buddyInfo.subscription);
 
			buddy->setGroups(buddyInfo.groups);
 
			buddy->setFlags((BuddyFlag) buddyInfo.flags);
 
			if (buddyInfo.settings.find("icon_hash") != buddyInfo.settings.end())
 
				buddy->setIconHash(buddyInfo.settings.find("icon_hash")->second.s);
 
			return buddy;
 
		}
 

	
 
	private:
 
		NetworkPluginServer *m_nps;
 
};
 

	
 
// Wraps google protobuf payload into WrapperMessage and serialize it to string
 
#define WRAP(MESSAGE, TYPE) 	pbnetwork::WrapperMessage wrap; \
 
	wrap.set_type(TYPE); \
 
	wrap.set_payload(MESSAGE); \
 
	wrap.SerializeToString(&MESSAGE);
 

	
 
// Executes new backend
 
static pid_t exec_(std::string path, const char *host, const char *port, const char *config) {
 
	// BACKEND_ID is replaced with unique ID. The ID is increasing for every backend.
 
	boost::replace_all(path, "BACKEND_ID", boost::lexical_cast<std::string>(backend_id++));
 

	
 
	// Add host and port.
 
	path += std::string(" --host ") + host + " --port " + port + " " + config;
 
	LOG4CXX_INFO(logger, "Starting new backend " << path);
 

	
 
	// Create array of char * from string using -lpopt library
 
	char *p = (char *) malloc(path.size() + 1);
 
	strcpy(p, path.c_str());
 
	int argc;
 
	char **argv;
 
	poptParseArgvString(p, &argc, (const char ***) &argv);
 

	
 
	// fork and exec
 
	pid_t pid = fork();
 
	if ( pid == 0 ) {
 
		// child process
 
		exit(execv(argv[0], argv));
 
	} else if ( pid < 0 ) {
 
		// fork failed
 
	}
 
	free(p);
 

	
 
	return pid;
 
}
 

	
 
static void SigCatcher(int n) {
 
	pid_t result;
 
	int status;
 
	// Read exit code from all children to not have zombies arround
 
	// WARNING: Do not put LOG4CXX_ here, because it can lead to deadlock
 
	while ((result = waitpid(0, &status, WNOHANG)) > 0) {
 
		if (result != 0) {
 
			if (WIFEXITED(status)) {
 
				if (WEXITSTATUS(status) != 0) {
 
// 					LOG4CXX_ERROR(logger, "Backend can not be started, exit_code=" << WEXITSTATUS(status));
 
				}
 
			}
 
			else {
 
// 				LOG4CXX_ERROR(logger, "Backend can not be started");
 
			}
 
		}
 
	}
 
}
 

	
 
static void handleBuddyPayload(LocalBuddy *buddy, const pbnetwork::Buddy &payload) {
 
	buddy->setName(payload.buddyname());
 
	// Set alias only if it's not empty. Backends are allowed to send empty alias if it has
 
	// not changed.
 
	if (!payload.alias().empty()) {
 
		buddy->setAlias(payload.alias());
 
	}
 

	
 
	// 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());
 
		buddy->setGroups(groups);
 
	}
 

	
 
	buddy->setStatus(Swift::StatusShow((Swift::StatusShow::Type) payload.status()), payload.statusmessage());
 
	buddy->setIconHash(payload.iconhash());
 
	buddy->setBlocked(payload.blocked());
 
}
 

	
 
NetworkPluginServer::NetworkPluginServer(Component *component, Config *config, UserManager *userManager) {
 
	m_userManager = userManager;
 
	m_config = config;
 
	m_component = component;
 
	m_isNextLongRun = false;
 
	m_component->m_factory = new NetworkFactory(this);
 
	m_userManager->onUserCreated.connect(boost::bind(&NetworkPluginServer::handleUserCreated, this, _1));
 
	m_userManager->onUserDestroyed.connect(boost::bind(&NetworkPluginServer::handleUserDestroyed, this, _1));
 

	
 
	m_pingTimer = component->getNetworkFactories()->getTimerFactory()->createTimer(20000);
 
	m_pingTimer->onTick.connect(boost::bind(&NetworkPluginServer::pingTimeout, this));
 
	m_pingTimer->start();
 

	
 
	m_collectTimer = component->getNetworkFactories()->getTimerFactory()->createTimer(2*3600000);
 
	m_collectTimer->onTick.connect(boost::bind(&NetworkPluginServer::collectBackend, this));
 
	m_collectTimer->start();
 

	
 
	m_vcardResponder = new VCardResponder(component->getIQRouter(), userManager);
 
	m_vcardResponder->onVCardRequired.connect(boost::bind(&NetworkPluginServer::handleVCardRequired, this, _1, _2, _3));
 
	m_vcardResponder->onVCardUpdated.connect(boost::bind(&NetworkPluginServer::handleVCardUpdated, this, _1, _2));
 
	m_vcardResponder->start();
 

	
 
	m_rosterResponder = new RosterResponder(component->getIQRouter(), userManager);
 
	m_rosterResponder->onBuddyAdded.connect(boost::bind(&NetworkPluginServer::handleBuddyAdded, this, _1, _2));
 
	m_rosterResponder->onBuddyRemoved.connect(boost::bind(&NetworkPluginServer::handleBuddyRemoved, this, _1));
 
	m_rosterResponder->onBuddyUpdated.connect(boost::bind(&NetworkPluginServer::handleBuddyUpdated, this, _1, _2));
 
	m_rosterResponder->start();
 

	
 
	m_blockResponder = new BlockResponder(component->getIQRouter(), userManager);
 
	m_blockResponder->onBlockToggled.connect(boost::bind(&NetworkPluginServer::handleBlockToggled, this, _1));
 
	m_blockResponder->start();
 

	
 
	m_server = Swift::BoostConnectionServer::create(Swift::HostAddress(CONFIG_STRING(m_config, "service.backend_host")), boost::lexical_cast<int>(CONFIG_STRING(m_config, "service.backend_port")), component->getNetworkFactories()->getIOServiceThread()->getIOService(), component->m_loop);
 
	m_server->onNewConnection.connect(boost::bind(&NetworkPluginServer::handleNewClientConnection, this, _1));
 
	m_server->start();
 

	
 
	LOG4CXX_INFO(logger, "Listening on host " << CONFIG_STRING(m_config, "service.backend_host") << " port " << CONFIG_STRING(m_config, "service.backend_port"));
 

	
 
	signal(SIGCHLD, SigCatcher);
 

	
 
	exec_(CONFIG_STRING(m_config, "service.backend"), CONFIG_STRING(m_config, "service.backend_host").c_str(), CONFIG_STRING(m_config, "service.backend_port").c_str(), m_config->getConfigFile().c_str());
 
}
 

	
 
NetworkPluginServer::~NetworkPluginServer() {
 
	m_pingTimer->stop();
 
	m_server->stop();
 
	m_server.reset();
 
	delete m_component->m_factory;
 
	delete m_vcardResponder;
 
	delete m_rosterResponder;
 
	delete m_blockResponder;
 
}
 

	
 
void NetworkPluginServer::handleNewClientConnection(boost::shared_ptr<Swift::Connection> c) {
 
	// Create new Backend instance
 
	Backend *client = new Backend;
 
	client->pongReceived = -1;
 
	client->connection = c;
 
	client->res = 0;
 
	client->init_res = 0;
 
	client->shared = 0;
 
@@ -596,385 +597,394 @@ void NetworkPluginServer::handleDataRead(Backend *c, const Swift::SafeByteArray
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_BUDDY_TYPED:
 
				handleChatStatePayload(wrapper.payload(), Swift::ChatState::Paused);
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_BUDDY_STOPPED_TYPING:
 
				handleChatStatePayload(wrapper.payload(), Swift::ChatState::Active);
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_AUTH_REQUEST:
 
				handleAuthorizationPayload(wrapper.payload());
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_ATTENTION:
 
				handleAttentionPayload(wrapper.payload());
 
				break;
 
			case pbnetwork::WrapperMessage_Type_TYPE_STATS:
 
				handleStatsPayload(c, wrapper.payload());
 
				break;
 
			default:
 
				return;
 
		}
 
	}
 
}
 

	
 
void NetworkPluginServer::send(boost::shared_ptr<Swift::Connection> &c, const std::string &data) {
 
	// generate header - size of wrapper message
 
	char header[4];
 
	*((int*)(header)) = htonl(data.size());
 

	
 
	// send header together with wrapper message
 
	c->write(Swift::createSafeByteArray(std::string(header, 4) + data));
 
}
 

	
 
void NetworkPluginServer::pingTimeout() {
 
	// TODO: move to separate timer, those 2 loops could be expensive
 
	// Some users are connected for weeks and they are blocking backend to be destroyed and its memory
 
	// to be freed. We are finding users who are inactive for more than "idle_reconnect_time" seconds and
 
	// reconnect them to long-running backend, where they can idle hapilly till the end of ages.
 
	time_t now = time(NULL);
 
	std::vector<User *> usersToMove;
 
	unsigned long diff = CONFIG_INT(m_config, "service.idle_reconnect_time");
 
	for (std::list<Backend *>::const_iterator it = m_clients.begin(); it != m_clients.end(); it++) {
 
		// Users from long-running backends can't be moved
 
		if ((*it)->longRun) {
 
			continue;
 
		}
 

	
 
		// Find users which are inactive for more than 'diff'
 
		BOOST_FOREACH(User *u, (*it)->users) {
 
			if (now - u->getLastActivity() > diff) {
 
				usersToMove.push_back(u);
 
			}
 
		}
 
	}
 

	
 
	// Move inactive users to long-running backend.
 
	BOOST_FOREACH(User *u, usersToMove) {
 
		LOG4CXX_INFO(logger, "Moving user " << u->getJID().toString() << " to long-running backend");
 
		if (!moveToLongRunBackend(u))
 
			break;
 
	}
 
	
 

	
 
	// check ping responses
 
	for (std::list<Backend *>::const_iterator it = m_clients.begin(); it != m_clients.end(); it++) {
 
		// pong has been received OR backend just connected and did not have time to answer the ping
 
		// request.
 
		if ((*it)->pongReceived || (*it)->pongReceived == -1) {
 
			sendPing((*it));
 
		}
 
		else {
 
			LOG4CXX_INFO(logger, "Disconnecting backend " << (*it) << ". PING response not received.");
 
			(*it)->connection->disconnect();
 
			(*it)->connection.reset();
 
// 			handleSessionFinished((*it));
 
		}
 

	
 
		if ((*it)->users.size() == 0) {
 
			LOG4CXX_INFO(logger, "Disconnecting backend " << (*it) << ". There are no users.");
 
// 			(*it)->connection->disconnect();
 
			(*it)->connection.reset();
 
		}
 
	}
 
	m_pingTimer->start();
 
}
 

	
 
void NetworkPluginServer::collectBackend() {
 
	// Stop accepting new users to backend with the biggest memory usage. This prevents backends
 
	// which are leaking to eat whole memory by connectin new users to legacy network.
 
	LOG4CXX_INFO(logger, "Collect backend called, finding backend which will be set to die");
 
	unsigned long max = 0;
 
	Backend *backend = NULL;
 
	for (std::list<Backend *>::const_iterator it = m_clients.begin(); it != m_clients.end(); it++) {
 
		if ((*it)->res > max) {
 
			max = (*it)->res;
 
			backend = (*it);
 
		}
 
	}
 

	
 
	if (backend) {
 
		m_collectTimer->start();
 
		LOG4CXX_INFO(logger, "Backend " << backend << "is set to die");
 
		backend->acceptUsers = false;
 
	}
 
}
 

	
 
bool NetworkPluginServer::moveToLongRunBackend(User *user) {
 
	// Check if user has already some backend
 
	Backend *old = (Backend *) user->getData();
 
	if (!old) {
 
		LOG4CXX_INFO(logger, "User " << user->getJID().toString() << " does not have old backend. Not moving.");
 
		return true;
 
	}
 

	
 
	// if he's already on long run, do nothing
 
	if (old->longRun) {
 
		LOG4CXX_INFO(logger, "User " << user->getJID().toString() << " is already on long-running backend. Not moving.");
 
		return true;
 
	}
 

	
 
	// Get free longrun backend, if there's no longrun backend, create one and wait
 
	// for its connection
 
	Backend *backend = getFreeClient(false, true);
 
	if (!backend) {
 
		LOG4CXX_INFO(logger, "No free long-running backend for user " << user->getJID().toString() << ". Will try later");
 
		return false;
 
	}
 

	
 
	// old backend will trigger disconnection which has to be ignored to keep user online
 
	user->setIgnoreDisconnect(true);
 

	
 
	// remove user from the old backend
 
	// If backend is empty, it will be collected by pingTimeout
 
	old->users.remove(user);
 

	
 
	// switch to new backend and connect
 
	user->setData(backend);
 
	backend->users.push_back(user);
 

	
 
	// connect him
 
	handleUserReadyToConnect(user);
 
	return true;
 
}
 

	
 
void NetworkPluginServer::handleUserCreated(User *user) {
 
	// Get free backend to handle this user or spawn new one if there's no free one.
 
	Backend *c = getFreeClient();
 

	
 
	// Add user to queue if there's no free backend to handle him so far.
 
	if (!c) {
 
		LOG4CXX_INFO(logger, "There is no backend to handle user " << user->getJID().toString() << ". Adding him to queue.");
 
		m_waitingUsers.push_back(user);
 
		return;
 
	}
 

	
 
	// Associate users with backend
 
	user->setData(c);
 
	c->users.push_back(user);
 

	
 
// 	UserInfo userInfo = user->getUserInfo();
 
	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));
 
	user->onRoomLeft.connect(boost::bind(&NetworkPluginServer::handleRoomLeft, this, user, _1));
 
}
 

	
 
void NetworkPluginServer::handleUserReadyToConnect(User *user) {
 
	UserInfo userInfo = user->getUserInfo();
 

	
 
	pbnetwork::Login login;
 
	login.set_user(user->getJID().toBare());
 
	login.set_legacyname(userInfo.uin);
 
	login.set_password(userInfo.password);
 

	
 
	std::string message;
 
	login.SerializeToString(&message);
 

	
 
	WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_LOGIN);
 

	
 
	Backend *c = (Backend *) user->getData();
 
	if (!c) {
 
		return;
 
	}
 
	send(c->connection, message);
 
}
 

	
 
void NetworkPluginServer::handleUserPresenceChanged(User *user, Swift::Presence::ref presence) {
 
	if (presence->getShow() == Swift::StatusShow::None)
 
		return;
 

	
 
	UserInfo userInfo = user->getUserInfo();
 

	
 
	pbnetwork::Status status;
 
	status.set_username(user->getJID().toBare());
 
	status.set_status((int) presence->getShow());
 

	
 
	bool isInvisible = presence->getPayload<Swift::InvisiblePayload>() != NULL;
 
	if (isInvisible) {
 
		LOG4CXX_INFO(logger, "This presence is invisible");
 
		status.set_status(255);
 
	}
 
	else {
 
		status.set_status((int) presence->getShow());
 
	}
 

	
 
	status.set_statusmessage(presence->getStatus());
 

	
 
	std::string message;
 
	status.SerializeToString(&message);
 

	
 
	WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_STATUS_CHANGED);
 

	
 
	Backend *c = (Backend *) user->getData();
 
	if (!c) {
 
		return;
 
	}
 
	send(c->connection, message);
 
}
 

	
 
void NetworkPluginServer::handleRoomJoined(User *user, const std::string &r, const std::string &nickname, const std::string &password) {
 
	UserInfo userInfo = user->getUserInfo();
 

	
 
	pbnetwork::Room room;
 
	room.set_username(user->getJID().toBare());
 
	room.set_nickname(nickname);
 
	room.set_room(r);
 
	room.set_password(password);
 

	
 
	std::string message;
 
	room.SerializeToString(&message);
 

	
 
	WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_JOIN_ROOM);
 
 
 
	Backend *c = (Backend *) user->getData();
 
	if (!c) {
 
		return;
 
	}
 
	send(c->connection, message);
 

	
 
	NetworkConversation *conv = new NetworkConversation(user->getConversationManager(), r, true);
 
	conv->onMessageToSend.connect(boost::bind(&NetworkPluginServer::handleMessageReceived, this, _1, _2));
 
	conv->setNickname(nickname);
 
}
 

	
 
void NetworkPluginServer::handleRoomLeft(User *user, const std::string &r) {
 
	UserInfo userInfo = user->getUserInfo();
 

	
 
	pbnetwork::Room room;
 
	room.set_username(user->getJID().toBare());
 
	room.set_nickname("");
 
	room.set_room(r);
 
	room.set_password("");
 

	
 
	std::string message;
 
	room.SerializeToString(&message);
 

	
 
	WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_LEAVE_ROOM);
 
 
 
	Backend *c = (Backend *) user->getData();
 
	if (!c) {
 
		return;
 
	}
 
	send(c->connection, message);
 

	
 
	NetworkConversation *conv = (NetworkConversation *) user->getConversationManager()->getConversation(r);
 
	if (!conv) {
 
		return;
 
	}
 

	
 
	user->getConversationManager()->removeConversation(conv);
 

	
 
	delete conv;
 
}
 

	
 
void NetworkPluginServer::handleUserDestroyed(User *user) {
 
	std::cout << "HANDLE_DESTROYED\n";
 
	m_waitingUsers.remove(user);
 
	UserInfo userInfo = user->getUserInfo();
 

	
 
	pbnetwork::Logout logout;
 
	logout.set_user(user->getJID().toBare());
 
	logout.set_legacyname(userInfo.uin);
 

	
 
	std::string message;
 
	logout.SerializeToString(&message);
 

	
 
	WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_LOGOUT);
 
 
 
	Backend *c = (Backend *) user->getData();
 
	if (!c) {
 
		return;
 
	}
 
	send(c->connection, message);
 
	c->users.remove(user);
 
	if (c->users.size() == 0) {
 
		LOG4CXX_INFO(logger, "Disconnecting backend " << c << ". There are no users.");
 
		c->connection->disconnect();
 
		c->connection.reset();
 
		
 
// 		handleSessionFinished(c);
 
// 		m_clients.erase(user->connection);
 
	}
 
}
 

	
 
void NetworkPluginServer::handleMessageReceived(NetworkConversation *conv, boost::shared_ptr<Swift::Message> &msg) {
 
	conv->getConversationManager()->getUser()->updateLastActivity();
 
	boost::shared_ptr<Swift::ChatState> statePayload = msg->getPayload<Swift::ChatState>();
 
	if (statePayload) {
 
		pbnetwork::WrapperMessage_Type type = pbnetwork::WrapperMessage_Type_TYPE_BUDDY_CHANGED;
 
		switch (statePayload->getChatState()) {
 
			case Swift::ChatState::Active:
 
				type = pbnetwork::WrapperMessage_Type_TYPE_BUDDY_STOPPED_TYPING;
 
				break;
 
			case Swift::ChatState::Composing:
 
				type = pbnetwork::WrapperMessage_Type_TYPE_BUDDY_TYPING;
 
				break;
 
			case Swift::ChatState::Paused:
 
				type = pbnetwork::WrapperMessage_Type_TYPE_BUDDY_TYPED;
 
				break;
 
			default:
 
				break;
 
		}
 
		if (type != pbnetwork::WrapperMessage_Type_TYPE_BUDDY_CHANGED) {
 
			pbnetwork::Buddy buddy;
 
			buddy.set_username(conv->getConversationManager()->getUser()->getJID().toBare());
 
			buddy.set_buddyname(conv->getLegacyName());
 

	
 
			std::string message;
 
			buddy.SerializeToString(&message);
 

	
 
			WRAP(message, type);
 

	
 
			Backend *c = (Backend *) conv->getConversationManager()->getUser()->getData();
 
			if (!c) {
 
				return;
 
			}
 
			send(c->connection, message);
 
		}
 
	}
 

	
 
	boost::shared_ptr<Swift::AttentionPayload> attentionPayload = msg->getPayload<Swift::AttentionPayload>();
 
	if (attentionPayload) {
 
		pbnetwork::ConversationMessage m;
 
		m.set_username(conv->getConversationManager()->getUser()->getJID().toBare());
 
		m.set_buddyname(conv->getLegacyName());
 
		m.set_message(msg->getBody());
 

	
 
		std::string message;
 
		m.SerializeToString(&message);
 

	
 
		WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_ATTENTION);
 

	
 
		Backend *c = (Backend *) conv->getConversationManager()->getUser()->getData();
 
		send(c->connection, message);
 
		return;
 
	}
 
	
 

	
 
	std::string xhtml;
 
	boost::shared_ptr<Swift::XHTMLIMPayload> xhtmlPayload = msg->getPayload<Swift::XHTMLIMPayload>();
 
	if (xhtmlPayload) {
 
		xhtml = xhtmlPayload->getBody();
 
	}
 

	
 
	// Send normal message
 
	if (!msg->getBody().empty() || !xhtml.empty()) {
 
		pbnetwork::ConversationMessage m;
 
		m.set_username(conv->getConversationManager()->getUser()->getJID().toBare());
 
		m.set_buddyname(conv->getLegacyName());
 
		m.set_message(msg->getBody());
 
		m.set_xhtml(xhtml);
 

	
 
		std::string message;
 
		m.SerializeToString(&message);
 

	
 
		WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_CONV_MESSAGE);
 

	
 
		Backend *c = (Backend *) conv->getConversationManager()->getUser()->getData();
 
		if (!c) {
 
			return;
 
		}
 
		send(c->connection, message);
 
	}
 
}
 

	
 
void NetworkPluginServer::handleBuddyRemoved(Buddy *b) {
 
	User *user = b->getRosterManager()->getUser();
 

	
 
	pbnetwork::Buddy buddy;
 
	buddy.set_username(user->getJID().toBare());
 
	buddy.set_buddyname(b->getName());
 
	buddy.set_alias(b->getAlias());
 
	buddy.set_groups(b->getGroups().size() == 0 ? "" : b->getGroups()[0]);
 
	buddy.set_status(Swift::StatusShow::None);
 

	
 
	std::string message;
 
	buddy.SerializeToString(&message);
0 comments (0 inline, 0 general)