diff --git a/CMakeLists.txt b/CMakeLists.txt index ee8e8623..11c1329a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -242,6 +242,7 @@ ADD_SUBDIRECTORY(spectrum) ADD_SUBDIRECTORY(backends) if (NOT WIN32) ADD_SUBDIRECTORY(spectrum_manager) +# ADD_SUBDIRECTORY(spectrum2_send_message) endif() if (CPPUNIT_FOUND) diff --git a/ChangeLog b/ChangeLog index d0d28034..69a7fd12 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,23 @@ +Version 2.0.0-beta2 (2012-XX-XX): + General: + * Fixed bug when Roster Item Exchange and subscribe stanzas were sent + repeatedly. + * Backends related logs now contain the backend PID. + * Fixed username_mask setting. + * Added new fields into statistics (backends_crashed, messages related + stats). + + libpurple: + * Added support for MUC for prpl-jabber protocol. + + Skype: + * Memory usage statistic now includes the Skype client. + * Fixed logging issue when the logs were not stored in the proper instance + directory. + * Skype backend includes also Skype client memory usage into the account. + * Working buddies adding/removing. + * Information about missed call is now forwarded to XMPP user. + Version 2.0.0-beta (2012-02-28): General: * Added PostreSQL support (thanks to Jadestorm). diff --git a/backends/libcommuni/main.cpp b/backends/libcommuni/main.cpp index f5af84fb..6443c2bd 100644 --- a/backends/libcommuni/main.cpp +++ b/backends/libcommuni/main.cpp @@ -10,6 +10,7 @@ #include "transport/config.h" #include "transport/networkplugin.h" +#include "transport/logging.h" #include "session.h" #include #include @@ -83,30 +84,7 @@ int main (int argc, char* argv[]) { } QCoreApplication app(argc, argv); - 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); - } + Logging::initBackendLogging(&config); Swift::QtEventLoop eventLoop; diff --git a/backends/libpurple/main.cpp b/backends/libpurple/main.cpp index cf8d3090..e05ba412 100644 --- a/backends/libpurple/main.cpp +++ b/backends/libpurple/main.cpp @@ -1,3 +1,5 @@ +#include "utils.h" + #include "glib.h" #include "purple.h" #include @@ -12,25 +14,7 @@ #include "log4cxx/helpers/properties.h" #include "log4cxx/helpers/fileinputstream.h" #include "log4cxx/helpers/transcoder.h" -#ifndef WIN32 -#include "sys/wait.h" -#include "sys/signal.h" -#include -#include -#include -#include -#include -#include -#include "sys/socket.h" -#include -#include -#include -#else -#include -#define getpid _getpid -#define ssize_t SSIZE_T -#include "win32/win32dep.h" -#endif + // #include "valgrind/memcheck.h" #include "malloc.h" #include @@ -40,11 +24,18 @@ #include #endif +#ifdef WIN32 +#include "win32/win32dep.h" +#define ssize_t SSIZE_T +#include +#define getpid _getpid +#endif + using namespace log4cxx; static LoggerPtr logger_libpurple = log4cxx::Logger::getLogger("libpurple"); static LoggerPtr logger = log4cxx::Logger::getLogger("backend"); -int m_sock; +int main_socket; static int writeInput; using namespace Transport; @@ -89,7 +80,7 @@ static std::string KEYFILE_STRING(const std::string &cat, const std::string &key return def; } std::string ret(str); - free(str); + g_free(str); if (ret.find("#") != std::string::npos) { ret = ret.substr(0, ret.find("#")); @@ -107,13 +98,8 @@ static std::string KEYFILE_STRING(const std::string &cat, const std::string &key #define KEYFILE_BOOL(CAT, KEY) g_key_file_get_boolean(keyfile, CAT, KEY, 0) -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; struct FTData { unsigned long id; @@ -122,121 +108,12 @@ struct FTData { }; 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 } }; static void *notify_user_info(PurpleConnection *gc, const char *who, PurpleNotifyUserInfo *user_info); -static GHashTable *ui_info = NULL; - -static GHashTable *spectrum_ui_get_info(void) -{ - if(NULL == ui_info) { - ui_info = g_hash_table_new(g_str_hash, g_str_equal); - - g_hash_table_insert(ui_info, g_strdup("name"), g_strdup("Spectrum")); - g_hash_table_insert(ui_info, g_strdup("version"), g_strdup("0.5")); - g_hash_table_insert(ui_info, g_strdup("website"), g_strdup("http://spectrum.im")); - g_hash_table_insert(ui_info, g_strdup("dev_website"), g_strdup("http://spectrum.im")); - g_hash_table_insert(ui_info, g_strdup("client_type"), g_strdup("pc")); - - /* - * This is the client key for "Pidgin." It is owned by the AIM - * account "markdoliner." Please don't use this key for other - * applications. You can either not specify a client key, in - * which case the default "libpurple" key will be used, or you - * can register for your own client key at - * http://developer.aim.com/manageKeys.jsp - */ - g_hash_table_insert(ui_info, g_strdup("prpl-aim-clientkey"), g_strdup("ma1cSASNCKFtrdv9")); - g_hash_table_insert(ui_info, g_strdup("prpl-icq-clientkey"), g_strdup("ma1cSASNCKFtrdv9")); - - /* - * This is the distid for Pidgin, given to us by AOL. Please - * don't use this for other applications. You can just not - * specify a distid and libpurple will use a default. - */ - g_hash_table_insert(ui_info, g_strdup("prpl-aim-distid"), GINT_TO_POINTER(1550)); - g_hash_table_insert(ui_info, g_strdup("prpl-icq-distid"), GINT_TO_POINTER(1550)); - } - - return ui_info; -} - -static gboolean -badchar(char c) -{ - switch (c) { - case ' ': - case ',': - case '\0': - case '\n': - case '\r': - case '<': - case '>': - case '"': - return TRUE; - default: - return FALSE; - } -} - -static gboolean -badentity(const char *c) -{ - if (!g_ascii_strncasecmp(c, "<", 4) || - !g_ascii_strncasecmp(c, ">", 4) || - !g_ascii_strncasecmp(c, """, 6)) { - return TRUE; - } - return FALSE; -} - -static const char * -process_link(GString *ret, - const char *start, const char *c, - int matchlen, - const char *urlprefix, - int inside_paren) -{ - char *url_buf; - const char *t; - - for (t = c;; t++) { - if (!badchar(*t) && !badentity(t)) - continue; - - if (t - c == matchlen) - break; - - if (*t == ',' && *(t + 1) != ' ') { - continue; - } - - if (t > start && *(t - 1) == '.') - t--; - if (t > start && *(t - 1) == ')' && inside_paren > 0) - t--; - - url_buf = g_strndup(c, t - c); -// tmpurlbuf = purple_unescape_html(url_buf); -// std::cout << url_buf << "\n"; - g_string_append_printf(ret, "%s", - urlprefix, - url_buf, url_buf); -// g_free(tmpurlbuf); - g_free(url_buf); - return t; - } - - return c; -} static gboolean ft_ui_ready(void *data) { PurpleXfer *xfer = (PurpleXfer *) data; @@ -246,184 +123,6 @@ static gboolean ft_ui_ready(void *data) { return FALSE; } -static char * -spectrum_markup_linkify(const char *text) -{ - const char *c, *t, *q = NULL; - char *tmpurlbuf, *url_buf; - gunichar g; - gboolean inside_html = FALSE; - int inside_paren = 0; - GString *ret; - - if (text == NULL) - return NULL; - - ret = g_string_new(""); - - c = text; - while (*c) { - - if(*c == '(' && !inside_html) { - inside_paren++; - ret = g_string_append_c(ret, *c); - c++; - } - - if(inside_html) { - if(*c == '>') { - inside_html = FALSE; - } else if(!q && (*c == '\"' || *c == '\'')) { - q = c; - } else if(q) { - if(*c == *q) - q = NULL; - } - } else if(*c == '<') { - inside_html = TRUE; - if (!g_ascii_strncasecmp(c, "", 3)) { - inside_html = FALSE; - break; - } - ret = g_string_append_c(ret, *c); - c++; - if (!(*c)) - break; - } - } - } else if (!g_ascii_strncasecmp(c, "http://", 7)) { - c = process_link(ret, text, c, 7, "", inside_paren); - } else if (!g_ascii_strncasecmp(c, "https://", 8)) { - c = process_link(ret, text, c, 8, "", inside_paren); - } else if (!g_ascii_strncasecmp(c, "ftp://", 6)) { - c = process_link(ret, text, c, 6, "", inside_paren); - } else if (!g_ascii_strncasecmp(c, "sftp://", 7)) { - c = process_link(ret, text, c, 7, "", inside_paren); - } else if (!g_ascii_strncasecmp(c, "file://", 7)) { - c = process_link(ret, text, c, 7, "", inside_paren); - } else if (!g_ascii_strncasecmp(c, "www.", 4) && c[4] != '.' && (c == text || badchar(c[-1]) || badentity(c-1))) { - c = process_link(ret, text, c, 4, "http://", inside_paren); - } else if (!g_ascii_strncasecmp(c, "ftp.", 4) && c[4] != '.' && (c == text || badchar(c[-1]) || badentity(c-1))) { - c = process_link(ret, text, c, 4, "ftp://", inside_paren); - } else if (!g_ascii_strncasecmp(c, "xmpp:", 5) && (c == text || badchar(c[-1]) || badentity(c-1))) { - c = process_link(ret, text, c, 5, "", inside_paren); - } else if (!g_ascii_strncasecmp(c, "mailto:", 7)) { - t = c; - while (1) { - if (badchar(*t) || badentity(t)) { - const char *d; - if (t - c == 7) { - break; - } - if (t > text && *(t - 1) == '.') - t--; - if ((d = strstr(c + 7, "?")) != NULL && d < t) - url_buf = g_strndup(c + 7, d - c - 7); - else - url_buf = g_strndup(c + 7, t - c - 7); - if (!purple_email_is_valid(url_buf)) { - g_free(url_buf); - break; - } - g_free(url_buf); - url_buf = g_strndup(c, t - c); -// tmpurlbuf = purple_unescape_html(url_buf); - g_string_append_printf(ret, "%s", - url_buf, url_buf); - g_free(url_buf); -// g_free(tmpurlbuf); - c = t; - break; - } - t++; - } - } else if (c != text && (*c == '@')) { - int flag; - GString *gurl_buf = NULL; - const char illegal_chars[] = "!@#$%^&*()[]{}/|\\<>\":;\r\n \0"; - - if (strchr(illegal_chars,*(c - 1)) || strchr(illegal_chars, *(c + 1))) - flag = 0; - else { - flag = 1; - gurl_buf = g_string_new(""); - } - - t = c; - while (flag) { - /* iterate backwards grabbing the local part of an email address */ - g = g_utf8_get_char(t); - if (badchar(*t) || (g >= 127) || (*t == '(') || - ((*t == ';') && ((t > (text+2) && (!g_ascii_strncasecmp(t - 3, "<", 4) || - !g_ascii_strncasecmp(t - 3, ">", 4))) || - (t > (text+4) && (!g_ascii_strncasecmp(t - 5, """, 6)))))) { - /* local part will already be part of ret, strip it out */ - ret = g_string_truncate(ret, ret->len - (c - t)); - ret = g_string_append_unichar(ret, g); - break; - } else { - g_string_prepend_unichar(gurl_buf, g); - t = g_utf8_find_prev_char(text, t); - if (t < text) { - ret = g_string_assign(ret, ""); - break; - } - } - } - - t = g_utf8_find_next_char(c, NULL); - - while (flag) { - /* iterate forwards grabbing the domain part of an email address */ - g = g_utf8_get_char(t); - if (badchar(*t) || (g >= 127) || (*t == ')') || badentity(t)) { - char *d; - - url_buf = g_string_free(gurl_buf, FALSE); - - /* strip off trailing periods */ - if (strlen(url_buf) > 0) { - for (d = url_buf + strlen(url_buf) - 1; *d == '.'; d--, t--) - *d = '\0'; - } - - tmpurlbuf = purple_unescape_html(url_buf); - if (purple_email_is_valid(tmpurlbuf)) { - g_string_append_printf(ret, "%s", - url_buf, url_buf); - } else { - g_string_append(ret, url_buf); - } - g_free(url_buf); - g_free(tmpurlbuf); - c = t; - - break; - } else { - g_string_append_unichar(gurl_buf, g); - t = g_utf8_find_next_char(t, NULL); - } - } - } - - if(*c == ')' && !inside_html) { - inside_paren--; - ret = g_string_append_c(ret, *c); - c++; - } - - if (*c == 0) - break; - - ret = g_string_append_c(ret, *c); - c++; - - } - return g_string_free(ret, FALSE); -} - struct authRequest { PurpleAccountRequestAuthorizationCb authorize_cb; PurpleAccountRequestAuthorizationCb deny_cb; @@ -509,13 +208,13 @@ static std::string getAlias(PurpleBuddy *m_buddy) { class SpectrumNetworkPlugin : public NetworkPlugin { public: - SpectrumNetworkPlugin(const std::string &host, int port) : NetworkPlugin() { + SpectrumNetworkPlugin() : NetworkPlugin() { } void handleExitRequest() { LOG4CXX_INFO(logger, "Exiting..."); - exit(1); + exit(0); } void getProtocolAndName(const std::string &legacyName, std::string &name, std::string &protocol) { @@ -597,7 +296,7 @@ class SpectrumNetworkPlugin : public NetworkPlugin { void handleLoginRequest(const std::string &user, const std::string &legacyName, const std::string &password) { PurpleAccount *account = NULL; - + std::string name; std::string protocol; getProtocolAndName(legacyName, name, protocol); @@ -614,7 +313,6 @@ class SpectrumNetworkPlugin : public NetworkPlugin { return; } - 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()); @@ -637,11 +335,13 @@ class SpectrumNetworkPlugin : public NetworkPlugin { setDefaultAccountOptions(account); + // Enable account + privacy lists purple_account_set_enabled(account, "spectrum", TRUE); if (KEYFILE_BOOL("service", "enable_privacy_lists")) { purple_account_set_privacy_type(account, PURPLE_PRIVACY_DENY_USERS); } + // Set the status const PurpleStatusType *status_type = purple_account_get_status_type_with_primitive(account, PURPLE_STATUS_AVAILABLE); if (status_type != NULL) { purple_account_set_status(account, purple_status_type_get_id(status_type), TRUE, NULL); @@ -661,50 +361,6 @@ class SpectrumNetworkPlugin : public NetworkPlugin { m_accounts.erase(account); purple_accounts_delete(account); -// -// // Remove conversations. -// // This has to be called before m_account->ui_data = NULL;, because it uses -// // ui_data to call SpectrumMessageHandler::purpleConversationDestroyed() callback. -// GList *iter; -// for (iter = purple_get_conversations(); iter; ) { -// PurpleConversation *conv = (PurpleConversation*) iter->data; -// iter = iter->next; -// if (purple_conversation_get_account(conv) == account) -// purple_conversation_destroy(conv); -// } -// -// g_free(account->ui_data); -// account->ui_data = NULL; -// m_accounts.erase(account); -// -// purple_notify_close_with_handle(account); -// purple_request_close_with_handle(account); -// -// purple_accounts_remove(account); -// -// GSList *buddies = purple_find_buddies(account, NULL); -// while(buddies) { -// PurpleBuddy *b = (PurpleBuddy *) buddies->data; -// purple_blist_remove_buddy(b); -// buddies = g_slist_delete_link(buddies, buddies); -// } -// -// /* Remove any open conversation for this account */ -// for (GList *it = purple_get_conversations(); it; ) { -// PurpleConversation *conv = (PurpleConversation *) it->data; -// it = it->next; -// if (purple_conversation_get_account(conv) == account) -// purple_conversation_destroy(conv); -// } -// -// /* Remove this account's pounces */ -// // purple_pounce_destroy_all_by_account(account); -// -// /* This will cause the deletion of an old buddy icon. */ -// purple_buddy_icons_set_account_icon(account, NULL, 0); -// -// purple_account_destroy(account); - // force returning of memory chunks allocated by libxml2 to kernel #ifndef WIN32 malloc_trim(0); #endif @@ -768,17 +424,30 @@ class SpectrumNetworkPlugin : public NetworkPlugin { void handleMessageSendRequest(const std::string &user, const std::string &legacyName, const std::string &message, const std::string &xhtml) { PurpleAccount *account = m_sessions[user]; if (account) { - PurpleConversation *conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, legacyName.c_str(), account); + PurpleConversation *conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, legacyName.c_str(), account); if (!conv) { - conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, legacyName.c_str()); + conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, legacyName.c_str(), account); + if (!conv) { + conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, legacyName.c_str()); + } } if (xhtml.empty()) { gchar *_markup = purple_markup_escape_text(message.c_str(), -1); - purple_conv_im_send(PURPLE_CONV_IM(conv), _markup); + if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) { + purple_conv_im_send(PURPLE_CONV_IM(conv), _markup); + } + else if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) { + purple_conv_chat_send(PURPLE_CONV_CHAT(conv), _markup); + } g_free(_markup); } else { - purple_conv_im_send(PURPLE_CONV_IM(conv), xhtml.c_str()); + if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) { + purple_conv_im_send(PURPLE_CONV_IM(conv), xhtml.c_str()); + } + else if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_CHAT) { + purple_conv_chat_send(PURPLE_CONV_CHAT(conv), xhtml.c_str()); + } } } } @@ -989,9 +658,9 @@ class SpectrumNetworkPlugin : public NetworkPlugin { } void sendData(const std::string &string) { - write(m_sock, string.c_str(), string.size()); + write(main_socket, string.c_str(), string.size()); if (writeInput == 0) - writeInput = purple_input_add(m_sock, PURPLE_INPUT_WRITE, &transportDataReceived, NULL); + writeInput = purple_input_add(main_socket, PURPLE_INPUT_WRITE, &transportDataReceived, NULL); } void readyForData() { @@ -1213,8 +882,9 @@ static PurpleBlistUiOps blistUiOps = static void conv_write_im(PurpleConversation *conv, const char *who, const char *msg, PurpleMessageFlags flags, time_t mtime) { // Don't forwards our own messages. - if (flags & PURPLE_MESSAGE_SEND || flags & PURPLE_MESSAGE_SYSTEM) + if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM && (flags & PURPLE_MESSAGE_SEND || flags & PURPLE_MESSAGE_SYSTEM)) { return; + } PurpleAccount *account = purple_conversation_get_account(conv); // char *striped = purple_markup_strip_html(message); @@ -1253,19 +923,67 @@ static void conv_write_im(PurpleConversation *conv, const char *who, const char // LOG4CXX_INFO(logger, "Received message body='" << message_ << "' xhtml='" << xhtml_ << "'"); - np->handleMessage(np->m_accounts[account], w, message_, "", xhtml_); + if (purple_conversation_get_type(conv) == PURPLE_CONV_TYPE_IM) { + np->handleMessage(np->m_accounts[account], w, message_, "", xhtml_); + } + else { + LOG4CXX_INFO(logger, "Received message body='" << message_ << "' name='" << purple_conversation_get_name(conv) << "' " << w); + np->handleMessage(np->m_accounts[account], purple_conversation_get_name(conv), message_, w, xhtml_); + } +} + +static void conv_chat_add_users(PurpleConversation *conv, GList *cbuddies, gboolean new_arrivals) { + PurpleAccount *account = purple_conversation_get_account(conv); + + GList *l = cbuddies; + while (l != NULL) { + PurpleConvChatBuddy *cb = (PurpleConvChatBuddy *)l->data; + std::string name(cb->name); + int flags = GPOINTER_TO_INT(cb->flags); + if (flags & PURPLE_CBFLAGS_OP || flags & PURPLE_CBFLAGS_HALFOP) { +// item->addAttribute("affiliation", "admin"); +// item->addAttribute("role", "moderator"); + flags = 1; + } + else if (flags & PURPLE_CBFLAGS_FOUNDER) { +// item->addAttribute("affiliation", "owner"); +// item->addAttribute("role", "moderator"); + flags = 1; + } + else { + flags = 0; +// item->addAttribute("affiliation", "member"); +// item->addAttribute("role", "participant"); + } + + np->handleParticipantChanged(np->m_accounts[account], name, purple_conversation_get_name(conv), (int) flags, pbnetwork::STATUS_ONLINE); + + l = l->next; + } +} + +static void conv_chat_remove_users(PurpleConversation *conv, GList *users) { + PurpleAccount *account = purple_conversation_get_account(conv); + + GList *l = users; + while (l != NULL) { + std::string name((char *) l->data); + np->handleParticipantChanged(np->m_accounts[account], name, purple_conversation_get_name(conv), 0, pbnetwork::STATUS_NONE); + + l = l->next; + } } static PurpleConversationUiOps conversation_ui_ops = { NULL, NULL, - NULL,//conv_write_chat, /* write_chat */ + conv_write_im,//conv_write_chat, /* write_chat */ conv_write_im, /* write_im */ NULL,//conv_write_conv, /* write_conv */ - NULL,//conv_chat_add_users, /* chat_add_users */ + conv_chat_add_users, /* chat_add_users */ NULL,//conv_chat_rename_user, /* chat_rename_user */ - NULL,//conv_chat_remove_users, /* chat_remove_users */ + conv_chat_remove_users, /* chat_remove_users */ NULL,//pidgin_conv_chat_update_user, /* chat_update_user */ NULL,//pidgin_conv_present_conversation, /* present */ NULL,//pidgin_conv_has_focus, /* has_focus */ @@ -1407,11 +1125,13 @@ static void *notify_user_info(PurpleConnection *gc, const char *who, PurpleNotif if (true) { gchar *data; gchar *path = purple_buddy_icon_get_full_path(icon); - if (g_file_get_contents (path, &data, &len, NULL)) { - photo = std::string(data, len); - free(data); + if (path) { + if (g_file_get_contents(path, &data, &len, NULL)) { + photo = std::string(data, len); + g_free(data); + } + g_free(path); } - free(path); } else { const gchar * data = (gchar*)purple_buddy_icon_get_data(icon, &len); @@ -1800,9 +1520,8 @@ static bool initPurple() { remove("./accounts.xml"); remove("./blist.xml"); -// if (m_configuration.logAreas & LOG_AREA_PURPLE) - purple_debug_set_ui_ops(&debugUiOps); - purple_debug_set_verbose(true); + purple_debug_set_ui_ops(&debugUiOps); + purple_debug_set_verbose(true); purple_core_set_ui_ops(&coreUiOps); if (KEYFILE_STRING("service", "eventloop") == "libev") { @@ -1866,50 +1585,7 @@ static bool initPurple() { } return ret; } -#ifndef WIN32 -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); - } -} -#endif - -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 void transportDataReceived(gpointer data, gint source, PurpleInputCondition cond) { if (cond & PURPLE_INPUT_READ) { @@ -1938,17 +1614,10 @@ int main(int argc, char **argv) { 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"; + std::cerr << "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"; @@ -1972,27 +1641,6 @@ int main(int argc, char **argv) { 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 keyfile = g_key_file_new (); if (!g_key_file_load_from_file (keyfile, argv[1], (GKeyFileFlags) 0, 0)) { @@ -2027,12 +1675,11 @@ int main(int argc, char **argv) { initPurple(); - m_sock = create_socket(host, port); - - purple_input_add(m_sock, PURPLE_INPUT_READ, &transportDataReceived, NULL); + main_socket = create_socket(host, port); + purple_input_add(main_socket, PURPLE_INPUT_READ, &transportDataReceived, NULL); purple_timeout_add_seconds(30, pingTimeout, NULL); - np = new SpectrumNetworkPlugin(host, port); + np = new SpectrumNetworkPlugin(); bool libev = KEYFILE_STRING("service", "eventloop") == "libev"; GMainLoop *m_loop; diff --git a/backends/libpurple/utils.cpp b/backends/libpurple/utils.cpp new file mode 100644 index 00000000..417ad67d --- /dev/null +++ b/backends/libpurple/utils.cpp @@ -0,0 +1,129 @@ +/** + * 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 + */ + +#include "utils.h" + +#include "glib.h" +#include "purple.h" +#include +#include + +#include "errno.h" + +#ifndef WIN32 +#include "sys/wait.h" +#include "sys/signal.h" +#include +#include +#include +#include +#include +#include +#include "sys/socket.h" +#include +#include +#include +#else +#include +#define getpid _getpid +#define ssize_t SSIZE_T +#include "win32/win32dep.h" +#endif + +static GHashTable *ui_info = NULL; + +GHashTable *spectrum_ui_get_info(void) +{ + if(NULL == ui_info) { + ui_info = g_hash_table_new(g_str_hash, g_str_equal); + + g_hash_table_insert(ui_info, g_strdup("name"), g_strdup("Spectrum")); + g_hash_table_insert(ui_info, g_strdup("version"), g_strdup("0.5")); + g_hash_table_insert(ui_info, g_strdup("website"), g_strdup("http://spectrum.im")); + g_hash_table_insert(ui_info, g_strdup("dev_website"), g_strdup("http://spectrum.im")); + g_hash_table_insert(ui_info, g_strdup("client_type"), g_strdup("pc")); + + /* + * This is the client key for "Pidgin." It is owned by the AIM + * account "markdoliner." Please don't use this key for other + * applications. You can either not specify a client key, in + * which case the default "libpurple" key will be used, or you + * can register for your own client key at + * http://developer.aim.com/manageKeys.jsp + */ + g_hash_table_insert(ui_info, g_strdup("prpl-aim-clientkey"), g_strdup("ma1cSASNCKFtrdv9")); + g_hash_table_insert(ui_info, g_strdup("prpl-icq-clientkey"), g_strdup("ma1cSASNCKFtrdv9")); + + /* + * This is the distid for Pidgin, given to us by AOL. Please + * don't use this for other applications. You can just not + * specify a distid and libpurple will use a default. + */ + g_hash_table_insert(ui_info, g_strdup("prpl-aim-distid"), GINT_TO_POINTER(1550)); + g_hash_table_insert(ui_info, g_strdup("prpl-icq-distid"), GINT_TO_POINTER(1550)); + } + + return ui_info; +} + +#ifndef WIN32 +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); + } +} +#endif + +int create_socket(char *host, int portno) { + struct sockaddr_in serv_addr; + + int main_socket = 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(main_socket, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { + close(main_socket); + main_socket = 0; + } + + int flags = fcntl(main_socket, F_GETFL); + flags |= O_NONBLOCK; + fcntl(main_socket, F_SETFL, flags); + return main_socket; +} diff --git a/backends/libpurple/utils.h b/backends/libpurple/utils.h new file mode 100644 index 00000000..2055b6c1 --- /dev/null +++ b/backends/libpurple/utils.h @@ -0,0 +1,30 @@ +/** + * 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 "purple.h" + +#ifndef WIN32 +void spectrum_sigchld_handler(int sig); +#endif + +int create_socket(char *host, int portno); +GHashTable *spectrum_ui_get_info(void); \ No newline at end of file diff --git a/backends/skype/main.cpp b/backends/skype/main.cpp index 9ede8a7f..ccb60559 100644 --- a/backends/skype/main.cpp +++ b/backends/skype/main.cpp @@ -2,8 +2,10 @@ #include #include "transport/config.h" +#include "transport/logging.h" #include "transport/transport.h" #include "transport/usermanager.h" +#include "transport/memoryusage.h" #include "transport/logger.h" #include "transport/sqlite3backend.h" #include "transport/userregistration.h" @@ -34,6 +36,10 @@ using namespace Transport; class SpectrumNetworkPlugin; +#define GET_RESPONSE_DATA(RESP, DATA) ((RESP.find(std::string(DATA) + " ") != std::string::npos) ? RESP.substr(RESP.find(DATA) + strlen(DATA) + 1) : ""); +#define GET_PROPERTY(VAR, OBJ, WHICH, PROP) std::string VAR = sk->send_command(std::string("GET ") + OBJ + " " + WHICH + " " + PROP); \ + VAR = GET_RESPONSE_DATA(VAR, PROP); + SpectrumNetworkPlugin *np; @@ -81,7 +87,7 @@ static pbnetwork::StatusType getStatus(const std::string &st) { class Skype { public: Skype(const std::string &user, const std::string &username, const std::string &password); - ~Skype() { logout(); } + ~Skype() { LOG4CXX_INFO(logger, "Skype instance desctuctor"); logout(); } void login(); void logout(); std::string send_command(const std::string &message); @@ -97,6 +103,10 @@ class Skype { bool createDBusProxy(); bool loadSkypeBuddies(); + int getPid() { + return (int) m_pid; + } + private: std::string m_username; std::string m_password; @@ -124,7 +134,9 @@ class SpectrumNetworkPlugin : public NetworkPlugin { void handleLoginRequest(const std::string &user, const std::string &legacyName, const std::string &password) { std::string name = legacyName; - name = name.substr(name.find(".") + 1); + if (name.find("skype.") == 0 || name.find("prpl-skype.") == 0) { + name = name.substr(name.find(".") + 1); + } LOG4CXX_INFO(logger, "Creating account with name '" << name << "'"); Skype *skype = new Skype(user, name, password); @@ -134,9 +146,25 @@ class SpectrumNetworkPlugin : public NetworkPlugin { skype->login(); } + void handleMemoryUsage(double &res, double &shared) { + res = 0; + shared = 0; + for(std::map::const_iterator it = m_sessions.begin(); it != m_sessions.end(); it++) { + Skype *skype = it->second; + if (skype) { + double r; + double s; + process_mem_usage(s, r, skype->getPid()); + res += r; + shared += s; + } + } + } + void handleLogoutRequest(const std::string &user, const std::string &legacyName) { Skype *skype = m_sessions[user]; if (skype) { + LOG4CXX_INFO(logger, "User wants to logout, logging out"); skype->logout(); exit(1); } @@ -178,6 +206,21 @@ class SpectrumNetworkPlugin : public NetworkPlugin { } } + void handleBuddyUpdatedRequest(const std::string &user, const std::string &buddyName, const std::string &alias, const std::vector &groups) { + Skype *skype = m_sessions[user]; + if (skype) { + skype->send_command("SET USER " + buddyName + " BUDDYSTATUS 2 Please authorize me"); + skype->send_command("SET USER " + buddyName + " ISAUTHORIZED TRUE"); + } + } + + void handleBuddyRemovedRequest(const std::string &user, const std::string &buddyName, const std::vector &groups) { + Skype *skype = m_sessions[user]; + if (skype) { + skype->send_command("SET USER " + buddyName + " BUDDYSTATUS 1"); + } + } + 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) { @@ -253,7 +296,7 @@ class SpectrumNetworkPlugin : public NetworkPlugin { std::cout << skype->getUsername() << " " << name << "\n"; if (skype->getUsername() == name) { alias = skype->send_command("GET PROFILE FULLNAME"); - alias = alias.substr(17); + alias = GET_RESPONSE_DATA(alias, "FULLNAME") } handleVCard(user, id, legacyName, "", alias, photo); } @@ -268,13 +311,6 @@ class SpectrumNetworkPlugin : public NetworkPlugin { 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) { } @@ -331,7 +367,8 @@ bool Skype::createDBusProxy() { LOG4CXX_INFO(logger, m_username << ":" << error->message); if (m_counter == 15) { - np->handleDisconnected(m_user, 0, error->message); + LOG4CXX_ERROR(logger, "Logging out, proxy couldn't be created"); + np->handleDisconnected(m_user, pbnetwork::CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE, error->message); logout(); g_error_free(error); return FALSE; @@ -360,6 +397,12 @@ static gboolean create_dbus_proxy(gpointer data) { } void Skype::login() { + if (m_username.find("..") == 0 || m_username.find("/") != std::string::npos) { + np->handleDisconnected(m_user, pbnetwork::CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE, "Invalid username"); + return; + } + boost::filesystem::remove_all(std::string("/tmp/skype/") + m_username); + boost::filesystem::path path(std::string("/tmp/skype/") + m_username); if (!boost::filesystem::exists(path)) { boost::filesystem::create_directories(path); @@ -394,6 +437,7 @@ void Skype::login() { "\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); + sleep(1); std::string db_path = std::string("/tmp/skype/") + m_username; char *db = (char *) malloc(db_path.size() + 1); strcpy(db, db_path.c_str()); @@ -437,6 +481,7 @@ void Skype::login() { } } + sleep(1); m_timer = g_timeout_add_seconds(1, create_dbus_proxy, this); } @@ -449,28 +494,35 @@ bool Skype::loadSkypeBuddies() { 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 b(buffer); + LOG4CXX_WARN(logger, "Skype wrote this on stdout '" << b << "'"); + if (b.find("Incorrect Password") != std::string::npos) { + LOG4CXX_INFO(logger, "Incorrect password, logging out") + np->handleDisconnected(m_user, pbnetwork::CONNECTION_ERROR_AUTHENTICATION_FAILED, "Incorrect password"); + close(fd_output); + logout(); + return FALSE; + } } std::string re = send_command("NAME Spectrum"); if (m_counter++ > 15) { - np->handleDisconnected(m_user, 0, ""); + LOG4CXX_ERROR(logger, "Logging out, because we tried to connect the Skype over DBUS 15 times without success"); + np->handleDisconnected(m_user, pbnetwork::CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE, "Skype is not ready. This issue have been logged and admins will check it and try to fix it soon."); close(fd_output); logout(); return FALSE; } - if (re.empty() || re == "CONNSTATUS OFFLINE") { + if (re.empty() || re == "CONNSTATUS OFFLINE" || re == "ERROR 68") { return TRUE; } close(fd_output); if (send_command("PROTOCOL 7") != "PROTOCOL 7") { - np->handleDisconnected(m_user, 0, "Skype is not ready"); + LOG4CXX_ERROR(logger, "PROTOCOL 7 failed, logging out"); + np->handleDisconnected(m_user, pbnetwork::CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE, "Skype is not ready. This issue have been logged and admins will check it and try to fix it soon."); logout(); return FALSE; } @@ -479,20 +531,27 @@ bool Skype::loadSkypeBuddies() { 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); + if (groups.find(' ') != std::string::npos) { + 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"); - 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; + if (name.find("ERROR") == 0) { + continue; + } + + boost::split(data, name, boost::is_any_of(" ")); + name = GET_RESPONSE_DATA(name, "DISPLAYNAME"); + + std::string users = send_command("GET GROUP " + data[1] + " USERS"); + users = GET_RESPONSE_DATA(users, "USERS"); + boost::split(data, users, boost::is_any_of(",")); + BOOST_FOREACH(std::string u, data) { + group_map[u] = grp; + } } } @@ -565,6 +624,7 @@ std::string Skype::send_command(const std::string &message) { // int message_num; // gchar error_return[30]; + LOG4CXX_INFO(logger, "Sending: '" << message << "'"); 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)) { @@ -572,14 +632,16 @@ std::string Skype::send_command(const std::string &message) { { LOG4CXX_INFO(logger, m_username << ": DBUS Error: " << error->message); g_error_free(error); + return ""; } else { LOG4CXX_INFO(logger, m_username << ": DBUS no response"); + return ""; } } if (str != NULL) { - LOG4CXX_INFO(logger, m_username << ": DBUS:" << str); + LOG4CXX_INFO(logger, m_username << ": DBUS:'" << str << "'"); } return str ? std::string(str) : std::string(); } @@ -599,70 +661,73 @@ static void handle_skype_message(std::string &message, Skype *sk) { } 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); + GET_PROPERTY(mood_text, "USER", cmd[1], "MOOD_TEXT"); + GET_PROPERTY(alias, "USER", cmd[1], "FULLNAME"); 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); + GET_PROPERTY(st, "USER", cmd[1], "ONLINESTATUS"); pbnetwork::StatusType status = getStatus(st); - std::string mood_text = message.substr(message.find("MOOD_TEXT") + 10); + std::string mood_text = GET_RESPONSE_DATA(message, "MOOD_TEXT"); 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); + GET_PROPERTY(mood_text, "USER", cmd[1], "MOOD_TEXT"); + GET_PROPERTY(st, "USER", cmd[1], "ONLINESTATUS"); 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); + GET_PROPERTY(alias, "USER", cmd[1], "FULLNAME"); + GET_PROPERTY(mood_text, "USER", cmd[1], "MOOD_TEXT"); + GET_PROPERTY(st, "USER", cmd[1], "ONLINESTATUS"); 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); + GET_PROPERTY(body, "CHATMESSAGE", cmd[1], "BODY"); + GET_PROPERTY(from_handle, "CHATMESSAGE", cmd[1], "FROM_HANDLE"); - 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); + np->handleMessage(sk->getUser(), from_handle, body); + + sk->send_command("SET CHATMESSAGE " + cmd[1] + " SEEN"); + } + } + else if (cmd[0] == "CALL") { + // CALL 884 STATUS RINGING + if (cmd[2] == "STATUS") { + if (cmd[3] == "RINGING" || cmd[3] == "MISSED") { + // handle only incoming calls + GET_PROPERTY(type, "CALL", cmd[1], "TYPE"); + if (type.find("INCOMING") != 0) { + return; + } + + GET_PROPERTY(from, "CALL", cmd[1], "PARTNER_HANDLE"); + GET_PROPERTY(dispname, "CALL", cmd[1], "PARTNER_DISPNAME"); + + if (cmd[3] == "RINGING") { + np->handleMessage(sk->getUser(), from, "User " + dispname + " is calling you."); + } + else { + np->handleMessage(sk->getUser(), from, "You have missed call from user " + dispname + "."); + } + } } } } @@ -825,18 +890,7 @@ int main(int argc, char **argv) { 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); - } + Logging::initBackendLogging(&config); // initPurple(config); diff --git a/backends/smstools3/main.cpp b/backends/smstools3/main.cpp index 1b0bb044..a6502bdc 100644 --- a/backends/smstools3/main.cpp +++ b/backends/smstools3/main.cpp @@ -9,6 +9,7 @@ */ #include "transport/config.h" +#include "transport/logging.h" #include "transport/networkplugin.h" #include "transport/sqlite3backend.h" #include "transport/mysqlbackend.h" @@ -312,80 +313,19 @@ int main (int argc, char* argv[]) { 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); - } + Logging::initBackendLogging(&config); -#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; + std::string error; + StorageBackend *storageBackend = StorageBackend::createBackend(&config, error); + if (storageBackend == NULL) { + if (!error.empty()) { + std::cerr << error << "\n"; + return -2; } } -#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; + else if (!storageBackend->connect()) { + std::cerr << "Can't connect to database. Check the log to find out the reason.\n"; + return -1; } Swift::SimpleEventLoop eventLoop; diff --git a/include/transport/buddy.h b/include/transport/buddy.h index 5e8f1088..929ba337 100644 --- a/include/transport/buddy.h +++ b/include/transport/buddy.h @@ -39,6 +39,9 @@ typedef enum { BUDDY_NO_FLAG = 0, /// Represents one legacy network Buddy. class Buddy { public: + typedef enum { Ask, + Both, + } Subscription; /// Constructor. /// \param rosterManager RosterManager associated with this buddy. @@ -93,12 +96,12 @@ class Buddy { /// Sets current subscription. /// \param subscription "to", "from", "both", "ask" - void setSubscription(const std::string &subscription); + void setSubscription(Subscription subscription); /// Returns current subscription /// \return subscription "to", "from", "both", "ask" - const std::string getSubscription(); + Subscription getSubscription(); /// Sets this buddy's flags. @@ -172,6 +175,7 @@ class Buddy { Swift::JID m_jid; BuddyFlag m_flags; RosterManager *m_rosterManager; + Subscription m_subscription; }; } diff --git a/include/transport/logging.h b/include/transport/logging.h new file mode 100644 index 00000000..79b1dfd5 --- /dev/null +++ b/include/transport/logging.h @@ -0,0 +1,39 @@ +/** + * 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 +#include +#include + +namespace Transport { + +class Config; + +namespace Logging { + +void initBackendLogging(Config *config); +void initMainLogging(Config *config); + +} + +} diff --git a/include/transport/memoryusage.h b/include/transport/memoryusage.h index d2080e21..d38917d9 100644 --- a/include/transport/memoryusage.h +++ b/include/transport/memoryusage.h @@ -22,10 +22,14 @@ #include +#ifndef WIN32 +#include "signal.h" +#endif + namespace Transport { #ifndef WIN32 - void process_mem_usage(double& shared, double& resident_set); + void process_mem_usage(double& shared, double& resident_set, pid_t pid = 0); #endif } \ No newline at end of file diff --git a/include/transport/networkplugin.h b/include/transport/networkplugin.h index bba9c013..9a0dfb9b 100644 --- a/include/transport/networkplugin.h +++ b/include/transport/networkplugin.h @@ -211,6 +211,8 @@ class NetworkPlugin { virtual void handleFTPauseRequest(unsigned long ftID) {} virtual void handleFTContinueRequest(unsigned long ftID) {} + virtual void handleMemoryUsage(double &res, double &shared) {res = 0; shared = 0;} + virtual void handleExitRequest() { exit(1); } void handleDataRead(std::string &data); virtual void sendData(const std::string &string) {} diff --git a/include/transport/networkpluginserver.h b/include/transport/networkpluginserver.h index 671c8ba4..5572780a 100644 --- a/include/transport/networkpluginserver.h +++ b/include/transport/networkpluginserver.h @@ -56,6 +56,7 @@ class NetworkPluginServer { bool acceptUsers; bool longRun; bool willDie; + std::string id; }; NetworkPluginServer(Component *component, Config *config, UserManager *userManager, FileTransferManager *ftManager); @@ -70,6 +71,10 @@ class NetworkPluginServer { return m_clients; } + const std::vector &getCrashedBackends() { + return m_crashedBackends; + } + void collectBackend(); bool moveToLongRunBackend(User *user); @@ -137,6 +142,7 @@ class NetworkPluginServer { bool m_isNextLongRun; std::map m_filetransfers; FileTransferManager *m_ftManager; + std::vector m_crashedBackends; }; } diff --git a/include/transport/protocol.proto b/include/transport/protocol.proto index 9a296392..8f3ac42c 100644 --- a/include/transport/protocol.proto +++ b/include/transport/protocol.proto @@ -106,6 +106,7 @@ message Stats { required int32 res = 1; required int32 init_res = 2; required int32 shared = 3; + required string id = 4; } message File { diff --git a/include/transport/storagebackend.h b/include/transport/storagebackend.h index 6bac4aa7..f0a13df5 100644 --- a/include/transport/storagebackend.h +++ b/include/transport/storagebackend.h @@ -80,6 +80,8 @@ struct BuddyInfo { int flags; }; +class Config; + /// Abstract class defining storage backends. class StorageBackend { @@ -87,6 +89,8 @@ class StorageBackend /// Virtual desctructor. virtual ~StorageBackend() {} + static StorageBackend *createBackend(Config *config, std::string &error); + /// connect virtual bool connect() = 0; diff --git a/include/transport/usermanager.h b/include/transport/usermanager.h index 5ea47d5c..2601be21 100644 --- a/include/transport/usermanager.h +++ b/include/transport/usermanager.h @@ -116,6 +116,13 @@ class UserManager : public Swift::EntityCapsProvider { /// \param user JID of user. void disconnectUser(const Swift::JID &user); + void messageToXMPPSent() { m_sentToXMPP++; } + void messageToBackendSent() { m_sentToBackend++; } + + unsigned long getMessagesToXMPP() { return m_sentToXMPP; } + unsigned long getMessagesToBackend() { return m_sentToBackend; } + + private: void handlePresence(Swift::Presence::ref presence); void handleMessageReceived(Swift::Message::ref message); @@ -134,6 +141,8 @@ class UserManager : public Swift::EntityCapsProvider { StorageResponder *m_storageResponder; UserRegistry *m_userRegistry; Swift::Timer::ref m_removeTimer; + unsigned long m_sentToXMPP; + unsigned long m_sentToBackend; friend class RosterResponder; }; diff --git a/plugin/CMakeLists.txt b/plugin/CMakeLists.txt index 4b7537b5..798bc96f 100644 --- a/plugin/CMakeLists.txt +++ b/plugin/CMakeLists.txt @@ -1 +1,2 @@ -ADD_SUBDIRECTORY(src) +ADD_SUBDIRECTORY(cpp) +ADD_SUBDIRECTORY(python) diff --git a/plugin/src/CMakeLists.txt b/plugin/cpp/CMakeLists.txt similarity index 100% rename from plugin/src/CMakeLists.txt rename to plugin/cpp/CMakeLists.txt diff --git a/plugin/src/networkplugin.cpp b/plugin/cpp/networkplugin.cpp similarity index 97% rename from plugin/src/networkplugin.cpp rename to plugin/cpp/networkplugin.cpp index df4a6a38..1b7f448c 100644 --- a/plugin/src/networkplugin.cpp +++ b/plugin/cpp/networkplugin.cpp @@ -28,6 +28,8 @@ #else #include #include +#include +#define getpid _getpid #endif using namespace log4cxx; @@ -41,6 +43,12 @@ namespace Transport { wrap.set_payload(MESSAGE); \ wrap.SerializeToString(&MESSAGE); +template std::string stringOf(T object) { + std::ostringstream os; + os << object; + return (os.str()); +} + NetworkPlugin::NetworkPlugin() { m_pingReceived = false; @@ -587,13 +595,19 @@ void NetworkPlugin::sendMemoryUsage() { pbnetwork::Stats stats; stats.set_init_res(m_init_res); - double res; - double shared; + double res = 0; + double shared = 0; #ifndef WIN32 process_mem_usage(shared, res); #endif - stats.set_res(res); - stats.set_shared(shared); + + double e_res; + double e_shared; + handleMemoryUsage(e_res, e_shared); + + stats.set_res(res + e_res); + stats.set_shared(shared + e_shared); + stats.set_id(stringOf(getpid())); std::string message; stats.SerializeToString(&message); diff --git a/plugin/python/CMakeLists.txt b/plugin/python/CMakeLists.txt new file mode 100644 index 00000000..5bc15fb8 --- /dev/null +++ b/plugin/python/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 2.6) + +if (PROTOBUF_FOUND) + ADD_CUSTOM_COMMAND( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/protocol_pb2.py + COMMAND ${PROTOBUF_PROTOC_EXECUTABLE} --python_out ${CMAKE_CURRENT_BINARY_DIR} --proto_path ${CMAKE_CURRENT_BINARY_DIR}/../../include/transport/ ${CMAKE_CURRENT_BINARY_DIR}/../../include/transport/protocol.proto + COMMENT "Running Python protocol buffer compiler on protocol.proto" + DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/../../include/transport/protocol.proto + ) + ADD_CUSTOM_TARGET(pb-python DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/protocol_pb2.py) +endif() + + diff --git a/spectrum/src/main.cpp b/spectrum/src/main.cpp index e29ab59a..be525f60 100644 --- a/spectrum/src/main.cpp +++ b/spectrum/src/main.cpp @@ -13,6 +13,7 @@ #include "transport/usersreconnecter.h" #include "transport/util.h" #include "transport/gatewayresponder.h" +#include "transport/logging.h" #include "Swiften/EventLoop/SimpleEventLoop.h" #include #include @@ -235,71 +236,7 @@ int main(int argc, char **argv) } #endif - if (CONFIG_STRING(&config, "logging.config").empty()) { - LoggerPtr root = log4cxx::Logger::getRootLogger(); -#ifdef WIN32 - root->addAppender(new ConsoleAppender(new PatternLayout(L"%d %-5p %c: %m%n"))); -#else - root->addAppender(new ConsoleAppender(new PatternLayout("%d %-5p %c: %m%n"))); -#endif - } - else { - log4cxx::helpers::Properties p; - log4cxx::helpers::FileInputStream *istream = new log4cxx::helpers::FileInputStream(CONFIG_STRING(&config, "logging.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 WIN32 - p.setProperty(L"pid", pid); - p.setProperty(L"jid", jid); -#else - p.setProperty("pid", pid); - p.setProperty("jid", jid); -#endif - - std::string dir; - BOOST_FOREACH(const log4cxx::LogString &prop, p.propertyNames()) { - if (boost::ends_with(prop, ".File")) { - log4cxx::helpers::Transcoder::encode(p.get(prop), dir); - boost::replace_all(dir, "${jid}", jid); - break; - } - } - - if (!dir.empty()) { - // create directories - try { - boost::filesystem::create_directories( - boost::filesystem::path(dir).parent_path().string() - ); - } - catch (...) { - std::cerr << "Can't create logging directory directory " << boost::filesystem::path(dir).parent_path().string() << ".\n"; - return 1; - } - -#ifndef WIN32 - if (!CONFIG_STRING(&config, "service.group").empty() && !CONFIG_STRING(&config, "service.user").empty()) { - struct group *gr; - if ((gr = getgrnam(CONFIG_STRING(&config, "service.group").c_str())) == NULL) { - std::cerr << "Invalid service.group name " << CONFIG_STRING(&config, "service.group") << "\n"; - return 1; - } - struct passwd *pw; - if ((pw = getpwnam(CONFIG_STRING(&config, "service.user").c_str())) == NULL) { - std::cerr << "Invalid service.user name " << CONFIG_STRING(&config, "service.user") << "\n"; - return 1; - } - chown(dir.c_str(), pw->pw_uid, gr->gr_gid); - } - -#endif - } - - log4cxx::PropertyConfigurator::configure(p); - } + Logging::initMainLogging(&config); #ifndef WIN32 if (!CONFIG_STRING(&config, "service.group").empty() ||!CONFIG_STRING(&config, "service.user").empty() ) { @@ -349,57 +286,17 @@ int main(int argc, char **argv) component_ = &transport; // Logger logger(&transport); - StorageBackend *storageBackend = NULL; - -#ifdef WITH_SQLITE - if (CONFIG_STRING(&config, "database.type") == "sqlite3") { - storageBackend = new SQLite3Backend(&config); - if (!storageBackend->connect()) { - std::cerr << "Can't connect to database. Check the log to find out the reason.\n"; - return -1; + std::string error; + StorageBackend *storageBackend = StorageBackend::createBackend(&config, error); + if (storageBackend == NULL) { + if (!error.empty()) { + std::cerr << error << "\n"; + return -2; } } -#else - if (CONFIG_STRING(&config, "database.type") == "sqlite3") { - std::cerr << "Spectrum2 is not compiled with sqlite3 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; + else if (!storageBackend->connect()) { + std::cerr << "Can't connect to database. Check the log to find out the reason.\n"; + return -1; } UserManager userManager(&transport, &userRegistry, storageBackend); @@ -440,4 +337,5 @@ int main(int argc, char **argv) delete storageBackend; delete factories; + return 0; } diff --git a/spectrum/src/sample.cfg b/spectrum/src/sample.cfg index d0e234e9..d3b4f317 100644 --- a/spectrum/src/sample.cfg +++ b/spectrum/src/sample.cfg @@ -7,27 +7,22 @@ server_mode = 1 backend_host=localhost pidfile=./test.pid # < this option doesn't work yet -backend_port=10001 -admin_username=admin +#backend_port=10001 +admin_jid=admin@localhost admin_password=test #cert=server.pfx #patch to PKCS#12 certificate #cert_password=test #password to that certificate if any users_per_backend=10 -backend=/home/hanzz/code/libtransport/backends/libpurple/spectrum2_libpurple_backend -#backend=/home/hanzz/code/libtransport/backends/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 +backend=../..//backends/libpurple/spectrum2_libpurple_backend +protocol=prpl-xmpp #protocol=prpl-msn -protocol=any +#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 diff --git a/src/admininterface.cpp b/src/admininterface.cpp index 7df84772..cec4dfa6 100644 --- a/src/admininterface.cpp +++ b/src/admininterface.cpp @@ -112,7 +112,7 @@ void AdminInterface::handleMessageReceived(Swift::Message::ref message) { const std::list &backends = m_server->getBackends(); for (std::list ::const_iterator b = backends.begin(); b != backends.end(); b++) { NetworkPluginServer::Backend *backend = *b; - lst += "Backend " + boost::lexical_cast(id); + lst += "Backend " + boost::lexical_cast(id) + " (ID=" + backend->id + ")"; lst += backend->acceptUsers ? "" : " - not-accepting"; lst += backend->longRun ? " - long-running" : ""; lst += ":\n"; @@ -204,7 +204,7 @@ void AdminInterface::handleMessageReceived(Swift::Message::ref message) { int id = 1; const std::list &backends = m_server->getBackends(); BOOST_FOREACH(NetworkPluginServer::Backend * backend, backends) { - lst += "Backend " + boost::lexical_cast(id) + ": " + boost::lexical_cast(backend->res) + "\n"; + lst += "Backend " + boost::lexical_cast(id) + " (ID=" + backend->id + "): " + boost::lexical_cast(backend->res) + "\n"; id++; } @@ -215,7 +215,7 @@ void AdminInterface::handleMessageReceived(Swift::Message::ref message) { int id = 1; const std::list &backends = m_server->getBackends(); BOOST_FOREACH(NetworkPluginServer::Backend * backend, backends) { - lst += "Backend " + boost::lexical_cast(id) + ": " + boost::lexical_cast(backend->shared) + "\n"; + lst += "Backend " + boost::lexical_cast(id) + " (ID=" + backend->id + "): " + boost::lexical_cast(backend->shared) + "\n"; id++; } @@ -226,7 +226,7 @@ void AdminInterface::handleMessageReceived(Swift::Message::ref message) { int id = 1; const std::list &backends = m_server->getBackends(); BOOST_FOREACH(NetworkPluginServer::Backend * backend, backends) { - lst += "Backend " + boost::lexical_cast(id) + ": " + boost::lexical_cast(backend->res - backend->shared) + "\n"; + lst += "Backend " + boost::lexical_cast(id) + " (ID=" + backend->id + "): " + boost::lexical_cast(backend->res - backend->shared) + "\n"; id++; } @@ -238,10 +238,10 @@ void AdminInterface::handleMessageReceived(Swift::Message::ref message) { const std::list &backends = m_server->getBackends(); BOOST_FOREACH(NetworkPluginServer::Backend * backend, backends) { if (backend->users.size() == 0) { - lst += "Backend " + boost::lexical_cast(id) + ": 0\n"; + lst += "Backend " + boost::lexical_cast(id) + " (ID=" + backend->id + "): 0\n"; } else { - lst += "Backend " + boost::lexical_cast(id) + ": " + boost::lexical_cast((backend->res - backend->init_res) / backend->users.size()) + "\n"; + lst += "Backend " + boost::lexical_cast(id) + " (ID=" + backend->id + "): " + boost::lexical_cast((backend->res - backend->init_res) / backend->users.size()) + "\n"; } id++; } @@ -251,6 +251,14 @@ void AdminInterface::handleMessageReceived(Swift::Message::ref message) { else if (message->getBody() == "collect_backend") { m_server->collectBackend(); } + else if (message->getBody() == "crashed_backends") { + std::string lst; + const std::vector &backends = m_server->getCrashedBackends(); + BOOST_FOREACH(const std::string &backend, backends) { + lst += backend + "\n"; + } + message->setBody(lst); + } else if (message->getBody().find("help") == 0) { std::string help; help += "General:\n"; @@ -263,6 +271,7 @@ void AdminInterface::handleMessageReceived(Swift::Message::ref message) { help += " has_online_user - returns 1 if user is online\n"; help += "Backends:\n"; help += " backends_count - number of active backends\n"; + help += " crashed_backends - returns IDs of crashed backends\n"; help += "Memory:\n"; help += " res_memory - Total RESident memory spectrum2 and its backends use in KB\n"; help += " shr_memory - Total SHaRed memory spectrum2 backends share together in KB\n"; diff --git a/src/buddy.cpp b/src/buddy.cpp index 6c023539..55f35713 100644 --- a/src/buddy.cpp +++ b/src/buddy.cpp @@ -26,7 +26,8 @@ namespace Transport { -Buddy::Buddy(RosterManager *rosterManager, long id) : m_id(id), m_flags(BUDDY_NO_FLAG), m_rosterManager(rosterManager){ +Buddy::Buddy(RosterManager *rosterManager, long id) : m_id(id), m_flags(BUDDY_NO_FLAG), m_rosterManager(rosterManager), + m_subscription(Ask) { // m_rosterManager->setBuddy(this); } @@ -64,12 +65,12 @@ const Swift::JID &Buddy::getJID() { return m_jid; } -void Buddy::setSubscription(const std::string &subscription) { -// m_subscription = subscription; +void Buddy::setSubscription(Subscription subscription) { + m_subscription = subscription; } -const std::string Buddy::getSubscription() { - return "ask"; +Buddy::Subscription Buddy::getSubscription() { + return m_subscription; } Swift::Presence::ref Buddy::generatePresenceStanza(int features, bool only_new) { diff --git a/src/logging.cpp b/src/logging.cpp new file mode 100644 index 00000000..dfc3f447 --- /dev/null +++ b/src/logging.cpp @@ -0,0 +1,134 @@ +/** + * 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 + */ + +#include "transport/logging.h" +#include "transport/config.h" +#include +#include +#include +#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 "log4cxx/helpers/transcoder.h" +#include +#include + +#ifndef WIN32 +#include "sys/signal.h" +#include +#include +#include +#include "libgen.h" +#else +#include +#include +#define getpid _getpid +#endif + +using namespace boost::filesystem; +using namespace log4cxx; + +namespace Transport { + +namespace Logging { + +static LoggerPtr root; + +static void initLogging(Config *config, std::string key) { + if (CONFIG_STRING(config, key).empty()) { + root = log4cxx::Logger::getRootLogger(); +#ifdef WIN32 + root->addAppender(new ConsoleAppender(new PatternLayout(L"%d %-5p %c: %m%n"))); +#else + root->addAppender(new ConsoleAppender(new PatternLayout("%d %-5p %c: %m%n"))); +#endif + } + else { + log4cxx::helpers::Properties p; + log4cxx::helpers::FileInputStream *istream = new log4cxx::helpers::FileInputStream(CONFIG_STRING(config, key)); + + 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 WIN32 + p.setProperty(L"pid", pid); + p.setProperty(L"jid", jid); +#else + p.setProperty("pid", pid); + p.setProperty("jid", jid); +#endif + + std::string dir; + BOOST_FOREACH(const log4cxx::LogString &prop, p.propertyNames()) { + if (boost::ends_with(prop, ".File")) { + log4cxx::helpers::Transcoder::encode(p.get(prop), dir); + boost::replace_all(dir, "${jid}", jid); + break; + } + } + + if (!dir.empty()) { + // create directories + try { + boost::filesystem::create_directories( + boost::filesystem::path(dir).parent_path().string() + ); + } + catch (...) { + std::cerr << "Can't create logging directory directory " << boost::filesystem::path(dir).parent_path().string() << ".\n"; + } + +#ifndef WIN32 + if (!CONFIG_STRING(config, "service.group").empty() && !CONFIG_STRING(config, "service.user").empty()) { + struct group *gr; + if ((gr = getgrnam(CONFIG_STRING(config, "service.group").c_str())) == NULL) { + std::cerr << "Invalid service.group name " << CONFIG_STRING(config, "service.group") << "\n"; + } + struct passwd *pw; + if ((pw = getpwnam(CONFIG_STRING(config, "service.user").c_str())) == NULL) { + std::cerr << "Invalid service.user name " << CONFIG_STRING(config, "service.user") << "\n"; + } + chown(dir.c_str(), pw->pw_uid, gr->gr_gid); + } + +#endif + } + + log4cxx::PropertyConfigurator::configure(p); + } +} + +void initBackendLogging(Config *config) { + initLogging(config, "logging.backend_config"); +} + +void initMainLogging(Config *config) { + initLogging(config, "logging.config"); +} + +} + +} diff --git a/src/memoryusage.cpp b/src/memoryusage.cpp index ded5130e..d620569b 100644 --- a/src/memoryusage.cpp +++ b/src/memoryusage.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #ifndef WIN32 #include #endif @@ -41,13 +42,18 @@ namespace Transport { #ifndef WIN32 #ifdef BSD -void process_mem_usage(double& vm_usage, double& resident_set) { +void process_mem_usage(double& vm_usage, double& resident_set, pid_t pid) { int mib[4]; size_t size; mib[0] = CTL_KERN; mib[1] = KERN_PROC; mib[2] = KERN_PROC_PID; - mib[3] = getpid(); + if (pid == 0) { + mib[3] = getpid(); + } + else { + mib[3] = pid; + } struct kinfo_proc proc; size = sizeof(struct kinfo_proc); @@ -75,7 +81,7 @@ void process_mem_usage(double& vm_usage, double& resident_set) { vm_usage = (double) proc.ki_size; } #else /* BSD */ -void process_mem_usage(double& shared, double& resident_set) { +void process_mem_usage(double& shared, double& resident_set, pid_t pid) { using std::ios_base; using std::ifstream; using std::string; @@ -84,8 +90,11 @@ void process_mem_usage(double& shared, double& resident_set) { resident_set = 0.0; // 'file' stat seems to give the most reliable results - // - ifstream stat_stream("/proc/self/statm",ios_base::in); + std::string f = "/proc/self/statm"; + if (pid != 0) { + f = "/proc/" + boost::lexical_cast(pid) + "/statm"; + } + ifstream stat_stream(f.c_str(), ios_base::in); if (!stat_stream.is_open()) { shared = 0; resident_set = 0; @@ -94,7 +103,7 @@ void process_mem_usage(double& shared, double& resident_set) { // dummy vars for leading entries in stat that we don't care about // - string pid, comm, state, ppid, pgrp, session, tty_nr; + string pid1, comm, state, ppid, pgrp, session, tty_nr; string tpgid, flags, minflt, cminflt, majflt, cmajflt; string utime, stime, cutime, cstime, priority, nice; string O, itrealvalue, starttime; diff --git a/src/networkpluginserver.cpp b/src/networkpluginserver.cpp index eeae4c31..a36699be 100644 --- a/src/networkpluginserver.cpp +++ b/src/networkpluginserver.cpp @@ -95,7 +95,12 @@ class NetworkFactory : public Factory { LocalBuddy *buddy = new LocalBuddy(rosterManager, buddyInfo.id); buddy->setAlias(buddyInfo.alias); buddy->setName(buddyInfo.legacyName); - buddy->setSubscription(buddyInfo.subscription); + if (buddyInfo.subscription == "both") { + buddy->setSubscription(Buddy::Both); + } + else { + buddy->setSubscription(Buddy::Ask); + } buddy->setGroups(buddyInfo.groups); buddy->setFlags((BuddyFlag) (buddyInfo.flags)); if (buddyInfo.settings.find("icon_hash") != buddyInfo.settings.end()) @@ -339,15 +344,19 @@ void NetworkPluginServer::handleNewClientConnection(boost::shared_ptrgetJID().toString()); + LOG4CXX_ERROR(logger, "Backend " << c << " (ID=" << c->id << ") disconnected (probably crashed) with active user " << (*it)->getJID().toString()); (*it)->setData(NULL); (*it)->handleDisconnected("Internal Server Error, please reconnect."); } @@ -570,6 +579,7 @@ void NetworkPluginServer::handleConvMessagePayload(const std::string &data, bool // Forward it conv->handleMessage(msg, payload.nickname()); + m_userManager->messageToXMPPSent(); } void NetworkPluginServer::handleAttentionPayload(const std::string &data) { @@ -607,6 +617,7 @@ void NetworkPluginServer::handleStatsPayload(Backend *c, const std::string &data c->res = payload.res(); c->init_res = payload.init_res(); c->shared = payload.shared(); + c->id = payload.id(); } void NetworkPluginServer::handleFTStartPayload(const std::string &data) { @@ -853,12 +864,12 @@ void NetworkPluginServer::pingTimeout() { sendPing((*it)); } else { - LOG4CXX_INFO(logger, "Disconnecting backend " << (*it) << ". PING response not received."); + LOG4CXX_INFO(logger, "Disconnecting backend " << (*it) << " (ID=" << (*it)->id << "). PING response not received."); toRemove.push_back(*it); } if ((*it)->users.size() == 0) { - LOG4CXX_INFO(logger, "Disconnecting backend " << (*it) << ". There are no users."); + LOG4CXX_INFO(logger, "Disconnecting backend " << (*it) << " (ID=" << (*it)->id << "). There are no users."); toRemove.push_back(*it); } } @@ -887,7 +898,7 @@ void NetworkPluginServer::collectBackend() { if (m_collectTimer) { m_collectTimer->start(); } - LOG4CXX_INFO(logger, "Backend " << backend << "is set to die"); + LOG4CXX_INFO(logger, "Backend " << backend << " (ID=" << backend->id << ") is set to die"); backend->acceptUsers = false; } } @@ -1355,7 +1366,7 @@ void NetworkPluginServer::sendPing(Backend *c) { wrap.SerializeToString(&message); if (c->connection) { - LOG4CXX_INFO(logger, "PING to " << c); + LOG4CXX_INFO(logger, "PING to " << c << " (ID=" << c->id << ")"); send(c->connection, message); c->pongReceived = false; } diff --git a/src/rostermanager.cpp b/src/rostermanager.cpp index 65135116..1bde0173 100644 --- a/src/rostermanager.cpp +++ b/src/rostermanager.cpp @@ -137,6 +137,11 @@ void RosterManager::sendBuddyRosterPush(Buddy *buddy) { request->send(); m_requests.push_back(request); } + + if (buddy->getSubscription() != Buddy::Both) { + buddy->setSubscription(Buddy::Both); + handleBuddyChanged(buddy); + } } void RosterManager::sendBuddySubscribePresence(Buddy *buddy) { @@ -165,6 +170,11 @@ void RosterManager::setBuddyCallback(Buddy *buddy) { sendBuddyRosterPush(buddy); } else { + if (buddy->getSubscription() == Buddy::Both) { + LOG4CXX_INFO(logger, m_user->getJID().toString() << ": Not forwarding this buddy, because subscription=both"); + return; + } + if (m_supportRemoteRoster) { sendBuddyRosterPush(buddy); } @@ -331,8 +341,8 @@ void RosterManager::handleSubscription(Swift::Presence::ref presence) { response->setType(Swift::Presence::Subscribed); break; case Swift::Presence::Subscribed: - onBuddyAdded(buddy); - break; +// onBuddyAdded(buddy); + return; // buddy is already there, so nothing to do, just answer case Swift::Presence::Unsubscribe: response->setType(Swift::Presence::Unsubscribed); @@ -361,6 +371,10 @@ void RosterManager::handleSubscription(Swift::Presence::ref presence) { currentPresence->setTo(presence->getFrom()); m_component->getStanzaChannel()->sendPresence(currentPresence); } + if (buddy->getSubscription() != Buddy::Both) { + buddy->setSubscription(Buddy::Both); + handleBuddyChanged(buddy); + } break; // remove buddy case Swift::Presence::Unsubscribe: @@ -370,7 +384,20 @@ void RosterManager::handleSubscription(Swift::Presence::ref presence) { // just send response case Swift::Presence::Unsubscribed: response->setType(Swift::Presence::Unsubscribe); + // We set both here, because this Unsubscribed can be response to + // subscribe presence and we don't want that unsubscribe presence + // to be send later again + if (buddy->getSubscription() != Buddy::Both) { + buddy->setSubscription(Buddy::Both); + handleBuddyChanged(buddy); + } break; + case Swift::Presence::Subscribed: + if (buddy->getSubscription() != Buddy::Both) { + buddy->setSubscription(Buddy::Both); + handleBuddyChanged(buddy); + } + return; default: return; } @@ -394,7 +421,7 @@ void RosterManager::handleSubscription(Swift::Presence::ref presence) { // buddy is already there, so nothing to do, just answer case Swift::Presence::Unsubscribe: response->setType(Swift::Presence::Unsubscribed); - onBuddyRemoved(buddy); +// onBuddyRemoved(buddy); break; // just send response case Swift::Presence::Unsubscribed: diff --git a/src/rosterstorage.cpp b/src/rosterstorage.cpp index 294f19e8..30679cb9 100644 --- a/src/rosterstorage.cpp +++ b/src/rosterstorage.cpp @@ -103,7 +103,7 @@ bool RosterStorage::storeBuddies() { buddyInfo.alias = buddy->getAlias(); buddyInfo.legacyName = buddy->getName(); buddyInfo.groups = buddy->getGroups(); - buddyInfo.subscription = buddy->getSubscription(); + buddyInfo.subscription = buddy->getSubscription() == Buddy::Ask ? "ask" : "both"; buddyInfo.id = buddy->getID(); buddyInfo.flags = buddy->getFlags(); buddyInfo.settings["icon_hash"].s = buddy->getIconHash(); diff --git a/src/statsresponder.cpp b/src/statsresponder.cpp index 802a1bbd..bf4e4b5f 100644 --- a/src/statsresponder.cpp +++ b/src/statsresponder.cpp @@ -81,7 +81,10 @@ bool StatsResponder::handleGetRequest(const Swift::JID& from, const Swift::JID& response->addItem(StatsPayload::Item("users/online")); response->addItem(StatsPayload::Item("contacts/online")); response->addItem(StatsPayload::Item("contacts/total")); - response->addItem(StatsPayload::Item("backends")); + response->addItem(StatsPayload::Item("messages/from-xmpp")); + response->addItem(StatsPayload::Item("messages/to-xmpp")); + response->addItem(StatsPayload::Item("backends/running")); + response->addItem(StatsPayload::Item("backends/crashed")); response->addItem(StatsPayload::Item("memory-usage")); } else { @@ -115,8 +118,11 @@ bool StatsResponder::handleGetRequest(const Swift::JID& from, const Swift::JID& else if (item.getName() == "users/online") { response->addItem(StatsPayload::Item("users/online", "users", boost::lexical_cast(m_userManager->getUserCount()))); } - else if (item.getName() == "backends") { - response->addItem(StatsPayload::Item("backends", "backends", boost::lexical_cast(m_server->getBackendCount()))); + else if (item.getName() == "backends/running") { + response->addItem(StatsPayload::Item("backends/running", "backends", boost::lexical_cast(m_server->getBackendCount()))); + } + else if (item.getName() == "backends/crashed") { + response->addItem(StatsPayload::Item("backends/crashed", "backends", boost::lexical_cast(m_server->getCrashedBackends().size()))); } else if (item.getName() == "memory-usage") { response->addItem(StatsPayload::Item("memory-usage", "KB", boost::lexical_cast(usedMemory()))); @@ -127,6 +133,12 @@ bool StatsResponder::handleGetRequest(const Swift::JID& from, const Swift::JID& else if (item.getName() == "contacts/total") { response->addItem(StatsPayload::Item("contacts/total", "contacts", boost::lexical_cast(contactsTotal))); } + else if (item.getName() == "messages/from-xmpp") { + response->addItem(StatsPayload::Item("messages/from-xmpp", "messages", boost::lexical_cast(m_userManager->getMessagesToBackend()))); + } + else if (item.getName() == "messages/to-xmpp") { + response->addItem(StatsPayload::Item("messages/to-xmpp", "messages", boost::lexical_cast(m_userManager->getMessagesToXMPP()))); + } } } diff --git a/src/storagebackend.cpp b/src/storagebackend.cpp new file mode 100644 index 00000000..17657a48 --- /dev/null +++ b/src/storagebackend.cpp @@ -0,0 +1,49 @@ +#include "transport/storagebackend.h" +#include "transport/config.h" + +#include "transport/sqlite3backend.h" +#include "transport/mysqlbackend.h" +#include "transport/pqxxbackend.h" + +namespace Transport { + +StorageBackend *StorageBackend::createBackend(Config *config, std::string &error) { + StorageBackend *storageBackend = NULL; +#ifdef WITH_SQLITE + if (CONFIG_STRING(config, "database.type") == "sqlite3") { + storageBackend = new SQLite3Backend(config); + } +#else + if (CONFIG_STRING(config, "database.type") == "sqlite3") { + error = "Libtransport is not compiled with sqlite3 backend support."; + } +#endif + +#ifdef WITH_MYSQL + if (CONFIG_STRING(config, "database.type") == "mysql") { + storageBackend = new MySQLBackend(config); + } +#else + if (CONFIG_STRING(config, "database.type") == "mysql") { + error = "Spectrum2 is not compiled with mysql backend support."; + } +#endif + +#ifdef WITH_PQXX + if (CONFIG_STRING(config, "database.type") == "pqxx") { + storageBackend = new PQXXBackend(config); + } +#else + if (CONFIG_STRING(config, "database.type") == "pqxx") { + error = "Spectrum2 is not compiled with pqxx backend support."; + } +#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") { + error = "Unknown storage backend " + CONFIG_STRING(config, "database.type"); + } + return storageBackend; +} + +} diff --git a/src/tests/basictest.h b/src/tests/basictest.h index c67691b3..832cada4 100644 --- a/src/tests/basictest.h +++ b/src/tests/basictest.h @@ -72,7 +72,7 @@ class TestingFactory : public Factory { LocalBuddy *buddy = new LocalBuddy(rosterManager, buddyInfo.id); buddy->setAlias(buddyInfo.alias); buddy->setName(buddyInfo.legacyName); - buddy->setSubscription(buddyInfo.subscription); + buddy->setSubscription(Buddy::Ask); buddy->setGroups(buddyInfo.groups); buddy->setFlags((BuddyFlag) buddyInfo.flags); if (buddyInfo.settings.find("icon_hash") != buddyInfo.settings.end()) diff --git a/src/user.cpp b/src/user.cpp index e94ae872..0baedfa9 100644 --- a/src/user.cpp +++ b/src/user.cpp @@ -210,6 +210,7 @@ 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) { @@ -328,7 +329,7 @@ void User::handleDisconnected(const std::string &error, Swift::SpectrumErrorPayl if (e == Swift::SpectrumErrorPayload::CONNECTION_ERROR_OTHER_ERROR || e == Swift::SpectrumErrorPayload::CONNECTION_ERROR_NETWORK_ERROR) { if (m_reconnectCounter < 3) { m_reconnectCounter++; - LOG4CXX_INFO(logger, m_jid.toString() << ": Disconnecting from legacy network for, trying to reconnect automatically."); + LOG4CXX_INFO(logger, m_jid.toString() << ": Disconnecting from legacy network " << error << ", trying to reconnect automatically."); // Simulate destruction/resurrection :) // TODO: If this stops working, create onReconnect signal m_userManager->onUserDestroyed(this); diff --git a/src/usermanager.cpp b/src/usermanager.cpp index 08823715..52e41607 100644 --- a/src/usermanager.cpp +++ b/src/usermanager.cpp @@ -42,6 +42,8 @@ static LoggerPtr logger = Logger::getLogger("UserManager"); UserManager::UserManager(Component *component, UserRegistry *userRegistry, StorageBackend *storageBackend) { m_cachedUser = NULL; m_onlineBuddies = 0; + m_sentToXMPP = 0; + m_sentToBackend = 0; m_component = component; m_storageBackend = storageBackend; m_storageResponder = NULL; @@ -150,6 +152,21 @@ void UserManager::handlePresence(Swift::Presence::ref presence) { if (!user) { // Admin user is not legacy network user, so do not create User class instance for him if (m_component->inServerMode() && CONFIG_STRING(m_component->getConfig(), "service.admin_jid") == presence->getFrom().toBare().toString()) { + // Send admin contact to the user. + Swift::RosterPayload::ref payload = Swift::RosterPayload::ref(new Swift::RosterPayload()); + Swift::RosterItemPayload item; + item.setJID(m_component->getJID()); + item.setName("Admin"); + item.setSubscription(Swift::RosterItemPayload::Both); + payload->addItem(item); + + Swift::SetRosterRequest::ref request = Swift::SetRosterRequest::create(payload, presence->getFrom(), m_component->getIQRouter()); + request->send(); + + Swift::Presence::ref response = Swift::Presence::create(); + response->setTo(presence->getFrom()); + response->setFrom(m_component->getJID()); + m_component->getStanzaChannel()->sendPresence(response); return; } @@ -266,6 +283,7 @@ void UserManager::handleMessageReceived(Swift::Message::ref message) { } user->getConversationManager()->handleMessageReceived(message); + messageToBackendSent(); } void UserManager::handleGeneralPresenceReceived(Swift::Presence::ref presence) { diff --git a/src/userregistration.cpp b/src/userregistration.cpp index f7de616e..db7ab22e 100644 --- a/src/userregistration.cpp +++ b/src/userregistration.cpp @@ -453,7 +453,7 @@ bool UserRegistration::handleSetRequest(const Swift::JID& from, const Swift::JID // #endif if (!registered) { res.jid = barejid; - res.uin = username; + res.uin = newUsername; res.password = *payload->getPassword(); res.language = language; res.encoding = encoding; @@ -462,7 +462,7 @@ bool UserRegistration::handleSetRequest(const Swift::JID& from, const Swift::JID } else { res.jid = barejid; - res.uin = username; + res.uin = newUsername; res.password = *payload->getPassword(); res.language = language; res.encoding = encoding;