Changeset - c643e5b81d60
[Not reviewed]
0 2 6
Jan Kaluza - 14 years ago 2012-01-18 09:39:32
hanzz.k@gmail.com
Skype backend
8 files changed with 1227 insertions and 0 deletions:
0 comments (0 inline, 0 general)
backends/CMakeLists.txt
Show inline comments
 
@@ -6,9 +6,10 @@ if (PROTOBUF_FOUND)
 
	if (IRC_FOUND)
 
		ADD_SUBDIRECTORY(libcommuni)
 
	endif()
 

	
 
if (NOT WIN32)
 
	ADD_SUBDIRECTORY(frotz)
 
	ADD_SUBDIRECTORY(skype)
 
endif()
 

	
 
endif()
backends/skype/CMakeLists.txt
Show inline comments
 
new file 100644
 
cmake_minimum_required(VERSION 2.6)
 
FILE(GLOB SRC *.cpp)
 

	
 
include_directories(/usr/include/dbus-1.0/)
 
include_directories(/usr/lib/dbus-1.0/include/)
 
include_directories(/usr/lib64/dbus-1.0/include/)
 
 
ADD_EXECUTABLE(spectrum2_skype_backend ${SRC})
 
 
target_link_libraries(spectrum2_skype_backend ${GLIB2_LIBRARIES} ${EVENT_LIBRARIES} transport pthread dbus-glib-1 dbus-1 gobject-2.0 transport-plugin)
 
 
INSTALL(TARGETS spectrum2_skype_backend RUNTIME DESTINATION bin)
 
backends/skype/geventloop.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 "geventloop.h"
 
#ifdef _WIN32
 
#include "win32/win32dep.h"
 
#endif
 
#ifdef WITH_LIBEVENT
 
#include "event.h"
 
#endif
 

	
 
typedef struct _PurpleIOClosure {
 
	PurpleInputFunction function;
 
	guint result;
 
	gpointer data;
 
#ifdef WITH_LIBEVENT
 
	GSourceFunc function2;
 
	struct timeval timeout;
 
	struct event evfifo;
 
#endif
 
} PurpleIOClosure;
 

	
 
static gboolean io_invoke(GIOChannel *source,
 
										GIOCondition condition,
 
										gpointer data)
 
{
 
	PurpleIOClosure *closure = (PurpleIOClosure* )data;
 
	PurpleInputCondition purple_cond = (PurpleInputCondition)0;
 

	
 
	int tmp = 0;
 
	if (condition & READ_COND)
 
	{
 
		tmp |= PURPLE_INPUT_READ;
 
		purple_cond = (PurpleInputCondition)tmp;
 
	}
 
	if (condition & WRITE_COND)
 
	{
 
		tmp |= PURPLE_INPUT_WRITE;
 
		purple_cond = (PurpleInputCondition)tmp;
 
	}
 

	
 
	closure->function(closure->data, g_io_channel_unix_get_fd(source), purple_cond);
 

	
 
	return TRUE;
 
}
 

	
 
static void io_destroy(gpointer data)
 
{
 
	g_free(data);
 
}
 

	
 
static guint input_add(gint fd,
 
								PurpleInputCondition condition,
 
								PurpleInputFunction function,
 
								gpointer data)
 
{
 
	PurpleIOClosure *closure = g_new0(PurpleIOClosure, 1);
 
	GIOChannel *channel;
 
	GIOCondition cond = (GIOCondition)0;
 
	closure->function = function;
 
	closure->data = data;
 

	
 
	int tmp = 0;
 
	if (condition & PURPLE_INPUT_READ)
 
	{
 
		tmp |= READ_COND;
 
		cond = (GIOCondition)tmp;
 
	}
 
	if (condition & PURPLE_INPUT_WRITE)
 
	{
 
		tmp |= WRITE_COND;
 
		cond = (GIOCondition)tmp;
 
	}
 

	
 
#ifdef WIN32
 
	channel = wpurple_g_io_channel_win32_new_socket(fd);
 
#else
 
	channel = g_io_channel_unix_new(fd);
 
#endif
 
	closure->result = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT, cond,
 
	io_invoke, closure, io_destroy);
 

	
 
	g_io_channel_unref(channel);
 
	return closure->result;
 
}
 

	
 
static PurpleEventLoopUiOps eventLoopOps =
 
{
 
	g_timeout_add,
 
	g_source_remove,
 
	input_add,
 
	g_source_remove,
 
	NULL,
 
#if GLIB_CHECK_VERSION(2,14,0)
 
	g_timeout_add_seconds,
 
#else
 
	NULL,
 
#endif
 

	
 
	NULL,
 
	NULL,
 
	NULL
 
};
 

	
 
#ifdef WITH_LIBEVENT
 

	
 
static GHashTable *events = NULL;
 
static unsigned long id = 0;
 

	
 
static void event_io_destroy(gpointer data)
 
{
 
	PurpleIOClosure *closure = (PurpleIOClosure* )data;
 
	event_del(&closure->evfifo);
 
	g_free(data);
 
}
 

	
 
static void event_io_invoke(int fd, short event, void *data)
 
{
 
	PurpleIOClosure *closure = (PurpleIOClosure* )data;
 
	PurpleInputCondition purple_cond = (PurpleInputCondition)0;
 
	int tmp = 0;
 
	if (event & EV_READ)
 
	{
 
		tmp |= PURPLE_INPUT_READ;
 
		purple_cond = (PurpleInputCondition)tmp;
 
	}
 
	if (event & EV_WRITE)
 
	{
 
		tmp |= PURPLE_INPUT_WRITE;
 
		purple_cond = (PurpleInputCondition)tmp;
 
	}
 
	if (event & EV_TIMEOUT)
 
	{
 
// 		tmp |= PURPLE_INPUT_WRITE;
 
// 		purple_cond = (PurpleInputCondition)tmp;
 
		if (closure->function2(closure->data))
 
			evtimer_add(&closure->evfifo, &closure->timeout);
 
// 		else
 
// 			event_io_destroy(data);
 
		return;
 
	}
 

	
 
	closure->function(closure->data, fd, purple_cond);
 
}
 

	
 
