This commit is contained in:
Vitaly Takmazov 2012-03-21 11:36:20 +04:00
commit bd4b3bcd1d
35 changed files with 854 additions and 794 deletions

View file

@ -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)

View file

@ -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).

View file

@ -10,6 +10,7 @@
#include "transport/config.h"
#include "transport/networkplugin.h"
#include "transport/logging.h"
#include "session.h"
#include <QtCore>
#include <QtNetwork>
@ -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<std::string>(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;

View file

@ -1,3 +1,5 @@
#include "utils.h"
#include "glib.h"
#include "purple.h"
#include <algorithm>
@ -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 <netinet/if_ether.h>
#include <netinet/ip.h>
#include <netinet/ip6.h>
#include <netinet/udp.h>
#include <netinet/tcp.h>
#include <netinet/ether.h>
#include "sys/socket.h"
#include <netdb.h>
#include <unistd.h>
#include <fcntl.h>
#else
#include <process.h>
#define getpid _getpid
#define ssize_t SSIZE_T
#include "win32/win32dep.h"
#endif
// #include "valgrind/memcheck.h"
#include "malloc.h"
#include <algorithm>
@ -40,11 +24,18 @@
#include <event.h>
#endif
#ifdef WIN32
#include "win32/win32dep.h"
#define ssize_t SSIZE_T
#include <process.h>
#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, "&lt;", 4) ||
!g_ascii_strncasecmp(c, "&gt;", 4) ||
!g_ascii_strncasecmp(c, "&quot;", 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, "<A HREF=\"%s%s\">%s</A>",
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, "<A", 2)) {
while (1) {
if (!g_ascii_strncasecmp(c, "/A>", 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, "<A HREF=\"%s\">%s</A>",
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, "&lt;", 4) ||
!g_ascii_strncasecmp(t - 3, "&gt;", 4))) ||
(t > (text+4) && (!g_ascii_strncasecmp(t - 5, "&quot;", 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, "<A HREF=\"mailto:%s\">%s</A>",
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 <configuration_file.cfg>\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;

View file

@ -0,0 +1,129 @@
/**
* libtransport -- C++ library for easy XMPP Transports development
*
* Copyright (C) 2011, Jan Kaluza <hanzz.k@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
#include "utils.h"
#include "glib.h"
#include "purple.h"
#include <algorithm>
#include <iostream>
#include "errno.h"
#ifndef WIN32
#include "sys/wait.h"
#include "sys/signal.h"
#include <netinet/if_ether.h>
#include <netinet/ip.h>
#include <netinet/ip6.h>
#include <netinet/udp.h>
#include <netinet/tcp.h>
#include <netinet/ether.h>
#include "sys/socket.h"
#include <netdb.h>
#include <unistd.h>
#include <fcntl.h>
#else
#include <process.h>
#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;
}

View file

@ -0,0 +1,30 @@
/**
* libtransport -- C++ library for easy XMPP Transports development
*
* Copyright (C) 2011, Jan Kaluza <hanzz.k@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
#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);

View file

@ -2,8 +2,10 @@
#include <iostream>
#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<std::string, Skype *>::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<std::string> &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<std::string> &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<std::string> &groups) {
}
void handleBuddyUpdatedRequest(const std::string &user, const std::string &buddyName, const std::string &alias, const std::vector<std::string> &groups) {
}
void handleBuddyBlockToggled(const std::string &user, const std::string &buddyName, bool blocked) {
}
@ -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() {
"</config>\n";
g_file_set_contents(std::string(std::string("/tmp/skype/") + m_username + "/" + m_username +"/config.xml").c_str(), config_xml.c_str(), -1, NULL);
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<std::string, std::string> group_map;
std::string groups = send_command("SEARCH GROUPS CUSTOM");
groups = groups.substr(groups.find(' ') + 1);
std::vector<std::string> grps;
boost::split(grps, groups, boost::is_any_of(","));
BOOST_FOREACH(std::string grp, grps) {
std::vector<std::string> data;
std::string name = send_command("GET GROUP " + grp + " DISPLAYNAME");
boost::split(data, name, boost::is_any_of(" "));
name = name.substr(name.find("DISPLAYNAME") + 12);
if (groups.find(' ') != std::string::npos) {
groups = groups.substr(groups.find(' ') + 1);
std::vector<std::string> grps;
boost::split(grps, groups, boost::is_any_of(","));
BOOST_FOREACH(std::string grp, grps) {
std::vector<std::string> data;
std::string name = send_command("GET GROUP " + grp + " DISPLAYNAME");
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<std::string> groups;
np->handleBuddyChanged(sk->getUser(), cmd[1], alias, groups, status, mood_text);
}
}
else if (cmd[2] == "MOOD_TEXT") {
std::string st = sk->send_command("GET USER " + cmd[1] + " ONLINESTATUS");
st = st.substr(st.find("ONLINESTATUS") + 13);
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<std::string> groups;
np->handleBuddyChanged(sk->getUser(), cmd[1], "", groups, status, mood_text);
}
else if (cmd[2] == "BUDDYSTATUS" && cmd[3] == "3") {
std::string st = sk->send_command("GET USER " + cmd[1] + " ONLINESTATUS");
st = st.substr(st.find("ONLINESTATUS") + 13);
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<std::string> 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<std::string> 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<std::string>(getpid()));
log4cxx::PropertyConfigurator::configure(p);
}
Logging::initBackendLogging(&config);
// initPurple(config);

View file

@ -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<std::string>(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;

View file

@ -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;
};
}

View file

@ -0,0 +1,39 @@
/**
* libtransport -- C++ library for easy XMPP Transports development
*
* Copyright (C) 2011, Jan Kaluza <hanzz.k@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <vector>
#include <string>
namespace Transport {
class Config;
namespace Logging {
void initBackendLogging(Config *config);
void initMainLogging(Config *config);
}
}

View file

@ -22,10 +22,14 @@
#include <vector>
#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
}

View file

@ -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) {}

View file

@ -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<std::string> &getCrashedBackends() {
return m_crashedBackends;
}
void collectBackend();
bool moveToLongRunBackend(User *user);
@ -137,6 +142,7 @@ class NetworkPluginServer {
bool m_isNextLongRun;
std::map<unsigned long, FileTransferManager::Transfer> m_filetransfers;
FileTransferManager *m_ftManager;
std::vector<std::string> m_crashedBackends;
};
}

View file

@ -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 {

View file

@ -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;

View file

@ -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;
};

View file

@ -1 +1,2 @@
ADD_SUBDIRECTORY(src)
ADD_SUBDIRECTORY(cpp)
ADD_SUBDIRECTORY(python)

View file

@ -28,6 +28,8 @@
#else
#include <winsock2.h>
#include <stdint.h>
#include <process.h>
#define getpid _getpid
#endif
using namespace log4cxx;
@ -41,6 +43,12 @@ namespace Transport {
wrap.set_payload(MESSAGE); \
wrap.SerializeToString(&MESSAGE);
template <class T> 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);

View file

@ -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()

View file

@ -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 <boost/filesystem.hpp>
#include <boost/algorithm/string.hpp>
@ -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<std::string>(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;
}

View file

@ -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

View file

@ -112,7 +112,7 @@ void AdminInterface::handleMessageReceived(Swift::Message::ref message) {
const std::list <NetworkPluginServer::Backend *> &backends = m_server->getBackends();
for (std::list <NetworkPluginServer::Backend *>::const_iterator b = backends.begin(); b != backends.end(); b++) {
NetworkPluginServer::Backend *backend = *b;
lst += "Backend " + boost::lexical_cast<std::string>(id);
lst += "Backend " + boost::lexical_cast<std::string>(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 <NetworkPluginServer::Backend *> &backends = m_server->getBackends();
BOOST_FOREACH(NetworkPluginServer::Backend * backend, backends) {
lst += "Backend " + boost::lexical_cast<std::string>(id) + ": " + boost::lexical_cast<std::string>(backend->res) + "\n";
lst += "Backend " + boost::lexical_cast<std::string>(id) + " (ID=" + backend->id + "): " + boost::lexical_cast<std::string>(backend->res) + "\n";
id++;
}
@ -215,7 +215,7 @@ void AdminInterface::handleMessageReceived(Swift::Message::ref message) {
int id = 1;
const std::list <NetworkPluginServer::Backend *> &backends = m_server->getBackends();
BOOST_FOREACH(NetworkPluginServer::Backend * backend, backends) {
lst += "Backend " + boost::lexical_cast<std::string>(id) + ": " + boost::lexical_cast<std::string>(backend->shared) + "\n";
lst += "Backend " + boost::lexical_cast<std::string>(id) + " (ID=" + backend->id + "): " + boost::lexical_cast<std::string>(backend->shared) + "\n";
id++;
}
@ -226,7 +226,7 @@ void AdminInterface::handleMessageReceived(Swift::Message::ref message) {
int id = 1;
const std::list <NetworkPluginServer::Backend *> &backends = m_server->getBackends();
BOOST_FOREACH(NetworkPluginServer::Backend * backend, backends) {
lst += "Backend " + boost::lexical_cast<std::string>(id) + ": " + boost::lexical_cast<std::string>(backend->res - backend->shared) + "\n";
lst += "Backend " + boost::lexical_cast<std::string>(id) + " (ID=" + backend->id + "): " + boost::lexical_cast<std::string>(backend->res - backend->shared) + "\n";
id++;
}
@ -238,10 +238,10 @@ void AdminInterface::handleMessageReceived(Swift::Message::ref message) {
const std::list <NetworkPluginServer::Backend *> &backends = m_server->getBackends();
BOOST_FOREACH(NetworkPluginServer::Backend * backend, backends) {
if (backend->users.size() == 0) {
lst += "Backend " + boost::lexical_cast<std::string>(id) + ": 0\n";
lst += "Backend " + boost::lexical_cast<std::string>(id) + " (ID=" + backend->id + "): 0\n";
}
else {
lst += "Backend " + boost::lexical_cast<std::string>(id) + ": " + boost::lexical_cast<std::string>((backend->res - backend->init_res) / backend->users.size()) + "\n";
lst += "Backend " + boost::lexical_cast<std::string>(id) + " (ID=" + backend->id + "): " + boost::lexical_cast<std::string>((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<std::string> &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 <bare_JID> - 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";

View file

@ -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) {

134
src/logging.cpp Normal file
View file

@ -0,0 +1,134 @@
/**
* libtransport -- C++ library for easy XMPP Transports development
*
* Copyright (C) 2011, Jan Kaluza <hanzz.k@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
*/
#include "transport/logging.h"
#include "transport/config.h"
#include <boost/foreach.hpp>
#include <iostream>
#include <iterator>
#include <algorithm>
#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 <boost/filesystem.hpp>
#include <boost/algorithm/string.hpp>
#ifndef WIN32
#include "sys/signal.h"
#include <pwd.h>
#include <grp.h>
#include <sys/resource.h>
#include "libgen.h"
#else
#include <windows.h>
#include <process.h>
#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<std::string>(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");
}
}
}

View file

@ -25,6 +25,7 @@
#include <sstream>
#include <fstream>
#include <algorithm>
#include <boost/lexical_cast.hpp>
#ifndef WIN32
#include <sys/param.h>
#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<std::string>(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;

View file

@ -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_ptr<Swift::Con
}
void NetworkPluginServer::handleSessionFinished(Backend *c) {
LOG4CXX_INFO(logger, "Backend " << c << " disconnected. Current backend count=" << (m_clients.size() - 1));
LOG4CXX_INFO(logger, "Backend " << c << " (ID=" << c->id << ") disconnected. Current backend count=" << (m_clients.size() - 1));
// This backend will do, so we can't reconnect users to it in User::handleDisconnected call
c->willDie = true;
// If there are users associated with this backend, it must have crashed, so print error output
// and disconnect users
if (!c->users.empty()) {
m_crashedBackends.push_back(c->id);
}
for (std::list<User *>::const_iterator it = c->users.begin(); it != c->users.end(); it++) {
LOG4CXX_ERROR(logger, "Backend " << c << " disconnected (probably crashed) with active user " << (*it)->getJID().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;
}

View file

@ -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:

View file

@ -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();

View file

@ -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<std::string>(m_userManager->getUserCount())));
}
else if (item.getName() == "backends") {
response->addItem(StatsPayload::Item("backends", "backends", boost::lexical_cast<std::string>(m_server->getBackendCount())));
else if (item.getName() == "backends/running") {
response->addItem(StatsPayload::Item("backends/running", "backends", boost::lexical_cast<std::string>(m_server->getBackendCount())));
}
else if (item.getName() == "backends/crashed") {
response->addItem(StatsPayload::Item("backends/crashed", "backends", boost::lexical_cast<std::string>(m_server->getCrashedBackends().size())));
}
else if (item.getName() == "memory-usage") {
response->addItem(StatsPayload::Item("memory-usage", "KB", boost::lexical_cast<std::string>(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<std::string>(contactsTotal)));
}
else if (item.getName() == "messages/from-xmpp") {
response->addItem(StatsPayload::Item("messages/from-xmpp", "messages", boost::lexical_cast<std::string>(m_userManager->getMessagesToBackend())));
}
else if (item.getName() == "messages/to-xmpp") {
response->addItem(StatsPayload::Item("messages/to-xmpp", "messages", boost::lexical_cast<std::string>(m_userManager->getMessagesToXMPP())));
}
}
}

49
src/storagebackend.cpp Normal file
View file

@ -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;
}
}

View file

@ -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())

View file

@ -210,6 +210,7 @@ void User::handlePresence(Swift::Presence::ref presence) {
}
}
}
bool isMUC = presence->getPayload<Swift::MUCPayload>() != 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);

View file

@ -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) {

View file

@ -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;