diff --git a/CMakeLists.txt b/CMakeLists.txt index 3f4802e1..e798a842 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,7 +38,7 @@ set(Boost_USE_STATIC_LIBS ON) set(Boost_USE_MULTITHREADED ON) set(Boost_USE_STATIC_RUNTIME OFF) endif() -find_package(Boost COMPONENTS program_options date_time system filesystem regex thread signals REQUIRED) +find_package(Boost COMPONENTS program_options date_time system filesystem regex signals REQUIRED) message( STATUS "Found Boost: ${Boost_LIBRARIES}, ${Boost_INCLUDE_DIR}") set(Protobuf_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") @@ -56,6 +56,9 @@ find_package(event) set(pqxx_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") find_package(pqxx) +set(dbus_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") +find_package(dbus) + find_package(Doxygen) INCLUDE(FindQt4) @@ -146,12 +149,21 @@ if (PROTOBUF_FOUND) endif() message("Frotz plugin : yes") + message("SMSTools3 plugin : yes") + + if(${LIBDBUSGLIB_FOUND}) + message("Skype plugin : yes") + include_directories(${LIBDBUSGLIB_INCLUDE_DIRS}) + else() + message("Skype plugin : no (install dbus-glib-devel)") + endif() else() message("Network plugins : no (install libprotobuf-dev)") message("Libpurple plugin : no (install libpurple and libprotobuf-dev)") message("IRC plugin : no (install libircclient-qt and libprotobuf-dev)") message("Frotz plugin : no (install libprotobuf-dev)") + message("SMSTools3 plugin : no (install libprotobuf-dev)") endif() if (LOG4CXX_FOUND) diff --git a/ChangeLog b/ChangeLog index 36a65268..97aaa33c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,11 +1,27 @@ Version 2.0.0-beta (X-X-X): General: + * Added PostreSQL support (thanks to Jadestorm). + * Send presences only "from" bare JID (fixed bug with buddies appearing + twice in the roster and potential unregistering issues). + * Fixed potential MySQL/SQLite3 deadlocks. + * Fixed disconnecting in server-mode when client does not send unavailable + presence before disconnection. + * Fixed crash in server-mode when client send its custom jabber:iq:storage + payload. * Fixed registration from Pidgin. * Unsubscribe presence sent to some buddy doesn't disconnect the account. * Remote Roster requests are not sent to resources, but to bare JID. * Added automatic reconnection in case of non-fatal error. * Added more error messages. + Skype: + * Initial support for Skype added, read more on + http://spectrum.im/projects/spectrum/wiki/Spectrum_2_Admin_-_Skype_backend + + SMSTools3: + * Initial support for SMSTools3, read more on + http://spectrum.im/projects/spectrum/wiki/Spectrum_2_Admin_-_SMSTools3_backend + version 2.0.0 alpha (2011-12-06): General: * First Spectrum 2.0.0 alpha release, check more on diff --git a/backends/CMakeLists.txt b/backends/CMakeLists.txt index b8278015..543b60b8 100644 --- a/backends/CMakeLists.txt +++ b/backends/CMakeLists.txt @@ -7,9 +7,13 @@ if (PROTOBUF_FOUND) ADD_SUBDIRECTORY(libcommuni) endif() + ADD_SUBDIRECTORY(smstools3) + if (NOT WIN32) ADD_SUBDIRECTORY(frotz) -# ADD_SUBDIRECTORY(skype) + if (${LIBDBUSGLIB_FOUND}) + ADD_SUBDIRECTORY(skype) + endif() endif() endif() diff --git a/backends/frotz/CMakeLists.txt b/backends/frotz/CMakeLists.txt index 7ce14f86..f9bc283a 100644 --- a/backends/frotz/CMakeLists.txt +++ b/backends/frotz/CMakeLists.txt @@ -6,7 +6,7 @@ FILE(GLOB SRC *.c *.cpp) ADD_EXECUTABLE(spectrum2_frotz_backend ${SRC}) -target_link_libraries(spectrum2_frotz_backend transport pthread transport-plugin ${Boost_LIBRARIES} ${SWIFTEN_LIBRARY} ${LOG4CXX_LIBRARIES}) +target_link_libraries(spectrum2_frotz_backend transport pthread ${Boost_LIBRARIES} ${SWIFTEN_LIBRARY} ${LOG4CXX_LIBRARIES}) INSTALL(TARGETS spectrum2_frotz_backend RUNTIME DESTINATION bin) diff --git a/backends/libcommuni/CMakeLists.txt b/backends/libcommuni/CMakeLists.txt index a2a14730..38e7507b 100644 --- a/backends/libcommuni/CMakeLists.txt +++ b/backends/libcommuni/CMakeLists.txt @@ -4,7 +4,7 @@ FILE(GLOB HEADERS *.h) QT4_WRAP_CPP(SRC ${HEADERS}) ADD_EXECUTABLE(spectrum2_libcommuni_backend ${SRC}) -target_link_libraries(spectrum2_libcommuni_backend ${IRC_LIBRARY} ${QT_LIBRARIES} transport-plugin transport pthread) +target_link_libraries(spectrum2_libcommuni_backend ${IRC_LIBRARY} ${QT_LIBRARIES} transport pthread) INSTALL(TARGETS spectrum2_libcommuni_backend RUNTIME DESTINATION bin) diff --git a/backends/skype/CMakeLists.txt b/backends/skype/CMakeLists.txt index fa6e9266..d27b4296 100644 --- a/backends/skype/CMakeLists.txt +++ b/backends/skype/CMakeLists.txt @@ -1,13 +1,9 @@ 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) +target_link_libraries(spectrum2_skype_backend ${GLIB2_LIBRARIES} ${EVENT_LIBRARIES} transport pthread ${LIBDBUSGLIB_LIBRARIES}) INSTALL(TARGETS spectrum2_skype_backend RUNTIME DESTINATION bin) diff --git a/backends/skype/geventloop.cpp b/backends/skype/geventloop.cpp deleted file mode 100644 index d00d7a56..00000000 --- a/backends/skype/geventloop.cpp +++ /dev/null @@ -1,248 +0,0 @@ -/** - * XMPP - libpurple transport - * - * Copyright (C) 2009, Jan Kaluza - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#include "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 -} diff --git a/backends/skype/geventloop.h b/backends/skype/geventloop.h deleted file mode 100644 index 3febd65e..00000000 --- a/backends/skype/geventloop.h +++ /dev/null @@ -1,33 +0,0 @@ -/** - * XMPP - libpurple transport - * - * Copyright (C) 2009, Jan Kaluza - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#ifndef _HI_EVENTLOOP_H -#define _HI_EVENTLOOP_H - -#include -#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 diff --git a/backends/skype/main.cpp b/backends/skype/main.cpp index 5a135410..c82792ce 100644 --- a/backends/skype/main.cpp +++ b/backends/skype/main.cpp @@ -12,9 +12,7 @@ #include "transport/rostermanager.h" #include "transport/conversation.h" #include "transport/networkplugin.h" -#include "spectrumeventloop.h" #include -#include "geventloop.h" #include "log4cxx/logger.h" #include "log4cxx/consoleappender.h" #include "log4cxx/patternlayout.h" @@ -96,6 +94,9 @@ class Skype { return m_username; } + bool createDBusProxy(); + bool loadSkypeBuddies(); + private: std::string m_username; std::string m_password; @@ -103,12 +104,16 @@ class Skype { DBusGConnection *m_connection; DBusGProxy *m_proxy; std::string m_user; + int m_timer; + int m_counter; + int fd_output; }; class SpectrumNetworkPlugin : public NetworkPlugin { public: SpectrumNetworkPlugin(Config *config, const std::string &host, int port) : NetworkPlugin() { this->config = config; + LOG4CXX_INFO(logger, "Starting the backend."); } ~SpectrumNetworkPlugin() { @@ -120,7 +125,7 @@ class SpectrumNetworkPlugin : public NetworkPlugin { 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); + LOG4CXX_INFO(logger, "Creating account with name '" << name << "'"); Skype *skype = new Skype(user, name, password); m_sessions[user] = skype; @@ -297,237 +302,288 @@ class SpectrumNetworkPlugin : public NetworkPlugin { }; - 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); - } +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; + m_timer = -1; + m_counter = 0; +} - std::string shared_xml = "\n" - "(time(NULL)) + ".0\">\n" - "\n" - "2\n" - "en\n" - "\n" - "\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 = "\n" - "(time(NULL)) + ".0\">\n" - "\n" - "\n" - "30000000\n" - "300000000\n" - "" + boost::lexical_cast(time(NULL)) + "\n" - "\n" - "\n" - "\n" - "\n" - "Spectrum\n" - "\n" - "\n" - "\n" - "\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}; +static gboolean load_skype_buddies(gpointer data) { + Skype *skype = (Skype *) data; + return skype->loadSkypeBuddies(); +} - 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); +bool Skype::createDBusProxy() { + if (m_proxy == NULL) { + LOG4CXX_INFO(logger, "Creating DBus proxy for com.Skype.Api."); + m_counter++; - free(db); + GError *error = 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); - 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"); + if (m_counter == 15) { + np->handleDisconnected(m_user, 0, error->message); logout(); - return; + g_error_free(error); + return FALSE; } - - np->handleConnected(m_user); - - std::map group_map; - std::string groups = send_command("SEARCH GROUPS CUSTOM"); - groups = groups.substr(groups.find(' ') + 1); - std::vector grps; - boost::split(grps, groups, boost::is_any_of(",")); - BOOST_FOREACH(std::string grp, grps) { - std::vector 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 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"); + g_error_free(error); } - 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; - } - } + if (m_proxy) { + LOG4CXX_INFO(logger, "Proxy created."); + DBusObjectPathVTable vtable; + vtable.message_function = &skype_notify_handler; + dbus_connection_register_object_path(dbus_g_connection_get_connection(m_connection), "/com/Skype/Client", &vtable, this); - std::string Skype::send_command(const std::string &message) { - GError *error = NULL; - gchar *str = NULL; + m_counter = 0; + m_timer = g_timeout_add_seconds(1, load_skype_buddies, this); + return FALSE; + } + return TRUE; + } + return FALSE; +} + +static gboolean create_dbus_proxy(gpointer data) { + Skype *skype = (Skype *) data; + return skype->createDBusProxy(); +} + +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 = "\n" + "(time(NULL)) + ".0\">\n" + "\n" + "2\n" + "en\n" + "\n" + "\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 = "\n" + "(time(NULL)) + ".0\">\n" + "\n" + "\n" + "30000000\n" + "300000000\n" + "" + boost::lexical_cast(time(NULL)) + "\n" + "\n" + "\n" + "\n" + "\n" + "Spectrum\n" + "\n" + "\n" + "\n" + "\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; + 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); + + //Initialise threading + dbus_threads_init_default(); + + if (m_connection == NULL) + { + LOG4CXX_INFO(logger, "Creating DBus connection."); + GError *error = 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; + } + } + + m_timer = g_timeout_add_seconds(1, create_dbus_proxy, this); +} + +bool Skype::loadSkypeBuddies() { +// std::string re = "CONNSTATUS OFFLINE"; +// while (re == "CONNSTATUS OFFLINE" || re.empty()) { +// sleep(1); + + gchar buffer[1024]; + int 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 FALSE; + } + + std::string re = send_command("NAME Spectrum"); + if (m_counter++ > 15) { + np->handleDisconnected(m_user, 0, ""); + close(fd_output); + logout(); + return FALSE; + } + + if (re.empty() || re == "CONNSTATUS OFFLINE") { + return TRUE; + } + + close(fd_output); + + if (send_command("PROTOCOL 7") != "PROTOCOL 7") { + np->handleDisconnected(m_user, 0, "Skype is not ready"); + logout(); + return FALSE; + } + + np->handleConnected(m_user); + + std::map group_map; + std::string groups = send_command("SEARCH GROUPS CUSTOM"); + groups = groups.substr(groups.find(' ') + 1); + std::vector grps; + boost::split(grps, groups, boost::is_any_of(",")); + BOOST_FOREACH(std::string grp, grps) { + std::vector 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()); + } + + if (buddy.rfind(",") != std::string::npos) { + buddy = buddy.substr(buddy.rfind(",")); + } + + if (buddy[0] == ',') { + buddy.erase(buddy.begin()); + } + + LOG4CXX_INFO(logger, "Got buddy " << buddy); + 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]; + } + + std::vector groups; + if (group_map.find(buddy) != group_map.end()) { + 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"); + send_command("SET USERSTATUS ONLINE"); + return FALSE; +} + +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 (!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) { - 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(); + 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 cmd; boost::split(cmd, message, boost::is_any_of(" ")); @@ -684,6 +740,10 @@ static void io_destroy(gpointer data) { exit(1); } +static void log_glib_error(const gchar *string) { + LOG4CXX_ERROR(logger, "GLIB ERROR:" << string); +} + int main(int argc, char **argv) { GError *error = NULL; GOptionContext *context; @@ -771,9 +831,10 @@ int main(int argc, char **argv) { m_sock = create_socket(host, port); + g_set_printerr_handler(log_glib_error); GIOChannel *channel; - GIOCondition cond = (GIOCondition) READ_COND; + GIOCondition cond = (GIOCondition) G_IO_IN; channel = g_io_channel_unix_new(m_sock); g_io_add_watch_full(channel, G_PRIORITY_DEFAULT, cond, transportDataReceived, NULL, io_destroy); diff --git a/backends/skype/spectrumeventloop.cpp b/backends/skype/spectrumeventloop.cpp deleted file mode 100644 index 20286e58..00000000 --- a/backends/skype/spectrumeventloop.cpp +++ /dev/null @@ -1,90 +0,0 @@ -/** - * XMPP - libpurple transport - * - * Copyright (C) 2009, Jan Kaluza - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#include "spectrumeventloop.h" -#include "glib.h" - -#include - -#ifdef WITH_LIBEVENT -#include -#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); -} diff --git a/backends/skype/spectrumeventloop.h b/backends/skype/spectrumeventloop.h deleted file mode 100644 index 7e811c89..00000000 --- a/backends/skype/spectrumeventloop.h +++ /dev/null @@ -1,49 +0,0 @@ -/** - * XMPP - libpurple transport - * - * Copyright (C) 2009, Jan Kaluza - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#ifndef SPECTRUM_EVENT_LOOP_H -#define SPECTRUM_EVENT_LOOP_H - -#include -#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 diff --git a/backends/smstools3/CMakeLists.txt b/backends/smstools3/CMakeLists.txt new file mode 100644 index 00000000..a557e243 --- /dev/null +++ b/backends/smstools3/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 2.6) + +FILE(GLOB SRC *.c *.cpp) + +ADD_EXECUTABLE(spectrum2_smstools3_backend ${SRC}) + +target_link_libraries(spectrum2_smstools3_backend transport pthread ${Boost_LIBRARIES} ${SWIFTEN_LIBRARY} ${LOG4CXX_LIBRARIES}) + +INSTALL(TARGETS spectrum2_smstools3_backend RUNTIME DESTINATION bin) + diff --git a/backends/smstools3/main.cpp b/backends/smstools3/main.cpp new file mode 100644 index 00000000..1b0bb044 --- /dev/null +++ b/backends/smstools3/main.cpp @@ -0,0 +1,397 @@ +/* + * Copyright (C) 2008-2009 J-P Nurmi jpnurmi@gmail.com + * + * This example is free, and not covered by LGPL license. There is no + * restriction applied to their modification, redistribution, using and so on. + * You can study them, modify them, use them in your own program - either + * completely or partially. By using it you may give me some credits in your + * program, but you don't have to. + */ + +#include "transport/config.h" +#include "transport/networkplugin.h" +#include "transport/sqlite3backend.h" +#include "transport/mysqlbackend.h" +#include "transport/pqxxbackend.h" +#include "transport/storagebackend.h" +#include "Swiften/Swiften.h" +#include +#include "unistd.h" +#include "signal.h" +#include "sys/wait.h" +#include "sys/signal.h" +#include +#include + +Swift::SimpleEventLoop *loop_; + +#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 "log4cxx/helpers/transcoder.h" +#include +#include + +using namespace boost::filesystem; + +using namespace boost::program_options; +using namespace Transport; + +using namespace log4cxx; + +static LoggerPtr logger = log4cxx::Logger::getLogger("SMSNetworkPlugin"); + +#define INTERNAL_USER "/sms@backend@internal@user" + +class SMSNetworkPlugin; +SMSNetworkPlugin * np = NULL; +StorageBackend *storageBackend; + +class SMSNetworkPlugin : public NetworkPlugin { + public: + Swift::BoostNetworkFactories *m_factories; + Swift::BoostIOServiceThread m_boostIOServiceThread; + boost::shared_ptr m_conn; + Swift::Timer::ref m_timer; + int m_internalUser; + + SMSNetworkPlugin(Config *config, Swift::SimpleEventLoop *loop, const std::string &host, int port) : NetworkPlugin() { + this->config = config; + m_factories = new Swift::BoostNetworkFactories(loop); + m_conn = m_factories->getConnectionFactory()->createConnection(); + m_conn->onDataRead.connect(boost::bind(&SMSNetworkPlugin::_handleDataRead, this, _1)); + m_conn->connect(Swift::HostAddressPort(Swift::HostAddress(host), port)); +// m_conn->onConnectFinished.connect(boost::bind(&FrotzNetworkPlugin::_handleConnected, this, _1)); +// m_conn->onDisconnected.connect(boost::bind(&FrotzNetworkPlugin::handleDisconnected, this)); + + LOG4CXX_INFO(logger, "Starting the plugin."); + + m_timer = m_factories->getTimerFactory()->createTimer(5000); + m_timer->onTick.connect(boost::bind(&SMSNetworkPlugin::handleSMSDir, this)); + m_timer->start(); + + // We're reusing our database model here. Buddies of user with JID INTERNAL_USER are there + // to match received GSM messages from number N with the XMPP users who sent message to number N. + // BuddyName = GSM number + // Alias = XMPP user JID to which the messages from this number is sent to. + // TODO: This should be per Modem!!! + UserInfo info; + info.jid = INTERNAL_USER; + info.password = ""; + storageBackend->setUser(info); + storageBackend->getUser(INTERNAL_USER, info); + m_internalUser = info.id; + } + + + void handleSMS(const std::string &sms) { + LOG4CXX_INFO(logger, "Handling SMS " << sms << ".") + std::ifstream t(sms.c_str()); + std::string str; + + t.seekg(0, std::ios::end); + str.reserve(t.tellg()); + t.seekg(0, std::ios::beg); + + str.assign((std::istreambuf_iterator(t)), std::istreambuf_iterator()); + + std::string from = ""; + std::string msg = ""; + while(str.find("\n") != std::string::npos) { + std::string line = str.substr(0, str.find("\n")); + if (line.find("From: ") == 0) { + from = line.substr(strlen("From: ")); + } + else if (line.empty()) { + msg = str.substr(1); + break; + } + str = str.substr(str.find("\n") + 1); + } + + std::list roster; + storageBackend->getBuddies(m_internalUser, roster); + + std::string to; + BOOST_FOREACH(BuddyInfo &b, roster) { + if (b.legacyName == from) { + to = b.alias; + } + } + + if (to.empty()) { + LOG4CXX_WARN(logger, "Received SMS from " << from << ", but this number is not associated with any XMPP user."); + } + + LOG4CXX_INFO(logger, "Forwarding SMS from " << from << " to " << to << "."); + handleMessage(to, from, msg); + } + + void handleSMSDir() { + std::string dir = "/var/spool/sms/incoming/"; + if (config->getUnregistered().find("backend.incoming_dir") != config->getUnregistered().end()) { + dir = config->getUnregistered().find("backend.incoming_dir")->second; + } + LOG4CXX_INFO(logger, "Checking directory " << dir << " for incoming SMS."); + + path p(dir); + directory_iterator end_itr; + for (directory_iterator itr(p); itr != end_itr; ++itr) { + + try { + if (is_regular(itr->path())) { + handleSMS(itr->path().string()); + remove(itr->path()); + } + } + catch (const filesystem_error& ex) { + LOG4CXX_ERROR(logger, "Error when removing the SMS: " << ex.what() << "."); + } + } + m_timer->start(); + } + + void sendSMS(const std::string &to, const std::string &msg) { + // TODO: Probably + std::string data = "To: " + to + "\n"; + data += "\n"; + data += msg; + + // generate random string here... + std::string bucket = "abcdefghijklmnopqrstuvwxyz"; + std::string uuid; + for (int i = 0; i < 10; i++) { + uuid += bucket[rand() % bucket.size()]; + } + std::ofstream myfile; + myfile.open (std::string("/var/spool/sms/outgoing/spectrum." + uuid).c_str()); + myfile << data; + myfile.close(); + } + + void sendData(const std::string &string) { + m_conn->write(Swift::createSafeByteArray(string)); + } + + void _handleDataRead(boost::shared_ptr data) { + std::string d(data->begin(), data->end()); + handleDataRead(d); + } + + void handleLoginRequest(const std::string &user, const std::string &legacyName, const std::string &password) { + UserInfo info; + if (!storageBackend->getUser(user, info)) { + handleDisconnected(user, 0, "Not registered user."); + return; + } + std::list roster; + storageBackend->getBuddies(info.id, roster); + + // Send available presence to every number in the roster. + BOOST_FOREACH(BuddyInfo &b, roster) { + handleBuddyChanged(user, b.legacyName, b.alias, b.groups, pbnetwork::STATUS_ONLINE); + } + + np->handleConnected(user); + } + + void handleLogoutRequest(const std::string &user, const std::string &legacyName) { + } + + void handleMessageSendRequest(const std::string &user, const std::string &legacyName, const std::string &message, const std::string &xhtml = "") { + // Remove trailing +, because smstools doesn't use it in "From: " field for received messages. + std::string n = legacyName; + if (n.find("+") == 0) { + n = n.substr(1); + } + + // Create GSM Number - XMPP user pair to match the potential response and send it to the proper JID. + BuddyInfo info; + info.legacyName = n; + info.alias = user; + info.id = -1; + info.subscription = "both"; + info.flags = 0; + storageBackend->addBuddy(m_internalUser, info); + + LOG4CXX_INFO(logger, "Sending SMS from " << user << " to " << n << "."); + sendSMS(n, message); + } + + void handleJoinRoomRequest(const std::string &user, const std::string &room, const std::string &nickname, const std::string &password) { + } + + void handleLeaveRoomRequest(const std::string &user, const std::string &room) { + } + + void handleBuddyUpdatedRequest(const std::string &user, const std::string &buddyName, const std::string &alias, const std::vector &groups) { + LOG4CXX_INFO(logger, user << ": Added buddy " << buddyName << "."); + handleBuddyChanged(user, buddyName, alias, groups, pbnetwork::STATUS_ONLINE); + } + + void handleBuddyRemovedRequest(const std::string &user, const std::string &buddyName, const std::vector &groups) { + + } + + + private: + + Config *config; +}; + +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); + } +} + + +int main (int argc, char* argv[]) { + std::string host; + int port; + + if (signal(SIGCHLD, spectrum_sigchld_handler) == SIG_ERR) { + std::cout << "SIGCHLD handler can't be set\n"; + return -1; + } + + boost::program_options::options_description desc("Usage: spectrum [OPTIONS] \nAllowed options"); + desc.add_options() + ("host,h", value(&host), "host") + ("port,p", value(&port), "port") + ; + try + { + boost::program_options::variables_map vm; + boost::program_options::store(boost::program_options::parse_command_line(argc, argv, desc), vm); + boost::program_options::notify(vm); + } + catch (std::runtime_error& e) + { + std::cout << desc << "\n"; + exit(1); + } + catch (...) + { + std::cout << desc << "\n"; + exit(1); + } + + + if (argc < 5) { + return 1; + } + +// QStringList channels; +// for (int i = 3; i < argc; ++i) +// { +// channels.append(argv[i]); +// } +// +// MyIrcSession session; +// session.setNick(argv[2]); +// session.setAutoJoinChannels(channels); +// session.connectToServer(argv[1], 6667); + + Config config; + if (!config.load(argv[5])) { + std::cerr << "Can't open " << argv[1] << " configuration file.\n"; + return 1; + } + + if (CONFIG_STRING(&config, "logging.backend_config").empty()) { + LoggerPtr root = log4cxx::Logger::getRootLogger(); +#ifndef _MSC_VER + root->addAppender(new ConsoleAppender(new PatternLayout("%d %-5p %c: %m%n"))); +#else + root->addAppender(new ConsoleAppender(new PatternLayout(L"%d %-5p %c: %m%n"))); +#endif + } + else { + log4cxx::helpers::Properties p; + log4cxx::helpers::FileInputStream *istream = new log4cxx::helpers::FileInputStream(CONFIG_STRING(&config, "logging.backend_config")); + p.load(istream); + LogString pid, jid; + log4cxx::helpers::Transcoder::decode(boost::lexical_cast(getpid()), pid); + log4cxx::helpers::Transcoder::decode(CONFIG_STRING(&config, "service.jid"), jid); +#ifdef _MSC_VER + p.setProperty(L"pid", pid); + p.setProperty(L"jid", jid); +#else + p.setProperty("pid", pid); + p.setProperty("jid", jid); +#endif + log4cxx::PropertyConfigurator::configure(p); + } + +#ifdef WITH_SQLITE + if (CONFIG_STRING(&config, "database.type") == "sqlite3") { + storageBackend = new SQLite3Backend(&config); + if (!storageBackend->connect()) { + std::cerr << "Can't connect to database. Check the log to find out the reason.\n"; + return -1; + } + } +#else + if (CONFIG_STRING(&config, "database.type") == "sqlite3") { + std::cerr << "Spectrum2 is not compiled with mysql backend.\n"; + return -2; + } +#endif + +#ifdef WITH_MYSQL + if (CONFIG_STRING(&config, "database.type") == "mysql") { + storageBackend = new MySQLBackend(&config); + if (!storageBackend->connect()) { + std::cerr << "Can't connect to database. Check the log to find out the reason.\n"; + return -1; + } + } +#else + if (CONFIG_STRING(&config, "database.type") == "mysql") { + std::cerr << "Spectrum2 is not compiled with mysql backend.\n"; + return -2; + } +#endif + +#ifdef WITH_PQXX + if (CONFIG_STRING(&config, "database.type") == "pqxx") { + storageBackend = new PQXXBackend(&config); + if (!storageBackend->connect()) { + std::cerr << "Can't connect to database. Check the log to find out the reason.\n"; + return -1; + } + } +#else + if (CONFIG_STRING(&config, "database.type") == "pqxx") { + std::cerr << "Spectrum2 is not compiled with pqxx backend.\n"; + return -2; + } +#endif + + if (CONFIG_STRING(&config, "database.type") != "mysql" && CONFIG_STRING(&config, "database.type") != "sqlite3" + && CONFIG_STRING(&config, "database.type") != "pqxx" && CONFIG_STRING(&config, "database.type") != "none") { + std::cerr << "Unknown storage backend " << CONFIG_STRING(&config, "database.type") << "\n"; + return -2; + } + + Swift::SimpleEventLoop eventLoop; + loop_ = &eventLoop; + np = new SMSNetworkPlugin(&config, &eventLoop, host, port); + loop_->run(); + + return 0; +} diff --git a/cmake_modules/dbusConfig.cmake b/cmake_modules/dbusConfig.cmake new file mode 100644 index 00000000..532e37da --- /dev/null +++ b/cmake_modules/dbusConfig.cmake @@ -0,0 +1,53 @@ +# - Try to find LIBDBUS GLIB Bindings +# Find LIBDBUSGLIB headers, libraries and the answer to all questions. +# +# LIBDBUSGLIB_FOUND True if libdbus-glib got found +# LIBDBUSGLIB_INCLUDE_DIRS Location of libdbus-glib headers +# LIBDBUSGLIB_LIBRARIES List of libraries to use libdbus-glib +# +# Copyright (c) 2008 Bjoern Ricks +# +# Redistribution and use is allowed according to the terms of the New +# BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. +# + +INCLUDE( FindPkgConfig ) + +IF ( LibDbusGlib_FIND_REQUIRED ) + SET( _pkgconfig_REQUIRED "REQUIRED" ) +ELSE( LibDbusGlib_FIND_REQUIRED ) + SET( _pkgconfig_REQUIRED "" ) +ENDIF ( LibDbusGlib_FIND_REQUIRED ) + +IF ( LIBDBUSGLIB_MIN_VERSION ) + PKG_SEARCH_MODULE( LIBDBUSGLIB ${_pkgconfig_REQUIRED} dbus-glib-1>=${LIBDBUSGLIB_MIN_VERSION} ) +ELSE ( LIBDBUSGLIB_MIN_VERSION ) + PKG_SEARCH_MODULE( LIBDBUSGLIB ${_pkgconfig_REQUIRED} dbus-glib-1 ) +ENDIF ( LIBDBUSGLIB_MIN_VERSION ) + + +IF( NOT LIBDBUSGLIB_FOUND AND NOT PKG_CONFIG_FOUND ) + FIND_PATH( LIBDBUSGLIB_INCLUDE_DIRS dbus/dbus-glib.h PATH_SUFFIXES dbus-1.0 dbus ) + FIND_LIBRARY( LIBDBUSGLIB_LIBRARIES dbus-glib dbus-glib-1) + + # Report results + IF ( LIBDBUSGLIB_LIBRARIES AND LIBDBUSGLIB_INCLUDE_DIRS ) + SET( LIBDBUSGLIB_FOUND 1 ) + IF ( NOT LIBDBUSGLIB_FIND_QUIETLY ) + MESSAGE( STATUS "Found libdbus-glib: ${LIBDBUSGLIB_LIBRARIES} ${LIBDBUSGLIB_INCLUDE_DIRS}" ) + ENDIF ( NOT LIBDBUSGLIB_FIND_QUIETLY ) + ELSE ( LIBDBUSGLIB_LIBRARIES AND LIBDBUSGLIB_INCLUDE_DIRS ) + IF ( LIBDBUSGLIB_FIND_REQUIRED ) + MESSAGE( SEND_ERROR "Could NOT find libdbus-glib" ) + ELSE ( LIBDBUSGLIB_FIND_REQUIRED ) + IF ( NOT LIBDBUSGLIB_FIND_QUIETLY ) + MESSAGE( STATUS "Could NOT find libdbus-glib" ) + ENDIF ( NOT LIBDBUSGLIB_FIND_QUIETLY ) + ENDIF ( LIBDBUSGLIB_FIND_REQUIRED ) + ENDIF ( LIBDBUSGLIB_LIBRARIES AND LIBDBUSGLIB_INCLUDE_DIRS ) +else() + MESSAGE( STATUS "Found libdbus-glib: ${LIBDBUSGLIB_LIBRARIES} ${LIBDBUSGLIB_INCLUDE_DIRS}" ) +ENDIF() + +MARK_AS_ADVANCED( LIBDBUSGLIB_LIBRARIES LIBDBUSGLIB_INCLUDE_DIRS ) \ No newline at end of file diff --git a/include/Swiften/TLS/OpenSSL/OpenSSLServerContext.cpp b/include/Swiften/TLS/OpenSSL/OpenSSLServerContext.cpp index ecc60733..ebf798be 100644 --- a/include/Swiften/TLS/OpenSSL/OpenSSLServerContext.cpp +++ b/include/Swiften/TLS/OpenSSL/OpenSSLServerContext.cpp @@ -14,6 +14,13 @@ #include #include +#include "log4cxx/logger.h" +#include "log4cxx/consoleappender.h" +#include "log4cxx/patternlayout.h" +#include "log4cxx/propertyconfigurator.h" +using namespace log4cxx; +static LoggerPtr logger = Logger::getLogger("OpenSSLServerContext"); + #include "Swiften/TLS/OpenSSL/OpenSSLServerContext.h" #include "Swiften/TLS/OpenSSL/OpenSSLCertificate.h" @@ -179,7 +186,7 @@ void OpenSSLServerContext::sendPendingDataToApplication() { bool OpenSSLServerContext::setServerCertificate(const PKCS12Certificate& certificate) { if (certificate.isNull()) { -// std::cout << "error 1\n"; + LOG4CXX_ERROR(logger, "TLS WILL NOT WORK: Certificate can't be loaded."); return false; } @@ -189,7 +196,7 @@ bool OpenSSLServerContext::setServerCertificate(const PKCS12Certificate& certifi boost::shared_ptr pkcs12(d2i_PKCS12_bio(bio, NULL), PKCS12_free); BIO_free(bio); if (!pkcs12) { -// std::cout << "error 2\n"; + LOG4CXX_ERROR(logger, "TLS WILL NOT WORK: Certificate is not in PKCS#12 format."); return false; } @@ -199,7 +206,7 @@ bool OpenSSLServerContext::setServerCertificate(const PKCS12Certificate& certifi STACK_OF(X509)* caCertsPtr = 0; int result = PKCS12_parse(pkcs12.get(), reinterpret_cast(vecptr(certificate.getPassword())), &privateKeyPtr, &certPtr, &caCertsPtr); if (result != 1) { -// std::cout << "error 3\n"; + LOG4CXX_ERROR(logger, "TLS WILL NOT WORK: Certificate is not in PKCS#12 format."); return false; } boost::shared_ptr cert(certPtr, X509_free); @@ -208,11 +215,11 @@ bool OpenSSLServerContext::setServerCertificate(const PKCS12Certificate& certifi // Use the key & certificates if (SSL_CTX_use_certificate(context_, cert.get()) != 1) { -// std::cout << "error 4\n"; + LOG4CXX_ERROR(logger, "TLS WILL NOT WORK: Can't use this certificate"); return false; } if (SSL_CTX_use_PrivateKey(context_, privateKey.get()) != 1) { -// std::cout << "error 5\n"; + LOG4CXX_ERROR(logger, "TLS WILL NOT WORK: Can't use this private key"); return false; } return true; diff --git a/spectrum/src/sample.cfg b/spectrum/src/sample.cfg index fcb060d3..cc57d216 100644 --- a/spectrum/src/sample.cfg +++ b/spectrum/src/sample.cfg @@ -13,7 +13,8 @@ admin_password=test #cert=server.pfx #patch to PKCS#12 certificate #cert_password=test #password to that certificate if any users_per_backend=10 -backend=/home/hanzz/code/libtransport/backends/libpurple/spectrum2_libpurple_backend +#backend=/home/hanzz/code/libtransport/backends/libpurple/spectrum2_libpurple_backend +backend=/home/hanzz/code/libtransport/backends/smstools3/spectrum2_smstools3_backend #backend=/usr/bin/mono /home/hanzz/code/networkplugin-csharp/msnp-sharp-backend/bin/Debug/msnp-sharp-backend.exe #backend=/home/hanzz/code/libtransport/backends/frotz/spectrum2_frotz_backend #backend=/home/hanzz/code/libtransport/backends/libircclient-qt/spectrum2_libircclient-qt_backend @@ -25,12 +26,19 @@ irc_server=irc.freenode.org [backend] #default_avatar=catmelonhead.jpg #no_vcard_fetch=true +incoming_dir=/var/spool/sms/incoming [logging] #config=logging.cfg # log4cxx/log4j logging configuration file #backend_config=/home/hanzz/code/libtransport/spectrum/src/backend-logging.cfg # log4cxx/log4j logging configuration file for backends [database] -type = none # or "none" without database backend -database = test.sql -prefix=icq +#type = sqlite3 # or "none" without database backend +#database = test.sql +#prefix=icq +type = mysql # or "none" without database backend....................................................................................................................... +database = test +prefix= +user=root +password=yourrootsqlpassword +#encryption_key=hanzzik diff --git a/spectrum/src/sample2.cfg b/spectrum/src/sample2.cfg index d91e2c40..3181a6a1 100644 --- a/spectrum/src/sample2.cfg +++ b/spectrum/src/sample2.cfg @@ -41,7 +41,7 @@ users_per_backend=10 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 +#backend=/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 @@ -95,3 +95,20 @@ type = none # Prefix used for tables #prefix = jabber_ + +[registration] +# Enable public registrations +enable_public_registration=1 + +# Text to display upon user registration form +#username_label=Jabber JID (e.g. user@server.tld): +#instructions=Enter your remote jabber JID and password as well as your local username and password + +# If True a local jabber account on is needed +# for transport registration, the idea is to enable public registration +# from other servers, but only for users, who have already local accounts +#require_local_account=1 +#local_username_label=Local username (without @server.tld): +#local_account_server=localhost +#local_account_server_timeout=10000 + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ac077b20..dcb192c4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -20,11 +20,11 @@ endif() if (PROTOBUF_FOUND) if (CMAKE_COMPILER_IS_GNUCXX) - ADD_LIBRARY(transport SHARED ${HEADERS} ${SRC} ${SWIFTEN_SRC} ${CMAKE_CURRENT_BINARY_DIR}/../include/transport/protocol.pb.cc) + ADD_LIBRARY(transport SHARED ${HEADERS} ${SRC} ${SWIFTEN_SRC}) else(CMAKE_COMPILER_IS_GNUCXX) - ADD_LIBRARY(transport STATIC ${HEADERS} ${SRC} ${SWIFTEN_SRC} ${CMAKE_CURRENT_BINARY_DIR}/../include/transport/protocol.pb.cc) + ADD_LIBRARY(transport STATIC ${HEADERS} ${SRC} ${SWIFTEN_SRC}) endif(CMAKE_COMPILER_IS_GNUCXX) - SET_SOURCE_FILES_PROPERTIES(${CMAKE_CURRENT_BINARY_DIR}/../include/transport/protocol.pb.cc PROPERTIES GENERATED 1) +# SET_SOURCE_FILES_PROPERTIES(${CMAKE_CURRENT_BINARY_DIR}/../include/transport/protocol.pb.cc PROPERTIES GENERATED 1) ADD_DEPENDENCIES(transport pb) else(PROTOBUF_FOUND) ADD_LIBRARY(transport SHARED ${HEADERS} ${SRC} ${SWIFTEN_SRC}) @@ -35,9 +35,9 @@ if (CMAKE_COMPILER_IS_GNUCXX) endif() if (WIN32) - TARGET_LINK_LIBRARIES(transport ${PQXX_LIBRARY} ${PQ_LIBRARY} ${SQLITE3_LIBRARIES} ${MYSQL_LIBRARIES} ${SWIFTEN_LIBRARY} ${PROTOBUF_LIBRARIES} ${LOG4CXX_LIBRARIES}) + TARGET_LINK_LIBRARIES(transport transport-plugin ${PQXX_LIBRARY} ${PQ_LIBRARY} ${SQLITE3_LIBRARIES} ${MYSQL_LIBRARIES} ${SWIFTEN_LIBRARY} ${PROTOBUF_LIBRARIES} ${LOG4CXX_LIBRARIES}) else (WIN32) - TARGET_LINK_LIBRARIES(transport ${PQXX_LIBRARY} ${PQ_LIBRARY} ${SQLITE3_LIBRARIES} ${MYSQL_LIBRARIES} ${SWIFTEN_LIBRARY} ${PROTOBUF_LIBRARIES} ${LOG4CXX_LIBRARIES} ${POPT_LIBRARY}) + TARGET_LINK_LIBRARIES(transport transport-plugin ${PQXX_LIBRARY} ${PQ_LIBRARY} ${SQLITE3_LIBRARIES} ${MYSQL_LIBRARIES} ${SWIFTEN_LIBRARY} ${PROTOBUF_LIBRARIES} ${LOG4CXX_LIBRARIES} ${POPT_LIBRARY}) endif(WIN32) SET_TARGET_PROPERTIES(transport PROPERTIES diff --git a/src/config.cpp b/src/config.cpp index c5ca48b5..7088889c 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -87,8 +87,12 @@ bool Config::load(std::istream &ifs, boost::program_options::options_description ("registration.username_label", value()->default_value("Legacy network username:"), "Label for username field") ("registration.username_mask", value()->default_value(""), "Username mask") ("registration.encoding", value()->default_value("utf8"), "Default encoding in registration form") + ("registration.require_local_account", value()->default_value(false), "True if users have to have a local account to register to this transport from remote servers.") + ("registration.local_username_label", value()->default_value("Local username:"), "Label for local usernme field") + ("registration.local_account_server", value()->default_value("localhost"), "The server on which the local accounts will be checked for validity") + ("registration.local_account_server_timeout", value()->default_value(10000), "Timeout when checking local user on local_account_server (msecs)") ("database.type", value()->default_value("none"), "Database type.") - ("database.database", value()->default_value(""), "Database used to store data") + ("database.database", value()->default_value("/var/lib/spectrum2/$jid/database.sql"), "Database used to store data") ("database.server", value()->default_value("localhost"), "Database server.") ("database.user", value()->default_value(""), "Database user.") ("database.password", value()->default_value(""), "Database Password.") @@ -107,6 +111,7 @@ bool Config::load(std::istream &ifs, boost::program_options::options_description bool found_working = false; bool found_pidfile = false; bool found_backend_port = false; + bool found_database = false; std::string jid = ""; BOOST_FOREACH(option &opt, parsed.options) { if (opt.string_key == "service.jid") { @@ -130,6 +135,9 @@ bool Config::load(std::istream &ifs, boost::program_options::options_description else if (opt.string_key == "service.pidfile") { found_pidfile = true; } + else if (opt.string_key == "database.database") { + found_database = true; + } } if (!found_working) { @@ -148,6 +156,11 @@ bool Config::load(std::istream &ifs, boost::program_options::options_description value.push_back(p); parsed.options.push_back(boost::program_options::basic_option("service.backend_port", value)); } + if (!found_database) { + std::vector value; + value.push_back("/var/lib/spectrum2/$jid/database.sql"); + parsed.options.push_back(boost::program_options::basic_option("database.database", value)); + } BOOST_FOREACH(option &opt, parsed.options) { if (opt.unregistered) { diff --git a/src/mysqlbackend.cpp b/src/mysqlbackend.cpp index 4bc2eec4..1af53c3a 100644 --- a/src/mysqlbackend.cpp +++ b/src/mysqlbackend.cpp @@ -470,7 +470,7 @@ long MySQLBackend::addBuddy(long userId, const BuddyInfo &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()) { + if (buddyInfo.settings.find("icon_hash") != buddyInfo.settings.end() && !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)); } @@ -597,6 +597,10 @@ void MySQLBackend::getUserSetting(long id, const std::string &variable, int &typ else { *m_getUserSetting >> type >> value; } + + while (m_getUserSetting->fetch() == 0) { + + } } void MySQLBackend::updateUserSetting(long id, const std::string &variable, const std::string &value) { @@ -606,11 +610,11 @@ void MySQLBackend::updateUserSetting(long id, const std::string &variable, const } void MySQLBackend::beginTransaction() { - exec("START TRANSACTION;"); + //exec("START TRANSACTION;"); } void MySQLBackend::commitTransaction() { - exec("COMMIT;"); + //exec("COMMIT;"); } } diff --git a/src/networkpluginserver.cpp b/src/networkpluginserver.cpp index feade943..a89117b6 100644 --- a/src/networkpluginserver.cpp +++ b/src/networkpluginserver.cpp @@ -157,6 +157,7 @@ static unsigned long exec_(std::string path, const char *host, const char *port, // fork and exec pid_t pid = fork(); if ( pid == 0 ) { + setsid(); // child process exit(execv(argv[0], argv)); } else if ( pid < 0 ) { @@ -174,7 +175,7 @@ static void SigCatcher(int n) { 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) { + while ((result = waitpid(-1, &status, WNOHANG)) > 0) { if (result != 0) { if (WIFEXITED(status)) { if (WEXITSTATUS(status) != 0) { @@ -257,6 +258,7 @@ NetworkPluginServer::NetworkPluginServer(Component *component, Config *config, U #endif 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()); + LOG4CXX_INFO(logger, "Backend should now connect to Spectrum2 instance. Spectrum2 won't accept any connection before backend connects"); } NetworkPluginServer::~NetworkPluginServer() { diff --git a/src/rostermanager.cpp b/src/rostermanager.cpp index 23b19a7f..44264588 100644 --- a/src/rostermanager.cpp +++ b/src/rostermanager.cpp @@ -285,8 +285,8 @@ void RosterManager::handleSubscription(Swift::Presence::ref presence) { // using roster pushes. if (m_component->inServerMode()) { Swift::Presence::ref response = Swift::Presence::create(); - response->setTo(presence->getFrom()); - response->setFrom(presence->getTo()); + response->setTo(presence->getFrom().toBare()); + response->setFrom(presence->getTo().toBare()); Buddy *buddy = getBuddy(Buddy::JIDToLegacyName(presence->getTo())); if (buddy) { LOG4CXX_INFO(logger, m_user->getJID().toString() << ": Subscription received and buddy " << Buddy::JIDToLegacyName(presence->getTo()) << " is already there => answering"); @@ -342,7 +342,7 @@ void RosterManager::handleSubscription(Swift::Presence::ref presence) { Swift::Presence::ref response = Swift::Presence::create(); Swift::Presence::ref currentPresence; response->setTo(presence->getFrom().toBare()); - response->setFrom(presence->getTo().toBare().toString() + "/bot"); + response->setFrom(presence->getTo().toBare()); Buddy *buddy = getBuddy(Buddy::JIDToLegacyName(presence->getTo())); if (buddy) { diff --git a/src/sqlite3backend.cpp b/src/sqlite3backend.cpp index ad03bf23..c61ca628 100644 --- a/src/sqlite3backend.cpp +++ b/src/sqlite3backend.cpp @@ -111,6 +111,8 @@ bool SQLite3Backend::connect() { return false; } + sqlite3_busy_timeout(m_db, 1500); + if (createDatabase() == false) return false; @@ -234,6 +236,8 @@ bool SQLite3Backend::getUser(const std::string &barejid, UserInfo &user) { 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; + while((ret = sqlite3_step(m_getUser)) == SQLITE_ROW) { + } return true; } @@ -388,6 +392,9 @@ bool SQLite3Backend::getBuddies(long id, std::list &roster) { roster.push_back(b); } + while((ret = sqlite3_step(m_getBuddiesSettings)) == SQLITE_ROW) { + } + if (ret != SQLITE_DONE) { LOG4CXX_ERROR(logger, "getBuddies query"<< (sqlite3_errmsg(m_db) == NULL ? "" : sqlite3_errmsg(m_db))); return false; @@ -444,6 +451,10 @@ void SQLite3Backend::getUserSetting(long id, const std::string &variable, int &t type = GET_INT(m_getUserSetting); value = GET_STR(m_getUserSetting); } + + int ret; + while((ret = sqlite3_step(m_getUserSetting)) == SQLITE_ROW) { + } } void SQLite3Backend::updateUserSetting(long id, const std::string &variable, const std::string &value) { diff --git a/src/usermanager.cpp b/src/usermanager.cpp index ccb036f4..08823715 100644 --- a/src/usermanager.cpp +++ b/src/usermanager.cpp @@ -306,8 +306,8 @@ void UserManager::handleSubscription(Swift::Presence::ref presence) { // answer to subscibe for transport itself if (presence->getType() == Swift::Presence::Subscribe && presence->getTo().getNode().empty()) { Swift::Presence::ref response = Swift::Presence::create(); - response->setFrom(presence->getTo()); - response->setTo(presence->getFrom()); + response->setFrom(presence->getTo().toBare()); + response->setTo(presence->getFrom().toBare()); response->setType(Swift::Presence::Subscribed); m_component->getStanzaChannel()->sendPresence(response); diff --git a/src/userregistration.cpp b/src/userregistration.cpp index 9160465f..f7de616e 100644 --- a/src/userregistration.cpp +++ b/src/userregistration.cpp @@ -26,6 +26,8 @@ #include "transport/user.h" #include "Swiften/Elements/ErrorPayload.h" #include +#include +#include #include "log4cxx/logger.h" using namespace Swift; @@ -241,6 +243,20 @@ bool UserRegistration::handleGetRequest(const Swift::JID& from, const Swift::JID boolean->setLabel((("Remove your registration"))); boolean->setValue(0); form->addField(boolean); + } else { + if (CONFIG_BOOL(m_config,"registration.require_local_account")) { + std::string localUsernameField = CONFIG_STRING(m_config, "registration.local_username_label"); + TextSingleFormField::ref local_username = TextSingleFormField::create(); + local_username->setName("local_username"); + local_username->setLabel((localUsernameField)); + local_username->setRequired(true); + form->addField(local_username); + TextPrivateFormField::ref local_password = TextPrivateFormField::create(); + local_password->setName("local_password"); + local_password->setLabel((("Local Password"))); + local_password->setRequired(true); + form->addField(local_password); + } } reg->setForm(form); @@ -273,6 +289,8 @@ bool UserRegistration::handleSetRequest(const Swift::JID& from, const Swift::JID std::string encoding; std::string language; + std::string local_username(""); + std::string local_password(""); Form::ref form = payload->getForm(); if (form) { @@ -290,6 +308,13 @@ bool UserRegistration::handleSetRequest(const Swift::JID& from, const Swift::JID else if (textSingle->getName() == "password") { payload->setPassword(textSingle->getValue()); } + else if (textSingle->getName() == "local_username") { + local_username = textSingle->getValue(); + } + // Pidgin sends it as textSingle, not sure why... + else if (textSingle->getName() == "local_password") { + local_password = textSingle->getValue(); + } continue; } @@ -298,6 +323,9 @@ bool UserRegistration::handleSetRequest(const Swift::JID& from, const Swift::JID if (textPrivate->getName() == "password") { payload->setPassword(textPrivate->getValue()); } + else if (textPrivate->getName() == "local_password") { + local_password = textPrivate->getValue(); + } continue; } @@ -327,6 +355,50 @@ bool UserRegistration::handleSetRequest(const Swift::JID& from, const Swift::JID return true; } + if (CONFIG_BOOL(m_config,"registration.require_local_account")) { + /* if (!local_username || !local_password) { + sendResponse(from, id, InBandRegistrationPayload::ref()); + return true + } else */ if (local_username == "" || local_password == "") { + sendResponse(from, id, InBandRegistrationPayload::ref()); + return true; + } +// Swift::logging = true; + bool validLocal = false; + std::string localLookupServer = CONFIG_STRING(m_config, "registration.local_account_server"); + std::string localLookupJID = local_username + std::string("@") + localLookupServer; + SimpleEventLoop localLookupEventLoop; + BoostNetworkFactories localLookupNetworkFactories(&localLookupEventLoop); + Client localLookupClient(localLookupJID, local_password, &localLookupNetworkFactories); + + // TODO: this is neccessary on my server ... but should maybe omitted + localLookupClient.setAlwaysTrustCertificates(); + localLookupClient.connect(); + + class SimpleLoopRunner { + public: + SimpleLoopRunner() {}; + + static void run(SimpleEventLoop * loop) { + loop->run(); + }; + }; + + // TODO: Really ugly and hacky solution, any other ideas more than welcome! + boost::thread thread(boost::bind(&(SimpleLoopRunner::run), &localLookupEventLoop)); + thread.timed_join(boost::posix_time::millisec(CONFIG_INT(m_config, "registration.local_account_server_timeout"))); + localLookupEventLoop.stop(); + thread.join(); + validLocal = localLookupClient.isAvailable(); + localLookupClient.disconnect(); + if (!validLocal) { + sendError(from, id, ErrorPayload::NotAuthorized, ErrorPayload::Modify); + return true; + } + } + + printf("here\n"); + if (!payload->getUsername() || !payload->getPassword()) { sendError(from, id, ErrorPayload::NotAcceptable, ErrorPayload::Modify); return true;