static gboolean event_input_remove(guint handle)
 
{
 
	PurpleIOClosure *closure = (PurpleIOClosure *) g_hash_table_lookup(events, &handle);
 
	if (closure)
 
		event_io_destroy(closure);
 
	return TRUE;
 
}
 

	
 
static guint event_input_add(gint fd,
 
								PurpleInputCondition condition,
 
								PurpleInputFunction function,
 
								gpointer data)
 
{
 
	PurpleIOClosure *closure = g_new0(PurpleIOClosure, 1);
 
	GIOChannel *channel;
 
	GIOCondition cond = (GIOCondition)0;
 
	closure->function = function;
 
	closure->data = data;
 

	
 
	int tmp = EV_PERSIST;
 
	if (condition & PURPLE_INPUT_READ)
 
	{
 
		tmp |= EV_READ;
 
	}
 
	if (condition & PURPLE_INPUT_WRITE)
 
	{
 
		tmp |= EV_WRITE;
 
	}
 

	
 
	event_set(&closure->evfifo, fd, tmp, event_io_invoke, closure);
 
	event_add(&closure->evfifo, NULL);
 
	
 
	int *f = (int *) g_malloc(sizeof(int));
 
	*f = id;
 
	id++;
 
	g_hash_table_replace(events, f, closure);
 
	
 
	return *f;
 
}
 

	
 
static guint event_timeout_add (guint interval, GSourceFunc function, gpointer data) {
 
	struct timeval timeout;
 
	PurpleIOClosure *closure = g_new0(PurpleIOClosure, 1);
 
	closure->function2 = function;
 
	closure->data = data;
 
	
 
	timeout.tv_sec = interval/1000;
 
	timeout.tv_usec = (interval%1000)*1000;
 
	evtimer_set(&closure->evfifo, event_io_invoke, closure);
 
	evtimer_add(&closure->evfifo, &timeout);
 
	closure->timeout = timeout;
 
	
 
	guint *f = (guint *) g_malloc(sizeof(guint));
 
	*f = id;
 
	id++;
 
	g_hash_table_replace(events, f, closure);
 
	return *f;
 
}
 

	
 
static PurpleEventLoopUiOps libEventLoopOps =
 
{
 
	event_timeout_add,
 
	event_input_remove,
 
	event_input_add,
 
	event_input_remove,
 
	NULL,
 
// #if GLIB_CHECK_VERSION(2,14,0)
 
// 	g_timeout_add_seconds,
 
// #else
 
	NULL,
 
// #endif
 

	
 
	NULL,
 
	NULL,
 
	NULL
 
};
 

	
 
#endif /* WITH_LIBEVENT*/
 

	
 
PurpleEventLoopUiOps * getEventLoopUiOps(void){
 
	return &eventLoopOps;
 
#ifdef WITH_LIBEVENT
 
	events = g_hash_table_new_full(g_int_hash, g_int_equal, g_free, NULL);
 
	return &libEventLoopOps;
 
#endif
 
}
backends/skype/geventloop.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
 
 */
 

	
 
#ifndef _HI_EVENTLOOP_H
 
#define _HI_EVENTLOOP_H
 

	
 
#include <glib.h>
 
#include "purple.h"
 
#include "eventloop.h"
 

	
 
#define READ_COND  (G_IO_IN | G_IO_HUP | G_IO_ERR)
 
#define WRITE_COND (G_IO_OUT | G_IO_HUP | G_IO_ERR | G_IO_NVAL)
 

	
 
PurpleEventLoopUiOps * getEventLoopUiOps(void);
 

	
 
#endif
backends/skype/main.cpp
Show inline comments
 
new file 100644
 
#include "glib.h"
 
#include <iostream>
 

	
 
#include "transport/config.h"
 
#include "transport/transport.h"
 
#include "transport/usermanager.h"
 
#include "transport/logger.h"
 
#include "transport/sqlite3backend.h"
 
#include "transport/userregistration.h"
 
#include "transport/user.h"
 
#include "transport/storagebackend.h"
 
#include "transport/rostermanager.h"
 
#include "transport/conversation.h"
 
#include "transport/networkplugin.h"
 
#include "spectrumeventloop.h"
 
#include <boost/filesystem.hpp>
 
#include "geventloop.h"
 
#include "log4cxx/logger.h"
 
#include "log4cxx/consoleappender.h"
 
#include "log4cxx/patternlayout.h"
 
#include "log4cxx/propertyconfigurator.h"
 
#include "log4cxx/helpers/properties.h"
 
#include "log4cxx/helpers/fileinputstream.h"
 
#include "sys/wait.h"
 
#include "sys/signal.h"
 
// #include "valgrind/memcheck.h"
 
#include "malloc.h"
 
#include <dbus-1.0/dbus/dbus-glib-lowlevel.h>
 

	
 

	
 
using namespace log4cxx;
 

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

	
 
using namespace Transport;
 

	
 
class SpectrumNetworkPlugin;
 

	
 

	
 
SpectrumNetworkPlugin *np;
 

	
 
static gboolean nodaemon = FALSE;
 
static gchar *logfile = NULL;
 
static gchar *lock_file = NULL;
 
static gchar *host = NULL;
 
static int port = 10000;
 
