diff --git a/CMakeLists.txt b/CMakeLists.txt index d7cb5928..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") @@ -53,6 +53,12 @@ find_package(log4cxx) set(event_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") 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) @@ -101,6 +107,16 @@ else (MYSQL_FOUND) message("MySQL : no (install mysql-devel)") endif (MYSQL_FOUND) +if (PQXX_FOUND) + ADD_DEFINITIONS(-DWITH_PQXX) + include_directories(${PQXX_INCLUDE_DIR}) + message("PostgreSQL : yes") +else (PQXX_FOUND) + set(PQXX_LIBRARY "") + set(PQ_LIBRARY "") + message("PostgreSQL : no (install libpqxx-devel)") +endif (PQXX_FOUND) + if (PROTOBUF_FOUND) ADD_DEFINITIONS(-DWITH_PROTOBUF) include_directories(${PROTOBUF_INCLUDE_DIRS}) @@ -133,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 3372f46a..d0d28034 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,28 @@ +Version 2.0.0-beta (2012-02-28): + General: + * Added PostreSQL support (thanks to Jadestorm). + * Added XEP-0100 (Gateway interaction) support. + * 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 9ecb3b5c..cdb2696f 100644 --- a/backends/CMakeLists.txt +++ b/backends/CMakeLists.txt @@ -7,8 +7,15 @@ if (PROTOBUF_FOUND) ADD_SUBDIRECTORY(libcommuni) endif() + ADD_SUBDIRECTORY(smstools3) + + ADD_SUBDIRECTORY(template) + if (NOT WIN32) ADD_SUBDIRECTORY(frotz) + 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/libcommuni/session.cpp b/backends/libcommuni/session.cpp index 088ebd1a..971c2c0c 100644 --- a/backends/libcommuni/session.cpp +++ b/backends/libcommuni/session.cpp @@ -26,6 +26,7 @@ MyIrcSession::MyIrcSession(const std::string &user, NetworkPlugin *np, const std this->np = np; this->user = user; this->suffix = suffix; + m_connected = false; rooms = 0; connect(this, SIGNAL(disconnected()), SLOT(on_disconnected())); connect(this, SIGNAL(connected()), SLOT(on_connected())); @@ -33,6 +34,7 @@ MyIrcSession::MyIrcSession(const std::string &user, NetworkPlugin *np, const std } void MyIrcSession::on_connected() { + m_connected = true; if (suffix.empty()) { np->handleConnected(user); } @@ -51,6 +53,7 @@ void MyIrcSession::on_connected() { void MyIrcSession::on_disconnected() { if (suffix.empty()) np->handleDisconnected(user, 0, ""); + m_connected = false; } bool MyIrcSession::correctNickname(std::string &nickname) { @@ -156,6 +159,9 @@ void MyIrcSession::on_messageReceived(IrcMessage *message) { } void MyIrcSession::on_numericMessageReceived(IrcMessage *message) { + QString channel; + QStringList members; + IrcNumericMessage *m = (IrcNumericMessage *) message; switch (m->code()) { case 332: @@ -165,8 +171,8 @@ void MyIrcSession::on_numericMessageReceived(IrcMessage *message) { np->handleSubject(user, m->parameters().value(1).toStdString() + suffix, m_topicData, m->parameters().value(2).toStdString()); break; case 353: - QString channel = m->parameters().value(2); - QStringList members = m->parameters().value(3).split(" "); + channel = m->parameters().value(2); + members = m->parameters().value(3).split(" "); for (int i = 0; i < members.size(); i++) { bool flags = 0; @@ -176,6 +182,13 @@ void MyIrcSession::on_numericMessageReceived(IrcMessage *message) { np->handleParticipantChanged(user, nickname, channel.toStdString() + suffix,(int) flags, pbnetwork::STATUS_ONLINE); } break; + case 432: + if (m_connected) { + np->handleDisconnected(user, pbnetwork::CONNECTION_ERROR_INVALID_USERNAME, "Erroneous Nickname"); + } + break; + default: + break; } //qDebug() << "numeric message received:" << receiver() << origin << code << params; @@ -208,5 +221,6 @@ void MyIrcSession::onMessageReceived(IrcMessage *message) { case IrcMessage::Numeric: on_numericMessageReceived(message); break; + default:break; } } diff --git a/backends/libcommuni/session.h b/backends/libcommuni/session.h index 3589dcb4..8b31da58 100644 --- a/backends/libcommuni/session.h +++ b/backends/libcommuni/session.h @@ -65,6 +65,7 @@ protected: std::string m_identify; std::list m_autoJoin; std::string m_topicData; + bool m_connected; }; //class MyIrcBuffer : public Irc::Buffer diff --git a/backends/libpurple/main.cpp b/backends/libpurple/main.cpp index 2c04df27..cf8d3090 100644 --- a/backends/libpurple/main.cpp +++ b/backends/libpurple/main.cpp @@ -441,6 +441,16 @@ static void * requestInput(const char *title, const char *primary,const char *se ((PurpleRequestInputCb) ok_cb)(user_data, "Please authorize me."); return NULL; } + else if (primaryString == "Authorization Request Message:") { + LOG4CXX_INFO(logger, "Authorization Request Message: calling ok_cb(...)"); + ((PurpleRequestInputCb) ok_cb)(user_data, "Please authorize me."); + return NULL; + } + else if (primaryString == "Authorization Denied Message:") { + LOG4CXX_INFO(logger, "Authorization Deined Message: calling ok_cb(...)"); + ((PurpleRequestInputCb) ok_cb)(user_data, "Authorization denied."); + return NULL; + } else { LOG4CXX_WARN(logger, "Unhandled request input. primary=" << primaryString); } @@ -604,11 +614,13 @@ class SpectrumNetworkPlugin : public NetworkPlugin { return; } - LOG4CXX_INFO(logger, "Creating account with name '" << name.c_str() << "' and protocol '" << protocol << "'"); - if (purple_accounts_find(name.c_str(), protocol.c_str()) != NULL){ + + if (purple_accounts_find(name.c_str(), protocol.c_str()) != NULL) { + LOG4CXX_INFO(logger, "Using previously created account with name '" << name.c_str() << "' and protocol '" << protocol << "'"); account = purple_accounts_find(name.c_str(), protocol.c_str()); } else { + LOG4CXX_INFO(logger, "Creating account with name '" << name.c_str() << "' and protocol '" << protocol << "'"); account = purple_account_new(name.c_str(), protocol.c_str()); purple_accounts_add(account); } @@ -903,6 +915,41 @@ class SpectrumNetworkPlugin : public NetworkPlugin { } } + void handleJoinRoomRequest(const std::string &user, const std::string &room, const std::string &nickname, const std::string &pasword) { + PurpleAccount *account = m_sessions[user]; + if (!account) { + return; + } + + PurpleConnection *gc = purple_account_get_connection(account); + GHashTable *comps = NULL; + + // Check if the PurpleChat is not stored in buddy list + PurpleChat *chat = purple_blist_find_chat(account, room.c_str()); + if (chat) { + comps = purple_chat_get_components(chat); + } + else if (PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl)->chat_info_defaults != NULL) { + comps = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl)->chat_info_defaults(gc, room.c_str()); + } + + LOG4CXX_INFO(logger, user << ": Joining the room " << room); + if (comps) { + serv_join_chat(gc, comps); + g_hash_table_destroy(comps); + } + } + + void handleLeaveRoomRequest(const std::string &user, const std::string &room) { + PurpleAccount *account = m_sessions[user]; + if (!account) { + return; + } + + PurpleConversation *conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, room.c_str(), account); + purple_conversation_destroy(conv); + } + void handleFTStartRequest(const std::string &user, const std::string &buddyName, const std::string &fileName, unsigned long size, unsigned long ftID) { PurpleXfer *xfer = m_unhandledXfers[user + fileName + buddyName]; if (xfer) { @@ -1078,6 +1125,8 @@ static void buddyListNewNode(PurpleBlistNode *node) { PurpleBuddy *buddy = (PurpleBuddy *) node; PurpleAccount *account = purple_buddy_get_account(buddy); + LOG4CXX_INFO(logger, "Buddy updated " << np->m_accounts[account] << " " << purple_buddy_get_name(buddy) << " " << getAlias(buddy)); + // Status pbnetwork::StatusType status = pbnetwork::STATUS_NONE; std::string message; diff --git a/backends/skype/CMakeLists.txt b/backends/skype/CMakeLists.txt new file mode 100644 index 00000000..d27b4296 --- /dev/null +++ b/backends/skype/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 2.6) +FILE(GLOB SRC *.cpp) + +ADD_EXECUTABLE(spectrum2_skype_backend ${SRC}) + +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/main.cpp b/backends/skype/main.cpp new file mode 100644 index 00000000..9ede8a7f --- /dev/null +++ b/backends/skype/main.cpp @@ -0,0 +1,865 @@ +#include "glib.h" +#include + +#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 +#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 + + +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; + } + + bool createDBusProxy(); + bool loadSkypeBuddies(); + + private: + std::string m_username; + std::string m_password; + GPid m_pid; + 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() { + for (std::map::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 &groups) { + } + + void handleBuddyUpdatedRequest(const std::string &user, const std::string &buddyName, const std::string &alias, const std::vector &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 m_sessions; + std::map m_accounts; + std::map 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; + m_timer = -1; + m_counter = 0; +} + + +static gboolean load_skype_buddies(gpointer data) { + Skype *skype = (Skype *) data; + return skype->loadSkypeBuddies(); +} + +bool Skype::createDBusProxy() { + if (m_proxy == NULL) { + LOG4CXX_INFO(logger, "Creating DBus proxy for com.Skype.Api."); + m_counter++; + + 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); + + if (m_counter == 15) { + np->handleDisconnected(m_user, 0, error->message); + logout(); + g_error_free(error); + return FALSE; + } + g_error_free(error); + } + + 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); + + 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+1] && *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 (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 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 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 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 groups; + np->handleBuddyChanged(sk->getUser(), cmd[1], "", groups, status, mood_text); + } + else if (cmd[2] == "FULLNAME") { + 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 = sk->send_command("GET USER " + cmd[1] + " MOOD_TEXT"); + mood_text = mood_text.substr(mood_text.find("MOOD_TEXT") + 10); + + std::string alias = message.substr(message.find("FULLNAME") + 9); + + std::vector groups; + np->handleBuddyChanged(sk->getUser(), cmd[1], alias, 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); +} + +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; + 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 \n"; +#else + +#if GLIB_CHECK_VERSION(2,14,0) + std::cout << g_option_context_get_help(context, FALSE, NULL); +#else + std::cout << "Usage: spectrum \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(getpid())); + log4cxx::PropertyConfigurator::configure(p); + } + +// initPurple(config); + + g_type_init(); + + m_sock = create_socket(host, port); + + g_set_printerr_handler(log_glib_error); + + GIOChannel *channel; + 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); + + 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); +} 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/backends/template/CMakeLists.txt b/backends/template/CMakeLists.txt new file mode 100644 index 00000000..b35d57a3 --- /dev/null +++ b/backends/template/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 2.6) + +FILE(GLOB SRC *.c *.cpp) + +ADD_EXECUTABLE(spectrum2_template_backend ${SRC}) + +target_link_libraries(spectrum2_template_backend transport pthread ${Boost_LIBRARIES} ${SWIFTEN_LIBRARY} ${LOG4CXX_LIBRARIES}) + +#INSTALL(TARGETS spectrum2_template_backend RUNTIME DESTINATION bin) + diff --git a/backends/template/README b/backends/template/README new file mode 100644 index 00000000..b5b85a34 --- /dev/null +++ b/backends/template/README @@ -0,0 +1 @@ +This is just template for creating new generic spectrum2 backends. It does not do anything! \ No newline at end of file diff --git a/backends/template/main.cpp b/backends/template/main.cpp new file mode 100644 index 00000000..44fc8623 --- /dev/null +++ b/backends/template/main.cpp @@ -0,0 +1,181 @@ +// Transport includes +#include "transport/config.h" +#include "transport/networkplugin.h" + +// Swiften +#include "Swiften/Swiften.h" + +// for signal handler +#include "unistd.h" +#include "signal.h" +#include "sys/wait.h" +#include "sys/signal.h" + +// Log4cxx +#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" + +// Boost +#include +using namespace boost::filesystem; +using namespace boost::program_options; +using namespace Transport; + +// log4cxx main logger +using namespace log4cxx; +static LoggerPtr logger = log4cxx::Logger::getLogger("Backend Template"); + +// eventloop +Swift::SimpleEventLoop *loop_; + +// Plugin +class TemplatePlugin; +TemplatePlugin * np = NULL; + +class TemplatePlugin : public NetworkPlugin { + public: + Swift::BoostNetworkFactories *m_factories; + Swift::BoostIOServiceThread m_boostIOServiceThread; + boost::shared_ptr m_conn; + + TemplatePlugin(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(&TemplatePlugin::_handleDataRead, this, _1)); + m_conn->connect(Swift::HostAddressPort(Swift::HostAddress(host), port)); + + LOG4CXX_INFO(logger, "Starting the plugin."); + } + + // NetworkPlugin uses this method to send the data to networkplugin server + void sendData(const std::string &string) { + m_conn->write(Swift::createSafeByteArray(string)); + } + + // This method has to call handleDataRead with all received data from network plugin server + 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) { + 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 = "") { + LOG4CXX_INFO(logger, "Sending message from " << user << " to " << legacyName << "."); + } + + 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; + } + + 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); + } + + Swift::SimpleEventLoop eventLoop; + loop_ = &eventLoop; + np = new TemplatePlugin(&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/cmake_modules/pqxxConfig.cmake b/cmake_modules/pqxxConfig.cmake new file mode 100644 index 00000000..9c53550c --- /dev/null +++ b/cmake_modules/pqxxConfig.cmake @@ -0,0 +1,16 @@ +FIND_PATH(PQXX_INCLUDE_DIR pqxx/pqxx PATHS) +MARK_AS_ADVANCED(PQXX_INCLUDE_DIR) + +FIND_LIBRARY(PQXX_LIBRARY pqxx ) +MARK_AS_ADVANCED(PQXX_LIBRARY) + +FIND_LIBRARY(PQ_LIBRARY pq ) +MARK_AS_ADVANCED(PQ_LIBRARY) + +if(PQXX_LIBRARY AND PQ_LIBRARY AND PQXX_INCLUDE_DIR) + set( PQXX_FOUND 1 ) + message( STATUS "Found pqxx: ${PQXX_LIBRARY}, ${PQ_LIBRARY}, ${PQXX_INCLUDE_DIR}") +else() + message(STATUS "Could NOT find pqxx and pq library") +endif() + diff --git a/include/Swiften/Elements/GatewayPayload.cpp b/include/Swiften/Elements/GatewayPayload.cpp new file mode 100644 index 00000000..59b6a9f6 --- /dev/null +++ b/include/Swiften/Elements/GatewayPayload.cpp @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2011 Jan Kaluza + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include + +namespace Swift { + +GatewayPayload::GatewayPayload(const JID &jid, const std::string &desc, const std::string &prompt) : + jid(jid), desc(desc), prompt(prompt) { + + } + +} diff --git a/include/Swiften/Elements/GatewayPayload.h b/include/Swiften/Elements/GatewayPayload.h new file mode 100644 index 00000000..2f6bbc54 --- /dev/null +++ b/include/Swiften/Elements/GatewayPayload.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2011 Jan Kaluza + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include + +#include +#include +#include + +namespace Swift { + class GatewayPayload : public Payload { + public: + GatewayPayload(const JID &jid = JID(), const std::string &desc = "", const std::string &prompt = ""); + + void setJID(const JID &jid) { + this->jid = jid; + } + + const JID &getJID() const { + return jid; + } + + void setDesc(const std::string &desc) { + this->desc = desc; + } + + const std::string &getDesc() const { + return desc; + } + + void setPrompt(const std::string &prompt) { + this->prompt = prompt; + } + + const std::string &getPrompt() const { + return prompt; + } + + private: + JID jid; + std::string desc; + std::string prompt; + }; +} diff --git a/include/Swiften/Elements/PubSubItem.cpp b/include/Swiften/Elements/PubSubItem.cpp new file mode 100644 index 00000000..730ad89e --- /dev/null +++ b/include/Swiften/Elements/PubSubItem.cpp @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2011 Jan Kaluza + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include + +namespace Swift { + +PubSubItem::PubSubItem() { +} + +} diff --git a/include/Swiften/Elements/PubSubItem.h b/include/Swiften/Elements/PubSubItem.h new file mode 100644 index 00000000..f0b3ef70 --- /dev/null +++ b/include/Swiften/Elements/PubSubItem.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2011 Jan Kaluza + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include + +#include +#include + +namespace Swift { + class PubSubItem : public Payload { + public: + PubSubItem(); + + void addPayload(boost::shared_ptr payload) { + payloads.push_back(payload); + } + + const std::vector > getPayloads() const { + return payloads; + } + + template + const std::vector > getPayloads() const { + std::vector > matched_payloads; + for (std::vector >::const_iterator i = payloads.begin(); i != payloads.end(); ++i) { + boost::shared_ptr result = boost::dynamic_pointer_cast(*i); + if (result) { + matched_payloads.push_back(result); + } + } + + return matched_payloads; + + } + + template + const boost::shared_ptr getPayload() const { + boost::shared_ptr result; + for (std::vector >::const_iterator i = payloads.begin(); i != payloads.end(); ++i) { + result = boost::dynamic_pointer_cast(*i); + if (result) { + return result; + } + } + + return result; + } + + const std::string& getId() const { return id; } + + void setId(const std::string& id) { + this->id = id; + } + + private: + std::vector > payloads; + std::string id; + }; +} diff --git a/include/Swiften/Elements/PubSubPayload.cpp b/include/Swiften/Elements/PubSubPayload.cpp new file mode 100644 index 00000000..663e8eb7 --- /dev/null +++ b/include/Swiften/Elements/PubSubPayload.cpp @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2011 Jan Kaluza + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include + +namespace Swift { + +PubSubPayload::PubSubPayload() { +} + +} diff --git a/include/Swiften/Elements/PubSubPayload.h b/include/Swiften/Elements/PubSubPayload.h new file mode 100644 index 00000000..46c8b82e --- /dev/null +++ b/include/Swiften/Elements/PubSubPayload.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2012 Jan Kaluza + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include + +#include +#include + +namespace Swift { + class PubSubPayload : public Payload { + public: + PubSubPayload(); + + void addPayload(boost::shared_ptr payload) { + payloads.push_back(payload); + } + + const std::vector > getPayloads() const { + return payloads; + } + + template + const std::vector > getPayloads() const { + std::vector > matched_payloads; + for (std::vector >::const_iterator i = payloads.begin(); i != payloads.end(); ++i) { + boost::shared_ptr result = boost::dynamic_pointer_cast(*i); + if (result) { + matched_payloads.push_back(result); + } + } + + return matched_payloads; + + } + + template + const boost::shared_ptr getPayload() const { + boost::shared_ptr result; + for (std::vector >::const_iterator i = payloads.begin(); i != payloads.end(); ++i) { + result = boost::dynamic_pointer_cast(*i); + if (result) { + return result; + } + } + + return result; + } + + private: + std::vector > payloads; + }; +} diff --git a/include/Swiften/Elements/PubSubPublishPayload.cpp b/include/Swiften/Elements/PubSubPublishPayload.cpp new file mode 100644 index 00000000..dec34e92 --- /dev/null +++ b/include/Swiften/Elements/PubSubPublishPayload.cpp @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2011 Jan Kaluza + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include + +namespace Swift { + +PubSubPublishPayload::PubSubPublishPayload(const std::string &node) : + node(node) { + + } + +} diff --git a/include/Swiften/Elements/PubSubPublishPayload.h b/include/Swiften/Elements/PubSubPublishPayload.h new file mode 100644 index 00000000..930cca6b --- /dev/null +++ b/include/Swiften/Elements/PubSubPublishPayload.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2011 Jan Kaluza + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include + +#include +#include +#include +#include + +namespace Swift { + class PubSubPublishPayload : public Payload { + public: + enum Type { None, Pending, Subscribed, Unconfigured }; + + PubSubPublishPayload(const std::string &node = ""); + + void setNode(const std::string &node) { + this->node = node; + } + + const std::string &getNode() const { + return node; + } + + void addItem(const boost::shared_ptr &item) { + items.push_back(item); + } + + const std::vector > &getItems() const { + return items; + } + + private: + std::string node; + std::vector > items; + }; +} diff --git a/include/Swiften/Elements/PubSubSubscribePayload.cpp b/include/Swiften/Elements/PubSubSubscribePayload.cpp new file mode 100644 index 00000000..24aa6fd6 --- /dev/null +++ b/include/Swiften/Elements/PubSubSubscribePayload.cpp @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2011 Jan Kaluza + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include + +namespace Swift { + +PubSubSubscribePayload::PubSubSubscribePayload(const JID &jid, const std::string &node) : + jid(jid), node(node) { + + } + +} diff --git a/include/Swiften/Elements/PubSubSubscribePayload.h b/include/Swiften/Elements/PubSubSubscribePayload.h new file mode 100644 index 00000000..f2b011a2 --- /dev/null +++ b/include/Swiften/Elements/PubSubSubscribePayload.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2011 Jan Kaluza + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include + +#include +#include +#include + +namespace Swift { + class PubSubSubscribePayload : public Payload { + public: + PubSubSubscribePayload(const JID &jid = JID(), const std::string &node = ""); + + void setJID(const JID &jid) { + this->jid = jid; + } + + const JID &getJID() const { + return jid; + } + + void setNode(const std::string &node) { + this->node = node; + } + + const std::string &getNode() const { + return node; + } + + private: + JID jid; + std::string node; + }; +} diff --git a/include/Swiften/Elements/PubSubSubscriptionPayload.cpp b/include/Swiften/Elements/PubSubSubscriptionPayload.cpp new file mode 100644 index 00000000..293505ec --- /dev/null +++ b/include/Swiften/Elements/PubSubSubscriptionPayload.cpp @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2011 Jan Kaluza + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include + +namespace Swift { + +PubSubSubscriptionPayload::PubSubSubscriptionPayload(const JID &jid, const std::string &node) : + jid(jid), node(node), type(None) { + + } + +} diff --git a/include/Swiften/Elements/PubSubSubscriptionPayload.h b/include/Swiften/Elements/PubSubSubscriptionPayload.h new file mode 100644 index 00000000..c404e6ff --- /dev/null +++ b/include/Swiften/Elements/PubSubSubscriptionPayload.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2011 Jan Kaluza + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include + +#include +#include +#include + +namespace Swift { + class PubSubSubscriptionPayload : public Payload { + public: + enum Type { None, Pending, Subscribed, Unconfigured }; + + PubSubSubscriptionPayload(const JID &jid = JID(), const std::string &node = ""); + + void setJID(const JID &jid) { + this->jid = jid; + } + + const JID &getJID() const { + return jid; + } + + void setNode(const std::string &node) { + this->node = node; + } + + const std::string &getNode() const { + return node; + } + + void setId(const std::string &id) { + this->id = id; + } + + const std::string &getId() const { + return id; + } + + void setType(const Type &type) { + this->type = type; + } + + const Type &getType() const { + return type; + } + + private: + JID jid; + std::string node; + std::string id; + Type type; + }; +} diff --git a/include/Swiften/FileTransfer/CombinedOutgoingFileTransferManager.cpp b/include/Swiften/FileTransfer/CombinedOutgoingFileTransferManager.cpp index 710b8f83..ac06ab75 100644 --- a/include/Swiften/FileTransfer/CombinedOutgoingFileTransferManager.cpp +++ b/include/Swiften/FileTransfer/CombinedOutgoingFileTransferManager.cpp @@ -18,13 +18,12 @@ #include #include #include -#include #include namespace Swift { -CombinedOutgoingFileTransferManager::CombinedOutgoingFileTransferManager(JingleSessionManager* jingleSessionManager, IQRouter* router, EntityCapsProvider* capsProvider, RemoteJingleTransportCandidateSelectorFactory* remoteFactory, LocalJingleTransportCandidateGeneratorFactory* localFactory, SOCKS5BytestreamRegistry* bytestreamRegistry, SOCKS5BytestreamProxy* bytestreamProxy, PresenceOracle *presOracle, SOCKS5BytestreamServer *bytestreamServer) : jsManager(jingleSessionManager), iqRouter(router), capsProvider(capsProvider), remoteFactory(remoteFactory), localFactory(localFactory), bytestreamRegistry(bytestreamRegistry), bytestreamProxy(bytestreamProxy), presenceOracle(presOracle), bytestreamServer(bytestreamServer) { +CombinedOutgoingFileTransferManager::CombinedOutgoingFileTransferManager(JingleSessionManager* jingleSessionManager, IQRouter* router, EntityCapsProvider* capsProvider, RemoteJingleTransportCandidateSelectorFactory* remoteFactory, LocalJingleTransportCandidateGeneratorFactory* localFactory, SOCKS5BytestreamRegistry* bytestreamRegistry, SOCKS5BytestreamProxy* bytestreamProxy, Transport::PresenceOracle *presOracle, SOCKS5BytestreamServer *bytestreamServer) : jsManager(jingleSessionManager), iqRouter(router), capsProvider(capsProvider), remoteFactory(remoteFactory), localFactory(localFactory), bytestreamRegistry(bytestreamRegistry), bytestreamProxy(bytestreamProxy), presenceOracle(presOracle), bytestreamServer(bytestreamServer) { idGenerator = new IDGenerator(); } diff --git a/include/Swiften/FileTransfer/CombinedOutgoingFileTransferManager.h b/include/Swiften/FileTransfer/CombinedOutgoingFileTransferManager.h index 765e6ba2..17e7fc58 100644 --- a/include/Swiften/FileTransfer/CombinedOutgoingFileTransferManager.h +++ b/include/Swiften/FileTransfer/CombinedOutgoingFileTransferManager.h @@ -11,6 +11,8 @@ #include +#include "transport/presenceoracle.h" + namespace Swift { class JingleSessionManager; @@ -30,7 +32,7 @@ class PresenceOracle; class CombinedOutgoingFileTransferManager { public: - CombinedOutgoingFileTransferManager(JingleSessionManager* jingleSessionManager, IQRouter* router, EntityCapsProvider* capsProvider, RemoteJingleTransportCandidateSelectorFactory* remoteFactory, LocalJingleTransportCandidateGeneratorFactory* localFactory, SOCKS5BytestreamRegistry* bytestreamRegistry, SOCKS5BytestreamProxy* bytestreamProxy, PresenceOracle* presOracle, SOCKS5BytestreamServer *server); + CombinedOutgoingFileTransferManager(JingleSessionManager* jingleSessionManager, IQRouter* router, EntityCapsProvider* capsProvider, RemoteJingleTransportCandidateSelectorFactory* remoteFactory, LocalJingleTransportCandidateGeneratorFactory* localFactory, SOCKS5BytestreamRegistry* bytestreamRegistry, SOCKS5BytestreamProxy* bytestreamProxy, Transport::PresenceOracle* presOracle, SOCKS5BytestreamServer *server); ~CombinedOutgoingFileTransferManager(); boost::shared_ptr createOutgoingFileTransfer(const JID& from, const JID& to, boost::shared_ptr, const StreamInitiationFileInfo&); @@ -46,7 +48,7 @@ private: IDGenerator *idGenerator; SOCKS5BytestreamRegistry* bytestreamRegistry; SOCKS5BytestreamProxy* bytestreamProxy; - PresenceOracle* presenceOracle; + Transport::PresenceOracle* presenceOracle; SOCKS5BytestreamServer *bytestreamServer; }; diff --git a/include/Swiften/Network/DummyNetworkFactories.cpp b/include/Swiften/Network/DummyNetworkFactories.cpp index 4c477561..567d467a 100644 --- a/include/Swiften/Network/DummyNetworkFactories.cpp +++ b/include/Swiften/Network/DummyNetworkFactories.cpp @@ -18,6 +18,7 @@ DummyNetworkFactories::DummyNetworkFactories(EventLoop* eventLoop) { domainNameResolver = new PlatformDomainNameResolver(eventLoop); connectionServerFactory = new DummyConnectionServerFactory(eventLoop); m_platformXMLParserFactory = new PlatformXMLParserFactory(); + this->eventLoop = eventLoop; } DummyNetworkFactories::~DummyNetworkFactories() { diff --git a/include/Swiften/Network/DummyNetworkFactories.h b/include/Swiften/Network/DummyNetworkFactories.h index a945cdfa..546cb0cc 100644 --- a/include/Swiften/Network/DummyNetworkFactories.h +++ b/include/Swiften/Network/DummyNetworkFactories.h @@ -41,6 +41,10 @@ namespace Swift { return m_platformXMLParserFactory; } + EventLoop *getEventLoop() const { + return eventLoop; + } + Swift::TLSContextFactory* getTLSContextFactory() const { return 0; } @@ -55,5 +59,6 @@ namespace Swift { ConnectionFactory* connectionFactory; DomainNameResolver* domainNameResolver; ConnectionServerFactory* connectionServerFactory; + EventLoop *eventLoop; }; } diff --git a/include/Swiften/Parser/PayloadParsers/GatewayPayloadParser.cpp b/include/Swiften/Parser/PayloadParsers/GatewayPayloadParser.cpp new file mode 100644 index 00000000..30437926 --- /dev/null +++ b/include/Swiften/Parser/PayloadParsers/GatewayPayloadParser.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2012 Jan Kaluza + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include + +#include + +#include +#include +#include +#include +#include + +namespace Swift { + +void GatewayPayloadParser::handleTree(ParserElement::ref root) { + foreach (ParserElement::ref child, root->getAllChildren()) { + if (child->getName() == "desc") { + getPayloadInternal()->setDesc(child->getText()); + } + else if (child->getName() == "prompt") { + getPayloadInternal()->setPrompt(child->getText()); + } + else if (child->getName() == "jid") { + getPayloadInternal()->setJID(child->getText()); + } + } +} + +} diff --git a/include/Swiften/Parser/PayloadParsers/GatewayPayloadParser.h b/include/Swiften/Parser/PayloadParsers/GatewayPayloadParser.h new file mode 100644 index 00000000..db2c5782 --- /dev/null +++ b/include/Swiften/Parser/PayloadParsers/GatewayPayloadParser.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2012 Jan Kaluza + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include + +#include +#include +#include + +namespace Swift { + class GatewayPayloadParser : public GenericPayloadTreeParser { + public: + GatewayPayloadParser() {} + virtual void handleTree(ParserElement::ref root); + }; +} diff --git a/include/Swiften/Parser/PayloadParsers/PubSubItemParser.cpp b/include/Swiften/Parser/PayloadParsers/PubSubItemParser.cpp new file mode 100644 index 00000000..8f23c41a --- /dev/null +++ b/include/Swiften/Parser/PayloadParsers/PubSubItemParser.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2012 Jan Kaluza + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include + +#include + +#include +#include +#include +#include +#include + +namespace Swift { + +void PubSubItemParser::handleTree(ParserElement::ref root) { + std::string id = root->getAttributes().getAttribute("id"); + if (!id.empty()) { + getPayloadInternal()->setId(id); + } + + foreach (ParserElement::ref child, root->getAllChildren()) { + getPayloadInternal()->addPayload(TreeReparser::parseTree(child, factories)); + } +} + +} diff --git a/include/Swiften/Parser/PayloadParsers/PubSubItemParser.h b/include/Swiften/Parser/PayloadParsers/PubSubItemParser.h new file mode 100644 index 00000000..9326f92d --- /dev/null +++ b/include/Swiften/Parser/PayloadParsers/PubSubItemParser.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2012 Jan Kaluza + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include + +#include +#include +#include + +namespace Swift { + class PayloadParserFactoryCollection; + class PubSubItemParser : public GenericPayloadTreeParser { + public: + PubSubItemParser(PayloadParserFactoryCollection* collection) : factories(collection) {} + virtual void handleTree(ParserElement::ref root); + private: + PayloadParserFactoryCollection* factories; + }; +} diff --git a/include/Swiften/Parser/PayloadParsers/PubSubPayloadParser.cpp b/include/Swiften/Parser/PayloadParsers/PubSubPayloadParser.cpp new file mode 100644 index 00000000..c3e151bb --- /dev/null +++ b/include/Swiften/Parser/PayloadParsers/PubSubPayloadParser.cpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2012 Jan Kaluza + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include + +#include + +#include +#include +#include +#include +#include + +namespace Swift { + +void PubSubPayloadParser::handleTree(ParserElement::ref root) { + foreach (ParserElement::ref child, root->getAllChildren()) { + getPayloadInternal()->addPayload(TreeReparser::parseTree(child, factories)); + } +} + +} diff --git a/include/Swiften/Parser/PayloadParsers/PubSubPayloadParser.h b/include/Swiften/Parser/PayloadParsers/PubSubPayloadParser.h new file mode 100644 index 00000000..7bbb688c --- /dev/null +++ b/include/Swiften/Parser/PayloadParsers/PubSubPayloadParser.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2012 Jan Kaluza + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include + +#include +#include +#include + +namespace Swift { + class PayloadParserFactoryCollection; + class PubSubPayloadParser : public GenericPayloadTreeParser { + public: + PubSubPayloadParser(PayloadParserFactoryCollection* collection) : factories(collection) {} + virtual void handleTree(ParserElement::ref root); + private: + PayloadParserFactoryCollection* factories; + }; +} diff --git a/include/Swiften/Parser/PayloadParsers/PubSubPublishPayloadParser.cpp b/include/Swiften/Parser/PayloadParsers/PubSubPublishPayloadParser.cpp new file mode 100644 index 00000000..949ceba4 --- /dev/null +++ b/include/Swiften/Parser/PayloadParsers/PubSubPublishPayloadParser.cpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2012 Jan Kaluza + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include + +#include + +#include +#include +#include +#include +#include + +namespace Swift { + +void PubSubPublishPayloadParser::handleTree(ParserElement::ref root) { + std::string node = root->getAttributes().getAttribute("node"); + if (!node.empty()) { + getPayloadInternal()->setNode(node); + } + + foreach (ParserElement::ref child, root->getAllChildren()) { + getPayloadInternal()->addItem(TreeReparser::parseTree(child, factories)); + } +} + +} diff --git a/include/Swiften/Parser/PayloadParsers/PubSubPublishPayloadParser.h b/include/Swiften/Parser/PayloadParsers/PubSubPublishPayloadParser.h new file mode 100644 index 00000000..b0575b72 --- /dev/null +++ b/include/Swiften/Parser/PayloadParsers/PubSubPublishPayloadParser.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2012 Jan Kaluza + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include + +#include +#include +#include + +namespace Swift { + class PayloadParserFactoryCollection; + class PubSubPublishPayloadParser : public GenericPayloadTreeParser { + public: + PubSubPublishPayloadParser(PayloadParserFactoryCollection* collection) : factories(collection) {} + virtual void handleTree(ParserElement::ref root); + private: + PayloadParserFactoryCollection* factories; + }; +} diff --git a/include/Swiften/Parser/PayloadParsers/PubSubSubscribePayloadParser.cpp b/include/Swiften/Parser/PayloadParsers/PubSubSubscribePayloadParser.cpp new file mode 100644 index 00000000..b7913d24 --- /dev/null +++ b/include/Swiften/Parser/PayloadParsers/PubSubSubscribePayloadParser.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2012 Jan Kaluza + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include + +#include + +#include +#include +#include +#include +#include + +namespace Swift { + +void PubSubSubscribePayloadParser::handleTree(ParserElement::ref root) { + std::string node = root->getAttributes().getAttribute("node"); + if (!node.empty()) { + getPayloadInternal()->setNode(node); + } + + std::string jid = root->getAttributes().getAttribute("jid"); + if (!jid.empty()) { + getPayloadInternal()->setJID(jid); + } + +} + +} diff --git a/include/Swiften/Parser/PayloadParsers/PubSubSubscribePayloadParser.h b/include/Swiften/Parser/PayloadParsers/PubSubSubscribePayloadParser.h new file mode 100644 index 00000000..54a2b05a --- /dev/null +++ b/include/Swiften/Parser/PayloadParsers/PubSubSubscribePayloadParser.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2012 Jan Kaluza + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include + +#include +#include +#include + +namespace Swift { + class PayloadParserFactoryCollection; + class PubSubSubscribePayloadParser : public GenericPayloadTreeParser { + public: + PubSubSubscribePayloadParser(PayloadParserFactoryCollection* collection) : factories(collection) {} + virtual void handleTree(ParserElement::ref root); + private: + PayloadParserFactoryCollection* factories; + }; +} diff --git a/include/Swiften/Parser/PayloadParsers/PubSubSubscriptionPayloadParser.cpp b/include/Swiften/Parser/PayloadParsers/PubSubSubscriptionPayloadParser.cpp new file mode 100644 index 00000000..0c964762 --- /dev/null +++ b/include/Swiften/Parser/PayloadParsers/PubSubSubscriptionPayloadParser.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2012 Jan Kaluza + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include + +#include + +#include +#include +#include +#include +#include + +namespace Swift { + +void PubSubSubscriptionPayloadParser::handleTree(ParserElement::ref root) { + std::string node = root->getAttributes().getAttribute("node"); + if (!node.empty()) { + getPayloadInternal()->setNode(node); + } + + std::string jid = root->getAttributes().getAttribute("jid"); + if (!jid.empty()) { + getPayloadInternal()->setJID(jid); + } + + std::string id = root->getAttributes().getAttribute("subid"); + if (!id.empty()) { + getPayloadInternal()->setId(id); + } + + std::string type = root->getAttributes().getAttribute("subscription"); + if (type == "none") { + getPayloadInternal()->setType(PubSubSubscriptionPayload::None); + } + else if (type == "subscribed") { + getPayloadInternal()->setType(PubSubSubscriptionPayload::Subscribed); + } + else if (type == "pending") { + getPayloadInternal()->setType(PubSubSubscriptionPayload::Pending); + } + else if (type == "unconfigured") { + getPayloadInternal()->setType(PubSubSubscriptionPayload::Unconfigured); + } + +} + +} diff --git a/include/Swiften/Parser/PayloadParsers/PubSubSubscriptionPayloadParser.h b/include/Swiften/Parser/PayloadParsers/PubSubSubscriptionPayloadParser.h new file mode 100644 index 00000000..7bd1c2d1 --- /dev/null +++ b/include/Swiften/Parser/PayloadParsers/PubSubSubscriptionPayloadParser.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2012 Jan Kaluza + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include + +#include +#include +#include + +namespace Swift { + class PayloadParserFactoryCollection; + class PubSubSubscriptionPayloadParser : public GenericPayloadTreeParser { + public: + PubSubSubscriptionPayloadParser(PayloadParserFactoryCollection* collection) : factories(collection) {} + virtual void handleTree(ParserElement::ref root); + private: + PayloadParserFactoryCollection* factories; + }; +} diff --git a/include/Swiften/Serializer/PayloadSerializers/GatewayPayloadSerializer.cpp b/include/Swiften/Serializer/PayloadSerializers/GatewayPayloadSerializer.cpp new file mode 100644 index 00000000..6b7cd9e7 --- /dev/null +++ b/include/Swiften/Serializer/PayloadSerializers/GatewayPayloadSerializer.cpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2011 Jan Kaluza + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include +#include +#include +#include +#include +#include + +namespace Swift { + +GatewayPayloadSerializer::GatewayPayloadSerializer() + : GenericPayloadSerializer() { +} + +std::string GatewayPayloadSerializer::serializePayload(boost::shared_ptr payload) const { + XMLElement query("query", "jabber:iq:gateway"); + + if (payload->getJID().isValid()) { + boost::shared_ptr jid(new XMLElement("jid", "", payload->getJID().toBare().toString())); + query.addNode(jid); + } + + if (!payload->getDesc().empty()) { + boost::shared_ptr desc(new XMLElement("desc", "", payload->getDesc())); + query.addNode(desc); + } + + if (!payload->getPrompt().empty()) { + boost::shared_ptr prompt(new XMLElement("prompt", "", payload->getPrompt())); + query.addNode(prompt); + } + + return query.serialize(); +} + +} diff --git a/include/Swiften/Serializer/PayloadSerializers/GatewayPayloadSerializer.h b/include/Swiften/Serializer/PayloadSerializers/GatewayPayloadSerializer.h new file mode 100644 index 00000000..1b64ac17 --- /dev/null +++ b/include/Swiften/Serializer/PayloadSerializers/GatewayPayloadSerializer.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2011 Jan Kaluza + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include +#include + +namespace Swift { + class GatewayPayloadSerializer : public GenericPayloadSerializer { + public: + GatewayPayloadSerializer(); + + virtual std::string serializePayload(boost::shared_ptr item) const; + }; +} diff --git a/include/Swiften/Serializer/PayloadSerializers/PubSubItemSerializer.cpp b/include/Swiften/Serializer/PayloadSerializers/PubSubItemSerializer.cpp new file mode 100644 index 00000000..47ab6492 --- /dev/null +++ b/include/Swiften/Serializer/PayloadSerializers/PubSubItemSerializer.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2011 Jan Kaluza + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include +#include +#include +#include +#include +#include + +namespace Swift { + +PubSubItemSerializer::PubSubItemSerializer(PayloadSerializerCollection *serializers) : + GenericPayloadSerializer(), serializers(serializers) { +} + +std::string PubSubItemSerializer::serializePayload(boost::shared_ptr payload) const { + XMLElement item("item"); + if (!payload->getId().empty()) { + item.setAttribute("id", payload->getId()); + } + + if (!payload->getPayloads().empty()) { + foreach(boost::shared_ptr subPayload, payload->getPayloads()) { + PayloadSerializer* serializer = serializers->getPayloadSerializer(subPayload); + if (serializer) { + item.addNode(boost::shared_ptr(new XMLRawTextNode(serializer->serialize(subPayload)))); + } + } + } + + return item.serialize(); +} + +} diff --git a/include/Swiften/Serializer/PayloadSerializers/PubSubItemSerializer.h b/include/Swiften/Serializer/PayloadSerializers/PubSubItemSerializer.h new file mode 100644 index 00000000..795c63ba --- /dev/null +++ b/include/Swiften/Serializer/PayloadSerializers/PubSubItemSerializer.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2011 Jan Kaluza + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include +#include + +namespace Swift { + class PayloadSerializerCollection; + class PubSubItemSerializer : public GenericPayloadSerializer { + public: + PubSubItemSerializer(PayloadSerializerCollection *serializers); + + virtual std::string serializePayload(boost::shared_ptr item) const; + private: + PayloadSerializerCollection *serializers; + }; +} diff --git a/include/Swiften/Serializer/PayloadSerializers/PubSubPayloadSerializer.cpp b/include/Swiften/Serializer/PayloadSerializers/PubSubPayloadSerializer.cpp new file mode 100644 index 00000000..9706d681 --- /dev/null +++ b/include/Swiften/Serializer/PayloadSerializers/PubSubPayloadSerializer.cpp @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2011 Jan Kaluza + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include +#include +#include +#include +#include +#include + +namespace Swift { + +PubSubPayloadSerializer::PubSubPayloadSerializer(PayloadSerializerCollection *serializers) + : GenericPayloadSerializer(), + serializers(serializers) { +} + +std::string PubSubPayloadSerializer::serializePayload(boost::shared_ptr payload) const { + XMLElement pubsub("pubsub", "http://jabber.org/protocol/pubsub"); + + if (!payload->getPayloads().empty()) { + foreach(boost::shared_ptr subPayload, payload->getPayloads()) { + PayloadSerializer* serializer = serializers->getPayloadSerializer(subPayload); + if (serializer) { + pubsub.addNode(boost::shared_ptr(new XMLRawTextNode(serializer->serialize(subPayload)))); + } + } + } + + return pubsub.serialize(); +} + +} diff --git a/include/Swiften/Serializer/PayloadSerializers/PubSubPayloadSerializer.h b/include/Swiften/Serializer/PayloadSerializers/PubSubPayloadSerializer.h new file mode 100644 index 00000000..0f8f251a --- /dev/null +++ b/include/Swiften/Serializer/PayloadSerializers/PubSubPayloadSerializer.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2011 Jan Kaluza + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include +#include + +namespace Swift { + class PayloadSerializerCollection; + + class PubSubPayloadSerializer : public GenericPayloadSerializer { + public: + PubSubPayloadSerializer(PayloadSerializerCollection *serializers); + + virtual std::string serializePayload(boost::shared_ptr item) const; + private: + PayloadSerializerCollection *serializers; + }; +} diff --git a/include/Swiften/Serializer/PayloadSerializers/PubSubPublishPayloadSerializer.cpp b/include/Swiften/Serializer/PayloadSerializers/PubSubPublishPayloadSerializer.cpp new file mode 100644 index 00000000..09cfbb5f --- /dev/null +++ b/include/Swiften/Serializer/PayloadSerializers/PubSubPublishPayloadSerializer.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2011 Jan Kaluza + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include +#include +#include +#include +#include +#include + +namespace Swift { + +PubSubPublishPayloadSerializer::PubSubPublishPayloadSerializer(PayloadSerializerCollection *serializers) + : GenericPayloadSerializer(), + serializers(serializers) { +} + +std::string PubSubPublishPayloadSerializer::serializePayload(boost::shared_ptr payload) const { + XMLElement publish("publish"); + + if (!payload->getNode().empty()) { + publish.setAttribute("node", payload->getNode()); + } + + if (!payload->getItems().empty()) { + foreach(boost::shared_ptr subPayload, payload->getItems()) { + PayloadSerializer* serializer = serializers->getPayloadSerializer(subPayload); + if (serializer) { + publish.addNode(boost::shared_ptr(new XMLRawTextNode(serializer->serialize(subPayload)))); + } + } + } + + return publish.serialize(); +} + +} diff --git a/include/Swiften/Serializer/PayloadSerializers/PubSubPublishPayloadSerializer.h b/include/Swiften/Serializer/PayloadSerializers/PubSubPublishPayloadSerializer.h new file mode 100644 index 00000000..ae290559 --- /dev/null +++ b/include/Swiften/Serializer/PayloadSerializers/PubSubPublishPayloadSerializer.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2011 Jan Kaluza + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include +#include + +namespace Swift { + class PayloadSerializerCollection; + + class PubSubPublishPayloadSerializer : public GenericPayloadSerializer { + public: + PubSubPublishPayloadSerializer(PayloadSerializerCollection *serializers); + + virtual std::string serializePayload(boost::shared_ptr item) const; + private: + PayloadSerializerCollection *serializers; + }; +} diff --git a/include/Swiften/Serializer/PayloadSerializers/PubSubSubscribePayloadSerializer.cpp b/include/Swiften/Serializer/PayloadSerializers/PubSubSubscribePayloadSerializer.cpp new file mode 100644 index 00000000..f6a96626 --- /dev/null +++ b/include/Swiften/Serializer/PayloadSerializers/PubSubSubscribePayloadSerializer.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2011 Jan Kaluza + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include +#include +#include +#include +#include +#include + +namespace Swift { + +PubSubSubscribePayloadSerializer::PubSubSubscribePayloadSerializer() + : GenericPayloadSerializer() { +} + +std::string PubSubSubscribePayloadSerializer::serializePayload(boost::shared_ptr payload) const { + XMLElement subscribe("subscribe"); + + if (!payload->getJID().isValid()) { + subscribe.setAttribute("jid", payload->getJID().toBare().toString()); + } + + if (!payload->getNode().empty()) { + subscribe.setAttribute("node", payload->getNode()); + } + + return subscribe.serialize(); +} + +} diff --git a/include/Swiften/Serializer/PayloadSerializers/PubSubSubscribePayloadSerializer.h b/include/Swiften/Serializer/PayloadSerializers/PubSubSubscribePayloadSerializer.h new file mode 100644 index 00000000..327703be --- /dev/null +++ b/include/Swiften/Serializer/PayloadSerializers/PubSubSubscribePayloadSerializer.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2011 Jan Kaluza + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include +#include + +namespace Swift { + class PubSubSubscribePayloadSerializer : public GenericPayloadSerializer { + public: + PubSubSubscribePayloadSerializer(); + + virtual std::string serializePayload(boost::shared_ptr item) const; + }; +} diff --git a/include/Swiften/Serializer/PayloadSerializers/PubSubSubscriptionPayloadSerializer.cpp b/include/Swiften/Serializer/PayloadSerializers/PubSubSubscriptionPayloadSerializer.cpp new file mode 100644 index 00000000..02a634a1 --- /dev/null +++ b/include/Swiften/Serializer/PayloadSerializers/PubSubSubscriptionPayloadSerializer.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2011 Jan Kaluza + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include +#include +#include +#include +#include +#include + +namespace Swift { + +PubSubSubscriptionPayloadSerializer::PubSubSubscriptionPayloadSerializer() + : GenericPayloadSerializer() { +} + +std::string PubSubSubscriptionPayloadSerializer::serializePayload(boost::shared_ptr payload) const { + XMLElement subscription("subscription"); + + if (!payload->getJID().isValid()) { + subscription.setAttribute("jid", payload->getJID().toBare().toString()); + } + + if (!payload->getNode().empty()) { + subscription.setAttribute("node", payload->getNode()); + } + + switch (payload->getType()) { + case PubSubSubscriptionPayload::None: + subscription.setAttribute("subscription", "none"); + break; + case PubSubSubscriptionPayload::Subscribed: + subscription.setAttribute("subscription", "subscribed"); + break; + case PubSubSubscriptionPayload::Unconfigured: + subscription.setAttribute("subscription", "unconfigured"); + break; + case PubSubSubscriptionPayload::Pending: + subscription.setAttribute("subscription", "pending"); + break; + } + + return subscription.serialize(); +} + +} diff --git a/include/Swiften/Serializer/PayloadSerializers/PubSubSubscriptionPayloadSerializer.h b/include/Swiften/Serializer/PayloadSerializers/PubSubSubscriptionPayloadSerializer.h new file mode 100644 index 00000000..0e06a34b --- /dev/null +++ b/include/Swiften/Serializer/PayloadSerializers/PubSubSubscriptionPayloadSerializer.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2011 Jan Kaluza + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include +#include + +namespace Swift { + class PubSubSubscriptionPayloadSerializer : public GenericPayloadSerializer { + public: + PubSubSubscriptionPayloadSerializer(); + + virtual std::string serializePayload(boost::shared_ptr item) const; + }; +} diff --git a/include/Swiften/Server/ServerStanzaChannel.cpp b/include/Swiften/Server/ServerStanzaChannel.cpp index bb3fbf6e..9597cc1c 100644 --- a/include/Swiften/Server/ServerStanzaChannel.cpp +++ b/include/Swiften/Server/ServerStanzaChannel.cpp @@ -32,11 +32,13 @@ void ServerStanzaChannel::addSession(boost::shared_ptr sessions[session->getRemoteJID().toBare().toString()].push_back(session); session->onSessionFinished.connect(boost::bind(&ServerStanzaChannel::handleSessionFinished, this, _1, session)); session->onElementReceived.connect(boost::bind(&ServerStanzaChannel::handleElement, this, _1, session)); + session->onDataRead.connect(boost::bind(&ServerStanzaChannel::handleDataRead, this, _1, session)); } void ServerStanzaChannel::removeSession(boost::shared_ptr session) { session->onSessionFinished.disconnect(boost::bind(&ServerStanzaChannel::handleSessionFinished, this, _1, session)); session->onElementReceived.disconnect(boost::bind(&ServerStanzaChannel::handleElement, this, _1, session)); + session->onDataRead.disconnect(boost::bind(&ServerStanzaChannel::handleDataRead, this, _1, session)); std::list > &lst = sessions[session->getRemoteJID().toBare().toString()]; lst.erase(std::remove(lst.begin(), lst.end(), session), lst.end()); } @@ -53,6 +55,15 @@ void ServerStanzaChannel::sendPresence(boost::shared_ptr presence) { send(presence); } +void ServerStanzaChannel::handleDataRead(const SafeByteArray &data, const boost::shared_ptr &session) { + if (safeByteArrayToString(data).find("") != std::string::npos) { + Swift::Presence::ref presence = Swift::Presence::create(); + presence->setFrom(session->getRemoteJID()); + presence->setType(Swift::Presence::Unavailable); + onPresenceReceived(presence); + } +} + void ServerStanzaChannel::finishSession(const JID& to, boost::shared_ptr element, bool last) { std::vector > candidateSessions; for (std::list >::const_iterator i = sessions[to.toBare().toString()].begin(); i != sessions[to.toBare().toString()].end(); ++i) { diff --git a/include/Swiften/Server/ServerStanzaChannel.h b/include/Swiften/Server/ServerStanzaChannel.h index 69158eea..99c857be 100644 --- a/include/Swiften/Server/ServerStanzaChannel.h +++ b/include/Swiften/Server/ServerStanzaChannel.h @@ -41,6 +41,7 @@ namespace Swift { void send(boost::shared_ptr stanza); void handleSessionFinished(const boost::optional&, const boost::shared_ptr &session); void handleElement(boost::shared_ptr element, const boost::shared_ptr &session); + void handleDataRead(const SafeByteArray &data, const boost::shared_ptr &session); void handleSessionInitialized(); private: 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/include/transport/gatewayresponder.h b/include/transport/gatewayresponder.h new file mode 100644 index 00000000..1acf4403 --- /dev/null +++ b/include/transport/gatewayresponder.h @@ -0,0 +1,43 @@ +/** + * libtransport -- C++ library for easy XMPP Transports development + * + * Copyright (C) 2011, 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 + */ + +#pragma once + +#include +#include "Swiften/Swiften.h" +#include "Swiften/Queries/Responder.h" +#include "Swiften/Elements/GatewayPayload.h" + +namespace Transport { + +class UserManager; + +class GatewayResponder : public Swift::Responder { + public: + GatewayResponder(Swift::IQRouter *router, UserManager *userManager); + ~GatewayResponder(); + + private: + virtual bool handleGetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr payload); + virtual bool handleSetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr payload); + UserManager *m_userManager; +}; + +} \ No newline at end of file diff --git a/include/transport/pqxxbackend.h b/include/transport/pqxxbackend.h new file mode 100644 index 00000000..f571433b --- /dev/null +++ b/include/transport/pqxxbackend.h @@ -0,0 +1,112 @@ +/** + * libtransport -- C++ library for easy XMPP Transports development + * + * Copyright (C) 2011, 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 + */ + +#pragma once + +#ifdef WITH_PQXX + +#include +#include +#include "Swiften/Swiften.h" +#include "transport/storagebackend.h" +#include "transport/config.h" +#include + +namespace Transport { + +/// Used to store transport data into SQLite3 database. +class PQXXBackend : public StorageBackend +{ + public: + /// Creates new PQXXBackend instance. + /// \param config cofiguration, this class uses following Config values: + /// - database.database - path to SQLite3 database file, database file is created automatically + /// - service.prefix - prefix for tables created by createDatabase method + PQXXBackend(Config *config); + + /// Destructor. + ~PQXXBackend(); + + /// Connects to the database and creates it if it's needed. This method call createDatabase() function + /// automatically. + /// \return true if database is opened successfully. + bool connect(); + void disconnect(); + + /// Creates database structure. + /// \see connect() + /// \return true if database structure has been created successfully. Note that it returns True also if database structure + /// already exists. + bool createDatabase(); + + /// Stores user into database. + /// \param user user struct containing all information about user which have to be stored + void setUser(const UserInfo &user); + + /// Gets user data from database and stores them into user reference. + /// \param barejid barejid of user + /// \param user UserInfo object where user data will be stored + /// \return true if user has been found in database + bool getUser(const std::string &barejid, UserInfo &user); + + /// Changes users online state variable in database. + /// \param id id of user - UserInfo.id + /// \param online online state + void setUserOnline(long id, bool online); + + /// Removes user and all connected data from database. + /// \param id id of user - UserInfo.id + /// \return true if user has been found in database and removed + bool removeUser(long id); + + /// Returns JIDs of all buddies in user's roster. + /// \param id id of user - UserInfo.id + /// \param roster string list used to store user's roster + /// \return true if user has been found in database and roster has been fetched + bool getBuddies(long id, std::list &roster); + + bool getOnlineUsers(std::vector &users); + + long addBuddy(long userId, const BuddyInfo &buddyInfo); + + void updateBuddy(long userId, const BuddyInfo &buddyInfo); + void removeBuddy(long id) {} + + void getUserSetting(long userId, const std::string &variable, int &type, std::string &value); + void updateUserSetting(long userId, const std::string &variable, const std::string &value); + + void beginTransaction(); + void commitTransaction(); + + private: + bool exec(const std::string &query, bool show_error = true); + bool exec(pqxx::nontransaction &txn, const std::string &query, bool show_error = true); + template + std::string quote(pqxx::nontransaction &txn, const T &t); + + Config *m_config; + std::string m_prefix; + + pqxx::connection *m_conn; +}; + +} + +#endif diff --git a/include/transport/presenceoracle.h b/include/transport/presenceoracle.h new file mode 100644 index 00000000..763cf83d --- /dev/null +++ b/include/transport/presenceoracle.h @@ -0,0 +1,57 @@ +/** + * 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 + */ + +#pragma once + +#include + +#include +#include +#include + +#include + +namespace Transport { + +class PresenceOracle { + public: + PresenceOracle(Swift::StanzaChannel* stanzaChannel); + ~PresenceOracle(); + + Swift::Presence::ref getLastPresence(const Swift::JID&) const; + Swift::Presence::ref getHighestPriorityPresence(const Swift::JID& bareJID) const; + std::vector getAllPresence(const Swift::JID& bareJID) const; + + public: + boost::signal onPresenceChange; + + private: + void handleIncomingPresence(Swift::Presence::ref presence); + void handleStanzaChannelAvailableChanged(bool); + + private: + typedef std::map PresenceMap; + typedef std::map PresencesMap; + PresencesMap entries_; + Swift::StanzaChannel* stanzaChannel_; +}; + +} + diff --git a/include/transport/transport.h b/include/transport/transport.h index 40442dcc..4a875754 100644 --- a/include/transport/transport.h +++ b/include/transport/transport.h @@ -27,7 +27,6 @@ #include "Swiften/Disco/EntityCapsManager.h" #include "Swiften/Disco/CapsManager.h" #include "Swiften/Disco/CapsMemoryStorage.h" -#include "Swiften/Presence/PresenceOracle.h" #include "Swiften/Network/BoostTimerFactory.h" #include "Swiften/Network/BoostIOServiceThread.h" #include "Swiften/Server/UserRegistry.h" @@ -37,6 +36,7 @@ #include #include "transport/config.h" #include "transport/factory.h" +#include "transport/presenceoracle.h" namespace Transport { // typedef enum { CLIENT_FEATURE_ROSTERX = 2, @@ -92,7 +92,7 @@ namespace Transport { /// You can use it to check current resource connected for particular user. /// \return Swift::PresenceOracle associated with this Transport::Component. - Swift::PresenceOracle *getPresenceOracle(); + PresenceOracle *getPresenceOracle(); /// Returns True if the component is in server mode. @@ -179,7 +179,7 @@ namespace Transport { Swift::EntityCapsManager *m_entityCapsManager; Swift::CapsManager *m_capsManager; Swift::CapsMemoryStorage *m_capsMemoryStorage; - Swift::PresenceOracle *m_presenceOracle; + PresenceOracle *m_presenceOracle; Swift::StanzaChannel *m_stanzaChannel; Swift::IQRouter *m_iqRouter; diff --git a/include/transport/user.h b/include/transport/user.h index de134634..4e98c4a8 100644 --- a/include/transport/user.h +++ b/include/transport/user.h @@ -22,7 +22,6 @@ #include #include "Swiften/Swiften.h" -#include "Swiften/Presence/PresenceOracle.h" #include "Swiften/Disco/EntityCapsManager.h" #include "Swiften/Disco/EntityCapsProvider.h" #include "storagebackend.h" @@ -35,6 +34,7 @@ class Component; class RosterManager; class ConversationManager; class UserManager; +class PresenceOracle; struct UserInfo; /// Represents online XMPP user. @@ -125,7 +125,7 @@ class User : public Swift::EntityCapsProvider { UserManager *m_userManager; ConversationManager *m_conversationManager; Swift::EntityCapsManager *m_entityCapsManager; - Swift::PresenceOracle *m_presenceOracle; + PresenceOracle *m_presenceOracle; UserInfo m_userInfo; void *m_data; bool m_connected; diff --git a/include/transport/usermanager.h b/include/transport/usermanager.h index 290c4235..5ea47d5c 100644 --- a/include/transport/usermanager.h +++ b/include/transport/usermanager.h @@ -104,6 +104,10 @@ class UserManager : public Swift::EntityCapsProvider { return m_userRegistry; } + Component *getComponent() { + return m_component; + } + /// Connects user manually. /// \param user JID of user. void connectUser(const Swift::JID &user); diff --git a/include/transport/util.h b/include/transport/util.h index fe171115..d79a555f 100644 --- a/include/transport/util.h +++ b/include/transport/util.h @@ -40,6 +40,8 @@ std::string serializeGroups(const std::vector &groups); std::vector deserializeGroups(std::string &groups); +int getRandomPort(const std::string &s); + } } diff --git a/spectrum/src/main.cpp b/spectrum/src/main.cpp index c0b8fb9b..d05bdeb8 100644 --- a/spectrum/src/main.cpp +++ b/spectrum/src/main.cpp @@ -5,12 +5,14 @@ #include "transport/logger.h" #include "transport/sqlite3backend.h" #include "transport/mysqlbackend.h" +#include "transport/pqxxbackend.h" #include "transport/userregistration.h" #include "transport/networkpluginserver.h" #include "transport/admininterface.h" #include "transport/statsresponder.h" #include "transport/usersreconnecter.h" #include "transport/util.h" +#include "transport/gatewayresponder.h" #include "Swiften/EventLoop/SimpleEventLoop.h" #include #include @@ -379,7 +381,23 @@ int main(int argc, char **argv) } #endif - if (CONFIG_STRING(&config, "database.type") != "mysql" && CONFIG_STRING(&config, "database.type") != "sqlite3") { +#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; } @@ -404,6 +422,9 @@ int main(int argc, char **argv) StatsResponder statsResponder(&transport, &userManager, &plugin, storageBackend); statsResponder.start(); + GatewayResponder gatewayResponder(transport.getIQRouter(), &userManager); + gatewayResponder.start(); + eventLoop_ = &eventLoop; eventLoop.run(); diff --git a/spectrum/src/sample.cfg b/spectrum/src/sample.cfg index fcb060d3..d0e234e9 100644 --- a/spectrum/src/sample.cfg +++ b/spectrum/src/sample.cfg @@ -14,6 +14,7 @@ admin_password=test #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/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 @@ -21,10 +22,12 @@ backend=/home/hanzz/code/libtransport/backends/libpurple/spectrum2_libpurple_bac protocol=any #protocol=prpl-icq irc_server=irc.freenode.org +working_dir=./ [backend] #default_avatar=catmelonhead.jpg #no_vcard_fetch=true +incoming_dir=/var/spool/sms/incoming [logging] #config=logging.cfg # log4cxx/log4j logging configuration file @@ -34,3 +37,9 @@ irc_server=irc.freenode.org type = none # 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 ae1f0d73..3181a6a1 100644 --- a/spectrum/src/sample2.cfg +++ b/spectrum/src/sample2.cfg @@ -24,7 +24,9 @@ port = 5222 backend_host = localhost # Port on which Spectrum listens for backends. -backend_port=10001 +# By default Spectrum chooses random backend port and there's +# no need to change it normally +#backend_port=10001 # Full path to PKCS#12 cetficiate used for TLS in server mode. #cert= @@ -38,6 +40,8 @@ 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/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 @@ -69,11 +73,11 @@ backend_config = /etc/spectrum2/backend-logging.cfg [database] # Database backend type -# "sqlite3", "mysql" or "none" without database backend +# "sqlite3", "mysql", "pqxx", or "none" without database backend type = none # For SQLite3: Full path to database -# For MySQL: name of database +# For MySQL and PostgreSQL: name of database # default database = /var/lib/spectrum2/$jid/database.sql #database = jabber_transport @@ -91,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/spectrum_manager/src/CMakeLists.txt b/spectrum_manager/src/CMakeLists.txt index 5024e70c..5f93dd9c 100644 --- a/spectrum_manager/src/CMakeLists.txt +++ b/spectrum_manager/src/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 2.6) FILE(GLOB SRC *.cpp) -ADD_EXECUTABLE(spectrum2_manager ${SRC} ../../src/config.cpp) +ADD_EXECUTABLE(spectrum2_manager ${SRC} ../../src/config.cpp ../../src/util.cpp) target_link_libraries(spectrum2_manager ${SWIFTEN_LIBRARY}) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7a258e7a..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 ${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 ${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 b72caa86..7088889c 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -19,6 +19,7 @@ */ #include "transport/config.h" +#include "transport/util.h" #include #ifdef _MSC_VER #include @@ -86,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.") @@ -106,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") { @@ -120,13 +126,7 @@ bool Config::load(std::istream &ifs, boost::program_options::options_description else if (opt.string_key == "service.backend_port") { found_backend_port = true; if (opt.value[0] == "0") { - unsigned long r = 0; - BOOST_FOREACH(char c, _jid) { - r += (int) c; - } - srand(time(NULL) + r); - int randomPort = 30000 + rand() % 10000; - opt.value[0] = boost::lexical_cast(randomPort); + opt.value[0] = boost::lexical_cast(Util::getRandomPort(_jid.empty() ? jid : _jid)); } } else if (opt.string_key == "service.working_dir") { @@ -135,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,16 +151,16 @@ bool Config::load(std::istream &ifs, boost::program_options::options_description parsed.options.push_back(boost::program_options::basic_option("service.pidfile", value)); } if (!found_backend_port) { - unsigned long r = 0; - BOOST_FOREACH(char c, _jid) { - r += (int) c; - } - srand(time(NULL) + r); - int randomPort = 30000 + rand() % 10000; std::vector value; - value.push_back(boost::lexical_cast(randomPort)); + std::string p = boost::lexical_cast(Util::getRandomPort(_jid.empty() ? jid : _jid)); + 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/gatewayresponder.cpp b/src/gatewayresponder.cpp new file mode 100644 index 00000000..903c069a --- /dev/null +++ b/src/gatewayresponder.cpp @@ -0,0 +1,63 @@ +/** + * 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 "transport/gatewayresponder.h" + +#include +#include +#include "Swiften/Queries/IQRouter.h" +#include "Swiften/Elements/RawXMLPayload.h" +#include "Swiften/Swiften.h" +#include "transport/usermanager.h" +#include "transport/user.h" +#include "transport/transport.h" +#include "log4cxx/logger.h" + +using namespace log4cxx; + +using namespace Swift; +using namespace boost; + +namespace Transport { + +static LoggerPtr logger = Logger::getLogger("GatewayResponder"); + +GatewayResponder::GatewayResponder(Swift::IQRouter *router, UserManager *userManager) : Swift::Responder(router) { + m_userManager = userManager; +} + +GatewayResponder::~GatewayResponder() { +} + +bool GatewayResponder::handleGetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr payload) { + sendResponse(from, id, boost::shared_ptr(new GatewayPayload(Swift::JID(), "Enter legacy network contact ID.", "Contact ID"))); + return true; +} + +bool GatewayResponder::handleSetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr payload) { + std::string prompt = payload->getPrompt(); + std::string escaped = Swift::JID::getEscapedNode(prompt); + std::string jid = escaped + "@" + m_userManager->getComponent()->getJID().toBare().toString(); + + sendResponse(from, id, boost::shared_ptr(new GatewayPayload(jid))); + return true; +} + +} diff --git a/src/localbuddy.cpp b/src/localbuddy.cpp index a12b9ee9..55f7e27c 100644 --- a/src/localbuddy.cpp +++ b/src/localbuddy.cpp @@ -32,11 +32,11 @@ LocalBuddy::~LocalBuddy() { } void LocalBuddy::setAlias(const std::string &alias) { - if (m_firstSet) { - m_firstSet = false; - m_alias = alias; - return; - } +// if (m_firstSet) { +// m_firstSet = false; +// m_alias = alias; +// return; +// } bool changed = m_alias != alias; m_alias = alias; 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..00b7f9cd 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) { @@ -194,6 +195,7 @@ static void handleBuddyPayload(LocalBuddy *buddy, const pbnetwork::Buddy &payloa // Set alias only if it's not empty. Backends are allowed to send empty alias if it has // not changed. if (!payload.alias().empty()) { + LOG4CXX_INFO(logger, "Setting alias to " << payload.alias() << " " << buddy->getAlias()); buddy->setAlias(payload.alias()); } @@ -257,6 +259,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() { @@ -476,6 +479,8 @@ void NetworkPluginServer::handleBuddyChangedPayload(const std::string &data) { if (!user) return; + LOG4CXX_INFO(logger, "HANDLE BUDDY CHANGED " << payload.buddyname() << "-" << payload.alias()); + LocalBuddy *buddy = (LocalBuddy *) user->getRosterManager()->getBuddy(payload.buddyname()); if (buddy) { handleBuddyPayload(buddy, payload); diff --git a/src/pqxxbackend.cpp b/src/pqxxbackend.cpp new file mode 100644 index 00000000..c2ae4580 --- /dev/null +++ b/src/pqxxbackend.cpp @@ -0,0 +1,391 @@ +/** + * libtransport -- C++ library for easy XMPP Transports development + * + * Copyright (C) 2011, 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 + */ + +#ifdef WITH_PQXX + +#include "transport/pqxxbackend.h" +#include "transport/util.h" +#include +#include "log4cxx/logger.h" + +using namespace log4cxx; +using namespace boost; + +namespace Transport { + +static LoggerPtr logger = Logger::getLogger("PQXXBackend"); + +PQXXBackend::PQXXBackend(Config *config) { + m_config = config; + m_prefix = CONFIG_STRING(m_config, "database.prefix"); +} + +PQXXBackend::~PQXXBackend(){ + disconnect(); +} + +void PQXXBackend::disconnect() { + LOG4CXX_INFO(logger, "Disconnecting"); + + delete m_conn; +} + +bool PQXXBackend::connect() { + LOG4CXX_INFO(logger, "Connecting PostgreSQL server " << CONFIG_STRING(m_config, "database.server") << ", user " << + CONFIG_STRING(m_config, "database.user") << ", database " << CONFIG_STRING(m_config, "database.database") << + ", port " << CONFIG_INT(m_config, "database.port") + ); + + std::string str = "dbname="; + str += CONFIG_STRING(m_config, "database.database") + " "; + + str += "user=" + CONFIG_STRING(m_config, "database.user") + " "; + + try { + m_conn = new pqxx::connection(str); + } + catch (std::exception& e) { + LOG4CXX_ERROR(logger, e.what()); + return false; + } + + createDatabase(); + + return true; +} + +bool PQXXBackend::createDatabase() { + + int exist = exec("SELECT * FROM " + m_prefix + "buddies_settings LIMIT 1;", false); + + if (!exist) { + exec("CREATE TABLE " + m_prefix + "buddies_settings (" + "user_id integer NOT NULL," + "buddy_id integer NOT NULL," + "var varchar(50) NOT NULL," + "type smallint NOT NULL," + "value varchar(255) NOT NULL," + "PRIMARY KEY (buddy_id,var)" + ");"); + + exec("CREATE TYPE Subscription AS ENUM ('to','from','both','ask','none');"); + exec("CREATE TABLE " + m_prefix + "buddies (" + "id SERIAL," + "user_id integer NOT NULL," + "uin varchar(255) NOT NULL," + "subscription Subscription NOT NULL," + "nickname varchar(255) NOT NULL," + "groups varchar(255) NOT NULL," + "flags smallint NOT NULL DEFAULT '0'," + "PRIMARY KEY (id)," + "UNIQUE (user_id,uin)" + ");"); + + exec("CREATE TABLE " + m_prefix + "users (" + "id SERIAL," + "jid varchar(255) NOT NULL," + "uin varchar(4095) NOT NULL," + "password varchar(255) NOT NULL," + "language varchar(25) NOT NULL," + "encoding varchar(50) NOT NULL default 'utf8'," + "last_login timestamp," + "vip boolean NOT NULL default 'false'," + "online boolean NOT NULL default 'false'," + "PRIMARY KEY (id)," + "UNIQUE (jid)" + ");"); + + exec("CREATE TABLE " + m_prefix + "users_settings (" + "user_id integer NOT NULL," + "var varchar(50) NOT NULL," + "type smallint NOT NULL," + "value varchar(255) NOT NULL," + "PRIMARY KEY (user_id,var)" + ");"); + + exec("CREATE TABLE " + m_prefix + "db_version (" + "ver integer NOT NULL default '1'," + "UNIQUE (ver)" + ");"); + + exec("INSERT INTO db_version (ver) VALUES ('1');"); + } + + return true; +} + +template +std::string PQXXBackend::quote(pqxx::nontransaction &txn, const T &t) { + return "'" + txn.esc(pqxx::to_string(t)) + "'"; +} + +bool PQXXBackend::exec(const std::string &query, bool show_error) { + pqxx::nontransaction txn(*m_conn); + return exec(txn, query, show_error); +} + +bool PQXXBackend::exec(pqxx::nontransaction &txn, const std::string &query, bool show_error) { + try { + txn.exec(query); + txn.commit(); + } + catch (std::exception& e) { + if (show_error) + LOG4CXX_ERROR(logger, e.what()); + return false; + } + return true; +} + +void PQXXBackend::setUser(const UserInfo &user) { + std::string encrypted = user.password; + if (!CONFIG_STRING(m_config, "database.encryption_key").empty()) { + encrypted = Util::encryptPassword(encrypted, CONFIG_STRING(m_config, "database.encryption_key")); + } + try { + pqxx::nontransaction txn(*m_conn); + txn.exec("INSERT INTO " + m_prefix + "users (jid, uin, password, language, encoding, last_login, vip) VALUES " + + "(" + quote(txn, user.jid) + "," + + quote(txn, user.uin) + "," + + quote(txn, encrypted) + "," + + quote(txn, user.language) + "," + + quote(txn, user.encoding) + "," + + "NOW()," + + (user.vip ? "'true'" : "'false'") +")"); + } + catch (std::exception& e) { + LOG4CXX_ERROR(logger, e.what()); + } +} + +bool PQXXBackend::getUser(const std::string &barejid, UserInfo &user) { + try { + pqxx::nontransaction txn(*m_conn); + + pqxx::result r = txn.exec("SELECT id, jid, uin, password, encoding, language, vip FROM " + m_prefix + "users WHERE jid=" + + quote(txn, barejid)); + + if (r.size() == 0) { + return false; + } + + user.id = r[0][0].as(); + user.jid = r[0][1].as(); + user.uin = r[0][2].as(); + user.password = r[0][3].as(); + user.encoding = r[0][4].as(); + user.language = r[0][5].as(); + user.vip = r[0][6].as(); + } + catch (std::exception& e) { + LOG4CXX_ERROR(logger, e.what()); + return false; + } + + return true; +} + +void PQXXBackend::setUserOnline(long id, bool online) { + try { + pqxx::nontransaction txn(*m_conn); + txn.exec("UPDATE " + m_prefix + "users SET online=" + (online ? "'true'" : "'false'") + ", last_login=NOW() WHERE id=" + pqxx::to_string(id)); + } + catch (std::exception& e) { + LOG4CXX_ERROR(logger, e.what()); + } +} + +bool PQXXBackend::getOnlineUsers(std::vector &users) { + try { + pqxx::nontransaction txn(*m_conn); + pqxx::result r = txn.exec("SELECT jid FROM " + m_prefix + "users WHERE online='true'"); + + for (pqxx::result::const_iterator it = r.begin(); it != r.end(); it++) { + users.push_back((*it)[0].as()); + } + } + catch (std::exception& e) { + LOG4CXX_ERROR(logger, e.what()); + return false; + } + + return true; +} + +long PQXXBackend::addBuddy(long userId, const BuddyInfo &buddyInfo) { + try { + pqxx::nontransaction txn(*m_conn); + pqxx::result r = txn.exec("INSERT INTO " + m_prefix + "buddies (user_id, uin, subscription, groups, nickname, flags) VALUES " + + "(" + pqxx::to_string(userId) + "," + + quote(txn, buddyInfo.legacyName) + "," + + quote(txn, buddyInfo.subscription) + "," + + quote(txn, Util::serializeGroups(buddyInfo.groups)) + "," + + quote(txn, buddyInfo.alias) + "," + + pqxx::to_string(buddyInfo.flags) + ") RETURNING id"); + + long id = r[0][0].as(); + + r = txn.exec("UPDATE " + m_prefix + "buddies_settings SET var = " + quote(txn, buddyInfo.settings.find("icon_hash")->first) + ", type = " + pqxx::to_string((int)TYPE_STRING) + ", value = " + quote(txn, buddyInfo.settings.find("icon_hash")->second.s) + " WHERE user_id = " + pqxx::to_string(userId) + " AND buddy_id = " + pqxx::to_string(id)); + if (r.affected_rows() == 0) { + txn.exec("INSERT INTO " + m_prefix + "buddies_settings (user_id, buddy_id, var, type, value) VALUES " + + "(" + pqxx::to_string(userId) + "," + + pqxx::to_string(id) + "," + + quote(txn, buddyInfo.settings.find("icon_hash")->first) + "," + + pqxx::to_string((int)TYPE_STRING) + "," + + quote(txn, buddyInfo.settings.find("icon_hash")->second.s) + ")"); + } + + return id; + } + catch (std::exception& e) { + LOG4CXX_ERROR(logger, e.what()); + return -1; + } +} + +void PQXXBackend::updateBuddy(long userId, const BuddyInfo &buddyInfo) { + try { + pqxx::nontransaction txn(*m_conn); + txn.exec("UPDATE " + m_prefix + "buddies SET groups=" + quote(txn, Util::serializeGroups(buddyInfo.groups)) + ", nickname=" + quote(txn, buddyInfo.alias) + ", flags=" + pqxx::to_string(buddyInfo.flags) + ", subscription=" + quote(txn, buddyInfo.subscription) + " WHERE user_id=" + pqxx::to_string(userId) + " AND uin=" + quote(txn, buddyInfo.legacyName)); + } + catch (std::exception& e) { + LOG4CXX_ERROR(logger, e.what()); + } +} + +bool PQXXBackend::getBuddies(long id, std::list &roster) { + try { + pqxx::nontransaction txn(*m_conn); + + pqxx::result r = txn.exec("SELECT id, uin, subscription, nickname, groups, flags FROM " + m_prefix + "buddies WHERE user_id=" + pqxx::to_string(id) + " ORDER BY id ASC"); + for (pqxx::result::const_iterator it = r.begin(); it != r.end(); it++) { + BuddyInfo b; + std::string group; + + b.id = r[0][0].as(); + b.legacyName = r[0][1].as(); + b.subscription = r[0][2].as(); + b.alias = r[0][3].as(); + group = r[0][4].as(); + b.flags = r[0][5].as(); + + if (!group.empty()) { + b.groups = Util::deserializeGroups(group); + } + + roster.push_back(b); + } + + + r = txn.exec("SELECT buddy_id, type, var, value FROM " + m_prefix + "buddies_settings WHERE user_id=" + pqxx::to_string(id) + " ORDER BY buddy_id ASC"); + for (pqxx::result::const_iterator it = r.begin(); it != r.end(); it++) { + SettingVariableInfo var; + long buddy_id = -1; + std::string key; + std::string val; + + buddy_id = r[0][0].as(); + var.type = r[0][1].as(); + key = r[0][2].as(); + val = r[0][3].as(); + switch (var.type) { + case TYPE_BOOLEAN: + var.b = atoi(val.c_str()); + break; + case TYPE_STRING: + var.s = val; + break; + default: + continue; + break; + } + + BOOST_FOREACH(BuddyInfo &b, roster) { + if (buddy_id == b.id) { + b.settings[key] = var; + break; + } + } + } + + return true; + } + catch (std::exception& e) { + LOG4CXX_ERROR(logger, e.what()); + } + + return false; +} + +bool PQXXBackend::removeUser(long id) { + try { + pqxx::nontransaction txn(*m_conn); + txn.exec("DELETE FROM " + m_prefix + "users SET id=" + pqxx::to_string(id)); + txn.exec("DELETE FROM " + m_prefix + "buddies SET user_id=" + pqxx::to_string(id)); + txn.exec("DELETE FROM " + m_prefix + "user_settings SET user_id=" + pqxx::to_string(id)); + txn.exec("DELETE FROM " + m_prefix + "buddies_settings SET user_id=" + pqxx::to_string(id)); + + return true; + } + catch (std::exception& e) { + LOG4CXX_ERROR(logger, e.what()); + } + return false; +} + +void PQXXBackend::getUserSetting(long id, const std::string &variable, int &type, std::string &value) { + try { + pqxx::nontransaction txn(*m_conn); + + pqxx::result r = txn.exec("SELECT type, value FROM " + m_prefix + "users_settings WHERE user_id=" + pqxx::to_string(id) + " AND var=" + quote(txn, variable)); + if (r.size() == 0) { + txn.exec("INSERT INTO " + m_prefix + "users_settings (user_id, var, type, value) VALUES(" + pqxx::to_string(id) + "," + quote(txn, variable) + "," + pqxx::to_string((int)type) + "," + quote(txn, value) + ")"); + } + else { + type = r[0][0].as(); + value = r[0][1].as(); + } + } + catch (std::exception& e) { + LOG4CXX_ERROR(logger, e.what()); + } +} + +void PQXXBackend::updateUserSetting(long id, const std::string &variable, const std::string &value) { + try { + pqxx::nontransaction txn(*m_conn); + txn.exec("UPDATE " + m_prefix + "users_settings SET value=" + quote(txn, value) + " WHERE user_id=" + pqxx::to_string(id) + " AND var=" + quote(txn, variable)); + } + catch (std::exception& e) { + LOG4CXX_ERROR(logger, e.what()); + } +} + +void PQXXBackend::beginTransaction() { + exec("BEGIN;"); +} + +void PQXXBackend::commitTransaction() { + exec("COMMIT;"); +} + +} + +#endif diff --git a/src/presenceoracle.cpp b/src/presenceoracle.cpp new file mode 100644 index 00000000..a3245502 --- /dev/null +++ b/src/presenceoracle.cpp @@ -0,0 +1,136 @@ +/** + * 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 "transport/presenceoracle.h" +#include "Swiften/Swiften.h" + +#include + +using namespace Swift; + +namespace Transport { + +PresenceOracle::PresenceOracle(StanzaChannel* stanzaChannel) { + stanzaChannel_ = stanzaChannel; + stanzaChannel_->onPresenceReceived.connect(boost::bind(&PresenceOracle::handleIncomingPresence, this, _1)); + stanzaChannel_->onAvailableChanged.connect(boost::bind(&PresenceOracle::handleStanzaChannelAvailableChanged, this, _1)); +} + +PresenceOracle::~PresenceOracle() { + stanzaChannel_->onPresenceReceived.disconnect(boost::bind(&PresenceOracle::handleIncomingPresence, this, _1)); + stanzaChannel_->onAvailableChanged.disconnect(boost::bind(&PresenceOracle::handleStanzaChannelAvailableChanged, this, _1)); +} + +void PresenceOracle::handleStanzaChannelAvailableChanged(bool available) { + if (available) { + entries_.clear(); + } +} + + +void PresenceOracle::handleIncomingPresence(Presence::ref presence) { + // ignore presences for some contact, we're checking only presences for the transport itself here. + bool isMUC = presence->getPayload() != NULL || *presence->getTo().getNode().c_str() == '#'; + // filter out login/logout presence spam + if (!presence->getTo().getNode().empty() && isMUC == false) + return; + + JID bareJID(presence->getFrom().toBare()); + if (presence->getType() == Presence::Subscribe || presence->getType() == Presence::Subscribed) { + } + else { + Presence::ref passedPresence = presence; + if (presence->getType() == Presence::Unsubscribe || presence->getType() == Presence::Unsubscribed) { + /* 3921bis says that we don't follow up with an unavailable, so simulate this ourselves */ + passedPresence = Presence::ref(new Presence()); + passedPresence->setType(Presence::Unavailable); + passedPresence->setFrom(bareJID); + passedPresence->setStatus(presence->getStatus()); + } + std::map > jidMap = entries_[bareJID]; + if (passedPresence->getFrom().isBare() && presence->getType() == Presence::Unavailable) { + /* Have a bare-JID only presence of offline */ + jidMap.clear(); + } else if (passedPresence->getType() == Presence::Available) { + /* Don't have a bare-JID only offline presence once there are available presences */ + jidMap.erase(bareJID); + } + if (passedPresence->getType() == Presence::Unavailable && jidMap.size() > 1) { + jidMap.erase(passedPresence->getFrom()); + } else { + jidMap[passedPresence->getFrom()] = passedPresence; + } + entries_[bareJID] = jidMap; + onPresenceChange(passedPresence); + } +} + +Presence::ref PresenceOracle::getLastPresence(const JID& jid) const { + PresencesMap::const_iterator i = entries_.find(jid.toBare()); + if (i == entries_.end()) { + return Presence::ref(); + } + PresenceMap presenceMap = i->second; + PresenceMap::const_iterator j = presenceMap.find(jid); + if (j != presenceMap.end()) { + return j->second; + } + else { + return Presence::ref(); + } +} + +std::vector PresenceOracle::getAllPresence(const JID& bareJID) const { + std::vector results; + PresencesMap::const_iterator i = entries_.find(bareJID); + if (i == entries_.end()) { + return results; + } + PresenceMap presenceMap = i->second; + PresenceMap::const_iterator j = presenceMap.begin(); + for (; j != presenceMap.end(); ++j) { + Presence::ref current = j->second; + results.push_back(current); + } + return results; +} + +Presence::ref PresenceOracle::getHighestPriorityPresence(const JID& bareJID) const { + PresencesMap::const_iterator i = entries_.find(bareJID); + if (i == entries_.end()) { + return Presence::ref(); + } + PresenceMap presenceMap = i->second; + PresenceMap::const_iterator j = presenceMap.begin(); + Presence::ref highest; + for (; j != presenceMap.end(); ++j) { + Presence::ref current = j->second; + if (!highest + || current->getPriority() > highest->getPriority() + || (current->getPriority() == highest->getPriority() + && StatusShow::typeToAvailabilityOrdering(current->getShow()) > StatusShow::typeToAvailabilityOrdering(highest->getShow()))) { + highest = current; + } + + } + return highest; +} + +} diff --git a/src/rostermanager.cpp b/src/rostermanager.cpp index 23b19a7f..65135116 100644 --- a/src/rostermanager.cpp +++ b/src/rostermanager.cpp @@ -110,7 +110,12 @@ void RosterManager::sendBuddyRosterPush(Buddy *buddy) { Swift::RosterPayload::ref payload = Swift::RosterPayload::ref(new Swift::RosterPayload()); Swift::RosterItemPayload item; item.setJID(buddy->getJID().toBare()); - item.setName(buddy->getAlias()); + if (buddy->getAlias().empty()) { + item.setName(buddy->getJID().toBare().toString()); + } + else { + item.setName(buddy->getAlias()); + } item.setGroups(buddy->getGroups()); item.setSubscription(Swift::RosterItemPayload::Both); @@ -285,8 +290,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 +347,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/storageresponder.cpp b/src/storageresponder.cpp index 84423593..b5f4e5aa 100644 --- a/src/storageresponder.cpp +++ b/src/storageresponder.cpp @@ -71,11 +71,19 @@ bool StorageResponder::handleSetRequest(const Swift::JID& from, const Swift::JID return true; } - StorageSerializer serializer; - std::string value = serializer.serializePayload(boost::dynamic_pointer_cast(payload->getPayload())); - m_storageBackend->updateUserSetting(user->getUserInfo().id, "storage", value); - LOG4CXX_INFO(logger, from.toBare().toString() << ": Storing jabber:iq:storage"); - sendResponse(from, id, boost::shared_ptr()); + boost::shared_ptr storage = boost::dynamic_pointer_cast(payload->getPayload()); + + if (storage) { + StorageSerializer serializer; + std::string value = serializer.serializePayload(boost::dynamic_pointer_cast(payload->getPayload())); + m_storageBackend->updateUserSetting(user->getUserInfo().id, "storage", value); + LOG4CXX_INFO(logger, from.toBare().toString() << ": Storing jabber:iq:storage"); + sendResponse(from, id, boost::shared_ptr()); + } + else { + LOG4CXX_INFO(logger, from.toBare().toString() << ": Unknown element. Libtransport does not support serialization of this."); + sendError(from, id, ErrorPayload::NotAcceptable, ErrorPayload::Cancel); + } return true; } diff --git a/src/transport.cpp b/src/transport.cpp index ddcda381..8eab7bf7 100644 --- a/src/transport.cpp +++ b/src/transport.cpp @@ -36,6 +36,8 @@ #include "Swiften/Serializer/PayloadSerializers/XHTMLIMSerializer.h" #include "Swiften/Parser/PayloadParsers/StatsParser.h" #include "Swiften/Serializer/PayloadSerializers/StatsSerializer.h" +#include "Swiften/Parser/PayloadParsers/GatewayPayloadParser.h" +#include "Swiften/Serializer/PayloadSerializers/GatewayPayloadSerializer.h" #include "Swiften/Serializer/PayloadSerializers/SpectrumErrorSerializer.h" #include "transport/BlockParser.h" #include "transport/BlockSerializer.h" @@ -45,6 +47,7 @@ #include "log4cxx/consoleappender.h" #include "log4cxx/patternlayout.h" #include "log4cxx/propertyconfigurator.h" +#include "Swiften/Swiften.h" using namespace Swift; using namespace boost; @@ -94,6 +97,7 @@ Component::Component(Swift::EventLoop *loop, Swift::NetworkFactories *factories, m_server->addPayloadParserFactory(new GenericPayloadParserFactory("block", "urn:xmpp:block:0")); m_server->addPayloadParserFactory(new GenericPayloadParserFactory("invisible", "urn:xmpp:invisible:0")); m_server->addPayloadParserFactory(new GenericPayloadParserFactory("query", "http://jabber.org/protocol/stats")); + m_server->addPayloadParserFactory(new GenericPayloadParserFactory("query", "jabber:iq:gateway")); m_server->addPayloadSerializer(new Swift::AttentionSerializer()); m_server->addPayloadSerializer(new Swift::XHTMLIMSerializer()); @@ -101,6 +105,7 @@ Component::Component(Swift::EventLoop *loop, Swift::NetworkFactories *factories, m_server->addPayloadSerializer(new Swift::InvisibleSerializer()); m_server->addPayloadSerializer(new Swift::StatsSerializer()); m_server->addPayloadSerializer(new Swift::SpectrumErrorSerializer()); + m_server->addPayloadSerializer(new Swift::GatewayPayloadSerializer()); m_server->onDataRead.connect(boost::bind(&Component::handleDataRead, this, _1)); m_server->onDataWritten.connect(boost::bind(&Component::handleDataWritten, this, _1)); @@ -120,6 +125,7 @@ Component::Component(Swift::EventLoop *loop, Swift::NetworkFactories *factories, m_component->addPayloadParserFactory(new GenericPayloadParserFactory("block", "urn:xmpp:block:0")); m_component->addPayloadParserFactory(new GenericPayloadParserFactory("invisible", "urn:xmpp:invisible:0")); m_component->addPayloadParserFactory(new GenericPayloadParserFactory("query", "http://jabber.org/protocol/stats")); + m_component->addPayloadParserFactory(new GenericPayloadParserFactory("query", "jabber:iq:gateway")); m_component->addPayloadSerializer(new Swift::AttentionSerializer()); m_component->addPayloadSerializer(new Swift::XHTMLIMSerializer()); @@ -127,6 +133,7 @@ Component::Component(Swift::EventLoop *loop, Swift::NetworkFactories *factories, m_component->addPayloadSerializer(new Swift::InvisibleSerializer()); m_component->addPayloadSerializer(new Swift::StatsSerializer()); m_component->addPayloadSerializer(new Swift::SpectrumErrorSerializer()); + m_component->addPayloadSerializer(new Swift::GatewayPayloadSerializer()); m_stanzaChannel = m_component->getStanzaChannel(); m_iqRouter = m_component->getIQRouter(); @@ -137,7 +144,7 @@ Component::Component(Swift::EventLoop *loop, Swift::NetworkFactories *factories, m_entityCapsManager = new EntityCapsManager(m_capsManager, m_stanzaChannel); m_entityCapsManager->onCapsChanged.connect(boost::bind(&Component::handleCapsChanged, this, _1)); - m_presenceOracle = new PresenceOracle(m_stanzaChannel); + m_presenceOracle = new Transport::PresenceOracle(m_stanzaChannel); m_presenceOracle->onPresenceChange.connect(bind(&Component::handlePresence, this, _1)); m_discoInfoResponder = new DiscoInfoResponder(m_iqRouter, m_config); @@ -170,7 +177,7 @@ Swift::StanzaChannel *Component::getStanzaChannel() { return m_stanzaChannel; } -Swift::PresenceOracle *Component::getPresenceOracle() { +Transport::PresenceOracle *Component::getPresenceOracle() { return m_presenceOracle; } diff --git a/src/user.cpp b/src/user.cpp index 61740525..e94ae872 100644 --- a/src/user.cpp +++ b/src/user.cpp @@ -24,6 +24,7 @@ #include "transport/rostermanager.h" #include "transport/usermanager.h" #include "transport/conversationmanager.h" +#include "transport/presenceoracle.h" #include "Swiften/Swiften.h" #include "Swiften/Server/ServerStanzaChannel.h" #include "Swiften/Elements/StreamError.h" @@ -212,8 +213,8 @@ void User::handlePresence(Swift::Presence::ref presence) { bool isMUC = presence->getPayload() != NULL || *presence->getTo().getNode().c_str() == '#'; if (isMUC) { if (presence->getType() == Swift::Presence::Unavailable) { - LOG4CXX_INFO(logger, m_jid.toString() << ": Going to left room " << presence->getTo().getNode()); std::string room = Buddy::JIDToLegacyName(presence->getTo()); + LOG4CXX_INFO(logger, m_jid.toString() << ": Going to left room " << room); onRoomLeft(room); } else { @@ -223,8 +224,8 @@ void User::handlePresence(Swift::Presence::ref presence) { m_readyForConnect = true; onReadyToConnect(); } - LOG4CXX_INFO(logger, m_jid.toString() << ": Going to join room " << presence->getTo().getNode() << " as " << presence->getTo().getResource()); std::string room = Buddy::JIDToLegacyName(presence->getTo()); + LOG4CXX_INFO(logger, m_jid.toString() << ": Going to join room " << room << " as " << presence->getTo().getResource()); std::string password = ""; if (presence->getPayload() != NULL) { password = presence->getPayload()->getPassword() ? *presence->getPayload()->getPassword() : ""; 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 646722ed..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) { @@ -286,6 +304,17 @@ bool UserRegistration::handleSetRequest(const Swift::JID& from, const Swift::JID else if (textSingle->getName() == "encoding") { encoding = textSingle->getValue(); } + // Pidgin sends it as textSingle, not sure why... + 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; } @@ -294,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; } @@ -323,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; diff --git a/src/util.cpp b/src/util.cpp index 7053ea06..315726cb 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -128,6 +128,15 @@ std::vector deserializeGroups(std::string &groups) { return ret; } +int getRandomPort(const std::string &s) { + unsigned long r = 0; + BOOST_FOREACH(char c, s) { + r += (int) c; + } + srand(time(NULL) + r); + return 30000 + rand() % 10000; +} + } }