static gboolean ver = FALSE;
 
static gboolean list_purple_settings = FALSE;
 

	
 
int m_sock;
 
static int writeInput;
 

	
 
static GOptionEntry options_entries[] = {
 
	{ "nodaemon", 'n', 0, G_OPTION_ARG_NONE, &nodaemon, "Disable background daemon mode", NULL },
 
	{ "logfile", 'l', 0, G_OPTION_ARG_STRING, &logfile, "Set file to log", NULL },
 
	{ "pidfile", 'p', 0, G_OPTION_ARG_STRING, &lock_file, "File where to write transport PID", NULL },
 
	{ "version", 'v', 0, G_OPTION_ARG_NONE, &ver, "Shows Spectrum version", NULL },
 
	{ "list-purple-settings", 's', 0, G_OPTION_ARG_NONE, &list_purple_settings, "Lists purple settings which can be used in config file", NULL },
 
	{ "host", 'h', 0, G_OPTION_ARG_STRING, &host, "Host to connect to", NULL },
 
	{ "port", 'p', 0, G_OPTION_ARG_INT, &port, "Port to connect to", NULL },
 
	{ NULL, 0, 0, G_OPTION_ARG_NONE, NULL, "", NULL }
 
};
 

	
 
DBusHandlerResult skype_notify_handler(DBusConnection *connection, DBusMessage *message, gpointer user_data);
 

	
 
static pbnetwork::StatusType getStatus(const std::string &st) {
 
	pbnetwork::StatusType status = pbnetwork::STATUS_ONLINE;
 
	if (st == "SKYPEOUT" || st == "OFFLINE") {
 
		status = pbnetwork::STATUS_NONE;
 
	}
 
	else if (st == "DND") {
 
		status = pbnetwork::STATUS_DND;
 
	}
 
	else if (st == "NA") {
 
		status = pbnetwork::STATUS_XA;
 
	}
 
	else if (st == "AWAY") {
 
		status = pbnetwork::STATUS_AWAY;
 
	}
 
	return status;
 
}
 

	
 
class Skype {
 
	public:
 
		Skype(const std::string &user, const std::string &username, const std::string &password);
 
		~Skype() { logout(); }
 
		void login();
 
		void logout();
 
		std::string send_command(const std::string &message);
 

	
 
		const std::string &getUser() {
 
			return m_user;
 
		}
 

	
 
		const std::string &getUsername() {
 
			return m_username;
 
		}
 

	
 
	private:
 
		std::string m_username;
 
		std::string m_password;
 
		GPid m_pid;
 
		DBusGConnection *m_connection;
 
		DBusGProxy *m_proxy;
 
		std::string m_user;
 
};
 

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

	
 
		~SpectrumNetworkPlugin() {
 
			for (std::map<Skype *, std::string>::iterator it = m_accounts.begin(); it != m_accounts.end(); it++) {
 
				delete (*it).first;
 
			}
 
		}
 

	
 
		void handleLoginRequest(const std::string &user, const std::string &legacyName, const std::string &password) {
 
			std::string name = legacyName;
 
			name = name.substr(name.find(".") + 1);
 
			LOG4CXX_INFO(logger,  "Creating account with name '" << name);
 

	
 
			Skype *skype = new Skype(user, name, password);
 
			m_sessions[user] = skype;
 
			m_accounts[skype] = user;
 

	
 
			skype->login();
 
		}
 

	
 
		void handleLogoutRequest(const std::string &user, const std::string &legacyName) {
 
			Skype *skype = m_sessions[user];
 
			if (skype) {
 
				skype->logout();
 
				exit(1);
 
			}
 
		}
 

	
 
		void handleStatusChangeRequest(const std::string &user, int status, const std::string &statusMessage) {
 
			Skype *skype = m_sessions[user];
 
			if (!skype)
 
				return;
 

	
 
			std::string st;
 
			switch(status) {
 
				case Swift::StatusShow::Away: {
 
					st = "AWAY";
 
					break;
 
				}
 
				case Swift::StatusShow::DND: {
 
					st = "DND";
 
					break;
 
				}
 
				case Swift::StatusShow::XA: {
 
					st = "NA";
 
					break;
 
				}
 
				case Swift::StatusShow::None: {
 
					break;
 
				}
 
				case pbnetwork::STATUS_INVISIBLE:
 
					st = "INVISIBLE";
 
					break;
 
				default:
 
					st = "ONLINE";
 
					break;
 
			}
 
			skype->send_command("SET USERSTATUS " + st);
 

	
 
			if (!statusMessage.empty()) {
 
				skype->send_command("SET PROFILE MOOD_TEXT " + statusMessage);
 
			}
 
		}
 

	
 
		void handleMessageSendRequest(const std::string &user, const std::string &legacyName, const std::string &message, const std::string &xhtml) {
 
			Skype *skype = m_sessions[user];
 
			if (skype) {
 
				skype->send_command("MESSAGE " + legacyName + " " + message);
 
			}
 
			
 
		}
 

	
 
		void handleVCardRequest(const std::string &user, const std::string &legacyName, unsigned int id) {
 
			Skype *skype = m_sessions[user];
 
			if (skype) {
 
				std::string name = legacyName;
 
				if (name.find("skype.") == 0) {
 
					name = name.substr(6);
 
				}
 
				std::string photo;
 
				gchar *filename = NULL;
 
				gchar *new_filename = NULL;
 
				gchar *image_data = NULL;
 
				gsize image_data_len = 0;
 
				gchar *ret;
 
				int fh;
 
				GError *error;
 
				const gchar *userfiles[] = {"user256", "user1024", "user4096", "user16384", "user32768", "user65536",
 
											"profile256", "profile1024", "profile4096", "profile16384", "profile32768", 
 
											NULL};
 
				char *username = g_strdup_printf("\x03\x10%s", name.c_str());
 
				for (fh = 0; userfiles[fh]; fh++) {
 
					filename = g_strconcat("/tmp/skype/", skype->getUsername().c_str(), "/", skype->getUsername().c_str(), "/", userfiles[fh], ".dbb", NULL);
 
					std::cout << "getting filename:" << filename << "\n";
 
					if (g_file_get_contents(filename, &image_data, &image_data_len, NULL))
 
					{
 
						std::cout << "got\n";
 
						char *start = (char *)memmem(image_data, image_data_len, username, strlen(username)+1);
 
						if (start != NULL)
 
						{
 
							char *next = image_data;
 
							char *last = next;
 
							//find last index of l33l
 
							while ((next = (char *)memmem(next+4, start-next-4, "l33l", 4)))
 
							{
 
								last = next;
 
							}
 
							start = last;
 
							if (start != NULL)
 
							{
 
								char *img_start;
 
								//find end of l33l block
 
								char *end = (char *)memmem(start+4, image_data+image_data_len-start-4, "l33l", 4);
 
								if (!end) end = image_data+image_data_len;
 
								
 
								//look for start of JPEG block
 
								img_start = (char *)memmem(start, end-start, "\xFF\xD8", 2);
 
								if (img_start)
 
								{
 
									//look for end of JPEG block
 
									char *img_end = (char *)memmem(img_start, end-img_start, "\xFF\xD9", 2);
 
									if (img_end)
 
									{
 
										image_data_len = img_end - img_start + 2;
 
										photo = std::string(img_start, image_data_len);
 
									}
 
								}
 
							}
 
						}
 
						g_free(image_data);
 
					}
 
					g_free(filename);
 
				}
 
				g_free(username);
 
				
 
				std::string alias = "";
 
				std::cout << skype->getUsername() << " " << name << "\n";
 
				if (skype->getUsername() == name) {
 
					alias = skype->send_command("GET PROFILE FULLNAME");
 
					alias = alias.substr(17);
 
				}
 
				handleVCard(user, id, legacyName, "", alias, photo);
 
			}
 
		}
 

	
 
		void sendData(const std::string &string) {
 
			write(m_sock, string.c_str(), string.size());
 
// 			if (writeInput == 0)
 
// 				writeInput = purple_input_add(m_sock, PURPLE_INPUT_WRITE, &transportDataReceived, NULL);
 
		}
 

	
 
		void handleVCardUpdatedRequest(const std::string &user, const std::string &p, const std::string &nickname) {
 
		}
 

	
 
		void handleBuddyRemovedRequest(const std::string &user, const std::string &buddyName, const std::vector<std::string> &groups) {
 
		}
 

	
 
		void handleBuddyUpdatedRequest(const std::string &user, const std::string &buddyName, const std::string &alias, const std::vector<std::string> &groups) {
 

	
 
		}
 

	
 
		void handleBuddyBlockToggled(const std::string &user, const std::string &buddyName, bool blocked) {
 

	
 
		}
 

	
 
		void handleTypingRequest(const std::string &user, const std::string &buddyName) {
 

	
 
		}
 

	
 
		void handleTypedRequest(const std::string &user, const std::string &buddyName) {
 

	
 
		}
 

	
 
		void handleStoppedTypingRequest(const std::string &user, const std::string &buddyName) {
 

	
 
		}
 

	
 
		void handleAttentionRequest(const std::string &user, const std::string &buddyName, const std::string &message) {
 

	
 
		}
 

	
 
		std::map<std::string, Skype *> m_sessions;
 
		std::map<Skype *, std::string> m_accounts;
 
		std::map<std::string, unsigned int> m_vcards;
 
		Config *config;
 
		
 
};
 

	
 
		Skype::Skype(const std::string &user, const std::string &username, const std::string &password) {
 
			m_username = username;
 
			m_user = user;
 
			m_password = password;
 
			m_pid = 0;
 
			m_connection = 0;
 
			m_proxy = 0;
 
		}
 

	
 
		void Skype::login() {
 
			boost::filesystem::path	path(std::string("/tmp/skype/") + m_username);
 
			if (!boost::filesystem::exists(path)) {
 
				boost::filesystem::create_directories(path);
 
				boost::filesystem::path	path2(std::string("/tmp/skype/") + m_username + "/" + m_username );
 
				boost::filesystem::create_directories(path2);
 
			}
 

	
 
			std::string shared_xml = "<?xml version=\"1.0\"?>\n"
 
									"<config version=\"1.0\" serial=\"28\" timestamp=\"" + boost::lexical_cast<std::string>(time(NULL)) + ".0\">\n"
 
									"<UI>\n"
 
										"<Installed>2</Installed>\n"
 
										"<Language>en</Language>\n"
 
									"</UI>\n"
 
									"</config>\n";
 
			g_file_set_contents(std::string(std::string("/tmp/skype/") + m_username + "/shared.xml").c_str(), shared_xml.c_str(), -1, NULL);
 

	
 
			std::string config_xml = "<?xml version=\"1.0\"?>\n"
 
									"<config version=\"1.0\" serial=\"7\" timestamp=\"" + boost::lexical_cast<std::string>(time(NULL)) + ".0\">\n"
 
										"<Lib>\n"
 
											"<Account>\n"
 
											"<IdleTimeForAway>30000000</IdleTimeForAway>\n"
 
											"<IdleTimeForNA>300000000</IdleTimeForNA>\n"
 
											"<LastUsed>" + boost::lexical_cast<std::string>(time(NULL)) + "</LastUsed>\n"
 
											"</Account>\n"
 
										"</Lib>\n"
 
										"<UI>\n"
 
											"<API>\n"
 
											"<Authorizations>Spectrum</Authorizations>\n"
 
											"<BlockedPrograms></BlockedPrograms>\n"
 
											"</API>\n"
 
										"</UI>\n"
 
									"</config>\n";
 
			g_file_set_contents(std::string(std::string("/tmp/skype/") + m_username + "/" + m_username +"/config.xml").c_str(), config_xml.c_str(), -1, NULL);
 
			std::string db_path = std::string("/tmp/skype/") + m_username;
 
			char *db = (char *) malloc(db_path.size() + 1);
 
			strcpy(db, db_path.c_str());
 
			LOG4CXX_INFO(logger,  m_username << ": Spawning new Skype instance dbpath=" << db);
 
			gchar* argv[6] = {"skype", "--disable-cleanlooks", "--pipelogin", "--dbpath", db, 0};
 

	
 
			int fd;
 
			int fd_output;
 
			g_spawn_async_with_pipes(NULL,
 
				argv,
 
				NULL /*envp*/,
 
				G_SPAWN_SEARCH_PATH,
 
				NULL /*child_setup*/,
 
				NULL /*user_data*/,
 
				&m_pid /*child_pid*/,
 
				&fd,
 
				NULL,
 
				&fd_output,
 
				NULL /*error*/);
 
			std::string login_data = std::string(m_username + " " + m_password + "\n");
 
			LOG4CXX_INFO(logger,  m_username << ": Login data=" << login_data);
 
			write(fd, login_data.c_str(), login_data.size());
 
			close(fd);
 
			
 
			fcntl (fd_output, F_SETFL, O_NONBLOCK);
 

	
 
			free(db);
 

	
 
			sleep(2);
 

	
 
			GError *error = NULL;
 
			DBusObjectPathVTable vtable;
 

	
 
			//Initialise threading
 
			dbus_threads_init_default();
 
			
 
			if (m_connection == NULL)
 
			{
 
				m_connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
 
				if (m_connection == NULL && error != NULL)
 
				{
 
					LOG4CXX_INFO(logger,  m_username << ": DBUS Error: " << error->message);
 
					g_error_free(error);
 
					return;
 
				}
 
			}
 
			
 
			if (m_proxy == NULL)
 
			{
 
				m_proxy = dbus_g_proxy_new_for_name_owner (m_connection,
 
												"com.Skype.API",
 
												"/com/Skype",
 
												"com.Skype.API",
 
												&error);
 
				if (m_proxy == NULL && error != NULL)
 
				{
 
					LOG4CXX_INFO(logger,  m_username << ":" << error->message);
 
					g_error_free(error);
 
				}
 
				
 
				vtable.message_function = &skype_notify_handler;
 
				dbus_connection_register_object_path(dbus_g_connection_get_connection(m_connection), "/com/Skype/Client", &vtable, this);
 
			}
 

	
 
			int counter = 0;
 
			std::string re = "CONNSTATUS OFFLINE";
 
			while (re == "CONNSTATUS OFFLINE" || re.empty()) {
 
				sleep(1);
 
				gchar buffer[1024];
 
				int bytes_read;
 
				bytes_read = read (fd_output, buffer, 1023);
 
				if (bytes_read > 0) {
 
					buffer[bytes_read] = 0;
 
					np->handleDisconnected(m_user, 0, buffer);
 
					close(fd_output);
 
					logout();
 
					return;
 
				}
 
				re = send_command("NAME Spectrum");
 
				if (counter++ > 15)
 
					break;
 
			}
 

	
 
			close(fd_output);
 

	
 
			if (send_command("PROTOCOL 7") != "PROTOCOL 7") {
 
				np->handleDisconnected(m_user, 0, "Skype is not ready");
 
				logout();
 
				return;
 
			}
 
			
 
			np->handleConnected(m_user);
 

	
 
			std::map<std::string, std::string> group_map;
 
			std::string groups = send_command("SEARCH GROUPS CUSTOM");
 
			groups = groups.substr(groups.find(' ') + 1);
 
			std::vector<std::string> grps;
 
			boost::split(grps, groups, boost::is_any_of(","));
 
			BOOST_FOREACH(std::string grp, grps) {
 
				std::vector<std::string> data;
 
				std::string name = send_command("GET GROUP " + grp + " DISPLAYNAME");
 
				boost::split(data, name, boost::is_any_of(" "));
 
				name = name.substr(name.find("DISPLAYNAME") + 12);
 

	
 
				std::string users = send_command("GET GROUP " + data[1] + " USERS");
 
				users = name.substr(name.find("USERS") + 6);
 
				boost::split(data, users, boost::is_any_of(","));
 
				BOOST_FOREACH(std::string u, data) {
 
					group_map[u] = grp;
 
				}
 
			}
 

	
 
			std::string friends = send_command("GET AUTH_CONTACTS_PROFILES");
 

	
 
			char **full_friends_list = g_strsplit((strchr(friends.c_str(), ' ')+1), ";", 0);
 
			if (full_friends_list && full_friends_list[0])
 
			{
 
				//in the format of: username;full name;phone;office phone;mobile phone;
 
				//                  online status;friendly name;voicemail;mood
 
				// (comma-seperated lines, usernames can have comma's)
 

	
 
				for (int i=0; full_friends_list[i] && *full_friends_list[i] != '\0'; i+=8)
 
				{
 
					std::string buddy = full_friends_list[i];
 

	
 
					if (buddy[0] == ',') {
 
						buddy.erase(buddy.begin());
 
					}
 
					std::cout << "BUDDY '" << buddy << "'\n";
 
					std::string st = full_friends_list[i + 5];
 
					
 
					pbnetwork::StatusType status = getStatus(st);
 

	
 
					std::string alias = full_friends_list[i + 6];
 

	
 
					std::string mood_text = "";
 
					if (full_friends_list[i + 8] && *full_friends_list[i + 8] != '\0' && *full_friends_list[i + 8] != ',') {
 
						mood_text = full_friends_list[i + 8];
 
						i++;
 
					}
 

	
 
					std::vector<std::string> groups;
 
					groups.push_back(group_map[buddy]);
 
					np->handleBuddyChanged(m_user, buddy, alias, groups, status, mood_text);
 
				}
 
			}
 
			g_strfreev(full_friends_list);
 
			
 
			send_command("SET AUTOAWAY OFF");
 
		}
 

	
 
		void Skype::logout() {
 
			if (m_pid != 0) {
 
				send_command("SET USERSTATUS INVISIBLE");
 
				send_command("SET USERSTATUS OFFLINE");
 
				sleep(2);
 
				g_object_unref(m_proxy);
 
				LOG4CXX_INFO(logger,  m_username << ": Killing Skype instance");
 
				kill((int) m_pid, SIGTERM);
 
				m_pid = 0;
 
			}
 
		}
 

	
 
		std::string Skype::send_command(const std::string &message) {
 
			GError *error = NULL;
 
			gchar *str = NULL;
 
// 			int message_num;
 
// 			gchar error_return[30];
 
			
 
			if (!dbus_g_proxy_call (m_proxy, "Invoke", &error, G_TYPE_STRING, message.c_str(), G_TYPE_INVALID,
 
								G_TYPE_STRING, &str, G_TYPE_INVALID))
 
			{
 
					if (error && error->message)
 
					{
 
					LOG4CXX_INFO(logger,  m_username << ": DBUS Error: " << error->message);
 
					g_error_free(error);
 
				} else {
 
					LOG4CXX_INFO(logger,  m_username << ": DBUS no response");
 
				}
 
				
 
			}
 
			if (str != NULL)
 
			{
 
				LOG4CXX_INFO(logger,  m_username << ": DBUS:" << str);
 
			}
 
			return str ? std::string(str) : std::string();
 
		}
 

	
 
static void handle_skype_message(std::string &message, Skype *sk) {
 
	std::vector<std::string> cmd;
 
	boost::split(cmd, message, boost::is_any_of(" "));
 

	
 
	if (cmd[0] == "USER") {
 
		if (cmd[1] == sk->getUsername()) {
 
			return;
 
		}
 

	
 
		if (cmd[2] == "ONLINESTATUS") {
 
			if (cmd[3] == "SKYPEOUT" || cmd[3] == "UNKNOWN") {
 
				return;
 
			}
 
			else {
 
				pbnetwork::StatusType status = getStatus(cmd[3]);
 
				std::string mood_text = sk->send_command("GET USER " + cmd[1] + " MOOD_TEXT");
 
				mood_text = mood_text.substr(mood_text.find("MOOD_TEXT") + 10);
 

	
 
				std::string alias = sk->send_command("GET USER " + cmd[1] + " FULLNAME");
 
				alias = alias.substr(alias.find("FULLNAME") + 9);
 

	
 
				std::vector<std::string> groups;
 
				np->handleBuddyChanged(sk->getUser(), cmd[1], alias, groups, status, mood_text);
 
			}
 
		}
 
		else if (cmd[2] == "MOOD_TEXT") {
 
			std::string st = sk->send_command("GET USER " + cmd[1] + " ONLINESTATUS");
 
			st = st.substr(st.find("ONLINESTATUS") + 13);
 
			pbnetwork::StatusType status = getStatus(st);
 

	
 
			std::string mood_text = message.substr(message.find("MOOD_TEXT") + 10);
 

	
 
			std::vector<std::string> groups;
 
			np->handleBuddyChanged(sk->getUser(), cmd[1], "", groups, status, mood_text);
 
		}
 
		else if (cmd[2] == "BUDDYSTATUS" && cmd[3] == "3") {
 
			std::string st = sk->send_command("GET USER " + cmd[1] + " ONLINESTATUS");
 
			st = st.substr(st.find("ONLINESTATUS") + 13);
 
			pbnetwork::StatusType status = getStatus(st);
 

	
 
			std::string mood_text = message.substr(message.find("MOOD_TEXT") + 10);
 

	
 
			std::vector<std::string> groups;
 
			np->handleBuddyChanged(sk->getUser(), cmd[1], "", groups, status, mood_text);
 
		}
 
	}
 
	else if (cmd[0] == "CHATMESSAGE") {
 
		if (cmd[3] == "RECEIVED") {
 
			std::string body = sk->send_command("GET CHATMESSAGE " + cmd[1] + " BODY");
 
			body = body.substr(body.find("BODY") + 5);
 

	
 
			std::string chatname = sk->send_command("GET CHATMESSAGE " + cmd[1] + " CHATNAME");
 
			size_t start = chatname.find("$") + 1;
 
			size_t len = chatname.find(";") - start;
 
			std::string from = chatname.substr(start, len);
 

	
 
			std::string from_handle = sk->send_command("GET CHATMESSAGE " + cmd[1] + " FROM_HANDLE");
 
			from_handle = from_handle.substr(from_handle.find("FROM_HANDLE") + 12);
 

	
 
// 			if (from_handle != sk->getUsername()) {
 
				from = from_handle;
 
// 			}
 
			if (from_handle == sk->getUsername())
 
				return;
 

	
 
			np->handleMessage(sk->getUser(), from, body);
 
		}
 
	}
 
}
 

	
 
DBusHandlerResult skype_notify_handler(DBusConnection *connection, DBusMessage *message, gpointer user_data) {
 
	DBusMessageIter iterator;
 
	gchar *message_temp;
 
	DBusMessage *temp_message;
 
	
 
	temp_message = dbus_message_ref(message);
 
	dbus_message_iter_init(temp_message, &iterator);
 
	if (dbus_message_iter_get_arg_type(&iterator) != DBUS_TYPE_STRING)
 
	{
 
		dbus_message_unref(message);
 
		return (DBusHandlerResult) FALSE;
 
	}
 
	
 
	do {
 
		dbus_message_iter_get_basic(&iterator, &message_temp);
 
		std::string m(message_temp);
 
		LOG4CXX_INFO(logger,"DBUS message: " << m);
 
		handle_skype_message(m, (Skype *) user_data);
 
	} while(dbus_message_iter_has_next(&iterator) && dbus_message_iter_next(&iterator));
 
	
 
	dbus_message_unref(message);
 
	
 
	return DBUS_HANDLER_RESULT_HANDLED;
 
}
 

	
 
static void spectrum_sigchld_handler(int sig)
 
{
 
	int status;
 
	pid_t pid;
 

	
 
	do {
 
		pid = waitpid(-1, &status, WNOHANG);
 
	} while (pid != 0 && pid != (pid_t)-1);
 

	
 
	if ((pid == (pid_t) - 1) && (errno != ECHILD)) {
 
		char errmsg[BUFSIZ];
 
		snprintf(errmsg, BUFSIZ, "Warning: waitpid() returned %d", pid);
 
		perror(errmsg);
 
	}
 
}
 

	
 
static int create_socket(char *host, int portno) {
 
	struct sockaddr_in serv_addr;
 
	
 
	int m_sock = socket(AF_INET, SOCK_STREAM, 0);
 
	memset((char *) &serv_addr, 0, sizeof(serv_addr));
 
	serv_addr.sin_family = AF_INET;
 
	serv_addr.sin_port = htons(portno);
 

	
 
	hostent *hos;  // Resolve name
 
	if ((hos = gethostbyname(host)) == NULL) {
 
		// strerror() will not work for gethostbyname() and hstrerror() 
 
		// is supposedly obsolete
 
		exit(1);
 
	}
 
	serv_addr.sin_addr.s_addr = *((unsigned long *) hos->h_addr_list[0]);
 

	
 
	if (connect(m_sock, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
 
		close(m_sock);
 
		m_sock = 0;
 
	}
 

	
 
	int flags = fcntl(m_sock, F_GETFL);
 
	flags |= O_NONBLOCK;
 
	fcntl(m_sock, F_SETFL, flags);
 
	return m_sock;
 
}
 

	
 

	
 
static gboolean transportDataReceived(GIOChannel *source, GIOCondition condition, gpointer data) {
 
	char buffer[65535];
 
	char *ptr = buffer;
 
	ssize_t n = read(m_sock, ptr, sizeof(buffer));
 
	if (n <= 0) {
 
		LOG4CXX_INFO(logger, "Diconnecting from spectrum2 server");
 
		exit(errno);
 
	}
 
	std::string d = std::string(buffer, n);
 
	np->handleDataRead(d);
 
	return TRUE;
 
}
 

	
 
static void io_destroy(gpointer data) {
 
	exit(1);
 
}
 

	
 
int main(int argc, char **argv) {
 
	GError *error = NULL;
 
	GOptionContext *context;
 
	context = g_option_context_new("config_file_name or profile name");
 
	g_option_context_add_main_entries(context, options_entries, "");
 
	if (!g_option_context_parse (context, &argc, &argv, &error)) {
 
		std::cout << "option parsing failed: " << error->message << "\n";
 
		return -1;
 
	}
 

	
 
	if (ver) {
 
// 		std::cout << VERSION << "\n";
 
		std::cout << "verze\n";
 
		g_option_context_free(context);
 
		return 0;
 
	}
 

	
 
	if (argc != 2) {
 
#ifdef WIN32
 
		std::cout << "Usage: spectrum.exe <configuration_file.cfg>\n";
 
#else
 

	
 
#if GLIB_CHECK_VERSION(2,14,0)
 
	std::cout << g_option_context_get_help(context, FALSE, NULL);
 
#else
 
	std::cout << "Usage: spectrum <configuration_file.cfg>\n";
 
	std::cout << "See \"man spectrum\" for more info.\n";
 
#endif
 
		
 
#endif
 
	}
 
	else {
 
#ifndef WIN32
 
		signal(SIGPIPE, SIG_IGN);
 

	
 
		if (signal(SIGCHLD, spectrum_sigchld_handler) == SIG_ERR) {
 
			std::cout << "SIGCHLD handler can't be set\n";
 
			g_option_context_free(context);
 
			return -1;
 
		}
 
// 
 
// 		if (signal(SIGINT, spectrum_sigint_handler) == SIG_ERR) {
 
// 			std::cout << "SIGINT handler can't be set\n";
 
// 			g_option_context_free(context);
 
// 			return -1;
 
// 		}
 
// 
 
// 		if (signal(SIGTERM, spectrum_sigterm_handler) == SIG_ERR) {
 
// 			std::cout << "SIGTERM handler can't be set\n";
 
// 			g_option_context_free(context);
 
// 			return -1;
 
// 		}
 
// 
 
// 		struct sigaction sa;
 
// 		memset(&sa, 0, sizeof(sa)); 
 
// 		sa.sa_handler = spectrum_sighup_handler;
 
// 		if (sigaction(SIGHUP, &sa, NULL)) {
 
// 			std::cout << "SIGHUP handler can't be set\n";
 
// 			g_option_context_free(context);
 
// 			return -1;
 
//		}
 
#endif
 
		Config config;
 
		if (!config.load(argv[1])) {
 
			std::cout << "Can't open " << argv[1] << " configuration file.\n";
 
			return 1;
 
		}
 

	
 
		if (CONFIG_STRING(&config, "logging.backend_config").empty()) {
 
			LoggerPtr root = log4cxx::Logger::getRootLogger();
 
			root->addAppender(new ConsoleAppender(new PatternLayout("%d %-5p %c: %m%n")));
 
		}
 
		else {
 
			log4cxx::helpers::Properties p;
 
			log4cxx::helpers::FileInputStream *istream = new log4cxx::helpers::FileInputStream(CONFIG_STRING(&config, "logging.backend_config"));
 

	
 
			p.load(istream);
 
			p.setProperty("pid", boost::lexical_cast<std::string>(getpid()));
 
			log4cxx::PropertyConfigurator::configure(p);
 
		}
 

	
 
// 		initPurple(config);
 

	
 
		g_type_init();
 

	
 
		m_sock = create_socket(host, port);
 

	
 

	
 
	GIOChannel *channel;
 
	GIOCondition cond = (GIOCondition) READ_COND;
 
	channel = g_io_channel_unix_new(m_sock);
 
	g_io_add_watch_full(channel, G_PRIORITY_DEFAULT, cond, transportDataReceived, NULL, io_destroy);
 

	
 
		np = new SpectrumNetworkPlugin(&config, host, port);
 

	
 
		GMainLoop *m_loop;
 
		m_loop = g_main_loop_new(NULL, FALSE);
 

	
 
		if (m_loop) {
 
			g_main_loop_run(m_loop);
 
		}
 
	}
 

	
 
	g_option_context_free(context);
 
}
backends/skype/spectrumeventloop.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 "spectrumeventloop.h"
 
#include "glib.h"
 

	
 
#include <iostream>
 

	
 
#ifdef WITH_LIBEVENT
 
#include <event.h>
 
#endif
 

	
 

	
 
using namespace Swift;
 

	
 
// Fires the event's callback and frees the event
 
static gboolean processEvent(void *data) {
 
	Event *ev = (Event *) data;
 
	ev->callback();
 
	delete ev;
 
	return FALSE;
 
}
 

	
 
SpectrumEventLoop::SpectrumEventLoop() : m_isRunning(false) {
 
	m_loop = NULL;
 
	if (true) {
 
		m_loop = g_main_loop_new(NULL, FALSE);
 
	}
 
#ifdef WITH_LIBEVENT
 
	else {
 
		/*struct event_base *base = (struct event_base *)*/
 
		event_init();
 
	}
 
#endif
 
}
 

	
 
SpectrumEventLoop::~SpectrumEventLoop() {
 
	stop();
 
}
 

	
 
void SpectrumEventLoop::run() {
 
	m_isRunning = true;
 
	if (m_loop) {
 
		g_main_loop_run(m_loop);
 
	}
 
#ifdef WITH_LIBEVENT
 
	else {
 
		event_loop(0);
 
	}
 
#endif
 
}
 

	
 
void SpectrumEventLoop::stop() {
 
	std::cout << "stopped loop\n";
 
	if (!m_isRunning)
 
		return;
 
	if (m_loop) {
 
		g_main_loop_quit(m_loop);
 
		g_main_loop_unref(m_loop);
 
		m_loop = NULL;
 
	}
 
#ifdef WITH_LIBEVENT
 
	else {
 
		event_loopexit(NULL);
 
	}
 
#endif
 
}
 

	
 
void SpectrumEventLoop::post(const Event& event) {
 
	// pass copy of event to main thread
 
	Event *ev = new Event(event.owner, event.callback);
 
	g_timeout_add(0, processEvent, ev);
 
}
backends/skype/spectrumeventloop.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
 
 */
 

	
 
#ifndef SPECTRUM_EVENT_LOOP_H
 
#define SPECTRUM_EVENT_LOOP_H
 

	
 
#include <vector>
 
#include "Swiften/EventLoop/EventLoop.h"
 
#include "glib.h"
 

	
 
// Event loop implementation for Spectrum
 
class SpectrumEventLoop : public Swift::EventLoop {
 
	public:
 
		// Creates event loop according to CONFIG().eventloop settings.
 
		SpectrumEventLoop();
 
		~SpectrumEventLoop();
 

	
 
		// Executes the eventloop.
 
		void run();
 

	
 
		// Stops tht eventloop.
 
		void stop();
 

	
 
		// Posts new Swift::Event to main thread.
 
		virtual void post(const Swift::Event& event);
 

	
 
	private:
 
		bool m_isRunning;
 
		GMainLoop *m_loop;
 
};
 

	
 
#endif
spectrum/src/sample2.cfg
Show inline comments
 
@@ -37,12 +37,14 @@ backend_host = localhost
 
# Number of users per one legacy network backend.
 
users_per_backend=10
 

	
 
# Full path to backend binary.
 
backend=/usr/bin/spectrum2_libpurple_backend
 
#backend=/usr/bin/spectrum2_libircclient-qt_backend
 
# For skype:
 
#backend=/usr/bin/setsid /usr/bin/xvfb-run -n BACKEND_ID -s "-screen 0 10x10x8" -f /tmp/x-skype-gw /usr/bin/spectrum2_skype_backend
 

	
 
# Libpurple protocol-id for spectrum_libpurple_backend
 
protocol=prpl-jabber
 
#protocol=prpl-msn
 
#protocol=prpl-icq
 

	
0 comments (0 inline, 0 general)