This commit is contained in:
Vitaly Takmazov 2013-02-10 17:30:08 +04:00
commit ea970de471
97 changed files with 2910 additions and 1160 deletions

View file

@ -29,7 +29,7 @@ option(ENABLE_TWITTER "Build Twitter plugin" ON)
option(ENABLE_YAHOO2 "Build Libyahoo2 plugin" ON)
option(ENABLE_DOCS "Build Docs" ON)
option(ENABLE_LOG "Build with logging using Log4cxx" ON)
# option(ENABLE_LOG "Build with logging using Log4cxx" ON)
option(ENABLE_TESTS "Build Tests using CppUnit" OFF)
MACRO(LIST_CONTAINS var value)
@ -109,7 +109,7 @@ endif()
# FIND SQLITE3
if (ENABLE_SQLITE3)
if (NOT CMAKE_COMPILER_IS_GNUCXX)
if (MSVC)
ADD_SUBDIRECTORY(${CMAKE_SOURCE_DIR}/msvc-deps)
else()
if (WIN32)
@ -225,7 +225,7 @@ if(ENABLE_DOCS)
find_package(Doxygen)
endif()
if(ENABLE_LOG)
# if(ENABLE_LOG)
if(LOG4CXX_INCLUDE_DIR AND LOG4CXX_LIBRARY)
set(LOG4CXX_LIBRARIES ${LOG4CXX_LIBRARY})
set(LOG4CXX_FOUND 1)
@ -234,7 +234,7 @@ if(ENABLE_LOG)
set(log4cxx_DIR "${CMAKE_SOURCE_DIR}/cmake_modules")
find_package(log4cxx)
endif()
endif()
# endif()
# FIND CPPUNIT
if(ENABLE_TESTS)
@ -248,6 +248,16 @@ if(ENABLE_TESTS)
endif()
endif()
if (APPLE)
FIND_LIBRARY(IOKIT_FRAMEWORK IOKit)
FIND_LIBRARY(SECURITY_FRAMEWORK Security)
FIND_LIBRARY(APPKIT_FRAMEWORK AppKit)
FIND_LIBRARY(SYSTEMCONFIGURATION_FRAMEWORK SystemConfiguration)
FIND_LIBRARY(SECURITYINTERFACE_FRAMEWORK SecurityInterface)
MARK_AS_ADVANCED(IOKIT_FRAMEWORK APPKIT_FRAMEWORK SYSTEMCONFIGURATION_FRAMEWORK SECURITY_FRAMEWORK SECURITYINTERFACE_FRAMEWORK)
SET (APPLE_FRAMEWORKS ${IOKIT_FRAMEWORK} ${APPKIT_FRAMEWORK} ${SYSTEMCONFIGURATION_FRAMEWORK} ${SECURITY_FRAMEWORK} ${SECURITYINTERFACE_FRAMEWORK})
endif()
message(" Supported features")
message("-----------------------")
@ -423,10 +433,10 @@ if (LOG4CXX_FOUND)
ADD_DEFINITIONS(-DWITH_LOG4CXX)
else()
set(LOG4CXX_LIBRARIES "")
if(ENABLE_LOG)
if (WIN32)
message("Log4cxx : no (install log4cxx-devel)")
else(ENABLE_LOG)
message("Log4cxx : no (user disabled)")
else()
message(FATAL_ERROR "Log4cxx : no (install log4cxx-devel)")
endif()
endif()

View file

@ -9,6 +9,7 @@ if (PROTOBUF_FOUND)
if (ENABLE_SWIFTEN)
ADD_SUBDIRECTORY(swiften)
ADD_SUBDIRECTORY(swiften_raw)
endif()
ADD_SUBDIRECTORY(template)

View file

@ -56,6 +56,7 @@ void IRCNetworkPlugin::readData() {
if (m_servers.empty()) {
NetworkPlugin::PluginConfig cfg;
cfg.setNeedRegistration(false);
cfg.setSupportMUC(true);
sendConfig(cfg);
}
}

View file

@ -33,10 +33,10 @@ int main (int argc, char* argv[]) {
return 1;
}
QCoreApplication app(argc, argv);
Logging::initBackendLogging(cfg);
QCoreApplication app(argc, argv);
Swift::QtEventLoop eventLoop;

View file

@ -86,6 +86,9 @@ bool MyIrcSession::correctNickname(std::string &nickname) {
switch(nickname.at(0)) {
case '@': nickname = nickname.substr(1); flags = 1; break;
case '+': nickname = nickname.substr(1); break;
case '~': nickname = nickname.substr(1); break;
case '&': nickname = nickname.substr(1); break;
case '%': nickname = nickname.substr(1); break;
default: break;
}
return flags;
@ -96,9 +99,9 @@ void MyIrcSession::on_joined(IrcMessage *message) {
bool op = 0;
std::string nickname = TO_UTF8(m->sender().name());
op = correctNickname(nickname);
getIRCBuddy(TO_UTF8(m->channel()), nickname).setOp(op);
np->handleParticipantChanged(user, nickname, TO_UTF8(m->channel()) + suffix, op, pbnetwork::STATUS_ONLINE);
LOG4CXX_INFO(logger, user << ": " << nickname << " joined " << TO_UTF8(m->channel()) + suffix);
getIRCBuddy(TO_UTF8(m->channel().toLower()), nickname).setOp(op);
np->handleParticipantChanged(user, nickname, TO_UTF8(m->channel().toLower()) + suffix, op, pbnetwork::STATUS_ONLINE);
LOG4CXX_INFO(logger, user << ": " << nickname << " joined " << TO_UTF8(m->channel().toLower()) + suffix);
}
@ -107,9 +110,9 @@ void MyIrcSession::on_parted(IrcMessage *message) {
bool op = 0;
std::string nickname = TO_UTF8(m->sender().name());
op = correctNickname(nickname);
removeIRCBuddy(TO_UTF8(m->channel()), nickname);
LOG4CXX_INFO(logger, user << ": " << nickname << " parted " << TO_UTF8(m->channel()) + suffix);
np->handleParticipantChanged(user, nickname, TO_UTF8(m->channel()) + suffix, op, pbnetwork::STATUS_NONE, TO_UTF8(m->reason()));
removeIRCBuddy(TO_UTF8(m->channel().toLower()), nickname);
LOG4CXX_INFO(logger, user << ": " << nickname << " parted " << TO_UTF8(m->channel().toLower()) + suffix);
np->handleParticipantChanged(user, nickname, TO_UTF8(m->channel().toLower()) + suffix, op, pbnetwork::STATUS_NONE, TO_UTF8(m->reason()));
}
void MyIrcSession::on_quit(IrcMessage *message) {
@ -172,7 +175,7 @@ void MyIrcSession::on_topicChanged(IrcMessage *message) {
correctNickname(nickname);
LOG4CXX_INFO(logger, user << ": " << nickname << " topic changed to " << TO_UTF8(m->topic()));
np->handleSubject(user, TO_UTF8(m->channel()) + suffix, TO_UTF8(m->topic()), nickname);
np->handleSubject(user, TO_UTF8(m->channel().toLower()) + suffix, TO_UTF8(m->topic()), nickname);
}
void MyIrcSession::on_messageReceived(IrcMessage *message) {
@ -190,7 +193,7 @@ void MyIrcSession::on_messageReceived(IrcMessage *message) {
msg = QString("/me ") + msg;
}
std::string target = TO_UTF8(m->target());
std::string target = TO_UTF8(m->target().toLower());
LOG4CXX_INFO(logger, user << ": Message from " << target);
if (target.find("#") == 0) {
std::string nickname = TO_UTF8(m->sender().name());
@ -229,10 +232,10 @@ void MyIrcSession::on_numericMessageReceived(IrcMessage *message) {
if (nick.find("/") != std::string::npos) {
nick = nick.substr(0, nick.find("/"));
}
np->handleSubject(user, TO_UTF8(parameters[1]) + suffix, m_topicData, nick);
np->handleSubject(user, TO_UTF8(parameters[1].toLower()) + suffix, m_topicData, nick);
break;
case 352: {
channel = parameters[1];
channel = parameters[1].toLower();
nick = TO_UTF8(parameters[5]);
IRCBuddy &buddy = getIRCBuddy(TO_UTF8(channel), nick);
@ -249,7 +252,7 @@ void MyIrcSession::on_numericMessageReceived(IrcMessage *message) {
break;
}
case 353:
channel = parameters[2];
channel = parameters[2].toLower();
members = parameters[3].split(" ");
LOG4CXX_INFO(logger, user << ": Received members for " << TO_UTF8(channel) << suffix);
@ -265,13 +268,33 @@ void MyIrcSession::on_numericMessageReceived(IrcMessage *message) {
break;
case 366:
// ask /who to get away states
channel = parameters[1];
channel = parameters[1].toLower();
LOG4CXX_INFO(logger, user << "Asking /who for channel " << TO_UTF8(channel));
sendCommand(IrcCommand::createWho(channel));
break;
case 432:
np->handleDisconnected(user, pbnetwork::CONNECTION_ERROR_INVALID_USERNAME, "Erroneous Nickname");
break;
case 433:
for(AutoJoinMap::iterator it = m_autoJoin.begin(); it != m_autoJoin.end(); it++) {
np->handleParticipantChanged(user, TO_UTF8(nickName()), it->second->getChannel() + suffix, pbnetwork::PARTICIPANT_FLAG_CONFLICT);
}
if (suffix.empty()) {
np->handleDisconnected(user, pbnetwork::CONNECTION_ERROR_INVALID_USERNAME, "Nickname is already in use");
}
break;
case 436:
for(AutoJoinMap::iterator it = m_autoJoin.begin(); it != m_autoJoin.end(); it++) {
np->handleParticipantChanged(user, TO_UTF8(nickName()), it->second->getChannel() + suffix, pbnetwork::PARTICIPANT_FLAG_CONFLICT);
}
np->handleDisconnected(user, pbnetwork::CONNECTION_ERROR_INVALID_USERNAME, "Nickname collision KILL");
case 464:
for(AutoJoinMap::iterator it = m_autoJoin.begin(); it != m_autoJoin.end(); it++) {
np->handleParticipantChanged(user, TO_UTF8(nickName()), it->second->getChannel() + suffix, pbnetwork::PARTICIPANT_FLAG_NOT_AUTHORIZED);
}
if (suffix.empty()) {
np->handleDisconnected(user, pbnetwork::CONNECTION_ERROR_INVALID_USERNAME, "Password incorrect");
}
case 321:
m_rooms.clear();
m_names.clear();
@ -287,6 +310,10 @@ void MyIrcSession::on_numericMessageReceived(IrcMessage *message) {
break;
}
if (m->code() >= 400 && m->code() < 500) {
LOG4CXX_INFO(logger, user << ": Error message received: " << message->toData().data());
}
//qDebug() << "numeric message received:" << receiver() << origin << code << params;
}

View file

@ -43,6 +43,7 @@ DEFINE_LOGGER(logger, "backend");
int main_socket;
static int writeInput;
bool firstPing = true;
using namespace Transport;
@ -90,6 +91,14 @@ struct FTData {
bool paused;
};
struct NodeCache {
PurpleAccount *account;
std::map<PurpleBlistNode *, int> nodes;
int timer;
};
bool caching = true;
static void *notify_user_info(PurpleConnection *gc, const char *who, PurpleNotifyUserInfo *user_info);
static gboolean ft_ui_ready(void *data) {
@ -356,6 +365,12 @@ class SpectrumNetworkPlugin : public NetworkPlugin {
void handleLogoutRequest(const std::string &user, const std::string &legacyName) {
PurpleAccount *account = m_sessions[user];
if (account) {
if (account->ui_data) {
NodeCache *cache = (NodeCache *) account->ui_data;
purple_timeout_remove_wrapped(cache->timer);
delete cache;
account->ui_data = NULL;
}
if (purple_account_get_int_wrapped(account, "version", 0) != 0) {
std::string data = stringOf(purple_account_get_int_wrapped(account, "version", 0));
g_file_set_contents ("gfire.cfg", data.c_str(), data.size(), NULL);
@ -549,6 +564,7 @@ class SpectrumNetworkPlugin : public NetworkPlugin {
}
purple_blist_add_buddy_wrapped(buddy, NULL, group ,NULL);
purple_account_add_buddy_wrapped(account, buddy);
LOG4CXX_INFO(logger, "Adding new buddy " << buddyName.c_str() << " to legacy network roster");
}
}
}
@ -804,12 +820,56 @@ static std::vector<std::string> getGroups(PurpleBuddy *m_buddy) {
return groups;
}
static void buddyListNewNode(PurpleBlistNode *node) {
void buddyListNewNode(PurpleBlistNode *node);
static gboolean new_node_cache(void *data) {
NodeCache *cache = (NodeCache *) data;
caching = false;
for (std::map<PurpleBlistNode *, int>::const_iterator it = cache->nodes.begin(); it != cache->nodes.end(); it++) {
buddyListNewNode(it->first);
}
caching = true;
cache->account->ui_data = NULL;
delete cache;
return FALSE;
}
static void buddyNodeRemoved(PurpleBuddyList *list, PurpleBlistNode *node) {
if (!PURPLE_BLIST_NODE_IS_BUDDY_WRAPPED(node))
return;
PurpleBuddy *buddy = (PurpleBuddy *) node;
PurpleAccount *account = purple_buddy_get_account_wrapped(buddy);
if (!account->ui_data) {
return;
}
NodeCache *cache = (NodeCache *) account->ui_data;
cache->nodes.erase(node);
}
void buddyListNewNode(PurpleBlistNode *node) {
if (!PURPLE_BLIST_NODE_IS_BUDDY_WRAPPED(node))
return;
PurpleBuddy *buddy = (PurpleBuddy *) node;
PurpleAccount *account = purple_buddy_get_account_wrapped(buddy);
if (caching) {
if (!account->ui_data) {
NodeCache *cache = new NodeCache;
cache->account = account;
cache->timer = purple_timeout_add_wrapped(400, new_node_cache, cache);
account->ui_data = (void *) cache;
}
NodeCache *cache = (NodeCache *) account->ui_data;
cache->nodes[node] = 1;
return;
}
std::vector<std::string> groups = getGroups(buddy);
LOG4CXX_INFO(logger, "Buddy updated " << np->m_accounts[account] << " " << purple_buddy_get_name_wrapped(buddy) << " " << getAlias(buddy) << " group (" << groups.size() << ")=" << groups[0]);
@ -879,21 +939,35 @@ static void NodeRemoved(PurpleBlistNode *node, void *data) {
// PurpleBuddy *buddy = (PurpleBuddy *) node;
}
static void buddyListSaveNode(PurpleBlistNode *node) {
if (!PURPLE_BLIST_NODE_IS_BUDDY(node))
return;
}
static void buddyListSaveAccount(PurpleAccount *account) {
}
static void buddyListRemoveNode(PurpleBlistNode *node) {
if (!PURPLE_BLIST_NODE_IS_BUDDY(node))
return;
}
static PurpleBlistUiOps blistUiOps =
{
NULL,
buddyListNewNode,
NULL,
buddyListUpdate,
NULL, //NodeRemoved,
buddyNodeRemoved,
NULL,
NULL,
NULL, // buddyListAddBuddy,
NULL,
NULL,
NULL, //buddyListSaveNode,
NULL, //buddyListRemoveNode,
NULL, //buddyListSaveAccount,
buddyListSaveNode,
buddyListRemoveNode,
buddyListSaveAccount,
NULL
};
@ -908,10 +982,6 @@ static void conv_write_im(PurpleConversation *conv, const char *who, const char
// std::string msg = striped;
// g_free(striped);
std::string w = purple_normalize_wrapped(account, who);
size_t pos = w.find("/");
if (pos != std::string::npos)
w.erase((int) pos, w.length() - (int) pos);
// Escape HTML characters.
char *newline = purple_strdup_withhtml_wrapped(msg);
@ -948,11 +1018,15 @@ static void conv_write_im(PurpleConversation *conv, const char *who, const char
// LOG4CXX_INFO(logger, "Received message body='" << message_ << "' xhtml='" << xhtml_ << "'");
if (purple_conversation_get_type_wrapped(conv) == PURPLE_CONV_TYPE_IM) {
std::string w = purple_normalize_wrapped(account, who);
size_t pos = w.find("/");
if (pos != std::string::npos)
w.erase((int) pos, w.length() - (int) pos);
np->handleMessage(np->m_accounts[account], w, message_, "", xhtml_, timestamp);
}
else {
LOG4CXX_INFO(logger, "Received message body='" << message_ << "' name='" << purple_conversation_get_name_wrapped(conv) << "' " << w);
np->handleMessage(np->m_accounts[account], purple_conversation_get_name_wrapped(conv), message_, w, xhtml_, timestamp);
LOG4CXX_INFO(logger, "Received message body='" << message_ << "' name='" << purple_conversation_get_name_wrapped(conv) << "' " << who);
np->handleMessage(np->m_accounts[account], purple_conversation_get_name_wrapped(conv), message_, who, xhtml_, timestamp);
}
}
@ -1099,7 +1173,7 @@ static void *notify_user_info(PurpleConnection *gc, const char *who, PurpleNotif
else if (label=="Nickname" || label == "Nick") {
nickname = purple_notify_user_info_entry_get_value_wrapped(vcardEntry);
}
else if (label=="Full Name") {
else if (label=="Full Name" || label == "Display name") {
fullName = purple_notify_user_info_entry_get_value_wrapped(vcardEntry);
}
else {
@ -1655,6 +1729,14 @@ static void transportDataReceived(gpointer data, gint source, PurpleInputConditi
exit(errno);
}
std::string d = std::string(buffer, n);
if (firstPing) {
firstPing = false;
NetworkPlugin::PluginConfig cfg;
cfg.setSupportMUC(true);
np->sendConfig(cfg);
}
np->handleDataRead(d);
}
else {

View file

@ -1,4 +1,6 @@
#include "glib.h"
#include <dbus-1.0/dbus/dbus-glib-lowlevel.h>
#include "sqlite3.h"
#include <iostream>
#include "transport/config.h"
@ -20,758 +22,15 @@
#ifndef __FreeBSD__
#include "malloc.h"
#endif
#include <dbus-1.0/dbus/dbus-glib-lowlevel.h>
#include "skype.h"
#include "skypeplugin.h"
DEFINE_LOGGER(logger, "backend");
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); \
try {\
VAR = GET_RESPONSE_DATA(VAR, PROP);\
}\
catch (std::out_of_range& oor) {\
VAR="";\
}
SpectrumNetworkPlugin *np;
int m_sock;
static int writeInput;
static std::string host;
static int port = 10000;
DBusHandlerResult skype_notify_handler(DBusConnection *connection, DBusMessage *message, gpointer user_data);
static pbnetwork::StatusType getStatus(const std::string &st) {
pbnetwork::StatusType status = pbnetwork::STATUS_ONLINE;
if (st == "SKYPEOUT" || st == "OFFLINE") {
status = pbnetwork::STATUS_NONE;
}
else if (st == "DND") {
status = pbnetwork::STATUS_DND;
}
else if (st == "NA") {
status = pbnetwork::STATUS_XA;
}
else if (st == "AWAY") {
status = pbnetwork::STATUS_AWAY;
}
return status;
}
class Skype {
public:
Skype(const std::string &user, const std::string &username, const std::string &password);
~Skype() { LOG4CXX_INFO(logger, "Skype instance desctuctor"); logout(); }
void login();
void logout();
std::string send_command(const std::string &message);
const std::string &getUser() {
return m_user;
}
const std::string &getUsername() {
return m_username;
}
bool createDBusProxy();
bool loadSkypeBuddies();
int getPid() {
return (int) m_pid;
}
private:
std::string m_username;
std::string m_password;
GPid m_pid;
DBusGConnection *m_connection;
DBusGProxy *m_proxy;
std::string m_user;
int m_timer;
int m_counter;
int fd_output;
};
class SpectrumNetworkPlugin : public NetworkPlugin {
public:
SpectrumNetworkPlugin(Config *config, const std::string &host, int port) : NetworkPlugin() {
this->config = config;
LOG4CXX_INFO(logger, "Starting the backend.");
}
~SpectrumNetworkPlugin() {
for (std::map<Skype *, std::string>::iterator it = m_accounts.begin(); it != m_accounts.end(); it++) {
delete (*it).first;
}
}
void handleLoginRequest(const std::string &user, const std::string &legacyName, const std::string &password) {
std::string name = legacyName;
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);
m_sessions[user] = skype;
m_accounts[skype] = user;
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();
Logging::shutdownLogging();
exit(1);
}
}
void handleStatusChangeRequest(const std::string &user, int status, const std::string &statusMessage) {
Skype *skype = m_sessions[user];
if (!skype)
return;
std::string st;
switch(status) {
case Swift::StatusShow::Away: {
st = "AWAY";
break;
}
case Swift::StatusShow::DND: {
st = "DND";
break;
}
case Swift::StatusShow::XA: {
st = "NA";
break;
}
case Swift::StatusShow::None: {
break;
}
case pbnetwork::STATUS_INVISIBLE:
st = "INVISIBLE";
break;
default:
st = "ONLINE";
break;
}
skype->send_command("SET USERSTATUS " + st);
if (!statusMessage.empty()) {
skype->send_command("SET PROFILE MOOD_TEXT " + statusMessage);
}
}
void 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 = "", const std::string &id = "") {
Skype *skype = m_sessions[user];
if (skype) {
skype->send_command("MESSAGE " + legacyName + " " + message);
}
}
void handleVCardRequest(const std::string &user, const std::string &legacyName, unsigned int id) {
Skype *skype = m_sessions[user];
if (skype) {
std::string name = legacyName;
if (name.find("skype.") == 0) {
name = name.substr(6);
}
std::string photo;
gchar *filename = NULL;
gchar *new_filename = NULL;
gchar *image_data = NULL;
gsize image_data_len = 0;
gchar *ret;
int fh;
GError *error;
const gchar *userfiles[] = {"user256", "user1024", "user4096", "user16384", "user32768", "user65536",
"profile256", "profile1024", "profile4096", "profile16384", "profile32768",
NULL};
char *username = g_strdup_printf("\x03\x10%s", name.c_str());
for (fh = 0; userfiles[fh]; fh++) {
filename = g_strconcat("/tmp/skype/", skype->getUsername().c_str(), "/", skype->getUsername().c_str(), "/", userfiles[fh], ".dbb", NULL);
std::cout << "getting filename:" << filename << "\n";
if (g_file_get_contents(filename, &image_data, &image_data_len, NULL))
{
std::cout << "got\n";
char *start = (char *)memmem(image_data, image_data_len, username, strlen(username)+1);
if (start != NULL)
{
char *next = image_data;
char *last = next;
//find last index of l33l
while ((next = (char *)memmem(next+4, start-next-4, "l33l", 4)))
{
last = next;
}
start = last;
if (start != NULL)
{
char *img_start;
//find end of l33l block
char *end = (char *)memmem(start+4, image_data+image_data_len-start-4, "l33l", 4);
if (!end) end = image_data+image_data_len;
//look for start of JPEG block
img_start = (char *)memmem(start, end-start, "\xFF\xD8", 2);
if (img_start)
{
//look for end of JPEG block
char *img_end = (char *)memmem(img_start, end-img_start, "\xFF\xD9", 2);
if (img_end)
{
image_data_len = img_end - img_start + 2;
photo = std::string(img_start, image_data_len);
}
}
}
}
g_free(image_data);
}
g_free(filename);
}
g_free(username);
std::string alias = "";
std::cout << skype->getUsername() << " " << name << "\n";
if (skype->getUsername() == name) {
alias = skype->send_command("GET PROFILE FULLNAME");
alias = GET_RESPONSE_DATA(alias, "FULLNAME")
}
handleVCard(user, id, legacyName, "", alias, photo);
}
}
void sendData(const std::string &string) {
write(m_sock, string.c_str(), string.size());
// if (writeInput == 0)
// writeInput = purple_input_add(m_sock, PURPLE_INPUT_WRITE, &transportDataReceived, NULL);
}
void handleVCardUpdatedRequest(const std::string &user, const std::string &p, const std::string &nickname) {
}
void handleBuddyBlockToggled(const std::string &user, const std::string &buddyName, bool blocked) {
}
void handleTypingRequest(const std::string &user, const std::string &buddyName) {
}
void handleTypedRequest(const std::string &user, const std::string &buddyName) {
}
void handleStoppedTypingRequest(const std::string &user, const std::string &buddyName) {
}
void handleAttentionRequest(const std::string &user, const std::string &buddyName, const std::string &message) {
}
std::map<std::string, Skype *> m_sessions;
std::map<Skype *, std::string> m_accounts;
std::map<std::string, unsigned int> m_vcards;
Config *config;
};
Skype::Skype(const std::string &user, const std::string &username, const std::string &password) {
m_username = username;
m_user = user;
m_password = password;
m_pid = 0;
m_connection = 0;
m_proxy = 0;
m_timer = -1;
m_counter = 0;
}
static gboolean load_skype_buddies(gpointer data) {
Skype *skype = (Skype *) data;
return skype->loadSkypeBuddies();
}
bool Skype::createDBusProxy() {
if (m_proxy == NULL) {
LOG4CXX_INFO(logger, "Creating DBus proxy for com.Skype.Api.");
m_counter++;
GError *error = NULL;
m_proxy = dbus_g_proxy_new_for_name_owner (m_connection, "com.Skype.API", "/com/Skype", "com.Skype.API", &error);
if (m_proxy == NULL && error != NULL) {
LOG4CXX_INFO(logger, m_username << ":" << error->message);
if (m_counter == 15) {
LOG4CXX_ERROR(logger, "Logging out, proxy couldn't be created: " << error->message);
np->handleDisconnected(m_user, pbnetwork::CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE, error->message);
logout();
g_error_free(error);
return FALSE;
}
g_error_free(error);
}
if (m_proxy) {
LOG4CXX_INFO(logger, "Proxy created.");
DBusObjectPathVTable vtable;
vtable.message_function = &skype_notify_handler;
dbus_connection_register_object_path(dbus_g_connection_get_connection(m_connection), "/com/Skype/Client", &vtable, this);
m_counter = 0;
m_timer = g_timeout_add_seconds(1, load_skype_buddies, this);
return FALSE;
}
return TRUE;
}
return FALSE;
}
static gboolean create_dbus_proxy(gpointer data) {
Skype *skype = (Skype *) data;
return skype->createDBusProxy();
}
void Skype::login() {
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);
boost::filesystem::path path2(std::string("/tmp/skype/") + m_username + "/" + m_username );
boost::filesystem::create_directories(path2);
}
std::string shared_xml = "<?xml version=\"1.0\"?>\n"
"<config version=\"1.0\" serial=\"28\" timestamp=\"" + boost::lexical_cast<std::string>(time(NULL)) + ".0\">\n"
"<UI>\n"
"<Installed>2</Installed>\n"
"<Language>en</Language>\n"
"</UI>\n"
"</config>\n";
g_file_set_contents(std::string(std::string("/tmp/skype/") + m_username + "/shared.xml").c_str(), shared_xml.c_str(), -1, NULL);
std::string config_xml = "<?xml version=\"1.0\"?>\n"
"<config version=\"1.0\" serial=\"7\" timestamp=\"" + boost::lexical_cast<std::string>(time(NULL)) + ".0\">\n"
"<Lib>\n"
"<Account>\n"
"<IdleTimeForAway>30000000</IdleTimeForAway>\n"
"<IdleTimeForNA>300000000</IdleTimeForNA>\n"
"<LastUsed>" + boost::lexical_cast<std::string>(time(NULL)) + "</LastUsed>\n"
"</Account>\n"
"</Lib>\n"
"<UI>\n"
"<API>\n"
"<Authorizations>Spectrum</Authorizations>\n"
"<BlockedPrograms></BlockedPrograms>\n"
"</API>\n"
"</UI>\n"
"</config>\n";
g_file_set_contents(std::string(std::string("/tmp/skype/") + m_username + "/" + m_username +"/config.xml").c_str(), config_xml.c_str(), -1, NULL);
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());
LOG4CXX_INFO(logger, m_username << ": Spawning new Skype instance dbpath=" << db);
gchar* argv[6] = {"skype", "--disable-cleanlooks", "--pipelogin", "--dbpath", db, 0};
int fd;
GError *error = NULL;
bool spawned = g_spawn_async_with_pipes(NULL,
argv,
NULL /*envp*/,
G_SPAWN_SEARCH_PATH,
NULL /*child_setup*/,
NULL /*user_data*/,
&m_pid /*child_pid*/,
&fd,
NULL,
&fd_output,
&error);
if (!spawned) {
LOG4CXX_ERROR(logger, "Error spawning the Skype instance: " << error->message)
np->handleDisconnected(m_user, pbnetwork::CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE, "Error spawning the Skype instance.");
return;
}
std::string login_data = std::string(m_username + " " + m_password + "\n");
LOG4CXX_INFO(logger, m_username << ": Login data=" << m_username);
write(fd, login_data.c_str(), login_data.size());
close(fd);
fcntl (fd_output, F_SETFL, O_NONBLOCK);
free(db);
//Initialise threading
dbus_threads_init_default();
if (m_connection == NULL)
{
LOG4CXX_INFO(logger, "Creating DBUS connection.");
GError *error = NULL;
m_connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
if (m_connection == NULL && error != NULL)
{
LOG4CXX_INFO(logger, m_username << ": Creating DBUS Connection error: " << error->message);
g_error_free(error);
return;
}
}
sleep(1);
m_timer = g_timeout_add_seconds(1, create_dbus_proxy, this);
}
bool Skype::loadSkypeBuddies() {
// std::string re = "CONNSTATUS OFFLINE";
// while (re == "CONNSTATUS OFFLINE" || re.empty()) {
// sleep(1);
gchar buffer[1024];
int bytes_read = read(fd_output, buffer, 1023);
if (bytes_read > 0) {
buffer[bytes_read] = 0;
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) {
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" || re == "ERROR 68") {
return TRUE;
}
close(fd_output);
if (send_command("PROTOCOL 7") != "PROTOCOL 7") {
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;
}
np->handleConnected(m_user);
std::map<std::string, std::string> group_map;
std::string groups = send_command("SEARCH GROUPS CUSTOM");
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");
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");
try {
users = GET_RESPONSE_DATA(users, "USERS");
}
catch (std::out_of_range& oor) {
continue;
}
boost::split(data, users, boost::is_any_of(","));
BOOST_FOREACH(std::string u, data) {
group_map[u] = grp;
}
}
}
std::string friends = send_command("GET AUTH_CONTACTS_PROFILES");
char **full_friends_list = g_strsplit((strchr(friends.c_str(), ' ')+1), ";", 0);
if (full_friends_list && full_friends_list[0])
{
//in the format of: username;full name;phone;office phone;mobile phone;
// online status;friendly name;voicemail;mood
// (comma-seperated lines, usernames can have comma's)
for (int i=0; full_friends_list[i] && full_friends_list[i+1] && *full_friends_list[i] != '\0'; i+=8)
{
std::string buddy = full_friends_list[i];
if (buddy[0] == ',') {
buddy.erase(buddy.begin());
}
if (buddy.rfind(",") != std::string::npos) {
buddy = buddy.substr(buddy.rfind(","));
}
if (buddy[0] == ',') {
buddy.erase(buddy.begin());
}
LOG4CXX_INFO(logger, "Got buddy " << buddy);
std::string st = full_friends_list[i + 5];
pbnetwork::StatusType status = getStatus(st);
std::string alias = full_friends_list[i + 6];
std::string mood_text = "";
if (full_friends_list[i + 8] && *full_friends_list[i + 8] != '\0' && *full_friends_list[i + 8] != ',') {
mood_text = full_friends_list[i + 8];
}
std::vector<std::string> groups;
if (group_map.find(buddy) != group_map.end()) {
groups.push_back(group_map[buddy]);
}
np->handleBuddyChanged(m_user, buddy, alias, groups, status, mood_text);
}
}
g_strfreev(full_friends_list);
send_command("SET AUTOAWAY OFF");
send_command("SET USERSTATUS ONLINE");
return FALSE;
}
void Skype::logout() {
if (m_pid != 0) {
send_command("SET USERSTATUS INVISIBLE");
send_command("SET USERSTATUS OFFLINE");
sleep(2);
g_object_unref(m_proxy);
LOG4CXX_INFO(logger, m_username << ": Terminating Skype instance (SIGTERM)");
kill((int) m_pid, SIGTERM);
// Give skype a chance
sleep(2);
LOG4CXX_INFO(logger, m_username << ": Killing Skype instance (SIGKILL)");
kill((int) m_pid, SIGKILL);
m_pid = 0;
}
}
std::string Skype::send_command(const std::string &message) {
GError *error = NULL;
gchar *str = NULL;
// int message_num;
// gchar error_return[30];
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))
{
if (error && error->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 << "'");
}
return str ? std::string(str) : std::string();
}
static void handle_skype_message(std::string &message, Skype *sk) {
std::vector<std::string> cmd;
boost::split(cmd, message, boost::is_any_of(" "));
if (cmd[0] == "USER") {
if (cmd[1] == sk->getUsername()) {
return;
}
if (cmd[2] == "ONLINESTATUS") {
if (cmd[3] == "SKYPEOUT" || cmd[3] == "UNKNOWN") {
return;
}
else {
pbnetwork::StatusType status = getStatus(cmd[3]);
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") {
GET_PROPERTY(st, "USER", cmd[1], "ONLINESTATUS");
pbnetwork::StatusType status = getStatus(st);
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") {
GET_PROPERTY(mood_text, "USER", cmd[1], "MOOD_TEXT");
GET_PROPERTY(st, "USER", cmd[1], "ONLINESTATUS");
pbnetwork::StatusType status = getStatus(st);
std::vector<std::string> groups;
np->handleBuddyChanged(sk->getUser(), cmd[1], "", groups, status, mood_text);
}
else if (cmd[2] == "FULLNAME") {
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::vector<std::string> groups;
np->handleBuddyChanged(sk->getUser(), cmd[1], alias, groups, status, mood_text);
}
//TODO: handle RECEIVEDAUTHREQUEST and reply it with:
// void
// skype_auth_allow(gpointer sender)
// {
// skype_send_message("SET USER %s ISAUTHORIZED TRUE", sender);
// g_free(sender);
// }
//
// void
// skype_auth_deny(gpointer sender)
// {
// skype_send_message("SET USER %s ISAUTHORIZED FALSE", sender);
// g_free(sender);
// }
}
else if (cmd[0] == "CHATMESSAGE") {
if (cmd[3] == "RECEIVED") {
GET_PROPERTY(body, "CHATMESSAGE", cmd[1], "BODY");
GET_PROPERTY(from_handle, "CHATMESSAGE", cmd[1], "FROM_HANDLE");
if (from_handle == sk->getUsername())
return;
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 + ".");
}
}
}
}
}
DBusHandlerResult skype_notify_handler(DBusConnection *connection, DBusMessage *message, gpointer user_data) {
DBusMessageIter iterator;
gchar *message_temp;
DBusMessage *temp_message;
temp_message = dbus_message_ref(message);
dbus_message_iter_init(temp_message, &iterator);
if (dbus_message_iter_get_arg_type(&iterator) != DBUS_TYPE_STRING)
{
dbus_message_unref(message);
return (DBusHandlerResult) FALSE;
}
do {
dbus_message_iter_get_basic(&iterator, &message_temp);
std::string m(message_temp);
LOG4CXX_INFO(logger,"DBUS message: " << m);
handle_skype_message(m, (Skype *) user_data);
} while(dbus_message_iter_has_next(&iterator) && dbus_message_iter_next(&iterator));
dbus_message_unref(message);
return DBUS_HANDLER_RESULT_HANDLED;
}
static void spectrum_sigchld_handler(int sig)
{
int status;
@ -788,68 +47,24 @@ static void spectrum_sigchld_handler(int sig)
}
}
static int create_socket(const 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
Logging::shutdownLogging();
exit(1);
}
serv_addr.sin_addr.s_addr = *((unsigned long *) hos->h_addr_list[0]);
if (connect(m_sock, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
close(m_sock);
m_sock = 0;
}
int flags = fcntl(m_sock, F_GETFL);
flags |= O_NONBLOCK;
fcntl(m_sock, F_SETFL, flags);
return m_sock;
}
static gboolean transportDataReceived(GIOChannel *source, GIOCondition condition, gpointer data) {
char buffer[65535];
char *ptr = buffer;
ssize_t n = read(m_sock, ptr, sizeof(buffer));
if (n <= 0) {
LOG4CXX_INFO(logger, "Diconnecting from spectrum2 server");
Logging::shutdownLogging();
exit(errno);
}
std::string d = std::string(buffer, n);
np->handleDataRead(d);
return TRUE;
}
static void io_destroy(gpointer data) {
Logging::shutdownLogging();
exit(1);
}
static void log_glib_error(const gchar *string) {
LOG4CXX_ERROR(logger, "GLIB ERROR:" << string);
}
int main(int argc, char **argv) {
#ifndef WIN32
signal(SIGPIPE, SIG_IGN);
signal(SIGPIPE, SIG_IGN);
if (signal(SIGCHLD, spectrum_sigchld_handler) == SIG_ERR) {
std::cout << "SIGCHLD handler can't be set\n";
return -1;
}
if (signal(SIGCHLD, spectrum_sigchld_handler) == SIG_ERR) {
std::cout << "SIGCHLD handler can't be set\n";
return -1;
}
#endif
std::string host;
int port = 10000;
std::string error;
Config *cfg = Config::createFromArgs(argc, argv, error, host, port);
if (cfg == NULL) {
@ -861,16 +76,11 @@ int main(int argc, char **argv) {
g_type_init();
m_sock = create_socket(host.c_str(), port);
g_set_printerr_handler(log_glib_error);
GIOChannel *channel;
GIOCondition cond = (GIOCondition) G_IO_IN;
channel = g_io_channel_unix_new(m_sock);
g_io_add_watch_full(channel, G_PRIORITY_DEFAULT, cond, transportDataReceived, NULL, io_destroy);
dbus_threads_init_default();
np = new SpectrumNetworkPlugin(cfg, host, port);
SkypePlugin *np = new SkypePlugin(cfg, host, port);
GMainLoop *m_loop;
m_loop = g_main_loop_new(NULL, FALSE);
@ -879,4 +89,5 @@ int main(int argc, char **argv) {
g_main_loop_run(m_loop);
}
return 0;
}

570
backends/skype/skype.cpp Normal file
View file

@ -0,0 +1,570 @@
/**
* 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 "skype.h"
#include "skypeplugin.h"
#include "transport/config.h"
#include "transport/logging.h"
#include "transport/transport.h"
#include "transport/usermanager.h"
#include "transport/memoryusage.h"
#include "transport/sqlite3backend.h"
#include "transport/userregistration.h"
#include "transport/user.h"
#include "transport/storagebackend.h"
#include "transport/rostermanager.h"
#include "transport/conversation.h"
#include "transport/networkplugin.h"
#include <boost/filesystem.hpp>
#include "sys/wait.h"
#include "sys/signal.h"
// #include "valgrind/memcheck.h"
#ifndef __FreeBSD__
#include "malloc.h"
#endif
DEFINE_LOGGER(logger, "Skype");
Skype::Skype(SkypePlugin *np, const std::string &user, const std::string &username, const std::string &password) {
m_username = username;
m_user = user;
m_password = password;
m_pid = 0;
m_connection = 0;
m_proxy = 0;
m_timer = -1;
m_counter = 0;
m_np = np;
}
static gboolean load_skype_buddies(gpointer data) {
Skype *skype = (Skype *) data;
return skype->loadSkypeBuddies();
}
static gboolean create_dbus_proxy(gpointer data) {
Skype *skype = (Skype *) data;
return skype->createDBusProxy();
}
static pbnetwork::StatusType getStatus(const std::string &st) {
pbnetwork::StatusType status = pbnetwork::STATUS_ONLINE;
if (st == "SKYPEOUT" || st == "OFFLINE") {
status = pbnetwork::STATUS_NONE;
}
else if (st == "DND") {
status = pbnetwork::STATUS_DND;
}
else if (st == "NA") {
status = pbnetwork::STATUS_XA;
}
else if (st == "AWAY") {
status = pbnetwork::STATUS_AWAY;
}
return status;
}
DBusHandlerResult skype_notify_handler(DBusConnection *connection, DBusMessage *message, gpointer data) {
Skype *skype = (Skype *) data;
return skype->dbusMessageReceived(connection, message);
}
void Skype::login() {
// Do not allow usernames with unsecure symbols
if (m_username.find("..") == 0 || m_username.find("/") != std::string::npos) {
m_np->handleDisconnected(m_user, pbnetwork::CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE, "Invalid username");
return;
}
std::string db_path = createSkypeDirectory();
bool spawned = spawnSkype(db_path);
if (!spawned) {
m_np->handleDisconnected(m_user, pbnetwork::CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE, "Error spawning the Skype instance.");
return;
}
if (m_connection == NULL) {
LOG4CXX_INFO(logger, "Creating DBUS connection.");
GError *error = NULL;
m_connection = dbus_g_bus_get(DBUS_BUS_SESSION, &error);
if (m_connection == NULL && error != NULL)
{
LOG4CXX_INFO(logger, m_username << ": Creating DBUS Connection error: " << error->message);
g_error_free(error);
return;
}
}
m_timer = g_timeout_add_seconds(1, create_dbus_proxy, this);
}
bool Skype::createDBusProxy() {
if (m_proxy == NULL) {
LOG4CXX_INFO(logger, "Creating DBus proxy for com.Skype.Api.");
m_counter++;
GError *error = NULL;
m_proxy = dbus_g_proxy_new_for_name_owner(m_connection, "com.Skype.API", "/com/Skype", "com.Skype.API", &error);
if (m_proxy == NULL && error != NULL) {
LOG4CXX_INFO(logger, m_username << ":" << error->message);
if (m_counter == 15) {
LOG4CXX_ERROR(logger, "Logging out, proxy couldn't be created: " << error->message);
m_np->handleDisconnected(m_user, pbnetwork::CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE, error->message);
logout();
g_error_free(error);
return FALSE;
}
g_error_free(error);
}
if (m_proxy) {
LOG4CXX_INFO(logger, "Proxy created.");
DBusObjectPathVTable vtable;
vtable.message_function = &skype_notify_handler;
dbus_connection_register_object_path(dbus_g_connection_get_connection(m_connection), "/com/Skype/Client", &vtable, this);
m_counter = 0;
m_timer = g_timeout_add_seconds(1, load_skype_buddies, this);
return FALSE;
}
return TRUE;
}
return FALSE;
}
bool Skype::loadSkypeBuddies() {
// std::string re = "CONNSTATUS OFFLINE";
// while (re == "CONNSTATUS OFFLINE" || re.empty()) {
// sleep(1);
gchar buffer[1024];
int bytes_read = read(fd_output, buffer, 1023);
if (bytes_read > 0) {
buffer[bytes_read] = 0;
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")
m_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) {
LOG4CXX_ERROR(logger, "Logging out, because we tried to connect the Skype over DBUS 15 times without success");
m_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" || re == "ERROR 68") {
return TRUE;
}
close(fd_output);
if (send_command("PROTOCOL 7") != "PROTOCOL 7") {
LOG4CXX_ERROR(logger, "PROTOCOL 7 failed, logging out");
m_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;
}
m_np->handleConnected(m_user);
std::map<std::string, std::string> group_map;
std::string groups = send_command("SEARCH GROUPS CUSTOM");
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");
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");
try {
users = GET_RESPONSE_DATA(users, "USERS");
}
catch (std::out_of_range& oor) {
continue;
}
boost::split(data, users, boost::is_any_of(","));
BOOST_FOREACH(std::string u, data) {
group_map[u] = grp;
}
}
}
std::string friends = send_command("GET AUTH_CONTACTS_PROFILES");
char **full_friends_list = g_strsplit((strchr(friends.c_str(), ' ')+1), ";", 0);
if (full_friends_list && full_friends_list[0])
{
//in the format of: username;full name;phone;office phone;mobile phone;
// online status;friendly name;voicemail;mood
// (comma-seperated lines, usernames can have comma's)
for (int i=0; full_friends_list[i] && full_friends_list[i+1] && *full_friends_list[i] != '\0'; i+=8)
{
std::string buddy = full_friends_list[i];
if (buddy[0] == ',') {
buddy.erase(buddy.begin());
}
if (buddy.rfind(",") != std::string::npos) {
buddy = buddy.substr(buddy.rfind(","));
}
if (buddy[0] == ',') {
buddy.erase(buddy.begin());
}
LOG4CXX_INFO(logger, "Got buddy " << buddy);
std::string st = full_friends_list[i + 5];
pbnetwork::StatusType status = getStatus(st);
std::string alias = full_friends_list[i + 6];
std::string mood_text = "";
if (full_friends_list[i + 8] && *full_friends_list[i + 8] != '\0' && *full_friends_list[i + 8] != ',') {
mood_text = full_friends_list[i + 8];
}
std::vector<std::string> groups;
if (group_map.find(buddy) != group_map.end()) {
groups.push_back(group_map[buddy]);
}
m_np->handleBuddyChanged(m_user, buddy, alias, groups, status, mood_text);
}
}
g_strfreev(full_friends_list);
send_command("SET AUTOAWAY OFF");
send_command("SET USERSTATUS ONLINE");
return FALSE;
}
void Skype::logout() {
if (m_pid != 0) {
if (m_proxy) {
send_command("SET USERSTATUS INVISIBLE");
send_command("SET USERSTATUS OFFLINE");
sleep(2);
g_object_unref(m_proxy);
}
LOG4CXX_INFO(logger, m_username << ": Terminating Skype instance (SIGTERM)");
kill((int) m_pid, SIGTERM);
// Give skype a chance
sleep(2);
LOG4CXX_INFO(logger, m_username << ": Killing Skype instance (SIGKILL)");
kill((int) m_pid, SIGKILL);
m_pid = 0;
}
}
std::string Skype::send_command(const std::string &message) {
GError *error = NULL;
gchar *str = NULL;
// int message_num;
// gchar error_return[30];
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))
{
if (error && error->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 << "'");
}
return str ? std::string(str) : std::string();
}
void Skype::handleSkypeMessage(std::string &message) {
std::vector<std::string> cmd;
boost::split(cmd, message, boost::is_any_of(" "));
if (cmd[0] == "USER") {
if (cmd[1] == getUsername()) {
return;
}
if (cmd[2] == "ONLINESTATUS") {
if (cmd[3] == "SKYPEOUT" || cmd[3] == "UNKNOWN") {
return;
}
else {
pbnetwork::StatusType status = getStatus(cmd[3]);
GET_PROPERTY(mood_text, "USER", cmd[1], "MOOD_TEXT");
GET_PROPERTY(alias, "USER", cmd[1], "FULLNAME");
std::vector<std::string> groups;
m_np->handleBuddyChanged(getUser(), cmd[1], alias, groups, status, mood_text);
}
}
else if (cmd[2] == "MOOD_TEXT") {
GET_PROPERTY(st, "USER", cmd[1], "ONLINESTATUS");
pbnetwork::StatusType status = getStatus(st);
std::string mood_text = GET_RESPONSE_DATA(message, "MOOD_TEXT");
std::vector<std::string> groups;
m_np->handleBuddyChanged(getUser(), cmd[1], "", groups, status, mood_text);
}
else if (cmd[2] == "BUDDYSTATUS" && cmd[3] == "3") {
GET_PROPERTY(mood_text, "USER", cmd[1], "MOOD_TEXT");
GET_PROPERTY(st, "USER", cmd[1], "ONLINESTATUS");
pbnetwork::StatusType status = getStatus(st);
std::vector<std::string> groups;
m_np->handleBuddyChanged(getUser(), cmd[1], "", groups, status, mood_text);
}
else if (cmd[2] == "FULLNAME") {
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::vector<std::string> groups;
m_np->handleBuddyChanged(getUser(), cmd[1], alias, groups, status, mood_text);
}
else if(cmd[2] == "RECEIVEDAUTHREQUEST") {
m_np->handleAuthorization(getUser(), cmd[1]);
}
}
else if (cmd[0] == "GROUP") {
// if (cmd[2] == "DISPLAYNAME") {
// //GROUP 810 DISPLAYNAME My Friends
// std::string grp = GET_RESPONSE_DATA(message, "DISPLAYNAME");
// std::string users = send_command("GET GROUP " + cmd[1] + " USERS");
// try {
// users = GET_RESPONSE_DATA(users, "USERS");
// }
// catch (std::out_of_range& oor) {
// return;
// }
//
// std::vector<std::string> data;
// boost::split(data, users, boost::is_any_of(","));
// BOOST_FOREACH(std::string u, data) {
// GET_PROPERTY(alias, "USER", u, "FULLNAME");
// GET_PROPERTY(mood_text, "USER", u, "MOOD_TEXT");
// GET_PROPERTY(st, "USER", u, "ONLINESTATUS");
// pbnetwork::StatusType status = getStatus(st);
//
// std::vector<std::string> groups;
// groups.push_back(grp);
// m_np->handleBuddyChanged(getUser(), u, alias, groups, status, mood_text);
// }
// }
if (cmd[2] == "NROFUSERS" && cmd[3] != "0") {
GET_PROPERTY(grp, "GROUP", cmd[1], "DISPLAYNAME");
std::string users = send_command("GET GROUP " + cmd[1] + " USERS");
try {
users = GET_RESPONSE_DATA(users, "USERS");
}
catch (std::out_of_range& oor) {
return;
}
std::vector<std::string> data;
boost::split(data, users, boost::is_any_of(","));
BOOST_FOREACH(std::string u, data) {
GET_PROPERTY(alias, "USER", u, "FULLNAME");
GET_PROPERTY(mood_text, "USER", u, "MOOD_TEXT");
GET_PROPERTY(st, "USER", u, "ONLINESTATUS");
pbnetwork::StatusType status = getStatus(st);
std::vector<std::string> groups;
groups.push_back(grp);
m_np->handleBuddyChanged(getUser(), u, alias, groups, status, mood_text);
}
}
}
else if (cmd[0] == "CHATMESSAGE") {
if (cmd[3] == "RECEIVED") {
GET_PROPERTY(body, "CHATMESSAGE", cmd[1], "BODY");
GET_PROPERTY(from_handle, "CHATMESSAGE", cmd[1], "FROM_HANDLE");
if (from_handle == getUsername())
return;
m_np->handleMessage(getUser(), from_handle, body);
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") {
m_np->handleMessage(getUser(), from, "User " + dispname + " is calling you.");
}
else {
m_np->handleMessage(getUser(), from, "You have missed call from user " + dispname + ".");
}
}
}
}
}
DBusHandlerResult Skype::dbusMessageReceived(DBusConnection *connection, DBusMessage *message) {
DBusMessageIter iterator;
gchar *message_temp;
DBusMessage *temp_message;
temp_message = dbus_message_ref(message);
dbus_message_iter_init(temp_message, &iterator);
if (dbus_message_iter_get_arg_type(&iterator) != DBUS_TYPE_STRING)
{
dbus_message_unref(message);
return (DBusHandlerResult) FALSE;
}
do {
dbus_message_iter_get_basic(&iterator, &message_temp);
std::string m(message_temp);
LOG4CXX_INFO(logger,"DBUS message: " << m);
handleSkypeMessage(m);
} while(dbus_message_iter_has_next(&iterator) && dbus_message_iter_next(&iterator));
dbus_message_unref(message);
return DBUS_HANDLER_RESULT_HANDLED;
}
std::string Skype::createSkypeDirectory() {
std::string tmpdir = std::string("/tmp/skype/") + m_username;
// This should not be needed anymore...
// boost::filesystem::remove_all(std::string("/tmp/skype/") + m_username);
boost::filesystem::path path(tmpdir);
if (!boost::filesystem::exists(path)) {
boost::filesystem::create_directories(path);
boost::filesystem::path path2(tmpdir + "/" + m_username );
boost::filesystem::create_directories(path2);
}
std::string shared_xml = "<?xml version=\"1.0\"?>\n"
"<config version=\"1.0\" serial=\"28\" timestamp=\"" + boost::lexical_cast<std::string>(time(NULL)) + ".0\">\n"
"<UI>\n"
"<Installed>2</Installed>\n"
"<Language>en</Language>\n"
"</UI>\n"
"</config>\n";
g_file_set_contents(std::string(tmpdir + "/shared.xml").c_str(), shared_xml.c_str(), -1, NULL);
std::string config_xml = "<?xml version=\"1.0\"?>\n"
"<config version=\"1.0\" serial=\"7\" timestamp=\"" + boost::lexical_cast<std::string>(time(NULL)) + ".0\">\n"
"<Lib>\n"
"<Account>\n"
"<IdleTimeForAway>30000000</IdleTimeForAway>\n"
"<IdleTimeForNA>300000000</IdleTimeForNA>\n"
"<LastUsed>" + boost::lexical_cast<std::string>(time(NULL)) + "</LastUsed>\n"
"</Account>\n"
"</Lib>\n"
"<UI>\n"
"<API>\n"
"<Authorizations>Spectrum</Authorizations>\n"
"<BlockedPrograms></BlockedPrograms>\n"
"</API>\n"
"</UI>\n"
"</config>\n";
g_file_set_contents(std::string(tmpdir + "/" + m_username +"/config.xml").c_str(), config_xml.c_str(), -1, NULL);
return tmpdir;
}
bool Skype::spawnSkype(const std::string &db_path) {
char *db = (char *) malloc(db_path.size() + 1);
strcpy(db, db_path.c_str());
LOG4CXX_INFO(logger, m_username << ": Spawning new Skype instance dbpath=" << db);
gchar* argv[6] = {"skype", "--disable-cleanlooks", "--pipelogin", "--dbpath", db, 0};
int fd;
GError *error = NULL;
bool spawned = g_spawn_async_with_pipes(NULL,
argv,
NULL /*envp*/,
G_SPAWN_SEARCH_PATH,
NULL /*child_setup*/,
NULL /*user_data*/,
&m_pid /*child_pid*/,
&fd,
NULL,
&fd_output,
&error);
if (!spawned) {
LOG4CXX_ERROR(logger, "Error spawning the Skype instance: " << error->message)
return false;
}
std::string login_data = std::string(m_username + " " + m_password + "\n");
LOG4CXX_INFO(logger, m_username << ": Login data=" << m_username);
write(fd, login_data.c_str(), login_data.size());
close(fd);
fcntl (fd_output, F_SETFL, O_NONBLOCK);
free(db);
return true;
}

91
backends/skype/skype.h Normal file
View file

@ -0,0 +1,91 @@
/**
* 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 "glib.h"
#include <dbus-1.0/dbus/dbus-glib-lowlevel.h>
#include "sqlite3.h"
#include <iostream>
#include <map>
#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 = send_command(std::string("GET ") + OBJ + " " + WHICH + " " + PROP); \
try {\
VAR = GET_RESPONSE_DATA(VAR, PROP);\
}\
catch (std::out_of_range& oor) {\
VAR="";\
}
class SkypePlugin;
class Skype {
public:
Skype(SkypePlugin *np, const std::string &user, const std::string &username, const std::string &password);
virtual ~Skype() {
logout();
}
void login();
void logout();
std::string send_command(const std::string &message);
const std::string &getUser() {
return m_user;
}
const std::string &getUsername() {
return m_username;
}
int getPid() {
return (int) m_pid;
}
public: // but do not use them, should be used only internally
bool createDBusProxy();
bool loadSkypeBuddies();
void handleSkypeMessage(std::string &message);
DBusHandlerResult dbusMessageReceived(DBusConnection *connection, DBusMessage *message);
private:
std::string createSkypeDirectory();
bool spawnSkype(const std::string &db_path);
std::string m_username;
std::string m_password;
GPid m_pid;
DBusGConnection *m_connection;
DBusGProxy *m_proxy;
std::string m_user;
int m_timer;
int m_counter;
int fd_output;
std::map<std::string, std::string> m_groups;
SkypePlugin *m_np;
};

115
backends/skype/skypedb.cpp Normal file
View file

@ -0,0 +1,115 @@
/**
* 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 "skypedb.h"
#include "transport/config.h"
#include "transport/logging.h"
#include "transport/transport.h"
#include "transport/usermanager.h"
#include "transport/memoryusage.h"
#include "transport/sqlite3backend.h"
#include "transport/userregistration.h"
#include "transport/user.h"
#include "transport/storagebackend.h"
#include "transport/rostermanager.h"
#include "transport/conversation.h"
#include "transport/networkplugin.h"
#include <boost/filesystem.hpp>
#include "sys/wait.h"
#include "sys/signal.h"
// #include "valgrind/memcheck.h"
#ifndef __FreeBSD__
#include "malloc.h"
#endif
// Prepare the SQL statement
#define PREP_STMT(sql, str) \
if(sqlite3_prepare_v2(db, std::string(str).c_str(), -1, &sql, NULL)) { \
LOG4CXX_ERROR(logger, str<< (sqlite3_errmsg(db) == NULL ? "" : sqlite3_errmsg(db))); \
sql = NULL; \
}
// Finalize the prepared statement
#define FINALIZE_STMT(prep) \
if(prep != NULL) { \
sqlite3_finalize(prep); \
}
#define BEGIN(STATEMENT) sqlite3_reset(STATEMENT);\
int STATEMENT##_id = 1;\
int STATEMENT##_id_get = 0;\
(void)STATEMENT##_id_get;
#define BIND_INT(STATEMENT, VARIABLE) sqlite3_bind_int(STATEMENT, STATEMENT##_id++, VARIABLE)
#define BIND_STR(STATEMENT, VARIABLE) sqlite3_bind_text(STATEMENT, STATEMENT##_id++, VARIABLE.c_str(), -1, SQLITE_STATIC)
#define RESET_GET_COUNTER(STATEMENT) STATEMENT##_id_get = 0;
#define GET_INT(STATEMENT) sqlite3_column_int(STATEMENT, STATEMENT##_id_get++)
#define GET_STR(STATEMENT) (const char *) sqlite3_column_text(STATEMENT, STATEMENT##_id_get++)
#define GET_BLOB(STATEMENT) (const void *) sqlite3_column_blob(STATEMENT, STATEMENT##_id_get++)
#define EXECUTE_STATEMENT(STATEMENT, NAME) if(sqlite3_step(STATEMENT) != SQLITE_DONE) {\
LOG4CXX_ERROR(logger, NAME<< (sqlite3_errmsg(db) == NULL ? "" : sqlite3_errmsg(db)));\
}
using namespace Transport;
DEFINE_LOGGER(logger, "SkypeDB");
namespace SkypeDB {
bool getAvatar(const std::string &db_path, const std::string &name, std::string &photo) {
bool ret = false;
sqlite3 *db;
LOG4CXX_INFO(logger, "Opening database " << db_path);
if (sqlite3_open(db_path.c_str(), &db)) {
sqlite3_close(db);
LOG4CXX_ERROR(logger, "Can't open database");
}
else {
sqlite3_stmt *stmt;
PREP_STMT(stmt, "SELECT avatar_image FROM Contacts WHERE skypename=?");
if (stmt) {
BEGIN(stmt);
BIND_STR(stmt, name);
if(sqlite3_step(stmt) == SQLITE_ROW) {
int size = sqlite3_column_bytes(stmt, 0);
const void *data = sqlite3_column_blob(stmt, 0);
photo = std::string((const char *)data + 1, size - 1);
ret = true;
}
else {
LOG4CXX_ERROR(logger, (sqlite3_errmsg(db) == NULL ? "" : sqlite3_errmsg(db)));
}
int ret;
while((ret = sqlite3_step(stmt)) == SQLITE_ROW) {
}
FINALIZE_STMT(stmt);
}
else {
LOG4CXX_ERROR(logger, "Can't created prepared statement");
LOG4CXX_ERROR(logger, (sqlite3_errmsg(db) == NULL ? "" : sqlite3_errmsg(db)));
}
sqlite3_close(db);
}
return ret;
}
}

33
backends/skype/skypedb.h Normal file
View file

@ -0,0 +1,33 @@
/**
* 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 "glib.h"
#include <dbus-1.0/dbus/dbus-glib-lowlevel.h>
#include "sqlite3.h"
#include <iostream>
#include <map>
namespace SkypeDB {
bool getAvatar(const std::string &db, const std::string &name, std::string &avatar);
}

View file

@ -0,0 +1,349 @@
/**
* 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 "skypeplugin.h"
#include "skype.h"
#include "skypedb.h"
#include "transport/config.h"
#include "transport/logging.h"
#include "transport/transport.h"
#include "transport/usermanager.h"
#include "transport/memoryusage.h"
#include "transport/sqlite3backend.h"
#include "transport/userregistration.h"
#include "transport/user.h"
#include "transport/storagebackend.h"
#include "transport/rostermanager.h"
#include "transport/conversation.h"
#include "transport/networkplugin.h"
#include <boost/filesystem.hpp>
#include "sys/wait.h"
#include "sys/signal.h"
// #include "valgrind/memcheck.h"
#ifndef __FreeBSD__
#include "malloc.h"
#endif
// Prepare the SQL statement
#define PREP_STMT(sql, str) \
if(sqlite3_prepare_v2(db, std::string(str).c_str(), -1, &sql, NULL)) { \
LOG4CXX_ERROR(logger, str<< (sqlite3_errmsg(db) == NULL ? "" : sqlite3_errmsg(db))); \
sql = NULL; \
}
// Finalize the prepared statement
#define FINALIZE_STMT(prep) \
if(prep != NULL) { \
sqlite3_finalize(prep); \
}
#define BEGIN(STATEMENT) sqlite3_reset(STATEMENT);\
int STATEMENT##_id = 1;\
int STATEMENT##_id_get = 0;\
(void)STATEMENT##_id_get;
#define BIND_INT(STATEMENT, VARIABLE) sqlite3_bind_int(STATEMENT, STATEMENT##_id++, VARIABLE)
#define BIND_STR(STATEMENT, VARIABLE) sqlite3_bind_text(STATEMENT, STATEMENT##_id++, VARIABLE.c_str(), -1, SQLITE_STATIC)
#define RESET_GET_COUNTER(STATEMENT) STATEMENT##_id_get = 0;
#define GET_INT(STATEMENT) sqlite3_column_int(STATEMENT, STATEMENT##_id_get++)
#define GET_STR(STATEMENT) (const char *) sqlite3_column_text(STATEMENT, STATEMENT##_id_get++)
#define GET_BLOB(STATEMENT) (const void *) sqlite3_column_blob(STATEMENT, STATEMENT##_id_get++)
#define EXECUTE_STATEMENT(STATEMENT, NAME) if(sqlite3_step(STATEMENT) != SQLITE_DONE) {\
LOG4CXX_ERROR(logger, NAME<< (sqlite3_errmsg(db) == NULL ? "" : sqlite3_errmsg(db)));\
}
using namespace Transport;
DEFINE_LOGGER(logger, "SkypePlugin");
static int m_sock;
static gboolean transportDataReceived(GIOChannel *source, GIOCondition condition, gpointer data) {
SkypePlugin *np = (SkypePlugin *) data;
char buffer[65535];
char *ptr = buffer;
ssize_t n = read(m_sock, ptr, sizeof(buffer));
if (n <= 0) {
LOG4CXX_INFO(logger, "Diconnecting from spectrum2 server");
Logging::shutdownLogging();
exit(errno);
}
std::string d = std::string(buffer, n);
np->handleDataRead(d);
return TRUE;
}
static int create_socket(const 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
Logging::shutdownLogging();
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 io_destroy(gpointer data) {
Logging::shutdownLogging();
exit(1);
}
SkypePlugin::SkypePlugin(Config *config, const std::string &host, int port) : NetworkPlugin() {
this->config = config;
LOG4CXX_INFO(logger, "Starting the backend.");
m_sock = create_socket(host.c_str(), port);
GIOChannel *channel;
GIOCondition cond = (GIOCondition) G_IO_IN;
channel = g_io_channel_unix_new(m_sock);
g_io_add_watch_full(channel, G_PRIORITY_DEFAULT, cond, transportDataReceived, this, io_destroy);
}
SkypePlugin::~SkypePlugin() {
for (std::map<Skype *, std::string>::iterator it = m_accounts.begin(); it != m_accounts.end(); it++) {
delete (*it).first;
}
}
void SkypePlugin::handleLoginRequest(const std::string &user, const std::string &legacyName, const std::string &password) {
std::string name = legacyName;
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(this, user, name, password);
m_sessions[user] = skype;
m_accounts[skype] = user;
skype->login();
}
void SkypePlugin::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 SkypePlugin::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();
Logging::shutdownLogging();
exit(1);
}
}
void SkypePlugin::handleStatusChangeRequest(const std::string &user, int status, const std::string &statusMessage) {
Skype *skype = m_sessions[user];
if (!skype)
return;
std::string st;
switch(status) {
case Swift::StatusShow::Away: {
st = "AWAY";
break;
}
case Swift::StatusShow::DND: {
st = "DND";
break;
}
case Swift::StatusShow::XA: {
st = "NA";
break;
}
case Swift::StatusShow::None: {
break;
}
case pbnetwork::STATUS_INVISIBLE:
st = "INVISIBLE";
break;
default:
st = "ONLINE";
break;
}
skype->send_command("SET USERSTATUS " + st);
if (!statusMessage.empty()) {
skype->send_command("SET PROFILE MOOD_TEXT " + statusMessage);
}
}
void SkypePlugin::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 SkypePlugin::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");
skype->send_command("SET USER " + buddyName + " ISAUTHORIZED FALSE");
}
}
void SkypePlugin::handleMessageSendRequest(const std::string &user, const std::string &legacyName, const std::string &message, const std::string &xhtml, const std::string &id) {
Skype *skype = m_sessions[user];
if (skype) {
skype->send_command("MESSAGE " + legacyName + " " + message);
}
}
void SkypePlugin::handleVCardRequest(const std::string &user, const std::string &legacyName, unsigned int id) {
Skype *skype = m_sessions[user];
if (skype) {
std::string name = legacyName;
if (name.find("skype.") == 0) {
name = name.substr(6);
}
std::string photo;
gchar *filename = NULL;
gchar *new_filename = NULL;
gchar *image_data = NULL;
gsize image_data_len = 0;
gchar *ret;
int fh;
GError *error;
const gchar *userfiles[] = {"user256", "user1024", "user4096", "user16384", "user32768", "user65536",
"profile256", "profile1024", "profile4096", "profile16384", "profile32768",
NULL};
char *username = g_strdup_printf("\x03\x10%s", name.c_str());
for (fh = 0; userfiles[fh]; fh++) {
filename = g_strconcat("/tmp/skype/", skype->getUsername().c_str(), "/", skype->getUsername().c_str(), "/", userfiles[fh], ".dbb", NULL);
std::cout << "getting filename:" << filename << "\n";
if (g_file_get_contents(filename, &image_data, &image_data_len, NULL))
{
std::cout << "got\n";
char *start = (char *)memmem(image_data, image_data_len, username, strlen(username)+1);
if (start != NULL)
{
char *next = image_data;
char *last = next;
//find last index of l33l
while ((next = (char *)memmem(next+4, start-next-4, "l33l", 4)))
{
last = next;
}
start = last;
if (start != NULL)
{
char *img_start;
//find end of l33l block
char *end = (char *)memmem(start+4, image_data+image_data_len-start-4, "l33l", 4);
if (!end) end = image_data+image_data_len;
//look for start of JPEG block
img_start = (char *)memmem(start, end-start, "\xFF\xD8", 2);
if (img_start)
{
//look for end of JPEG block
char *img_end = (char *)memmem(img_start, end-img_start, "\xFF\xD9", 2);
if (img_end)
{
image_data_len = img_end - img_start + 2;
photo = std::string(img_start, image_data_len);
}
}
}
}
g_free(image_data);
}
g_free(filename);
}
g_free(username);
if (photo.empty()) {
std::string db_path = std::string("/tmp/skype/") + skype->getUsername() + "/" + skype->getUsername() + "/main.db";
SkypeDB::getAvatar(db_path, name, photo);
}
std::string alias;
std::cout << skype->getUsername() << " " << name << "\n";
if (skype->getUsername() == name) {
alias = skype->send_command("GET PROFILE FULLNAME");
alias = GET_RESPONSE_DATA(alias, "FULLNAME")
}
handleVCard(user, id, legacyName, "", alias, photo);
}
}
void SkypePlugin::sendData(const std::string &string) {
write(m_sock, string.c_str(), string.size());
// if (writeInput == 0)
// writeInput = purple_input_add(m_sock, PURPLE_INPUT_WRITE, &transportDataReceived, NULL);
}
void SkypePlugin::handleVCardUpdatedRequest(const std::string &user, const std::string &p, const std::string &nickname) {
}
void SkypePlugin::handleBuddyBlockToggled(const std::string &user, const std::string &buddyName, bool blocked) {
}
void SkypePlugin::handleTypingRequest(const std::string &user, const std::string &buddyName) {
}
void SkypePlugin::handleTypedRequest(const std::string &user, const std::string &buddyName) {
}
void SkypePlugin::handleStoppedTypingRequest(const std::string &user, const std::string &buddyName) {
}
void SkypePlugin::handleAttentionRequest(const std::string &user, const std::string &buddyName, const std::string &message) {
}

View file

@ -0,0 +1,74 @@
/**
* 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 "glib.h"
#include <dbus-1.0/dbus/dbus-glib-lowlevel.h>
#include "sqlite3.h"
#include <iostream>
#include <map>
#include "transport/networkplugin.h"
#include "transport/config.h"
class Skype;
class SkypePlugin : public Transport::NetworkPlugin {
public:
SkypePlugin(Transport::Config *config, const std::string &host, int port);
virtual ~SkypePlugin();
void handleLoginRequest(const std::string &user, const std::string &legacyName, const std::string &password);
void handleMemoryUsage(double &res, double &shared);
void handleLogoutRequest(const std::string &user, const std::string &legacyName);
void handleStatusChangeRequest(const std::string &user, int status, const std::string &statusMessage);
void handleBuddyUpdatedRequest(const std::string &user, const std::string &buddyName, const std::string &alias, const std::vector<std::string> &groups);
void handleBuddyRemovedRequest(const std::string &user, const std::string &buddyName, const std::vector<std::string> &groups);
void handleMessageSendRequest(const std::string &user, const std::string &legacyName, const std::string &message, const std::string &xhtml = "", const std::string &id = "");
void handleVCardRequest(const std::string &user, const std::string &legacyName, unsigned int id);
void sendData(const std::string &string);
void handleVCardUpdatedRequest(const std::string &user, const std::string &p, const std::string &nickname);
void handleBuddyBlockToggled(const std::string &user, const std::string &buddyName, bool blocked);
void handleTypingRequest(const std::string &user, const std::string &buddyName);
void handleTypedRequest(const std::string &user, const std::string &buddyName);
void handleStoppedTypingRequest(const std::string &user, const std::string &buddyName);
void handleAttentionRequest(const std::string &user, const std::string &buddyName, const std::string &message);
std::map<std::string, Skype *> m_sessions;
std::map<Skype *, std::string> m_accounts;
std::map<std::string, unsigned int> m_vcards;
Transport::Config *config;
};

View file

@ -3,6 +3,8 @@
#include "transport/networkplugin.h"
#include "transport/logging.h"
#include "boost/date_time/posix_time/posix_time.hpp"
// Swiften
#include "Swiften/Swiften.h"
@ -15,9 +17,11 @@
#endif
#ifndef __FreeBSD__
#ifndef __MACH__
// malloc_trim
#include "malloc.h"
#endif
#endif
// Boost
#include <boost/algorithm/string.hpp>
@ -32,7 +36,83 @@ Swift::SimpleEventLoop *loop_;
// Plugins
class SwiftenPlugin;
SwiftenPlugin *np = NULL;
NetworkPlugin *np = NULL;
class MUCController {
public:
MUCController(const std::string &user, boost::shared_ptr<Swift::Client> client, const std::string &room, const std::string &nickname, const std::string &password) {
m_user = user;
m_room = room;
muc = client->getMUCManager()->createMUC(room);
if (!password.empty()) {
muc->setPassword(password);
}
muc->onJoinComplete.connect(boost::bind(&MUCController::handleJoinComplete, this, _1));
muc->onJoinFailed.connect(boost::bind(&MUCController::handleJoinFailed, this, _1));
muc->onOccupantJoined.connect(boost::bind(&MUCController::handleOccupantJoined, this, _1));
muc->onOccupantPresenceChange.connect(boost::bind(&MUCController::handleOccupantPresenceChange, this, _1));
muc->onOccupantLeft.connect(boost::bind(&MUCController::handleOccupantLeft, this, _1, _2, _3));
muc->onOccupantRoleChanged.connect(boost::bind(&MUCController::handleOccupantRoleChanged, this, _1, _2, _3));
muc->onOccupantAffiliationChanged.connect(boost::bind(&MUCController::handleOccupantAffiliationChanged, this, _1, _2, _3));
muc->joinAs(nickname);
}
virtual ~MUCController() {
muc->onJoinComplete.disconnect(boost::bind(&MUCController::handleJoinComplete, this, _1));
muc->onJoinFailed.disconnect(boost::bind(&MUCController::handleJoinFailed, this, _1));
muc->onOccupantJoined.disconnect(boost::bind(&MUCController::handleOccupantJoined, this, _1));
muc->onOccupantPresenceChange.disconnect(boost::bind(&MUCController::handleOccupantPresenceChange, this, _1));
muc->onOccupantLeft.disconnect(boost::bind(&MUCController::handleOccupantLeft, this, _1, _2, _3));
muc->onOccupantRoleChanged.disconnect(boost::bind(&MUCController::handleOccupantRoleChanged, this, _1, _2, _3));
muc->onOccupantAffiliationChanged.disconnect(boost::bind(&MUCController::handleOccupantAffiliationChanged, this, _1, _2, _3));
}
const std::string &getNickname() {
//return muc->getCurrentNick();
return m_nick;
}
void handleOccupantJoined(const Swift::MUCOccupant& occupant) {
np->handleParticipantChanged(m_user, occupant.getNick(), m_room, occupant.getRole() == Swift::MUCOccupant::Moderator, pbnetwork::STATUS_ONLINE);
}
void handleOccupantLeft(const Swift::MUCOccupant& occupant, Swift::MUC::LeavingType type, const std::string& reason) {
np->handleParticipantChanged(m_user, occupant.getNick(), m_room, occupant.getRole() == Swift::MUCOccupant::Moderator, pbnetwork::STATUS_NONE);
}
void handleOccupantPresenceChange(boost::shared_ptr<Swift::Presence> presence) {
const Swift::MUCOccupant& occupant = muc->getOccupant(presence->getFrom().getResource());
np->handleParticipantChanged(m_user, presence->getFrom().getResource(), m_room, (int) occupant.getRole() == Swift::MUCOccupant::Moderator, (pbnetwork::StatusType) presence->getShow(), presence->getStatus());
}
void handleOccupantRoleChanged(const std::string& nick, const Swift::MUCOccupant& occupant, const Swift::MUCOccupant::Role& oldRole) {
}
void handleOccupantAffiliationChanged(const std::string& nick, const Swift::MUCOccupant::Affiliation& affiliation, const Swift::MUCOccupant::Affiliation& oldAffiliation) {
// np->handleParticipantChanged(m_user, occupant->getNick(), m_room, (int) occupant.getRole() == Swift::MUCOccupant::Moderator, pbnetwork::STATUS_ONLINE);
}
void handleJoinComplete(const std::string& nick) {
m_nick = nick;
}
void handleJoinFailed(boost::shared_ptr<Swift::ErrorPayload> error) {
}
void part() {
muc->part();
}
private:
Swift::MUC::ref muc;
std::string m_user;
std::string m_room;
std::string m_nick;
};
class SwiftenPlugin : public NetworkPlugin {
public:
@ -106,13 +186,16 @@ class SwiftenPlugin : public NetworkPlugin {
client->onDisconnected.disconnect(boost::bind(&SwiftenPlugin::handleSwiftDisconnected, this, user, _1));
client->onMessageReceived.disconnect(boost::bind(&SwiftenPlugin::handleSwiftMessageReceived, this, user, _1));
m_users.erase(user);
m_mucs.erase(user);
}
#ifndef WIN32
#ifndef __FreeBSD__
#ifndef __MACH__
// force returning of memory chunks allocated by libxml2 to kernel
malloc_trim(0);
#endif
#endif
#endif
}
@ -136,6 +219,15 @@ class SwiftenPlugin : public NetworkPlugin {
}
void handleSwiftPresenceChanged(const std::string &user, Swift::Presence::ref presence) {
boost::shared_ptr<Swift::Client> client = m_users[user];
if (client->getMUCRegistry()->isMUC(presence->getFrom().toBare())) {
return;
}
if (presence->getPayload<Swift::MUCUserPayload>() != NULL || presence->getPayload<Swift::MUCPayload>() != NULL) {
return;
}
LOG4CXX_INFO(logger, user << ": " << presence->getFrom().toBare().toString() << " presence changed");
std::string message = presence->getStatus();
@ -160,7 +252,22 @@ class SwiftenPlugin : public NetworkPlugin {
std::string body = message->getBody();
boost::shared_ptr<Swift::Client> client = m_users[user];
if (client) {
handleMessage(user, message->getFrom().toBare().toString(), body, "", "");
if (message->getType() == Swift::Message::Groupchat) {
boost::shared_ptr<Swift::Delay> delay = message->getPayload<Swift::Delay>();
std::string timestamp = "";
if (delay) {
timestamp = boost::posix_time::to_iso_string(delay->getStamp());
}
handleMessage(user, message->getFrom().toBare().toString(), body, message->getFrom().getResource(), "", timestamp);
}
else {
if (client->getMUCRegistry()->isMUC(message->getFrom().toBare())) {
handleMessage(user, message->getFrom().toBare().toString(), body, message->getFrom().getResource(), "", "", false, true);
}
else {
handleMessage(user, message->getFrom().toBare().toString(), body, "", "");
}
}
}
}
@ -199,6 +306,7 @@ class SwiftenPlugin : public NetworkPlugin {
client->getRoster()->onInitialRosterPopulated.disconnect(boost::bind(&SwiftenPlugin::handleSwiftRosterReceived, this, user));
client->getPresenceOracle()->onPresenceChange.disconnect(boost::bind(&SwiftenPlugin::handleSwiftPresenceChanged, this, user, _1));
client->disconnect();
m_mucs.erase(user);
}
}
@ -210,6 +318,11 @@ class SwiftenPlugin : public NetworkPlugin {
message->setTo(Swift::JID(legacyName));
message->setFrom(client->getJID());
message->setBody(msg);
if (client->getMUCRegistry()->isMUC(legacyName)) {
message->setType(Swift::Message::Groupchat);
boost::shared_ptr<MUCController> muc = m_mucs[user][legacyName];
// handleMessage(user, legacyName, msg, muc->getNickname(), xhtml);
}
client->sendMessage(message);
}
@ -226,17 +339,81 @@ class SwiftenPlugin : public NetworkPlugin {
}
void handleBuddyUpdatedRequest(const std::string &user, const std::string &buddyName, const std::string &alias, const std::vector<std::string> &groups) {
LOG4CXX_INFO(logger, user << ": Added/Updated buddy " << buddyName << ".");
// handleBuddyChanged(user, buddyName, alias, groups, pbnetwork::STATUS_ONLINE);
boost::shared_ptr<Swift::Client> client = m_users[user];
if (client) {
LOG4CXX_INFO(logger, user << ": Added/Updated buddy " << buddyName << ".");
if (!client->getRoster()->containsJID(buddyName)) {
Swift::RosterItemPayload item;
item.setName(alias);
item.setJID(buddyName);
item.setGroups(groups);
boost::shared_ptr<Swift::RosterPayload> roster(new Swift::RosterPayload());
roster->addItem(item);
Swift::SetRosterRequest::ref request = Swift::SetRosterRequest::create(roster, client->getIQRouter());
// request->onResponse.connect(boost::bind(&RosterController::handleRosterSetError, this, _1, roster));
request->send();
client->getSubscriptionManager()->requestSubscription(buddyName);
}
else {
Swift::JID contact(buddyName);
Swift::RosterItemPayload item(contact, alias, client->getRoster()->getSubscriptionStateForJID(contact));
item.setGroups(groups);
boost::shared_ptr<Swift::RosterPayload> roster(new Swift::RosterPayload());
roster->addItem(item);
Swift::SetRosterRequest::ref request = Swift::SetRosterRequest::create(roster, client->getIQRouter());
// request->onResponse.connect(boost::bind(&RosterController::handleRosterSetError, this, _1, roster));
request->send();
}
}
}
void handleBuddyRemovedRequest(const std::string &user, const std::string &buddyName, const std::vector<std::string> &groups) {
boost::shared_ptr<Swift::Client> client = m_users[user];
if (client) {
Swift::RosterItemPayload item(buddyName, "", Swift::RosterItemPayload::Remove);
boost::shared_ptr<Swift::RosterPayload> roster(new Swift::RosterPayload());
roster->addItem(item);
Swift::SetRosterRequest::ref request = Swift::SetRosterRequest::create(roster, client->getIQRouter());
// request->onResponse.connect(boost::bind(&RosterController::handleRosterSetError, this, _1, roster));
request->send();
}
}
void handleJoinRoomRequest(const std::string &user, const std::string &room, const std::string &nickname, const std::string &password) {
boost::shared_ptr<Swift::Client> client = m_users[user];
if (client) {
if (client->getMUCRegistry()->isMUC(room)) {
return;
}
boost::shared_ptr<MUCController> muc = boost::shared_ptr<MUCController>( new MUCController(user, client, room, nickname, password));
m_mucs[user][room] = muc;
}
}
void handleLeaveRoomRequest(const std::string &user, const std::string &room) {
boost::shared_ptr<Swift::Client> client = m_users[user];
if (client) {
if (!client->getMUCRegistry()->isMUC(room)) {
return;
}
boost::shared_ptr<MUCController> muc = m_mucs[user][room];
if (!muc) {
m_mucs[user].erase(room);
return;
}
muc->part();
m_mucs[user].erase(room);
}
}
private:
Config *config;
std::map<std::string, boost::shared_ptr<Swift::Client> > m_users;
std::map<std::string, std::map<std::string, boost::shared_ptr<MUCController> > > m_mucs;
};
#ifndef WIN32

View file

@ -0,0 +1,14 @@
cmake_minimum_required(VERSION 2.6)
FILE(GLOB SRC *.cpp)
ADD_EXECUTABLE(spectrum2_swiften_raw_backend ${SRC})
IF (NOT WIN32)
target_link_libraries(spectrum2_swiften_raw_backend transport pthread ${Boost_LIBRARIES} ${SWIFTEN_LIBRARY} ${LOG4CXX_LIBRARIES})
else()
target_link_libraries(spectrum2_swiften_raw_backend transport ${Boost_LIBRARIES} ${SWIFTEN_LIBRARY} ${LOG4CXX_LIBRARIES})
endif()
INSTALL(TARGETS spectrum2_swiften_raw_backend RUNTIME DESTINATION bin)

View file

@ -0,0 +1,427 @@
// Transport includes
#include "transport/config.h"
#include "transport/networkplugin.h"
#include "transport/logging.h"
#include "boost/date_time/posix_time/posix_time.hpp"
// Swiften
#include "Swiften/Swiften.h"
#ifndef WIN32
// for signal handler
#include "unistd.h"
#include "signal.h"
#include "sys/wait.h"
#include "sys/signal.h"
#endif
#ifndef __FreeBSD__
#ifndef __MACH__
// malloc_trim
#include "malloc.h"
#endif
#endif
// Boost
#include <boost/algorithm/string.hpp>
using namespace boost::filesystem;
using namespace boost::program_options;
using namespace Transport;
DEFINE_LOGGER(logger, "Swiften");
DEFINE_LOGGER(logger_xml, "backend.xml");
// eventloop
Swift::SimpleEventLoop *loop_;
// Plugins
class SwiftenPlugin;
NetworkPlugin *np = NULL;
Swift::XMPPSerializer *serializer;
class ForwardIQHandler : public Swift::IQHandler {
public:
std::map <std::string, std::string> m_id2resource;
ForwardIQHandler(NetworkPlugin *np, const std::string &user) {
m_np = np;
m_user = user;
}
bool handleIQ(boost::shared_ptr<Swift::IQ> iq) {
if (iq->getPayload<Swift::RosterPayload>() != NULL) {
return false;
}
if (iq->getType() == Swift::IQ::Get) {
m_id2resource[iq->getID()] = iq->getFrom().getResource();
}
iq->setTo(m_user);
std::string xml = safeByteArrayToString(serializer->serializeElement(iq));
m_np->sendRawXML(xml);
return true;
}
private:
NetworkPlugin *m_np;
std::string m_user;
};
class SwiftenPlugin : public NetworkPlugin, Swift::XMPPParserClient {
public:
Swift::BoostNetworkFactories *m_factories;
Swift::BoostIOServiceThread m_boostIOServiceThread;
boost::shared_ptr<Swift::Connection> m_conn;
bool m_firstPing;
Swift::FullPayloadSerializerCollection collection;
Swift::XMPPParser *m_xmppParser;
Swift::FullPayloadParserFactoryCollection m_collection2;
SwiftenPlugin(Config *config, Swift::SimpleEventLoop *loop, const std::string &host, int port) : NetworkPlugin() {
this->config = config;
m_firstPing = true;
m_factories = new Swift::BoostNetworkFactories(loop);
m_conn = m_factories->getConnectionFactory()->createConnection();
m_conn->onDataRead.connect(boost::bind(&SwiftenPlugin::_handleDataRead, this, _1));
m_conn->connect(Swift::HostAddressPort(Swift::HostAddress(host), port));
serializer = new Swift::XMPPSerializer(&collection, Swift::ClientStreamType);
m_xmppParser = new Swift::XMPPParser(this, &m_collection2, m_factories->getXMLParserFactory());
m_xmppParser->parse("<stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' to='localhost' version='1.0'>");
LOG4CXX_INFO(logger, "Starting the plugin.");
}
// NetworkPlugin uses this method to send the data to networkplugin server
void sendData(const std::string &string) {
m_conn->write(Swift::createSafeByteArray(string));
}
// This method has to call handleDataRead with all received data from network plugin server
void _handleDataRead(boost::shared_ptr<Swift::SafeByteArray> data) {
if (m_firstPing) {
m_firstPing = false;
NetworkPlugin::PluginConfig cfg;
cfg.setRawXML(true);
sendConfig(cfg);
}
std::string d(data->begin(), data->end());
handleDataRead(d);
}
void handleStreamStart(const Swift::ProtocolHeader&) {}
void handleElement(boost::shared_ptr<Swift::Element> element) {
boost::shared_ptr<Swift::Stanza> stanza = boost::dynamic_pointer_cast<Swift::Stanza>(element);
if (!stanza) {
return;
}
std::string user = stanza->getFrom().toBare();
boost::shared_ptr<Swift::Client> client = m_users[user];
if (!client)
return;
stanza->setFrom(client->getJID());
boost::shared_ptr<Swift::Message> message = boost::dynamic_pointer_cast<Swift::Message>(stanza);
if (message) {
client->sendMessage(message);
return;
}
boost::shared_ptr<Swift::Presence> presence = boost::dynamic_pointer_cast<Swift::Presence>(stanza);
if (presence) {
client->sendPresence(presence);
return;
}
boost::shared_ptr<Swift::IQ> iq = boost::dynamic_pointer_cast<Swift::IQ>(stanza);
if (iq) {
if (m_handlers[user]->m_id2resource.find(stanza->getID()) != m_handlers[user]->m_id2resource.end()) {
iq->setTo(Swift::JID(iq->getTo().getNode(), iq->getTo().getDomain(), m_handlers[user]->m_id2resource[stanza->getID()]));
m_handlers[user]->m_id2resource.erase(stanza->getID());
}
client->getIQRouter()->sendIQ(iq);
return;
}
}
void handleStreamEnd() {}
void handleRawXML(const std::string &xml) {
m_xmppParser->parse(xml);
}
void handleSwiftDisconnected(const std::string &user, const boost::optional<Swift::ClientError> &error) {
std::string message = "";
bool reconnect = false;
if (error) {
switch(error->getType()) {
case Swift::ClientError::UnknownError: message = ("Unknown Error"); reconnect = true; break;
case Swift::ClientError::DomainNameResolveError: message = ("Unable to find server"); break;
case Swift::ClientError::ConnectionError: message = ("Error connecting to server"); break;
case Swift::ClientError::ConnectionReadError: message = ("Error while receiving server data"); reconnect = true; break;
case Swift::ClientError::ConnectionWriteError: message = ("Error while sending data to the server"); reconnect = true; break;
case Swift::ClientError::XMLError: message = ("Error parsing server data"); reconnect = true; break;
case Swift::ClientError::AuthenticationFailedError: message = ("Login/password invalid"); break;
case Swift::ClientError::CompressionFailedError: message = ("Error while compressing stream"); break;
case Swift::ClientError::ServerVerificationFailedError: message = ("Server verification failed"); break;
case Swift::ClientError::NoSupportedAuthMechanismsError: message = ("Authentication mechanisms not supported"); break;
case Swift::ClientError::UnexpectedElementError: message = ("Unexpected response"); break;
case Swift::ClientError::ResourceBindError: message = ("Error binding resource"); break;
case Swift::ClientError::SessionStartError: message = ("Error starting session"); break;
case Swift::ClientError::StreamError: message = ("Stream error"); break;
case Swift::ClientError::TLSError: message = ("Encryption error"); break;
case Swift::ClientError::ClientCertificateLoadError: message = ("Error loading certificate (Invalid password?)"); break;
case Swift::ClientError::ClientCertificateError: message = ("Certificate not authorized"); break;
case Swift::ClientError::UnknownCertificateError: message = ("Unknown certificate"); break;
case Swift::ClientError::CertificateExpiredError: message = ("Certificate has expired"); break;
case Swift::ClientError::CertificateNotYetValidError: message = ("Certificate is not yet valid"); break;
case Swift::ClientError::CertificateSelfSignedError: message = ("Certificate is self-signed"); break;
case Swift::ClientError::CertificateRejectedError: message = ("Certificate has been rejected"); break;
case Swift::ClientError::CertificateUntrustedError: message = ("Certificate is not trusted"); break;
case Swift::ClientError::InvalidCertificatePurposeError: message = ("Certificate cannot be used for encrypting your connection"); break;
case Swift::ClientError::CertificatePathLengthExceededError: message = ("Certificate path length constraint exceeded"); break;
case Swift::ClientError::InvalidCertificateSignatureError: message = ("Invalid certificate signature"); break;
case Swift::ClientError::InvalidCAError: message = ("Invalid Certificate Authority"); break;
case Swift::ClientError::InvalidServerIdentityError: message = ("Certificate does not match the host identity"); break;
}
}
LOG4CXX_INFO(logger, user << ": Disconnected " << message);
handleDisconnected(user, reconnect ? 0 : 3, message);
boost::shared_ptr<Swift::Client> client = m_users[user];
if (client) {
client->onConnected.disconnect(boost::bind(&SwiftenPlugin::handleSwiftConnected, this, user));
client->onDisconnected.disconnect(boost::bind(&SwiftenPlugin::handleSwiftDisconnected, this, user, _1));
client->onMessageReceived.disconnect(boost::bind(&SwiftenPlugin::handleSwiftMessageReceived, this, user, _1));
m_users.erase(user);
m_handlers.erase(user);
}
#ifndef WIN32
#ifndef __FreeBSD__
#ifndef __MACH__
// force returning of memory chunks allocated by libxml2 to kernel
malloc_trim(0);
#endif
#endif
#endif
}
void handleSwiftConnected(const std::string &user) {
LOG4CXX_INFO(logger, user << ": Connected to XMPP server.");
handleConnected(user);
m_users[user]->requestRoster();
Swift::Presence::ref response = Swift::Presence::create();
response->setFrom(m_users[user]->getJID());
m_users[user]->sendPresence(response);
}
void handleSwiftRosterReceived(const std::string &user) {
Swift::PresenceOracle *oracle = m_users[user]->getPresenceOracle();
BOOST_FOREACH(const Swift::XMPPRosterItem &item, m_users[user]->getRoster()->getItems()) {
Swift::Presence::ref lastPresence = oracle->getLastPresence(item.getJID());
pbnetwork::StatusType status = lastPresence ? ((pbnetwork::StatusType) lastPresence->getShow()) : pbnetwork::STATUS_NONE;
handleBuddyChanged(user, item.getJID().toBare().toString(),
item.getName(), item.getGroups(), status);
}
}
void handleSwiftPresenceChanged(const std::string &user, Swift::Presence::ref presence) {
// boost::shared_ptr<Swift::Client> client = m_users[user];
// if (client->getMUCRegistry()->isMUC(presence->getFrom().toBare())) {
// return;
// }
//
// if (presence->getPayload<Swift::MUCUserPayload>() != NULL || presence->getPayload<Swift::MUCPayload>() != NULL) {
// return;
// }
//
// LOG4CXX_INFO(logger, user << ": " << presence->getFrom().toBare().toString() << " presence changed");
//
// std::string message = presence->getStatus();
// std::string photo = "";
//
// boost::shared_ptr<Swift::VCardUpdate> update = presence->getPayload<Swift::VCardUpdate>();
// if (update) {
// photo = update->getPhotoHash();
// }
//
// boost::optional<Swift::XMPPRosterItem> item = m_users[user]->getRoster()->getItem(presence->getFrom());
// if (item) {
// handleBuddyChanged(user, presence->getFrom().toBare().toString(), item->getName(), item->getGroups(), (pbnetwork::StatusType) presence->getShow(), message, photo);
// }
// else {
// std::vector<std::string> groups;
// handleBuddyChanged(user, presence->getFrom().toBare().toString(), presence->getFrom().toBare(), groups, (pbnetwork::StatusType) presence->getShow(), message, photo);
// }
presence->setTo(user);
std::string xml = safeByteArrayToString(serializer->serializeElement(presence));
sendRawXML(xml);
}
void handleSwiftMessageReceived(const std::string &user, Swift::Message::ref message) {
message->setTo(user);
std::string xml = safeByteArrayToString(serializer->serializeElement(message));
sendRawXML(xml);
}
void handleSwiftenDataRead(const Swift::SafeByteArray &data) {
std::string d = safeByteArrayToString(data);
if (!boost::starts_with(d, "<auth")) {
LOG4CXX_INFO(logger_xml, "XML IN " << d);
}
}
void handleSwiftenDataWritten(const Swift::SafeByteArray &data) {
LOG4CXX_INFO(logger_xml, "XML OUT " << safeByteArrayToString(data));
}
void handleLoginRequest(const std::string &user, const std::string &legacyName, const std::string &password) {
LOG4CXX_INFO(logger, user << ": connecting as " << legacyName);
boost::shared_ptr<Swift::Client> client = boost::make_shared<Swift::Client>(Swift::JID(legacyName), password, m_factories);
m_users[user] = client;
client->setAlwaysTrustCertificates();
client->onConnected.connect(boost::bind(&SwiftenPlugin::handleSwiftConnected, this, user));
client->onDisconnected.connect(boost::bind(&SwiftenPlugin::handleSwiftDisconnected, this, user, _1));
client->onMessageReceived.connect(boost::bind(&SwiftenPlugin::handleSwiftMessageReceived, this, user, _1));
client->getRoster()->onInitialRosterPopulated.connect(boost::bind(&SwiftenPlugin::handleSwiftRosterReceived, this, user));
client->getPresenceOracle()->onPresenceChange.connect(boost::bind(&SwiftenPlugin::handleSwiftPresenceChanged, this, user, _1));
client->onDataRead.connect(boost::bind(&SwiftenPlugin::handleSwiftenDataRead, this, _1));
client->onDataWritten.connect(boost::bind(&SwiftenPlugin::handleSwiftenDataWritten, this, _1));
Swift::ClientOptions opt;
opt.allowPLAINWithoutTLS = true;
client->connect(opt);
boost::shared_ptr<ForwardIQHandler> handler = boost::make_shared<ForwardIQHandler>(this, user);
client->getIQRouter()->addHandler(handler);
m_handlers[user] = handler;
}
void handleLogoutRequest(const std::string &user, const std::string &legacyName) {
boost::shared_ptr<Swift::Client> client = m_users[user];
if (client) {
client->onConnected.disconnect(boost::bind(&SwiftenPlugin::handleSwiftConnected, this, user));
// client->onDisconnected.disconnect(boost::bind(&SwiftenPlugin::handleSwiftDisconnected, this, user, _1));
client->onMessageReceived.disconnect(boost::bind(&SwiftenPlugin::handleSwiftMessageReceived, this, user, _1));
client->getRoster()->onInitialRosterPopulated.disconnect(boost::bind(&SwiftenPlugin::handleSwiftRosterReceived, this, user));
client->getPresenceOracle()->onPresenceChange.disconnect(boost::bind(&SwiftenPlugin::handleSwiftPresenceChanged, this, user, _1));
client->disconnect();
}
}
void handleMessageSendRequest(const std::string &user, const std::string &legacyName, const std::string &msg, const std::string &xhtml = "", const std::string &id = "") {
}
void handleVCardRequest(const std::string &user, const std::string &legacyName, unsigned int id) {
}
void handleBuddyUpdatedRequest(const std::string &user, const std::string &buddyName, const std::string &alias, const std::vector<std::string> &groups) {
boost::shared_ptr<Swift::Client> client = m_users[user];
if (client) {
LOG4CXX_INFO(logger, user << ": Added/Updated buddy " << buddyName << ".");
if (!client->getRoster()->containsJID(buddyName) || client->getRoster()->getSubscriptionStateForJID(buddyName) != Swift::RosterItemPayload::Both) {
Swift::RosterItemPayload item;
item.setName(alias);
item.setJID(buddyName);
item.setGroups(groups);
boost::shared_ptr<Swift::RosterPayload> roster(new Swift::RosterPayload());
roster->addItem(item);
Swift::SetRosterRequest::ref request = Swift::SetRosterRequest::create(roster, client->getIQRouter());
// request->onResponse.connect(boost::bind(&RosterController::handleRosterSetError, this, _1, roster));
request->send();
client->getSubscriptionManager()->requestSubscription(buddyName);
}
else {
Swift::JID contact(buddyName);
Swift::RosterItemPayload item(contact, alias, client->getRoster()->getSubscriptionStateForJID(contact));
item.setGroups(groups);
boost::shared_ptr<Swift::RosterPayload> roster(new Swift::RosterPayload());
roster->addItem(item);
Swift::SetRosterRequest::ref request = Swift::SetRosterRequest::create(roster, client->getIQRouter());
// request->onResponse.connect(boost::bind(&RosterController::handleRosterSetError, this, _1, roster));
request->send();
}
}
}
void handleBuddyRemovedRequest(const std::string &user, const std::string &buddyName, const std::vector<std::string> &groups) {
boost::shared_ptr<Swift::Client> client = m_users[user];
if (client) {
Swift::RosterItemPayload item(buddyName, "", Swift::RosterItemPayload::Remove);
boost::shared_ptr<Swift::RosterPayload> roster(new Swift::RosterPayload());
roster->addItem(item);
Swift::SetRosterRequest::ref request = Swift::SetRosterRequest::create(roster, client->getIQRouter());
// request->onResponse.connect(boost::bind(&RosterController::handleRosterSetError, this, _1, roster));
request->send();
}
}
void handleJoinRoomRequest(const std::string &user, const std::string &room, const std::string &nickname, const std::string &password) {
}
void handleLeaveRoomRequest(const std::string &user, const std::string &room) {
}
private:
Config *config;
std::map<std::string, boost::shared_ptr<Swift::Client> > m_users;
std::map<std::string, boost::shared_ptr<ForwardIQHandler> > m_handlers;
};
#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
int main (int argc, char* argv[]) {
std::string host;
int port;
#ifndef WIN32
if (signal(SIGCHLD, spectrum_sigchld_handler) == SIG_ERR) {
std::cout << "SIGCHLD handler can't be set\n";
return -1;
}
#endif
std::string error;
Config *cfg = Config::createFromArgs(argc, argv, error, host, port);
if (cfg == NULL) {
std::cerr << error;
return 1;
}
Logging::initBackendLogging(cfg);
Swift::SimpleEventLoop eventLoop;
loop_ = &eventLoop;
np = new SwiftenPlugin(cfg, &eventLoop, host, port);
loop_->run();
return 0;
}

View file

@ -3,16 +3,21 @@ DEFINE_LOGGER(logger, "HelpMessageRequest")
void HelpMessageRequest::run()
{
helpMsg = helpMsg
+ "\n******************************HELP************************************\n"
+ "#status <your status> ==> Update your status\n"
+ "You will receive tweets of people you follow from this contact."
+ "Following commands can be used to manage your Twitter account:"
+ "#timeline [username] ==> Retrieve <username>'s timeline; Default - own timeline\n"
+ "#status <your status> ==> Update your status\n"
+ "@<username> <message> ==> Send a directed message to the user <username>\n"
+ "#retweet <unique_tweet_id> ==> Retweet the tweet having id <unique_tweet_id> \n"
+ "#follow <username> ==> Follow user <username>\n"
+ "#unfollow <username> ==> Stop Following user <username>\n"
+ "#mode [012] ==> Switch mode to 0(single), 1(multiple) or 2(chatroom)\n"
+ "#mode [01] ==> Switch mode to 0(single), 1(multiple). See below\n"
+ "#help ==> Print this help message\n"
+ "************************************************************************\n";
+ "************************************************************************\n"
+ "There are 3 ways how to use the gateway:\n"
+ "Mode 0 - There is twitter.com contact in your roster and all Twitter messages are forwaded using this contact.\n"
+ "Mode 1 - Same as Mode 0, but the people you follow are displayed in your roster. You will receive/send directed messages using those contacts.\n"
+ "Joining the #twitter@" + jid + " room - You can join mentioned room and see people you follow there as well as the tweets they post. Private messages in that room are mapped to Twitter direct messages.";
}
void HelpMessageRequest::finalize()

View file

@ -14,13 +14,15 @@ using namespace Transport;
class HelpMessageRequest : public Thread
{
std::string user;
std::string jid;
std::string helpMsg;
boost::function<void (std::string &, std::string &)> callBack;
public:
HelpMessageRequest(const std::string &_user, boost::function<void (std::string &, std::string &)> cb) {
HelpMessageRequest(const std::string &_user, const std::string &jid, boost::function<void (std::string &, std::string &)> cb) {
user = _user;
callBack = cb;
this->jid = jid;
}
void run();

View file

@ -39,6 +39,7 @@ void PINExchangeProcess::finalize()
}
np->pinExchangeComplete(user, OAuthAccessTokenKey, OAuthAccessTokenSecret);
np->handleMessage(user, "twitter.com", "PIN is OK. You are now authorized.");
np->handleMessage(user, "twitter.com", "Send '#help' (without the quotes) to see how to use this transport.");
LOG4CXX_INFO(logger, user << ": Sent PIN " << data << " and obtained Access Token");
}
}

View file

@ -156,10 +156,13 @@ void TwitterPlugin::handleLoginRequest(const std::string &user, const std::strin
// User logging out
void TwitterPlugin::handleLogoutRequest(const std::string &user, const std::string &legacyName)
{
if(onlineUsers.count(user)) {
if (userdb.count(user)) {
delete userdb[user].sessions;
userdb[user].sessions = NULL;
userdb[user].connectionState = DISCONNECTED;
}
if(onlineUsers.count(user)) {
onlineUsers.erase(user);
}
}
@ -212,7 +215,7 @@ void TwitterPlugin::handleMessageSendRequest(const std::string &user, const std:
if(cmd == "#pin")
tp->runAsThread(new PINExchangeProcess(np, userdb[user].sessions, user, data));
else if(cmd == "#help")
tp->runAsThread(new HelpMessageRequest(user, boost::bind(&TwitterPlugin::helpMessageResponse, this, _1, _2)));
tp->runAsThread(new HelpMessageRequest(user, CONFIG_STRING(config, "service.jid"), boost::bind(&TwitterPlugin::helpMessageResponse, this, _1, _2)));
else if(cmd[0] == '@') {
std::string username = cmd.substr(1);
tp->runAsThread(new DirectMessageRequest(userdb[user].sessions, user, username, data,
@ -274,7 +277,7 @@ void TwitterPlugin::handleMessageSendRequest(const std::string &user, const std:
}
else {
std::string buddy;
std::string buddy = legacyName;
if(userdb[user].twitterMode == CHATROOM) buddy = legacyName.substr(legacyName.find("/") + 1);
if(legacyName != "twitter") {
tp->runAsThread(new DirectMessageRequest(userdb[user].sessions, user, buddy, message,

View file

@ -38,6 +38,7 @@ Server::Server(
NetworkFactories* networkFactories,
UserRegistry *userRegistry,
const JID& jid,
const std::string &address,
int port) :
userRegistry_(userRegistry),
port_(port),
@ -45,7 +46,8 @@ Server::Server(
networkFactories_(networkFactories),
stopping(false),
selfJID(jid),
stanzaChannel_(){
stanzaChannel_(),
address_(address){
stanzaChannel_ = new ServerStanzaChannel();
iqRouter_ = new IQRouter(stanzaChannel_);
tlsFactory = NULL;
@ -63,7 +65,12 @@ void Server::start() {
if (serverFromClientConnectionServer) {
return;
}
serverFromClientConnectionServer = networkFactories_->getConnectionServerFactory()->createConnectionServer(port_);
if (address_ == "0.0.0.0") {
serverFromClientConnectionServer = networkFactories_->getConnectionServerFactory()->createConnectionServer(port_);
}
else {
serverFromClientConnectionServer = networkFactories_->getConnectionServerFactory()->createConnectionServer(Swift::HostAddress(address_), port_);
}
serverFromClientConnectionServerSignalConnections.push_back(
serverFromClientConnectionServer->onNewConnection.connect(
boost::bind(&Server::handleNewClientConnection, this, _1)));

View file

@ -35,7 +35,7 @@ namespace Swift {
class Server : public Entity {
public:
Server(EventLoop* eventLoop, NetworkFactories* networkFactories, UserRegistry *userRegistry, const JID& jid, int port);
Server(EventLoop* eventLoop, NetworkFactories* networkFactories, UserRegistry *userRegistry, const JID& jid, const std::string &address, int port);
~Server();
void start();
@ -86,5 +86,6 @@ namespace Swift {
TLSServerContextFactory *tlsFactory;
CertificateWithKey::ref cert;
PlatformXMLParserFactory *parserFactory_;
std::string address_;
};
}

View file

@ -23,7 +23,10 @@
#include <string>
#include <algorithm>
#include <map>
#include "Swiften/Swiften.h"
#include <time.h>
#include "Swiften/Elements/FormField.h"
#include "Swiften/Elements/Command.h"
namespace Transport {

View file

@ -24,7 +24,6 @@
#include <algorithm>
#include <map>
#include "transport/adhoccommand.h"
#include "Swiften/Swiften.h"
namespace Transport {

View file

@ -23,7 +23,10 @@
#include <string>
#include <algorithm>
#include <map>
#include "Swiften/Swiften.h"
#include "Swiften/Queries/Responder.h"
#include "Swiften/Elements/Command.h"
#include "Swiften/Network/Timer.h"
namespace Transport {

View file

@ -22,7 +22,8 @@
#include <string>
#include <map>
#include "Swiften/Swiften.h"
#include "Swiften/Elements/Message.h"
namespace Transport {

View file

@ -23,8 +23,8 @@
#include <string>
#include <algorithm>
#include "transport/transport.h"
#include "Swiften/Elements/VCard.h"
#include "Swiften/Swiften.h"
namespace Transport {

View file

@ -24,7 +24,6 @@
#include <algorithm>
#include "transport/transport.h"
#include "Swiften/Swiften.h"
#include "Swiften/Elements/Message.h"
namespace Transport {
@ -34,15 +33,22 @@ class ConversationManager;
/// Represents one XMPP-Legacy network conversation.
class Conversation {
public:
typedef enum {
PARTICIPANT_FLAG_NONE = 0,
PARTICIPANT_FLAG_MODERATOR = 1,
PARTICIPANT_FLAG_CONFLICT = 2,
PARTICIPANT_FLAG_BANNED = 4,
PARTICIPANT_FLAG_NOT_AUTHORIZED = 8,
PARTICIPANT_FLAG_ME = 16,
PARTICIPANT_FLAG_KICKED = 32
} ParticipantFlag;
typedef struct _Participant {
int flag;
ParticipantFlag flag;
int status;
std::string statusMessage;
} Participant;
/// Type of participants in MUC rooms.
enum ParticipantFlag {None, Moderator};
/// Creates new conversation.
/// \param conversationManager ConversationManager associated with this Conversation.
@ -64,6 +70,8 @@ class Conversation {
/// \param nickname For MUC conversation this is nickname of room participant who sent this message.
void handleMessage(boost::shared_ptr<Swift::Message> &message, const std::string &nickname = "");
void handleRawMessage(boost::shared_ptr<Swift::Message> &message);
/// Handles participant change in MUC.
/// \param nickname Nickname of participant which changed.
@ -71,7 +79,7 @@ class Conversation {
/// \param status Current status of this participant.
/// \param statusMessage Current status message of this participant.
/// \param newname If participant was renamed, this variable contains his new name.
void handleParticipantChanged(const std::string &nickname, int flag, int status = Swift::StatusShow::None, const std::string &statusMessage = "", const std::string &newname = "");
void handleParticipantChanged(const std::string &nickname, ParticipantFlag flag, int status = Swift::StatusShow::None, const std::string &statusMessage = "", const std::string &newname = "");
/// Sets XMPP user nickname in MUC rooms.

View file

@ -23,7 +23,8 @@
#include <string>
#include <algorithm>
#include <map>
#include "Swiften/Swiften.h"
#include "Swiften/Elements/Message.h"
namespace Transport {

View file

@ -21,9 +21,9 @@
#pragma once
#include <vector>
#include "Swiften/Swiften.h"
#include "Swiften/Queries/GetResponder.h"
#include "Swiften/Elements/DiscoItems.h"
#include "Swiften/Elements/CapsInfo.h"
namespace Transport {

View file

@ -24,7 +24,6 @@
#include <algorithm>
#include "transport/transport.h"
#include "Swiften/Swiften.h"
#include "Swiften/Elements/Message.h"
#include "transport/conversation.h"
#include "transport/buddy.h"

View file

@ -21,7 +21,6 @@
#pragma once
#include <vector>
#include "Swiften/Swiften.h"
#include "Swiften/Queries/Responder.h"
#include "Swiften/Elements/GatewayPayload.h"

View file

@ -65,6 +65,8 @@ class LocalBuddy : public Buddy {
std::string m_statusMessage;
std::string m_iconHash;
Swift::StatusShow m_status;
friend class NetworkPluginServer;
};
}

View file

@ -22,7 +22,8 @@
#include <string>
#include <map>
#include "Swiften/Swiften.h"
#include "Swiften/FileTransfer/ReadBytestream.h"
namespace Transport {

View file

@ -24,12 +24,10 @@
#ifndef WIN32
#include "signal.h"
#else
#define pid_t void*
#endif
namespace Transport {
#ifndef WIN32
void process_mem_usage(double& shared, double& resident_set, pid_t pid = 0);
#endif
}

View file

@ -24,7 +24,6 @@
#include <string>
#include <map>
#include "Swiften/Swiften.h"
#include "transport/storagebackend.h"
#include "transport/config.h"
#include "mysql.h"

View file

@ -39,16 +39,20 @@ class NetworkPlugin {
class PluginConfig {
public:
PluginConfig() : m_needPassword(true), m_needRegistration(false) {}
PluginConfig() : m_needPassword(true), m_needRegistration(false), m_supportMUC(false), m_rawXML(false) {}
virtual ~PluginConfig() {}
void setNeedRegistration(bool needRegistration = false) { m_needRegistration = needRegistration; }
void setNeedPassword(bool needPassword = true) { m_needPassword = needPassword; }
void setSupportMUC(bool supportMUC = true) { m_supportMUC = supportMUC; }
void setExtraFields(const std::vector<std::string> &fields) { m_extraFields = fields; }
void setRawXML(bool rawXML = false) { m_rawXML = rawXML; }
private:
bool m_needPassword;
bool m_needRegistration;
bool m_supportMUC;
bool m_rawXML;
std::vector<std::string> m_extraFields;
friend class NetworkPlugin;
@ -65,6 +69,8 @@ class NetworkPlugin {
void sendConfig(const PluginConfig &cfg);
void sendRawXML(std::string &xml);
/// Call this function when legacy network buddy changed.
/// \param user XMPP JID of user for which this event occurs. You can get it from NetworkPlugin::handleLoginRequest(). (eg. "user%gmail.com@xmpp.domain.tld")
/// \param buddyName Name of legacy network buddy. (eg. "user2@gmail.com")
@ -111,7 +117,7 @@ class NetworkPlugin {
/// \param message Plain text message.
/// \param nickname Nickname of buddy in room. Empty if it's normal chat message.
/// \param xhtml XHTML message.
void handleMessage(const std::string &user, const std::string &legacyName, const std::string &message, const std::string &nickname = "", const std::string &xhtml = "", const std::string &timestamp = "", bool headline = false);
void handleMessage(const std::string &user, const std::string &legacyName, const std::string &message, const std::string &nickname = "", const std::string &xhtml = "", const std::string &timestamp = "", bool headline = false, bool pm = false);
void handleMessageAck(const std::string &user, const std::string &legacyName, const std::string &id);
@ -243,6 +249,8 @@ class NetworkPlugin {
virtual void handleFTPauseRequest(unsigned long ftID) {}
virtual void handleFTContinueRequest(unsigned long ftID) {}
virtual void handleRawXML(const std::string &xml) {}
virtual void handleMemoryUsage(double &res, double &shared) {res = 0; shared = 0;}
virtual void handleExitRequest() { exit(1); }

View file

@ -21,11 +21,18 @@
#pragma once
#include <time.h>
#include "Swiften/Swiften.h"
#include "Swiften/Presence/PresenceOracle.h"
#include "Swiften/Disco/EntityCapsManager.h"
#include "Swiften/Network/BoostConnectionServer.h"
#include "Swiften/Network/Connection.h"
#include "Swiften/Elements/ChatState.h"
#include "Swiften/Elements/RosterItemPayload.h"
#include "Swiften/Elements/VCard.h"
#include "Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.h"
#include "Swiften/Serializer/PayloadSerializers/FullPayloadSerializerCollection.h"
#include "Swiften/Parser/XMPPParser.h"
#include "Swiften/Parser/XMPPParserClient.h"
#include "Swiften/Serializer/XMPPSerializer.h"
#include "storagebackend.h"
#include "transport/filetransfermanager.h"
@ -45,7 +52,7 @@ class DummyReadBytestream;
class AdminInterface;
class DiscoItemsResponder;
class NetworkPluginServer {
class NetworkPluginServer : Swift::XMPPParserClient {
public:
struct Backend {
int pongReceived;
@ -114,6 +121,7 @@ class NetworkPluginServer {
void handleQueryPayload(Backend *b, const std::string &payload);
void handleBackendConfigPayload(const std::string &payload);
void handleRoomListPayload(const std::string &payload);
void handleRawXML(const std::string &xml);
void handleUserCreated(User *user);
void handleRoomJoined(User *user, const Swift::JID &who, const std::string &room, const std::string &nickname, const std::string &password);
@ -125,6 +133,8 @@ class NetworkPluginServer {
void handleBuddyUpdated(Buddy *buddy, const Swift::RosterItemPayload &item);
void handleBuddyRemoved(Buddy *buddy);
void handleBuddyAdded(Buddy *buddy, const Swift::RosterItemPayload &item);
void handleUserBuddyAdded(User *user, Buddy *buddy);
void handleUserBuddyRemoved(User *user, Buddy *buddy);
void handleBlockToggled(Buddy *buddy);
@ -145,6 +155,14 @@ class NetworkPluginServer {
Backend *getFreeClient(bool acceptUsers = true, bool longRun = false, bool check = false);
void connectWaitingUsers();
void loginDelayFinished();
void handleRawIQReceived(boost::shared_ptr<Swift::IQ> iq);
void handleRawPresenceReceived(boost::shared_ptr<Swift::Presence> presence);
void handleStreamStart(const Swift::ProtocolHeader&) {}
void handleElement(boost::shared_ptr<Swift::Element> element);
void handleStreamEnd() {}
UserManager *m_userManager;
VCardResponder *m_vcardResponder;
@ -167,6 +185,11 @@ class NetworkPluginServer {
bool m_startingBackend;
DiscoItemsResponder *m_discoItemsResponder;
time_t m_lastLogin;
Swift::XMPPParser *m_xmppParser;
Swift::FullPayloadParserFactoryCollection m_collection;
Swift::XMPPSerializer *m_serializer;
Swift::FullPayloadSerializerCollection m_collection2;
std::map <std::string, std::string> m_id2resource;
};
}

View file

@ -24,7 +24,6 @@
#include <string>
#include <map>
#include "Swiften/Swiften.h"
#include "transport/storagebackend.h"
#include "transport/config.h"
#include <pqxx/pqxx>

View file

@ -72,6 +72,7 @@ message ConversationMessage {
optional string timestamp = 6;
optional bool headline = 7;
optional string id = 8;
optional bool pm = 9;
}
message Room {
@ -86,6 +87,16 @@ message RoomList {
repeated string name = 2;
}
enum ParticipantFlag {
PARTICIPANT_FLAG_NONE = 0;
PARTICIPANT_FLAG_MODERATOR = 1;
PARTICIPANT_FLAG_CONFLICT = 2;
PARTICIPANT_FLAG_BANNED = 4;
PARTICIPANT_FLAG_NOT_AUTHORIZED = 8;
PARTICIPANT_FLAG_ME = 16;
PARTICIPANT_FLAG_KICKED = 32;
}
message Participant {
required string userName = 1;
required string room = 2;
@ -169,6 +180,7 @@ message WrapperMessage {
TYPE_QUERY = 31;
TYPE_ROOM_LIST = 32;
TYPE_CONV_MESSAGE_ACK = 33;
TYPE_RAW_XML = 34;
}
required Type type = 1;
optional bytes payload = 2;

View file

@ -25,8 +25,12 @@
#include <map>
#include <boost/pool/pool_alloc.hpp>
#include <boost/pool/object_pool.hpp>
#include "Swiften/Swiften.h"
// #include "rosterstorage.h"
#include "Swiften/Elements/RosterPayload.h"
#include "Swiften/Queries/GenericRequest.h"
#include "Swiften/Roster/SetRosterRequest.h"
#include "Swiften/Elements/Presence.h"
#include "Swiften/Network/Timer.h"
namespace Transport {

View file

@ -21,10 +21,11 @@
#pragma once
#include <vector>
#include "Swiften/Swiften.h"
#include "Swiften/Queries/Responder.h"
#include "Swiften/Elements/RosterPayload.h"
#include <boost/signal.hpp>
namespace Transport {
class UserManager;

View file

@ -22,7 +22,9 @@
#include <string>
#include <algorithm>
#include "Swiften/Swiften.h"
#include <map>
#include "Swiften/Network/Timer.h"
namespace Transport {

View file

@ -23,7 +23,6 @@
#include <string>
#include <algorithm>
#include <map>
#include "Swiften/Swiften.h"
#include "transport/adhoccommand.h"
#include "transport/adhoccommandfactory.h"

View file

@ -24,7 +24,6 @@
#include <string>
#include <map>
#include "Swiften/Swiften.h"
#include "transport/storagebackend.h"
#include "transport/config.h"
#include "sqlite3.h"

View file

@ -21,7 +21,6 @@
#pragma once
#include <vector>
#include "Swiften/Swiften.h"
#include "Swiften/Queries/SetResponder.h"
#include "Swiften/Elements/StatsPayload.h"

View file

@ -7,7 +7,7 @@
#include <queue>
#include <iostream>
#include "transport/logging.h"
#include "Swiften/Swiften.h"
#include "Swiften/EventLoop/EventLoop.h"
/*

View file

@ -21,7 +21,6 @@
#pragma once
#include <vector>
#include "Swiften/Swiften.h"
#include "Swiften/Server/Server.h"
#include "Swiften/Disco/GetDiscoInfoRequest.h"
#include "Swiften/Disco/EntityCapsManager.h"
@ -31,7 +30,11 @@
#include "Swiften/Network/BoostIOServiceThread.h"
#include "Swiften/Server/UserRegistry.h"
#include "Swiften/Base/SafeByteArray.h"
#include "Swiften/Queries/IQHandler.h"
#include "Swiften/Jingle/JingleSessionManager.h"
#include "Swiften/Component/ComponentError.h"
#include "Swiften/Component/Component.h"
#include "Swiften/Queries/IQHandler.h"
#include <boost/bind.hpp>
#include "transport/config.h"
@ -40,12 +43,6 @@
#include <Swiften/Network/BoostConnectionServer.h>
namespace Transport {
// typedef enum { CLIENT_FEATURE_ROSTERX = 2,
// CLIENT_FEATURE_XHTML_IM = 4,
// CLIENT_FEATURE_FILETRANSFER = 8,
// CLIENT_FEATURE_CHATSTATES = 16
// } SpectrumImportantFeatures;
//
class StorageBackend;
class Factory;
class UserRegistry;
@ -57,7 +54,7 @@ namespace Transport {
///
/// In server mode it represents Jabber server to which users can connect and use
/// it as transport.
class Component {
class Component : Swift::IQHandler {
public:
/// Creates new Component instance.
@ -68,7 +65,9 @@ namespace Transport {
/// - service.server
/// - service.port
/// - service.server_mode
/// \param factories Swift::NetworkFactories.
/// \param factory Transport Abstract factory used to create basic transport structures.
/// \param userRegistery UserRegistry class instance. It's needed only when running transport in server-mode.
Component(Swift::EventLoop *loop, Swift::NetworkFactories *factories, Config *config, Factory *factory, Transport::UserRegistry *userRegistry = NULL);
/// Component destructor.
@ -96,9 +95,13 @@ namespace Transport {
/// \return True if the component is in server mode.
bool inServerMode() { return m_server != NULL; }
/// Connects the Jabber server.
/// Starts the Component.
/// In server-mode, it starts listening on particular port for new client connections.
/// In gateway-mode, it connects the XMPP server.
void start();
/// Stops the component.
void stop();
/// Returns Jabber ID of this transport.
@ -139,13 +142,18 @@ namespace Transport {
/// This signal is emitted when presence from XMPP user is received.
/// It's emitted only for presences addressed to transport itself
/// (for example to="j2j.domain.tld").
/// \param presence presence data
/// (for example to="j2j.domain.tld") and for presences comming to
/// MUC (for example to="#chat%irc.freenode.org@irc.domain.tld")
/// \param presence Presence.
boost::signal<void (Swift::Presence::ref presence)> onUserPresenceReceived;
/// Component class asks the XMPP clients automatically for their capabilities.
/// This signal is emitted when capabilities have been received or changed.
/// \param jid JID of the client for which we received capabilities
/// \param info disco#info with response.
boost::signal<void (const Swift::JID& jid, boost::shared_ptr<Swift::DiscoInfo> info)> onUserDiscoInfoReceived;
// boost::signal<void (boost::shared_ptr<Swift::DiscoInfo> info, Swift::ErrorPayload::ref error, const Swift::JID& jid)> onDiscoInfoResponse;
boost::signal<void (boost::shared_ptr<Swift::IQ>)> onRawIQReceived;
private:
void handleConnected();
@ -158,6 +166,9 @@ namespace Transport {
void handleDiscoInfoResponse(boost::shared_ptr<Swift::DiscoInfo> info, Swift::ErrorPayload::ref error, const Swift::JID& jid);
void handleCapsChanged(const Swift::JID& jid);
void handleBackendConfigChanged();
bool handleIQ(boost::shared_ptr<Swift::IQ>);
Swift::NetworkFactories *m_factories;
Swift::Component *m_component;
Swift::Server *m_server;
@ -177,6 +188,7 @@ namespace Transport {
Swift::JID m_jid;
Factory *m_factory;
Swift::EventLoop *m_loop;
bool m_rawXML;
friend class User;
friend class UserRegistration;

View file

@ -21,12 +21,13 @@
#pragma once
#include <time.h>
#include "Swiften/Swiften.h"
#include "Swiften/Disco/EntityCapsManager.h"
#include "Swiften/Disco/EntityCapsProvider.h"
#include "storagebackend.h"
#include <Swiften/FileTransfer/OutgoingFileTransfer.h>
#include "Swiften/Elements/SpectrumErrorPayload.h"
#include "Swiften/Network/Timer.h"
#include "Swiften/Network/Connection.h"
namespace Transport {
@ -132,6 +133,7 @@ class User : public Swift::EntityCapsProvider {
boost::signal<void ()> onReadyToConnect;
boost::signal<void (Swift::Presence::ref presence)> onPresenceChanged;
boost::signal<void (Swift::Presence::ref presence)> onRawPresenceReceived;
boost::signal<void (const Swift::JID &who, const std::string &room, const std::string &nickname, const std::string &password)> onRoomJoined;
boost::signal<void (const std::string &room)> onRoomLeft;
boost::signal<void ()> onDisconnected;

View file

@ -22,8 +22,12 @@
#include <string>
#include <map>
#include "Swiften/Swiften.h"
#include "transport/userregistry.h"
#include "Swiften/Elements/Message.h"
#include "Swiften/Elements/Presence.h"
#include "Swiften/Disco/EntityCapsProvider.h"
#include "Swiften/Elements/DiscoInfo.h"
#include "Swiften/Network/Timer.h"
namespace Transport {
@ -133,6 +137,7 @@ class UserManager : public Swift::EntityCapsProvider {
void handleProbePresence(Swift::Presence::ref presence);
void handleErrorPresence(Swift::Presence::ref presence);
void handleSubscription(Swift::Presence::ref presence);
void handleMUCPresence(Swift::Presence::ref presence);
void handleRemoveTimeout(const std::string jid, User *user, bool reconnect);
void handleDiscoInfo(const Swift::JID& jid, boost::shared_ptr<Swift::DiscoInfo> info);
void addUser(User *user);

View file

@ -20,9 +20,10 @@
#pragma once
#include "Swiften/Swiften.h"
#include "Swiften/Queries/Responder.h"
#include "Swiften/Elements/InBandRegistrationPayload.h"
#include "Swiften/Elements/RosterPayload.h"
#include <boost/signal.hpp>
namespace Transport {

View file

@ -22,8 +22,10 @@
#include <string>
#include <map>
#include "Swiften/Swiften.h"
#include "Swiften/Server/UserRegistry.h"
#include "Swiften/Network/NetworkFactories.h"
#include "Swiften/Network/Timer.h"
#include "Swiften/Network/TimerFactory.h"
#include "transport/config.h"
namespace Transport {

View file

@ -23,7 +23,8 @@
#include <string>
#include <algorithm>
#include <vector>
#include "Swiften/Swiften.h"
#include "Swiften/Network/Timer.h"
namespace Transport {

View file

@ -21,9 +21,11 @@
#pragma once
#include <vector>
#include "Swiften/Swiften.h"
#include "Swiften/Queries/Responder.h"
#include "Swiften/Elements/VCard.h"
#include "Swiften/Network/NetworkFactories.h"
#include "Swiften/Network/Timer.h"
#include <boost/signal.hpp>
namespace Transport {

View file

@ -21,10 +21,9 @@
# You have to configure the plugin to run as user and group "spectrum".
#
# By default, the plugin monitors all instances configured in a config-file
# in /etc/spectrum. If you use a different directory, you can specify the
# environment-variable "base". If you do not want to monitor all instances,
# you can give an explicit listing of the corresponding configuration files
# with the environment variable "cfgs".
# in /etc/spectrum2/transports. If you do not want to monitor all instances,
# you can give an explicit listing of the corresponding instances
# with the environment variable "jids".
#
# Here is an example of a configuration. Note again that you can ommit both
# env.cfgs and env.base:
@ -32,8 +31,7 @@
# [spectrum_*]
# user spectrum
# group spectrum
# env.cfgs xmpp.example.com.cfg,irc.example.com.cfg
# env.base /etc/spectrum
# env.jids xmpp.example.com,irc.example.com
#
# Author:
# Mathias Ertl <mati@fsinf.at>
@ -94,9 +92,16 @@ def handle_field( string ):
# get runtime variables
suffix = sys.argv[0].partition('_')[2]
verbose = os.environ.get( 'verbose' )
proc = Popen(['spectrum2_manager', 'list'], stdout=PIPE, stderr=PIPE)
out, err = proc.communicate()
jids = out.split('\n')[:-1]
jids = []
base = os.environ.get( 'base', '/etc/spectrum' )
if 'jids' in os.environ.keys():
jids = os.environ.get( 'jids' ).split(',')
else:
proc = Popen(['spectrum2_manager', 'list'], stdout=PIPE, stderr=PIPE)
out, err = proc.communicate()
jids = out.split('\n')[:-1]
# set variables based on wildcard
if suffix == 'uptime':

View file

@ -71,6 +71,10 @@ void NetworkPlugin::sendConfig(const PluginConfig &cfg) {
data += std::string("extraField=") + (*it) + "\n";
}
data += "[features]\n";
data += std::string("muc=") + (cfg.m_supportMUC ? "1" : "0") + "\n";
data += std::string("rawxml=") + (cfg.m_rawXML ? "1" : "0") + "\n";
pbnetwork::BackendConfig m;
m.set_config(data);
@ -82,7 +86,13 @@ void NetworkPlugin::sendConfig(const PluginConfig &cfg) {
send(message);
}
void NetworkPlugin::handleMessage(const std::string &user, const std::string &legacyName, const std::string &msg, const std::string &nickname, const std::string &xhtml, const std::string &timestamp, bool headline) {
void NetworkPlugin::sendRawXML(std::string &xml) {
WRAP(xml, pbnetwork::WrapperMessage_Type_TYPE_RAW_XML);
send(xml);
}
void NetworkPlugin::handleMessage(const std::string &user, const std::string &legacyName, const std::string &msg, const std::string &nickname, const std::string &xhtml, const std::string &timestamp, bool headline, bool pm) {
pbnetwork::ConversationMessage m;
m.set_username(user);
m.set_buddyname(legacyName);
@ -91,6 +101,7 @@ void NetworkPlugin::handleMessage(const std::string &user, const std::string &le
m.set_xhtml(xhtml);
m.set_timestamp(timestamp);
m.set_headline(headline);
m.set_pm(pm);
std::string message;
m.SerializeToString(&message);
@ -643,6 +654,9 @@ void NetworkPlugin::handleDataRead(std::string &data) {
case pbnetwork::WrapperMessage_Type_TYPE_EXIT:
handleExitRequest();
break;
case pbnetwork::WrapperMessage_Type_TYPE_RAW_XML:
handleRawXML(wrapper.payload());
break;
default:
return;
}

View file

@ -17,6 +17,7 @@
#include "transport/adhocmanager.h"
#include "transport/settingsadhoccommand.h"
#include "Swiften/EventLoop/SimpleEventLoop.h"
#include "Swiften/Network/BoostNetworkFactories.h"
#include <boost/filesystem.hpp>
#include <boost/algorithm/string.hpp>
#ifndef WIN32
@ -27,8 +28,10 @@
#include <sys/resource.h>
#include "libgen.h"
#ifndef __FreeBSD__
#ifndef __MACH__
#include <malloc.h>
#endif
#endif
#else
#include <process.h>
#define getpid _getpid
@ -307,10 +310,12 @@ int main(int argc, char **argv)
setlocale(LC_ALL, "");
#ifndef WIN32
#ifndef __FreeBSD__
#ifndef __MACH__
mallopt(M_CHECK_ACTION, 2);
mallopt(M_PERTURB, 0xb);
#endif
#endif
#endif
#ifndef WIN32
if (signal(SIGINT, spectrum_sigint_handler) == SIG_ERR) {

View file

@ -13,15 +13,15 @@ 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=../..//backends/swiften/spectrum2_swiften_backend
#backend=../..//backends/swiften/spectrum2_swiften_backend
#backend=../../backends/twitter/spectrum2_twitter_backend
#backend=/home/hanzz/code/libtransport/backends/libcommuni/spectrum2_libcommuni_backend
backend=/home/hanzz/code/libtransport/backends/libcommuni/spectrum2_libcommuni_backend
protocol=prpl-jabber
#protocol=prpl-msn
#protocol=any
#protocol=prpl-icq
working_dir=./
portfile=$jid.port
portfile=./$jid.port
irc_server=irc.freenode.org
[backend]

View file

@ -14,7 +14,7 @@ jid = localhost
password = secret
# XMPP server to which Spectrum connects in gateway mode.
# In server mode, this option is ignored.
# To bind to all ipv4 interfaces, use server=0.0.0.0
server = 127.0.0.1
# XMPP server port.
@ -41,7 +41,7 @@ users_per_backend=10
backend=/usr/bin/spectrum2_libpurple_backend
#backend=/usr/bin/spectrum2_libcommuni_backend
# For skype:
#backend=/usr/bin/xvfb-run -n BACKEND_ID -s "-screen 0 10x10x8" -f /tmp/x-skype-gw /usr/bin/spectrum2_skype_backend
#backend=/usr/bin/xvfb-run -a -s "-screen 0 10x10x8" -f /tmp/x-skype-gw /usr/bin/spectrum2_skype_backend
# Libpurple protocol-id for spectrum_libpurple_backend
protocol=prpl-jabber

View file

@ -7,7 +7,10 @@ ADD_DEPENDENCIES(spectrum2_manager pb)
SET_SOURCE_FILES_PROPERTIES(${CMAKE_CURRENT_SOURCE_DIR}/../../include/transport/protocol.pb.cc PROPERTIES GENERATED 1)
target_link_libraries(spectrum2_manager ${SWIFTEN_LIBRARY} ${PROTOBUF_LIBRARIES})
if(APPLE)
target_link_libraries(spectrum2_manager ${APPLE_FRAMEWORKS})
endif()
INSTALL(TARGETS spectrum2_manager RUNTIME DESTINATION bin)
INSTALL(FILES

View file

@ -138,6 +138,9 @@ int main(int argc, char **argv)
else if (command[0] == "list") {
std::vector<std::string> list = show_list(&config);
}
else if (command[0] == "restart") {
return restart_instances(&config);
}
else if (command[0] == "server") {
Server server(&config);
if (server.start() == false) {

View file

@ -3,7 +3,7 @@ FILE(GLOB SRC *.cpp *.h)
FILE(GLOB_RECURSE SWIFTEN_SRC ../include/Swiften/*.cpp)
# Build without openssl on msvc
if (CMAKE_COMPILER_IS_GNUCXX)
if (NOT MSVC)
string(REGEX REPLACE "[^;]+;?/Schannel/[^;]+;?" "" SWIFTEN_SRC "${SWIFTEN_SRC}")
else()
string(REGEX REPLACE "[^;]+;?/OpenSSL/[^;]+;?" "" SWIFTEN_SRC "${SWIFTEN_SRC}")
@ -47,7 +47,7 @@ endif(PROTOBUF_FOUND)
if (WIN32)
include_directories("${CMAKE_SOURCE_DIR}/msvc-deps/sqlite3")
TARGET_LINK_LIBRARIES(transport transport-plugin sqlite3 ${PQXX_LIBRARY} ${PQ_LIBRARY} ${MYSQL_LIBRARIES} ${SWIFTEN_LIBRARY} ${LOG4CXX_LIBRARIES} ${PROTOBUF_LIBRARY})
TARGET_LINK_LIBRARIES(transport transport-plugin sqlite3 ${PQXX_LIBRARY} ${PQ_LIBRARY} ${MYSQL_LIBRARIES} ${SWIFTEN_LIBRARY} ${LOG4CXX_LIBRARIES} ${PROTOBUF_LIBRARY} psapi.lib)
else()
TARGET_LINK_LIBRARIES(transport transport-plugin ${PQXX_LIBRARY} ${PQ_LIBRARY} ${SQLITE3_LIBRARIES} ${MYSQL_LIBRARIES} ${SWIFTEN_LIBRARY} ${LOG4CXX_LIBRARIES} ${POPT_LIBRARY} ${PROTOBUF_LIBRARY})
endif()
@ -55,6 +55,9 @@ endif()
SET_TARGET_PROPERTIES(transport PROPERTIES
VERSION ${TRANSPORT_VERSION} SOVERSION ${TRANSPORT_VERSION}
)
if (APPLE)
TARGET_LINK_LIBRARIES(transport ${APPLE_FRAMEWORKS})
endif()
INSTALL(TARGETS transport LIBRARY DESTINATION ${LIB_INSTALL_DIR} ARCHIVE DESTINATION ${LIB_INSTALL_DIR} COMPONENT libraries)

View file

@ -136,10 +136,7 @@ void AdminInterface::handleQuery(Swift::Message::ref message) {
else if (message->getBody() == "res_memory") {
double shared = 0;
double rss = 0;
#ifndef WIN32
process_mem_usage(shared, rss);
#endif
const std::list <NetworkPluginServer::Backend *> &backends = m_server->getBackends();
BOOST_FOREACH(NetworkPluginServer::Backend * backend, backends) {
rss += backend->res;
@ -150,10 +147,7 @@ void AdminInterface::handleQuery(Swift::Message::ref message) {
else if (message->getBody() == "shr_memory") {
double shared = 0;
double rss = 0;
#ifndef WIN32
process_mem_usage(shared, rss);
#endif
const std::list <NetworkPluginServer::Backend *> &backends = m_server->getBackends();
BOOST_FOREACH(NetworkPluginServer::Backend * backend, backends) {
shared += backend->shared;
@ -164,9 +158,7 @@ void AdminInterface::handleQuery(Swift::Message::ref message) {
else if (message->getBody() == "used_memory") {
double shared = 0;
double rss = 0;
#ifndef WIN32
process_mem_usage(shared, rss);
#endif
rss -= shared;
const std::list <NetworkPluginServer::Backend *> &backends = m_server->getBackends();

View file

@ -24,7 +24,6 @@
#include <boost/bind.hpp>
#include "Swiften/Queries/IQRouter.h"
#include "transport/BlockPayload.h"
#include "Swiften/Swiften.h"
#include "transport/usermanager.h"
#include "transport/user.h"
#include "transport/buddy.h"

View file

@ -21,9 +21,9 @@
#pragma once
#include <vector>
#include "Swiften/Swiften.h"
#include "Swiften/Queries/SetResponder.h"
#include "transport/BlockPayload.h"
#include <boost/signal.hpp>
namespace Transport {

View file

@ -26,6 +26,8 @@
#include "transport/usermanager.h"
#include "transport/discoitemsresponder.h"
#include "Swiften/Elements/VCardUpdate.h"
namespace Transport {
Buddy::Buddy(RosterManager *rosterManager, long id, BuddyFlag flags) : m_id(id), m_flags(flags), m_rosterManager(rosterManager),
@ -101,15 +103,19 @@ Swift::Presence::ref Buddy::generatePresenceStanza(int features, bool only_new)
}
Swift::Presence::ref presence = Swift::Presence::create();
presence->setFrom(m_jid);
presence->setTo(m_rosterManager->getUser()->getJID().toBare());
presence->setType(Swift::Presence::Available);
if (!statusMessage.empty())
presence->setStatus(statusMessage);
if (s.getType() == Swift::StatusShow::None)
if (s.getType() == Swift::StatusShow::None) {
presence->setType(Swift::Presence::Unavailable);
presence->setFrom(Swift::JID(m_jid.getNode(), m_jid.getDomain()));
}
else {
presence->setFrom(m_jid);
}
presence->setShow(s.getType());
if (presence->getType() != Swift::Presence::Unavailable) {

View file

@ -314,6 +314,8 @@ void Config::updateBackendConfig(const std::string &backendConfig) {
("registration.needRegistration", value<bool>()->default_value(false), "")
("registration.extraField", value<std::vector<std::string> >()->multitoken(), "")
("features.receipts", value<bool>()->default_value(false), "")
("features.muc", value<bool>()->default_value(false), "")
("features.rawxml", value<bool>()->default_value(false), "")
;
std::stringstream ifs(backendConfig);

View file

@ -26,6 +26,12 @@
#include "transport/buddy.h"
#include "transport/rostermanager.h"
#include "Swiften/Elements/MUCItem.h"
#include "Swiften/Elements/MUCOccupant.h"
#include "Swiften/Elements/MUCUserPayload.h"
#include "Swiften/Elements/Delay.h"
#include "Swiften/Elements/MUCPayload.h"
namespace Transport {
Conversation::Conversation(ConversationManager *conversationManager, const std::string &legacyName, bool isMUC) : m_conversationManager(conversationManager) {
@ -33,6 +39,10 @@ Conversation::Conversation(ConversationManager *conversationManager, const std::
m_muc = isMUC;
m_jid = m_conversationManager->getUser()->getJID().toBare();
m_sentInitialPresence = false;
if (CONFIG_BOOL_DEFAULTED(conversationManager->getComponent()->getConfig(), "features.rawxml", false)) {
m_sentInitialPresence = true;
}
}
Conversation::~Conversation() {
@ -76,6 +86,47 @@ void Conversation::setRoom(const std::string &room) {
m_legacyName = m_room + "/" + m_legacyName;
}
void Conversation::handleRawMessage(boost::shared_ptr<Swift::Message> &message) {
if (message->getType() != Swift::Message::Groupchat) {
if (m_conversationManager->getComponent()->inServerMode() && m_conversationManager->getUser()->shouldCacheMessages()) {
boost::posix_time::ptime timestamp = boost::posix_time::second_clock::universal_time();
boost::shared_ptr<Swift::Delay> delay(boost::make_shared<Swift::Delay>());
delay->setStamp(timestamp);
message->addPayload(delay);
m_cachedMessages.push_back(message);
if (m_cachedMessages.size() > 100) {
m_cachedMessages.pop_front();
}
}
else {
m_conversationManager->getComponent()->getStanzaChannel()->sendMessage(message);
}
}
else {
if (m_jids.empty()) {
boost::posix_time::ptime timestamp = boost::posix_time::second_clock::universal_time();
boost::shared_ptr<Swift::Delay> delay(boost::make_shared<Swift::Delay>());
delay->setStamp(timestamp);
message->addPayload(delay);
m_cachedMessages.push_back(message);
if (m_cachedMessages.size() > 100) {
m_cachedMessages.pop_front();
}
}
else {
BOOST_FOREACH(const Swift::JID &jid, m_jids) {
message->setTo(jid);
// Subject has to be sent after our own presence (the one with code 110)
if (!message->getSubject().empty() && m_sentInitialPresence == false) {
m_subject = message;
return;
}
m_conversationManager->getComponent()->getStanzaChannel()->sendMessage(message);
}
}
}
}
void Conversation::handleMessage(boost::shared_ptr<Swift::Message> &message, const std::string &nickname) {
if (m_muc) {
message->setType(Swift::Message::Groupchat);
@ -124,23 +175,13 @@ void Conversation::handleMessage(boost::shared_ptr<Swift::Message> &message, con
message->setFrom(Swift::JID(n, m_conversationManager->getComponent()->getJID().toBare(), "user"));
}
else {
message->setFrom(Swift::JID(m_room, m_conversationManager->getComponent()->getJID().toBare(), n));
std::string legacyName = m_room;
if (legacyName.find_last_of("@") != std::string::npos) {
legacyName.replace(legacyName.find_last_of("@"), 1, "%"); // OK
}
message->setFrom(Swift::JID(legacyName, m_conversationManager->getComponent()->getJID().toBare(), n));
}
}
if (m_conversationManager->getComponent()->inServerMode() && m_conversationManager->getUser()->shouldCacheMessages()) {
boost::posix_time::ptime timestamp = boost::posix_time::second_clock::universal_time();
boost::shared_ptr<Swift::Delay> delay(boost::make_shared<Swift::Delay>());
delay->setStamp(timestamp);
message->addPayload(delay);
m_cachedMessages.push_back(message);
if (m_cachedMessages.size() > 100) {
m_cachedMessages.pop_front();
}
}
else {
m_conversationManager->getComponent()->getStanzaChannel()->sendMessage(message);
}
}
else {
std::string legacyName = m_legacyName;
@ -154,29 +195,9 @@ void Conversation::handleMessage(boost::shared_ptr<Swift::Message> &message, con
}
message->setFrom(Swift::JID(legacyName, m_conversationManager->getComponent()->getJID().toBare(), n));
if (m_jids.empty()) {
boost::posix_time::ptime timestamp = boost::posix_time::second_clock::universal_time();
boost::shared_ptr<Swift::Delay> delay(boost::make_shared<Swift::Delay>());
delay->setStamp(timestamp);
message->addPayload(delay);
m_cachedMessages.push_back(message);
if (m_cachedMessages.size() > 100) {
m_cachedMessages.pop_front();
}
}
else {
BOOST_FOREACH(const Swift::JID &jid, m_jids) {
message->setTo(jid);
// Subject has to be sent after our own presence (the one with code 110)
if (!message->getSubject().empty() && m_sentInitialPresence == false) {
m_subject = message;
return;
}
m_conversationManager->getComponent()->getStanzaChannel()->sendMessage(message);
}
}
}
handleRawMessage(message);
}
void Conversation::sendParticipants(const Swift::JID &to) {
@ -225,19 +246,35 @@ Swift::Presence::ref Conversation::generatePresence(const std::string &nick, int
Swift::MUCUserPayload *p = new Swift::MUCUserPayload ();
if (m_nickname == nickname) {
Swift::MUCUserPayload::StatusCode c;
c.code = 110;
p->addStatusCode(c);
m_sentInitialPresence = true;
if (flag & PARTICIPANT_FLAG_CONFLICT) {
delete p;
presence->setType(Swift::Presence::Error);
presence->addPayload(boost::shared_ptr<Swift::Payload>(new Swift::MUCPayload()));
presence->addPayload(boost::shared_ptr<Swift::Payload>(new Swift::ErrorPayload(Swift::ErrorPayload::Conflict)));
return presence;
}
else if (flag & PARTICIPANT_FLAG_NOT_AUTHORIZED) {
delete p;
presence->setType(Swift::Presence::Error);
presence->addPayload(boost::shared_ptr<Swift::Payload>(new Swift::MUCPayload()));
presence->addPayload(boost::shared_ptr<Swift::Payload>(new Swift::ErrorPayload(Swift::ErrorPayload::NotAuthorized, Swift::ErrorPayload::Auth)));
return presence;
}
else {
Swift::MUCUserPayload::StatusCode c;
c.code = 110;
p->addStatusCode(c);
m_sentInitialPresence = true;
}
}
Swift::MUCItem item;
item.affiliation = Swift::MUCOccupant::Member;
item.role = Swift::MUCOccupant::Participant;
if (flag & Moderator) {
if (flag & PARTICIPANT_FLAG_MODERATOR) {
item.affiliation = Swift::MUCOccupant::Admin;
item.role = Swift::MUCOccupant::Moderator;
}
@ -249,13 +286,13 @@ Swift::Presence::ref Conversation::generatePresence(const std::string &nick, int
p->addStatusCode(c);
presence->setType(Swift::Presence::Unavailable);
}
p->addItem(item);
presence->addPayload(boost::shared_ptr<Swift::Payload>(p));
return presence;
}
void Conversation::handleParticipantChanged(const std::string &nick, int flag, int status, const std::string &statusMessage, const std::string &newname) {
void Conversation::handleParticipantChanged(const std::string &nick, Conversation::ParticipantFlag flag, int status, const std::string &statusMessage, const std::string &newname) {
Swift::Presence::ref presence = generatePresence(nick, flag, status, statusMessage, newname);
if (presence->getType() == Swift::Presence::Unavailable) {

View file

@ -26,9 +26,9 @@
#include "Swiften/Disco/DiscoInfoResponder.h"
#include "Swiften/Queries/IQRouter.h"
#include "Swiften/Elements/DiscoInfo.h"
#include "Swiften/Swiften.h"
#include "transport/config.h"
#include "transport/logging.h"
#include "Swiften/Disco/CapsInfoGenerator.h"
using namespace Swift;
using namespace boost;
@ -39,28 +39,31 @@ namespace Transport {
DiscoInfoResponder::DiscoInfoResponder(Swift::IQRouter *router, Config *config) : Swift::GetResponder<DiscoInfo>(router) {
m_config = config;
m_config->onBackendConfigUpdated.connect(boost::bind(&DiscoInfoResponder::updateBuddyFeatures, this));
m_config->onBackendConfigUpdated.connect(boost::bind(&DiscoInfoResponder::updateFeatures, this));
m_buddyInfo = NULL;
m_transportInfo.addIdentity(DiscoInfo::Identity(CONFIG_STRING(m_config, "identity.name"),
CONFIG_STRING(m_config, "identity.category"),
CONFIG_STRING(m_config, "identity.type")));
std::list<std::string> features;
features.push_back("jabber:iq:register");
features.push_back("jabber:iq:gateway");
features.push_back("jabber:iq:private");
features.push_back("http://jabber.org/protocol/disco#info");
features.push_back("http://jabber.org/protocol/commands");
setTransportFeatures(features);
updateBuddyFeatures();
updateFeatures();
}
DiscoInfoResponder::~DiscoInfoResponder() {
delete m_buddyInfo;
}
void DiscoInfoResponder::updateBuddyFeatures() {
void DiscoInfoResponder::updateFeatures() {
std::list<std::string> features2;
features2.push_back("jabber:iq:register");
features2.push_back("jabber:iq:gateway");
features2.push_back("jabber:iq:private");
features2.push_back("http://jabber.org/protocol/disco#info");
features2.push_back("http://jabber.org/protocol/commands");
if (CONFIG_BOOL_DEFAULTED(m_config, "features.muc", false)) {
features2.push_back("http://jabber.org/protocol/muc");
}
setTransportFeatures(features2);
std::list<std::string> features;
features.push_back("http://jabber.org/protocol/disco#items");
features.push_back("http://jabber.org/protocol/disco#info");

View file

@ -21,7 +21,8 @@
#pragma once
#include <vector>
#include "Swiften/Swiften.h"
#include <list>
#include <boost/signal.hpp>
#include "Swiften/Queries/GetResponder.h"
#include "Swiften/Elements/DiscoInfo.h"
#include "Swiften/Elements/CapsInfo.h"
@ -51,7 +52,7 @@ class DiscoInfoResponder : public Swift::GetResponder<Swift::DiscoInfo> {
private:
virtual bool handleGetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr<Swift::DiscoInfo> payload);
void updateBuddyFeatures();
void updateFeatures();
Swift::DiscoInfo m_transportInfo;
Swift::DiscoInfo *m_buddyInfo;

View file

@ -23,7 +23,6 @@
#include <iostream>
#include <boost/bind.hpp>
#include "Swiften/Queries/IQRouter.h"
#include "Swiften/Swiften.h"
#include "transport/transport.h"
#include "transport/logging.h"
#include "discoinforesponder.h"

View file

@ -24,6 +24,7 @@
#include "transport/user.h"
#include "transport/buddy.h"
#include "transport/logging.h"
#include "Swiften/Network/ConnectionServerFactory.h"
namespace Transport {

View file

@ -24,7 +24,6 @@
#include <boost/bind.hpp>
#include "Swiften/Queries/IQRouter.h"
#include "Swiften/Elements/RawXMLPayload.h"
#include "Swiften/Swiften.h"
#include "transport/usermanager.h"
#include "transport/user.h"
#include "transport/transport.h"
@ -53,7 +52,14 @@ bool GatewayResponder::handleGetRequest(const Swift::JID& from, const Swift::JID
bool GatewayResponder::handleSetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr<Swift::GatewayPayload> payload) {
std::string prompt = payload->getPrompt();
std::string escaped = Swift::JID::getEscapedNode(prompt);
if (!CONFIG_BOOL_DEFAULTED(m_userManager->getComponent()->getConfig(), "service.jid_escaping", true)) {
escaped = prompt;
if (escaped.find_last_of("@") != std::string::npos) {
escaped.replace(escaped.find_last_of("@"), 1, "%");
}
}
// This code is here to workaround Gajim (and probably other clients bug too) bug
// https://trac.gajim.org/ticket/7277
if (prompt.find("\\40") != std::string::npos) {

View file

@ -136,30 +136,38 @@ static void initLogging(Config *config, std::string key, bool only_create_dir =
p.setProperty("id", id);
#endif
std::string dir;
std::vector<std::string> dirs;
BOOST_FOREACH(const log4cxx::LogString &prop, p.propertyNames()) {
// if (boost::ends_with(prop, ".File")) {
if (boost::ends_with(prop, ".File")) {
std::string dir;
log4cxx::helpers::Transcoder::encode(p.get(prop), dir);
boost::replace_all(dir, "${jid}", jid);
boost::replace_all(dir, "${pid}", pid);
boost::replace_all(dir, "${id}", id);
break;
// }
dirs.push_back(dir);
}
}
mode_t old_cmask;
if (!dir.empty()) {
// create directories
// create directories
#ifndef WIN32
old_cmask = umask(0007);
old_cmask = umask(0007);
#endif
try {
Transport::Util::createDirectories(config, boost::filesystem::path(dir).parent_path());
}
catch (const boost::filesystem::filesystem_error &e) {
std::cerr << "Can't create logging directory directory " << boost::filesystem::path(dir).parent_path().string() << ": " << e.what() << ".\n";
BOOST_FOREACH(std::string &dir, dirs) {
if (!dir.empty()) {
try {
Transport::Util::createDirectories(config, boost::filesystem::path(dir).parent_path());
}
catch (const boost::filesystem::filesystem_error &e) {
std::cerr << "Can't create logging directory directory " << boost::filesystem::path(dir).parent_path().string() << ": " << e.what() << ".\n";
}
}
}
#ifndef WIN32
umask(old_cmask);
#endif
if (only_create_dir) {
return;
}
@ -168,24 +176,20 @@ static void initLogging(Config *config, std::string key, bool only_create_dir =
// Change owner of main log file
#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";
BOOST_FOREACH(std::string &dir, dirs) {
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);
}
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
#ifndef WIN32
if (!dir.empty()) {
umask(old_cmask);
}
#endif
}
}

View file

@ -28,20 +28,41 @@
#include <boost/lexical_cast.hpp>
#ifndef WIN32
#include <sys/param.h>
#else
#include <windows.h>
#include <psapi.h>
#endif
#ifdef BSD
#ifdef __MACH__
#include <mach/mach.h>
#elif BSD
#include <sys/types.h>
#include <sys/sysctl.h>
#include <sys/param.h>
#include <sys/sysctl.h>
#include <sys/user.h>
#endif
namespace Transport {
#ifndef WIN32
#ifdef BSD
#ifdef __MACH__
void process_mem_usage(double& vm_usage, double& resident_set, pid_t pid) {
struct task_basic_info t_info;
mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT;
if (KERN_SUCCESS != task_info(mach_task_self(),
TASK_BASIC_INFO, (task_info_t)&t_info,
&t_info_count)) {
vm_usage = 0;
resident_set = 0;
return;
}
vm_usage = t_info.virtual_size;
resident_set = t_info.resident_size;
}
#elif BSD
void process_mem_usage(double& vm_usage, double& resident_set, pid_t pid) {
int mib[4];
size_t size;
@ -120,6 +141,15 @@ void process_mem_usage(double& shared, double& resident_set, pid_t pid) {
resident_set = rss * page_size_kb;
}
#endif /* else BSD */
#else
#define PSAPI_VERSION 1
#define pid_t void*
void process_mem_usage(double& shared, double& resident_set, pid_t pid) {
PROCESS_MEMORY_COUNTERS_EX pmc;
GetProcessMemoryInfo(GetCurrentProcess(), (PROCESS_MEMORY_COUNTERS*)&pmc, sizeof(pmc));
shared = (double)pmc.PrivateUsage;
resident_set = (double)pmc.WorkingSetSize;
}
#endif /* WIN32 */
}

View file

@ -34,18 +34,24 @@
#include "transport/logging.h"
#include "transport/admininterface.h"
#include "blockresponder.h"
#include "Swiften/Swiften.h"
#include "Swiften/Server/ServerStanzaChannel.h"
#include "Swiften/Elements/StreamError.h"
#include "Swiften/Network/BoostConnectionServer.h"
#include "Swiften/Network/ConnectionServerFactory.h"
#include "Swiften/Elements/AttentionPayload.h"
#include "Swiften/Elements/XHTMLIMPayload.h"
#include "Swiften/Elements/Delay.h"
#include "Swiften/Elements/DeliveryReceipt.h"
#include "Swiften/Elements/DeliveryReceiptRequest.h"
#include "Swiften/Elements/InvisiblePayload.h"
#include "Swiften/Elements/SpectrumErrorPayload.h"
#include "transport/protocol.pb.h"
#include "transport/util.h"
#include "transport/discoitemsresponder.h"
#include "boost/date_time/posix_time/posix_time.hpp"
#include "boost/signal.hpp"
#include "utf8.h"
#include <Swiften/FileTransfer/ReadBytestream.h>
@ -263,11 +269,16 @@ NetworkPluginServer::NetworkPluginServer(Component *component, Config *config, U
m_adminInterface = NULL;
m_startingBackend = false;
m_lastLogin = 0;
m_xmppParser = new Swift::XMPPParser(this, &m_collection, component->getNetworkFactories()->getXMLParserFactory());
m_xmppParser->parse("<stream:stream xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' to='localhost' version='1.0'>");
m_serializer = new Swift::XMPPSerializer(&m_collection2, Swift::ClientStreamType);
m_discoItemsResponder = discoItemsResponder;
m_component->m_factory = new NetworkFactory(this);
m_userManager->onUserCreated.connect(boost::bind(&NetworkPluginServer::handleUserCreated, this, _1));
m_userManager->onUserDestroyed.connect(boost::bind(&NetworkPluginServer::handleUserDestroyed, this, _1));
m_component->onRawIQReceived.connect(boost::bind(&NetworkPluginServer::handleRawIQReceived, this, _1));
m_pingTimer = component->getNetworkFactories()->getTimerFactory()->createTimer(20000);
m_pingTimer->onTick.connect(boost::bind(&NetworkPluginServer::pingTimeout, this));
m_pingTimer->start();
@ -346,6 +357,10 @@ void NetworkPluginServer::start() {
}
else {
LOG4CXX_ERROR(logger, "Backend can not be started, exit_code=" << WEXITSTATUS(status) << ", possible error: " << strerror(WEXITSTATUS(status)));
if (WEXITSTATUS(status) == ENOENT) {
LOG4CXX_ERROR(logger, "This usually means the path to backend executable defined in config file as '[service] backend=\"...\"' is wrong or the executable does not exists.");
}
}
LOG4CXX_ERROR(logger, "Check backend log for more details");
continue;
@ -619,7 +634,7 @@ void NetworkPluginServer::handleParticipantChangedPayload(const std::string &dat
return;
}
conv->handleParticipantChanged(payload.nickname(), payload.flag(), payload.status(), payload.statusmessage(), payload.newname());
conv->handleParticipantChanged(payload.nickname(), (Conversation::ParticipantFlag) payload.flag(), payload.status(), payload.statusmessage(), payload.newname());
}
void NetworkPluginServer::handleRoomChangedPayload(const std::string &data) {
@ -680,7 +695,6 @@ void NetworkPluginServer::handleConvMessagePayload(const std::string &data, bool
msg->addPayload(delay);
}
NetworkConversation *conv = (NetworkConversation *) user->getConversationManager()->getConversation(payload.buddyname());
// We can't create Conversation for payload with nickname, because this means the message is from room,
@ -689,6 +703,19 @@ void NetworkPluginServer::handleConvMessagePayload(const std::string &data, bool
return;
}
if (conv && payload.pm()) {
conv = (NetworkConversation *) user->getConversationManager()->getConversation(payload.buddyname() + "/" + payload.nickname());
if (!conv) {
conv = new NetworkConversation(user->getConversationManager(), payload.nickname());
std::string name = payload.buddyname();
conv->setRoom(name);
conv->setNickname(payload.buddyname() + "/" + payload.nickname());
user->getConversationManager()->addConversation(conv);
conv->onMessageToSend.connect(boost::bind(&NetworkPluginServer::handleMessageReceived, this, _1, _2));
}
}
// Create new Conversation if it does not exist
if (!conv) {
conv = new NetworkConversation(user->getConversationManager(), payload.buddyname());
@ -965,6 +992,133 @@ void NetworkPluginServer::handleRoomListPayload(const std::string &data) {
}
}
void NetworkPluginServer::handleElement(boost::shared_ptr<Swift::Element> element) {
boost::shared_ptr<Swift::Stanza> stanza = boost::dynamic_pointer_cast<Swift::Stanza>(element);
if (!stanza) {
return;
}
User *user = m_userManager->getUser(stanza->getTo().toBare());
if (!user)
return;
Swift::JID originalJID = stanza->getFrom();
LocalBuddy *buddy = (LocalBuddy *) user->getRosterManager()->getBuddy(stanza->getFrom().toBare());
if (buddy) {
const Swift::JID &jid = buddy->getJID();
if (stanza->getFrom().getResource().empty()) {
stanza->setFrom(Swift::JID(jid.getNode(), jid.getDomain()));
}
else {
stanza->setFrom(Swift::JID(jid.getNode(), jid.getDomain(), stanza->getFrom().getResource()));
}
}
else {
std::string name = stanza->getFrom().toBare();
if (CONFIG_BOOL_DEFAULTED(m_config, "service.jid_escaping", true)) {
name = Swift::JID::getEscapedNode(name);
}
else {
if (name.find_last_of("@") != std::string::npos) {
name.replace(name.find_last_of("@"), 1, "%");
}
}
if (stanza->getFrom().getResource().empty()) {
stanza->setFrom(Swift::JID(name, m_component->getJID().toString()));
}
else {
stanza->setFrom(Swift::JID(name, m_component->getJID().toString(), stanza->getFrom().getResource()));
}
}
boost::shared_ptr<Swift::Message> message = boost::dynamic_pointer_cast<Swift::Message>(stanza);
if (message) {
NetworkConversation *conv = (NetworkConversation *) user->getConversationManager()->getConversation(originalJID.toBare());
if (conv) {
conv->handleRawMessage(message);
return;
}
m_component->getStanzaChannel()->sendMessage(message);
return;
}
boost::shared_ptr<Swift::Presence> presence = boost::dynamic_pointer_cast<Swift::Presence>(stanza);
if (presence) {
m_component->getStanzaChannel()->sendPresence(presence);
if (buddy) {
buddy->m_statusMessage = presence->getStatus();
buddy->m_status = Swift::StatusShow(presence->getShow());
}
return;
}
boost::shared_ptr<Swift::IQ> iq = boost::dynamic_pointer_cast<Swift::IQ>(stanza);
if (iq) {
if (m_id2resource.find(stanza->getTo().toBare().toString() + stanza->getID()) != m_id2resource.end()) {
iq->setTo(Swift::JID(iq->getTo().getNode(), iq->getTo().getDomain(), m_id2resource[stanza->getTo().toBare().toString() + stanza->getID()]));
m_id2resource.erase(stanza->getTo().toBare().toString() + stanza->getID());
}
m_component->getIQRouter()->sendIQ(iq);
return;
}
}
void NetworkPluginServer::handleRawXML(const std::string &xml) {
m_xmppParser->parse(xml);
}
void NetworkPluginServer::handleRawPresenceReceived(boost::shared_ptr<Swift::Presence> presence) {
User *user = m_userManager->getUser(presence->getFrom().toBare());
if (!user)
return;
Backend *c = (Backend *) user->getData();
if (!c) {
return;
}
Swift::JID legacyname = Swift::JID(Buddy::JIDToLegacyName(presence->getTo()));
if (!presence->getTo().getResource().empty()) {
presence->setTo(Swift::JID(legacyname.getNode(), legacyname.getDomain(), presence->getTo().getResource()));
}
else {
presence->setTo(Swift::JID(legacyname.getNode(), legacyname.getDomain()));
}
std::string xml = safeByteArrayToString(m_serializer->serializeElement(presence));
WRAP(xml, pbnetwork::WrapperMessage_Type_TYPE_RAW_XML);
send(c->connection, xml);
}
void NetworkPluginServer::handleRawIQReceived(boost::shared_ptr<Swift::IQ> iq) {
User *user = m_userManager->getUser(iq->getFrom().toBare());
if (!user)
return;
Backend *c = (Backend *) user->getData();
if (!c) {
return;
}
if (iq->getType() == Swift::IQ::Get) {
m_id2resource[iq->getFrom().toBare().toString() + iq->getID()] = iq->getFrom().getResource();
}
Swift::JID legacyname = Swift::JID(Buddy::JIDToLegacyName(iq->getTo()));
if (!iq->getTo().getResource().empty()) {
iq->setTo(Swift::JID(legacyname.getNode(), legacyname.getDomain(), iq->getTo().getResource()));
}
else {
iq->setTo(Swift::JID(legacyname.getNode(), legacyname.getDomain()));
}
std::string xml = safeByteArrayToString(m_serializer->serializeElement(iq));
WRAP(xml, pbnetwork::WrapperMessage_Type_TYPE_RAW_XML);
send(c->connection, xml);
}
void NetworkPluginServer::handleDataRead(Backend *c, boost::shared_ptr<Swift::SafeByteArray> data) {
// Append data to buffer
c->data.insert(c->data.end(), data->begin(), data->end());
@ -1076,6 +1230,9 @@ void NetworkPluginServer::handleDataRead(Backend *c, boost::shared_ptr<Swift::Sa
case pbnetwork::WrapperMessage_Type_TYPE_CONV_MESSAGE_ACK:
handleConvMessageAckPayload(wrapper.payload());
break;
case pbnetwork::WrapperMessage_Type_TYPE_RAW_XML:
handleRawXML(wrapper.payload());
break;
default:
return;
}
@ -1245,8 +1402,12 @@ void NetworkPluginServer::handleUserCreated(User *user) {
// Don't forget to disconnect these in handleUserDestroyed!!!
user->onReadyToConnect.connect(boost::bind(&NetworkPluginServer::handleUserReadyToConnect, this, user));
user->onPresenceChanged.connect(boost::bind(&NetworkPluginServer::handleUserPresenceChanged, this, user, _1));
user->onRawPresenceReceived.connect(boost::bind(&NetworkPluginServer::handleRawPresenceReceived, this, _1));
user->onRoomJoined.connect(boost::bind(&NetworkPluginServer::handleRoomJoined, this, user, _1, _2, _3, _4));
user->onRoomLeft.connect(boost::bind(&NetworkPluginServer::handleRoomLeft, this, user, _1));
user->getRosterManager()->onBuddyAdded.connect(boost::bind(&NetworkPluginServer::handleUserBuddyAdded, this, user, _1));
user->getRosterManager()->onBuddyRemoved.connect(boost::bind(&NetworkPluginServer::handleUserBuddyRemoved, this, user, _1));
}
void NetworkPluginServer::handleUserReadyToConnect(User *user) {
@ -1349,9 +1510,13 @@ void NetworkPluginServer::handleUserDestroyed(User *user) {
user->onReadyToConnect.disconnect(boost::bind(&NetworkPluginServer::handleUserReadyToConnect, this, user));
user->onPresenceChanged.disconnect(boost::bind(&NetworkPluginServer::handleUserPresenceChanged, this, user, _1));
user->onRawPresenceReceived.disconnect(boost::bind(&NetworkPluginServer::handleRawPresenceReceived, this, _1));
user->onRoomJoined.disconnect(boost::bind(&NetworkPluginServer::handleRoomJoined, this, user, _1, _2, _3, _4));
user->onRoomLeft.disconnect(boost::bind(&NetworkPluginServer::handleRoomLeft, this, user, _1));
user->getRosterManager()->onBuddyAdded.disconnect(boost::bind(&NetworkPluginServer::handleUserBuddyAdded, this, user, _1));
user->getRosterManager()->onBuddyRemoved.disconnect(boost::bind(&NetworkPluginServer::handleUserBuddyRemoved, this, user, _1));
pbnetwork::Logout logout;
logout.set_user(user->getJID().toBare());
logout.set_legacyname(userInfo.uin);
@ -1378,6 +1543,25 @@ void NetworkPluginServer::handleUserDestroyed(User *user) {
void NetworkPluginServer::handleMessageReceived(NetworkConversation *conv, boost::shared_ptr<Swift::Message> &msg) {
conv->getConversationManager()->getUser()->updateLastActivity();
if (CONFIG_BOOL_DEFAULTED(m_config, "features.rawxml", false)) {
Backend *c = (Backend *) conv->getConversationManager()->getUser()->getData();
if (!c) {
return;
}
Swift::JID legacyname = Swift::JID(Buddy::JIDToLegacyName(msg->getTo()));
if (!msg->getTo().getResource().empty()) {
msg->setTo(Swift::JID(legacyname.getNode(), legacyname.getDomain(), msg->getTo().getResource()));
}
else {
msg->setTo(Swift::JID(legacyname.getNode(), legacyname.getDomain()));
}
std::string xml = safeByteArrayToString(m_serializer->serializeElement(msg));
WRAP(xml, pbnetwork::WrapperMessage_Type_TYPE_RAW_XML);
send(c->connection, xml);
return;
}
boost::shared_ptr<Swift::ChatState> statePayload = msg->getPayload<Swift::ChatState>();
if (statePayload) {
pbnetwork::WrapperMessage_Type type = pbnetwork::WrapperMessage_Type_TYPE_BUDDY_CHANGED;
@ -1532,6 +1716,32 @@ void NetworkPluginServer::handleBuddyAdded(Buddy *buddy, const Swift::RosterItem
handleBuddyUpdated(buddy, item);
}
void NetworkPluginServer::handleUserBuddyAdded(User *user, Buddy *b) {
pbnetwork::Buddy buddy;
buddy.set_username(user->getJID().toBare());
buddy.set_buddyname(b->getName());
buddy.set_alias(b->getAlias());
BOOST_FOREACH(const std::string &g, b->getGroups()) {
buddy.add_group(g);
}
buddy.set_status(pbnetwork::STATUS_NONE);
std::string message;
buddy.SerializeToString(&message);
WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_BUDDY_CHANGED);
Backend *c = (Backend *) user->getData();
if (!c) {
return;
}
send(c->connection, message);
}
void NetworkPluginServer::handleUserBuddyRemoved(User *user, Buddy *b) {
handleBuddyRemoved(b);
}
void NetworkPluginServer::handleBlockToggled(Buddy *b) {
User *user = b->getRosterManager()->getUser();

View file

@ -19,7 +19,7 @@
*/
#include "transport/presenceoracle.h"
#include "Swiften/Swiften.h"
#include "Swiften/Elements/MUCPayload.h"
#include <boost/bind.hpp>
@ -52,9 +52,8 @@ void PresenceOracle::clearPresences(const Swift::JID& bareJID) {
void PresenceOracle::handleIncomingPresence(Presence::ref presence) {
// ignore presences for some contact, we're checking only presences for the transport itself here.
bool isMUC = presence->getPayload<MUCPayload>() != NULL || *presence->getTo().getNode().c_str() == '#';
// filter out login/logout presence spam
if (!presence->getTo().getNode().empty() && isMUC == false)
if (!presence->getTo().getNode().empty())
return;
JID bareJID(presence->getFrom().toBare());
@ -62,29 +61,27 @@ void PresenceOracle::handleIncomingPresence(Presence::ref presence) {
}
else {
Presence::ref passedPresence = presence;
if (!isMUC) {
if (presence->getType() == Presence::Unsubscribe || presence->getType() == Presence::Unsubscribed) {
/* 3921bis says that we don't follow up with an unavailable, so simulate this ourselves */
passedPresence = Presence::ref(new Presence());
passedPresence->setType(Presence::Unavailable);
passedPresence->setFrom(bareJID);
passedPresence->setStatus(presence->getStatus());
}
std::map<JID, boost::shared_ptr<Presence> > jidMap = entries_[bareJID];
if (passedPresence->getFrom().isBare() && presence->getType() == Presence::Unavailable) {
/* Have a bare-JID only presence of offline */
jidMap.clear();
} else if (passedPresence->getType() == Presence::Available) {
/* Don't have a bare-JID only offline presence once there are available presences */
jidMap.erase(bareJID);
}
if (passedPresence->getType() == Presence::Unavailable && jidMap.size() > 1) {
jidMap.erase(passedPresence->getFrom());
} else {
jidMap[passedPresence->getFrom()] = passedPresence;
}
entries_[bareJID] = jidMap;
if (presence->getType() == Presence::Unsubscribe || presence->getType() == Presence::Unsubscribed) {
/* 3921bis says that we don't follow up with an unavailable, so simulate this ourselves */
passedPresence = Presence::ref(new Presence());
passedPresence->setType(Presence::Unavailable);
passedPresence->setFrom(bareJID);
passedPresence->setStatus(presence->getStatus());
}
std::map<JID, boost::shared_ptr<Presence> > jidMap = entries_[bareJID];
if (passedPresence->getFrom().isBare() && presence->getType() == Presence::Unavailable) {
/* Have a bare-JID only presence of offline */
jidMap.clear();
} else if (passedPresence->getType() == Presence::Available) {
/* Don't have a bare-JID only offline presence once there are available presences */
jidMap.erase(bareJID);
}
if (passedPresence->getType() == Presence::Unavailable && jidMap.size() > 1) {
jidMap.erase(passedPresence->getFrom());
} else {
jidMap[passedPresence->getFrom()] = passedPresence;
}
entries_[bareJID] = jidMap;
onPresenceChange(passedPresence);
}
}

View file

@ -30,7 +30,10 @@
#include "Swiften/Elements/RosterPayload.h"
#include "Swiften/Elements/RosterItemPayload.h"
#include "Swiften/Elements/RosterItemExchangePayload.h"
#include "Swiften/Elements/Nickname.h"
#include "Swiften/Queries/IQRouter.h"
#include <boost/foreach.hpp>
#include <boost/make_shared.hpp>
#include <map>
#include <iterator>
@ -151,12 +154,7 @@ void RosterManager::sendBuddyRosterPush(Buddy *buddy) {
Swift::RosterPayload::ref payload = Swift::RosterPayload::ref(new Swift::RosterPayload());
Swift::RosterItemPayload item;
item.setJID(buddy->getJID().toBare());
if (buddy->getAlias().empty()) {
item.setName(buddy->getJID().toBare().toString());
}
else {
item.setName(buddy->getAlias());
}
item.setName(buddy->getAlias());
item.setGroups(buddy->getGroups());
item.setSubscription(Swift::RosterItemPayload::Both);
@ -280,6 +278,15 @@ void RosterManager::handleRemoteRosterResponse(boost::shared_ptr<Swift::RosterPa
LOG4CXX_INFO(logger, m_user->getJID().toString() << ": This server supports remote roster protoXEP");
m_supportRemoteRoster = true;
//If we receive empty RosterPayload on login (not register) initiate full RosterPush
if(!m_buddies.empty() && payload->getItems().empty()){
LOG4CXX_INFO(logger, "Received empty Roster upon login. Pushing full Roster.");
for(std::map<std::string, Buddy *, std::less<std::string>, boost::pool_allocator< std::pair<std::string, Buddy *> > >::const_iterator c_it = m_buddies.begin();
c_it != m_buddies.end(); c_it++) {
sendBuddyRosterPush(c_it->second);
}
}
return;
BOOST_FOREACH(const Swift::RosterItemPayload &item, payload->getItems()) {
@ -490,6 +497,7 @@ void RosterManager::handleSubscription(Swift::Presence::ref presence) {
buddy = m_component->getFactory()->createBuddy(this, buddyInfo);
setBuddy(buddy);
onBuddyAdded(buddy);
LOG4CXX_INFO(logger, m_user->getJID().toString() << ": Subscription received for new buddy " << buddyInfo.legacyName << " => adding to legacy network");
response->setType(Swift::Presence::Subscribed);
break;
case Swift::Presence::Unsubscribe:

View file

@ -23,7 +23,6 @@
#include <iostream>
#include <boost/bind.hpp>
#include "Swiften/Queries/IQRouter.h"
#include "Swiften/Swiften.h"
#include "transport/user.h"
#include "transport/usermanager.h"
#include "transport/rostermanager.h"

View file

@ -24,6 +24,8 @@
#include "transport/storagebackend.h"
#include "transport/logging.h"
#include "Swiften/Network/NetworkFactories.h"
DEFINE_LOGGER(logger, "RosterStorage");
namespace Transport {

View file

@ -55,6 +55,7 @@
#define RESET_GET_COUNTER(STATEMENT) STATEMENT##_id_get = 0;
#define GET_INT(STATEMENT) sqlite3_column_int(STATEMENT, STATEMENT##_id_get++)
#define GET_STR(STATEMENT) (const char *) sqlite3_column_text(STATEMENT, STATEMENT##_id_get++)
#define GET_BLOB(STATEMENT) (const void *) sqlite3_column_blob(STATEMENT, STATEMENT##_id_get++)
#define EXECUTE_STATEMENT(STATEMENT, NAME) if(sqlite3_step(STATEMENT) != SQLITE_DONE) {\
LOG4CXX_ERROR(logger, NAME<< (sqlite3_errmsg(m_db) == NULL ? "" : sqlite3_errmsg(m_db)));\
}

View file

@ -24,7 +24,6 @@
#include <boost/bind.hpp>
#include "Swiften/Queries/IQRouter.h"
#include "transport/BlockPayload.h"
#include "Swiften/Swiften.h"
#include "transport/usermanager.h"
#include "transport/user.h"
#include "transport/buddy.h"

View file

@ -4,8 +4,8 @@
#include "transport/sqlite3backend.h"
#include "transport/mysqlbackend.h"
#include "transport/pqxxbackend.h"
#include "Swiften/StringCodecs/Base64.h"
#include "Swiften/Swiften.h"
namespace Transport {

View file

@ -24,7 +24,9 @@
#include <boost/bind.hpp>
#include "Swiften/Queries/IQRouter.h"
#include "Swiften/Elements/RawXMLPayload.h"
#include "Swiften/Swiften.h"
#include "Swiften/Elements/Storage.h"
#include "Swiften/Elements/Storage.h"
#include "Swiften/Serializer/PayloadSerializers/StorageSerializer.h"
#include "transport/usermanager.h"
#include "transport/user.h"
#include "transport/logging.h"

View file

@ -21,9 +21,9 @@
#pragma once
#include <vector>
#include "Swiften/Swiften.h"
#include "Swiften/Queries/Responder.h"
#include "Swiften/Elements/RosterPayload.h"
#include "Swiften/Elements/PrivateStorage.h"
namespace Transport {

View file

@ -35,6 +35,8 @@ class ConversationManagerTest : public CPPUNIT_NS :: TestFixture, public BasicTe
CPPUNIT_TEST(handleParticipantChangedTwoResources);
CPPUNIT_TEST(handlePMFromXMPP);
CPPUNIT_TEST(handleGroupchatRemoved);
CPPUNIT_TEST(handleNicknameConflict);
CPPUNIT_TEST(handleNotAuthorized);
CPPUNIT_TEST_SUITE_END();
public:
@ -115,7 +117,7 @@ class ConversationManagerTest : public CPPUNIT_NS :: TestFixture, public BasicTe
CPPUNIT_ASSERT_EQUAL(0, (int) received.size());
// this user presence - status code 110
conv->handleParticipantChanged("nickname", 1, Swift::StatusShow::Away, "my status message");
conv->handleParticipantChanged("nickname", Conversation::PARTICIPANT_FLAG_MODERATOR, Swift::StatusShow::Away, "my status message");
loop->processEvents();
CPPUNIT_ASSERT_EQUAL(2, (int) received.size());
@ -343,17 +345,17 @@ class ConversationManagerTest : public CPPUNIT_NS :: TestFixture, public BasicTe
injectPresence(response);
loop->processEvents();
CPPUNIT_ASSERT_EQUAL(4, (int) received.size());
CPPUNIT_ASSERT_EQUAL(3, (int) received.size());
CPPUNIT_ASSERT(dynamic_cast<Swift::Message *>(getStanza(received[1])));
CPPUNIT_ASSERT_EQUAL(std::string("hi there!"), dynamic_cast<Swift::Message *>(getStanza(received[1]))->getBody());
CPPUNIT_ASSERT_EQUAL(std::string("user@localhost/resource"), dynamic_cast<Swift::Message *>(getStanza(received[1]))->getTo().toString());
CPPUNIT_ASSERT_EQUAL(std::string("#room@localhost/anotheruser"), dynamic_cast<Swift::Message *>(getStanza(received[1]))->getFrom().toString());
CPPUNIT_ASSERT(dynamic_cast<Swift::Message *>(getStanza(received[2])));
CPPUNIT_ASSERT_EQUAL(std::string("hi there!"), dynamic_cast<Swift::Message *>(getStanza(received[2]))->getBody());
CPPUNIT_ASSERT_EQUAL(std::string("hi there2!"), dynamic_cast<Swift::Message *>(getStanza(received[2]))->getBody());
CPPUNIT_ASSERT_EQUAL(std::string("user@localhost/resource"), dynamic_cast<Swift::Message *>(getStanza(received[2]))->getTo().toString());
CPPUNIT_ASSERT_EQUAL(std::string("#room@localhost/anotheruser"), dynamic_cast<Swift::Message *>(getStanza(received[2]))->getFrom().toString());
CPPUNIT_ASSERT(dynamic_cast<Swift::Message *>(getStanza(received[3])));
CPPUNIT_ASSERT_EQUAL(std::string("hi there2!"), dynamic_cast<Swift::Message *>(getStanza(received[3]))->getBody());
CPPUNIT_ASSERT_EQUAL(std::string("user@localhost/resource"), dynamic_cast<Swift::Message *>(getStanza(received[3]))->getTo().toString());
CPPUNIT_ASSERT_EQUAL(std::string("#room@localhost/anotheruser"), dynamic_cast<Swift::Message *>(getStanza(received[3]))->getFrom().toString());
}
void handleGroupchatMessagesBouncerLeave() {
@ -408,17 +410,17 @@ class ConversationManagerTest : public CPPUNIT_NS :: TestFixture, public BasicTe
injectPresence(response);
loop->processEvents();
CPPUNIT_ASSERT_EQUAL(4, (int) received.size());
CPPUNIT_ASSERT_EQUAL(3, (int) received.size());
CPPUNIT_ASSERT(dynamic_cast<Swift::Message *>(getStanza(received[1])));
CPPUNIT_ASSERT_EQUAL(std::string("hi there!"), dynamic_cast<Swift::Message *>(getStanza(received[1]))->getBody());
CPPUNIT_ASSERT_EQUAL(std::string("user@localhost/resource"), dynamic_cast<Swift::Message *>(getStanza(received[1]))->getTo().toString());
CPPUNIT_ASSERT_EQUAL(std::string("#room@localhost/anotheruser"), dynamic_cast<Swift::Message *>(getStanza(received[1]))->getFrom().toString());
CPPUNIT_ASSERT(dynamic_cast<Swift::Message *>(getStanza(received[2])));
CPPUNIT_ASSERT_EQUAL(std::string("hi there!"), dynamic_cast<Swift::Message *>(getStanza(received[2]))->getBody());
CPPUNIT_ASSERT_EQUAL(std::string("hi there2!"), dynamic_cast<Swift::Message *>(getStanza(received[2]))->getBody());
CPPUNIT_ASSERT_EQUAL(std::string("user@localhost/resource"), dynamic_cast<Swift::Message *>(getStanza(received[2]))->getTo().toString());
CPPUNIT_ASSERT_EQUAL(std::string("#room@localhost/anotheruser"), dynamic_cast<Swift::Message *>(getStanza(received[2]))->getFrom().toString());
CPPUNIT_ASSERT(dynamic_cast<Swift::Message *>(getStanza(received[3])));
CPPUNIT_ASSERT_EQUAL(std::string("hi there2!"), dynamic_cast<Swift::Message *>(getStanza(received[3]))->getBody());
CPPUNIT_ASSERT_EQUAL(std::string("user@localhost/resource"), dynamic_cast<Swift::Message *>(getStanza(received[3]))->getTo().toString());
CPPUNIT_ASSERT_EQUAL(std::string("#room@localhost/anotheruser"), dynamic_cast<Swift::Message *>(getStanza(received[3]))->getFrom().toString());
}
void handleGroupchatMessagesTwoResources() {
@ -472,7 +474,7 @@ class ConversationManagerTest : public CPPUNIT_NS :: TestFixture, public BasicTe
conv->addJID("user@localhost/resource");
// normal presence
conv->handleParticipantChanged("anotheruser", 0, Swift::StatusShow::Away, "my status message");
conv->handleParticipantChanged("anotheruser", Conversation::PARTICIPANT_FLAG_NONE, Swift::StatusShow::Away, "my status message");
loop->processEvents();
CPPUNIT_ASSERT_EQUAL(1, (int) received.size());
@ -487,7 +489,7 @@ class ConversationManagerTest : public CPPUNIT_NS :: TestFixture, public BasicTe
received.clear();
// this user presence - status code 110
conv->handleParticipantChanged("nickname", 1, Swift::StatusShow::Away, "my status message");
conv->handleParticipantChanged("nickname", Conversation::PARTICIPANT_FLAG_MODERATOR, Swift::StatusShow::Away, "my status message");
loop->processEvents();
CPPUNIT_ASSERT_EQUAL(1, (int) received.size());
@ -503,7 +505,7 @@ class ConversationManagerTest : public CPPUNIT_NS :: TestFixture, public BasicTe
received.clear();
// renamed - status code 303
conv->handleParticipantChanged("anotheruser", 1, Swift::StatusShow::Away, "my status message", "hanzz");
conv->handleParticipantChanged("anotheruser", Conversation::PARTICIPANT_FLAG_MODERATOR, Swift::StatusShow::Away, "my status message", "hanzz");
loop->processEvents();
CPPUNIT_ASSERT_EQUAL(2, (int) received.size());
@ -530,7 +532,7 @@ class ConversationManagerTest : public CPPUNIT_NS :: TestFixture, public BasicTe
conv->addJID("user@localhost/resource2");
// normal presence
conv->handleParticipantChanged("anotheruser", 0, Swift::StatusShow::Away, "my status message");
conv->handleParticipantChanged("anotheruser", Conversation::PARTICIPANT_FLAG_NONE, Swift::StatusShow::Away, "my status message");
loop->processEvents();
CPPUNIT_ASSERT_EQUAL(1, (int) received2.size());
@ -551,7 +553,7 @@ class ConversationManagerTest : public CPPUNIT_NS :: TestFixture, public BasicTe
conv->setNickname("nickname");
conv->setJID("user@localhost/resource");
conv->handleParticipantChanged("anotheruser", 0, Swift::StatusShow::Away, "my status message");
conv->handleParticipantChanged("anotheruser", Conversation::PARTICIPANT_FLAG_NONE, Swift::StatusShow::Away, "my status message");
loop->processEvents();
received.clear();
@ -593,6 +595,44 @@ class ConversationManagerTest : public CPPUNIT_NS :: TestFixture, public BasicTe
CPPUNIT_ASSERT_EQUAL(332, getStanza(received[0])->getPayload<Swift::MUCUserPayload>()->getStatusCodes()[0].code);
}
void handleNicknameConflict() {
User *user = userManager->getUser("user@localhost");
TestingConversation *conv = new TestingConversation(user->getConversationManager(), "#room", true);
conv->onMessageToSend.connect(boost::bind(&ConversationManagerTest::handleMessageReceived, this, _1, _2));
conv->setNickname("nickname");
conv->addJID("user@localhost/resource");
// normal presence
conv->handleParticipantChanged("nickname", Conversation::PARTICIPANT_FLAG_CONFLICT, Swift::StatusShow::Away, "my status message");
loop->processEvents();
CPPUNIT_ASSERT_EQUAL(1, (int) received.size());
CPPUNIT_ASSERT(dynamic_cast<Swift::Presence *>(getStanza(received[0])));
CPPUNIT_ASSERT_EQUAL(Swift::Presence::Error, dynamic_cast<Swift::Presence *>(getStanza(received[0]))->getType());
CPPUNIT_ASSERT(getStanza(received[0])->getPayload<Swift::ErrorPayload>());
CPPUNIT_ASSERT_EQUAL(Swift::ErrorPayload::Conflict, getStanza(received[0])->getPayload<Swift::ErrorPayload>()->getCondition());
}
void handleNotAuthorized() {
User *user = userManager->getUser("user@localhost");
TestingConversation *conv = new TestingConversation(user->getConversationManager(), "#room", true);
conv->onMessageToSend.connect(boost::bind(&ConversationManagerTest::handleMessageReceived, this, _1, _2));
conv->setNickname("nickname");
conv->addJID("user@localhost/resource");
// normal presence
conv->handleParticipantChanged("nickname", Conversation::PARTICIPANT_FLAG_NOT_AUTHORIZED, Swift::StatusShow::Away, "my status message");
loop->processEvents();
CPPUNIT_ASSERT_EQUAL(1, (int) received.size());
CPPUNIT_ASSERT(dynamic_cast<Swift::Presence *>(getStanza(received[0])));
CPPUNIT_ASSERT_EQUAL(Swift::Presence::Error, dynamic_cast<Swift::Presence *>(getStanza(received[0]))->getType());
CPPUNIT_ASSERT(getStanza(received[0])->getPayload<Swift::ErrorPayload>());
CPPUNIT_ASSERT_EQUAL(Swift::ErrorPayload::NotAuthorized, getStanza(received[0])->getPayload<Swift::ErrorPayload>()->getCondition());
}
};
CPPUNIT_TEST_SUITE_REGISTRATION (ConversationManagerTest);

View file

@ -53,6 +53,7 @@ class NetworkPluginServerTest : public CPPUNIT_NS :: TestFixture, public BasicTe
CPPUNIT_TEST(handleBuddyChangedPayloadUserContactInRoster);
CPPUNIT_TEST(handleMessageHeadline);
CPPUNIT_TEST(handleConvMessageAckPayload);
CPPUNIT_TEST(handleRawXML);
CPPUNIT_TEST(benchmarkHandleBuddyChangedPayload);
CPPUNIT_TEST_SUITE_END();
@ -196,6 +197,17 @@ class NetworkPluginServerTest : public CPPUNIT_NS :: TestFixture, public BasicTe
CPPUNIT_ASSERT_EQUAL(0, (int) received.size());
}
void handleRawXML() {
User *user = userManager->getUser("user@localhost");
std::string xml = "<presence from='buddy1@domain.tld' to='user@localhost'/>";
serv->handleRawXML(xml);
CPPUNIT_ASSERT_EQUAL(1, (int) received.size());
CPPUNIT_ASSERT(dynamic_cast<Swift::Presence *>(getStanza(received[0])));
CPPUNIT_ASSERT_EQUAL(std::string("buddy1\\40domain.tld@localhost"), dynamic_cast<Swift::Presence *>(getStanza(received[0]))->getFrom().toString());
}
void handleMessageHeadline() {
User *user = userManager->getUser("user@localhost");

View file

@ -23,6 +23,7 @@ using namespace Transport;
class RosterManagerTest : public CPPUNIT_NS :: TestFixture, public BasicTest {
CPPUNIT_TEST_SUITE(RosterManagerTest);
CPPUNIT_TEST(setBuddy);
CPPUNIT_TEST(setBuddyNoAlias);
CPPUNIT_TEST(sendCurrentPresences);
CPPUNIT_TEST(sendUnavailablePresences);
CPPUNIT_TEST(sendCurrentPresence);
@ -72,6 +73,25 @@ class RosterManagerTest : public CPPUNIT_NS :: TestFixture, public BasicTest {
}
void setBuddyNoAlias() {
User *user = userManager->getUser("user@localhost");
CPPUNIT_ASSERT(user);
std::vector<std::string> grp;
grp.push_back("group1");
LocalBuddy *buddy = new LocalBuddy(user->getRosterManager(), -1, "buddy1", "", grp, BUDDY_JID_ESCAPING);
user->getRosterManager()->setBuddy(buddy);
CPPUNIT_ASSERT_EQUAL(1, (int) received.size());
Swift::RosterPayload::ref payload1 = getStanza(received[0])->getPayload<Swift::RosterPayload>();
CPPUNIT_ASSERT(payload1);
CPPUNIT_ASSERT_EQUAL(1, (int) payload1->getItems().size());
Swift::RosterItemPayload item = payload1->getItems()[0];
CPPUNIT_ASSERT_EQUAL(std::string("buddy1"), Buddy::JIDToLegacyName(item.getJID()));
CPPUNIT_ASSERT_EQUAL(std::string(""), item.getName());
}
void setBuddy() {
add2Buddies();
CPPUNIT_ASSERT_EQUAL(4, (int) received.size());

View file

@ -125,7 +125,7 @@ class UserTest : public CPPUNIT_NS :: TestFixture, public BasicTest {
User *user = userManager->getUser("user@localhost");
Swift::Presence::ref response = Swift::Presence::create();
response->setTo("#room@localhost/hanzz");
response->setTo("room@localhost/hanzz");
response->setFrom("user@localhost/resource");
Swift::MUCPayload *payload = new Swift::MUCPayload();
@ -134,11 +134,8 @@ class UserTest : public CPPUNIT_NS :: TestFixture, public BasicTest {
injectPresence(response);
loop->processEvents();
// no presence received in server mode, just disco#info
CPPUNIT_ASSERT_EQUAL(1, (int) received.size());
CPPUNIT_ASSERT(getStanza(received[0])->getPayload<Swift::DiscoInfo>());
CPPUNIT_ASSERT_EQUAL(std::string("#room"), room);
CPPUNIT_ASSERT_EQUAL(0, (int) received.size());
CPPUNIT_ASSERT_EQUAL(std::string("room"), room);
CPPUNIT_ASSERT_EQUAL(std::string("hanzz"), roomNickname);
CPPUNIT_ASSERT_EQUAL(std::string("password"), roomPassword);
@ -147,7 +144,7 @@ class UserTest : public CPPUNIT_NS :: TestFixture, public BasicTest {
roomPassword = "";
// simulate that backend joined the room
TestingConversation *conv = new TestingConversation(user->getConversationManager(), "#room", true);
TestingConversation *conv = new TestingConversation(user->getConversationManager(), "room", true);
conv->addJID("user@localhost/resource");
user->getConversationManager()->addConversation(conv);
@ -156,8 +153,7 @@ class UserTest : public CPPUNIT_NS :: TestFixture, public BasicTest {
loop->processEvents();
// no presence received in server mode, just disco#info
CPPUNIT_ASSERT_EQUAL(1, (int) received.size());
CPPUNIT_ASSERT(getStanza(received[0])->getPayload<Swift::DiscoInfo>());
CPPUNIT_ASSERT_EQUAL(0, (int) received.size());
CPPUNIT_ASSERT_EQUAL(std::string(""), room);
CPPUNIT_ASSERT_EQUAL(std::string(""), roomNickname);
@ -169,14 +165,14 @@ class UserTest : public CPPUNIT_NS :: TestFixture, public BasicTest {
User *user = userManager->getUser("user@localhost");
// Add 1 participant
Conversation *conv = user->getConversationManager()->getConversation("#room");
conv->handleParticipantChanged("anotheruser", 0, Swift::StatusShow::Away, "my status message");
Conversation *conv = user->getConversationManager()->getConversation("room");
conv->handleParticipantChanged("anotheruser", Conversation::PARTICIPANT_FLAG_NONE, Swift::StatusShow::Away, "my status message");
// Connect 2nd resource
connectSecondResource();
received2.clear();
Swift::Presence::ref response = Swift::Presence::create();
response->setTo("#room@localhost/hanzz");
response->setTo("room@localhost/hanzz");
response->setFrom("user@localhost/resource2");
Swift::MUCPayload *payload = new Swift::MUCPayload();
@ -189,19 +185,20 @@ class UserTest : public CPPUNIT_NS :: TestFixture, public BasicTest {
CPPUNIT_ASSERT_EQUAL(std::string(""), roomNickname);
CPPUNIT_ASSERT_EQUAL(std::string(""), roomPassword);
CPPUNIT_ASSERT_EQUAL(2, (int) received2.size());
CPPUNIT_ASSERT(dynamic_cast<Swift::Presence *>(getStanza(received2[1])));
CPPUNIT_ASSERT_EQUAL(Swift::StatusShow::Away, dynamic_cast<Swift::Presence *>(getStanza(received2[1]))->getShow());
CPPUNIT_ASSERT_EQUAL(std::string("user@localhost/resource2"), dynamic_cast<Swift::Presence *>(getStanza(received2[1]))->getTo().toString());
CPPUNIT_ASSERT_EQUAL(std::string("#room@localhost/anotheruser"), dynamic_cast<Swift::Presence *>(getStanza(received2[1]))->getFrom().toString());
CPPUNIT_ASSERT(getStanza(received2[1])->getPayload<Swift::MUCUserPayload>());
CPPUNIT_ASSERT_EQUAL(Swift::MUCOccupant::Member, *getStanza(received2[1])->getPayload<Swift::MUCUserPayload>()->getItems()[0].affiliation);
CPPUNIT_ASSERT_EQUAL(Swift::MUCOccupant::Participant, *getStanza(received2[1])->getPayload<Swift::MUCUserPayload>()->getItems()[0].role);
CPPUNIT_ASSERT_EQUAL(1, (int) received2.size());
CPPUNIT_ASSERT(dynamic_cast<Swift::Presence *>(getStanza(received2[0])));
CPPUNIT_ASSERT_EQUAL(Swift::StatusShow::Away, dynamic_cast<Swift::Presence *>(getStanza(received2[0]))->getShow());
CPPUNIT_ASSERT_EQUAL(std::string("user@localhost/resource2"), dynamic_cast<Swift::Presence *>(getStanza(received2[0]))->getTo().toString());
CPPUNIT_ASSERT_EQUAL(std::string("room@localhost/anotheruser"), dynamic_cast<Swift::Presence *>(getStanza(received2[0]))->getFrom().toString());
CPPUNIT_ASSERT(getStanza(received2[0])->getPayload<Swift::MUCUserPayload>());
CPPUNIT_ASSERT_EQUAL(Swift::MUCOccupant::Member, *getStanza(received2[0])->getPayload<Swift::MUCUserPayload>()->getItems()[0].affiliation);
CPPUNIT_ASSERT_EQUAL(Swift::MUCOccupant::Participant, *getStanza(received2[0])->getPayload<Swift::MUCUserPayload>()->getItems()[0].role);
}
void handlePresenceLeaveRoom() {
received.clear();
Swift::Presence::ref response = Swift::Presence::create();
response->setTo("#room@localhost/hanzz");
response->setTo("room@localhost/hanzz");
response->setFrom("user@localhost/resource");
response->setType(Swift::Presence::Unavailable);
@ -213,9 +210,9 @@ class UserTest : public CPPUNIT_NS :: TestFixture, public BasicTest {
CPPUNIT_ASSERT_EQUAL(0, (int) received.size());
CPPUNIT_ASSERT_EQUAL(std::string("#room"), room);
CPPUNIT_ASSERT_EQUAL(std::string(""), roomNickname);
CPPUNIT_ASSERT_EQUAL(std::string(""), roomPassword);
// CPPUNIT_ASSERT_EQUAL(std::string("room"), room);
// CPPUNIT_ASSERT_EQUAL(std::string(""), roomNickname);
// CPPUNIT_ASSERT_EQUAL(std::string(""), roomPassword);
}
void handlePresenceLeaveRoomTwoResources() {
@ -224,7 +221,7 @@ class UserTest : public CPPUNIT_NS :: TestFixture, public BasicTest {
// User is still connected from resource2, so he should not leave the room
Swift::Presence::ref response = Swift::Presence::create();
response->setTo("#room@localhost/hanzz");
response->setTo("room@localhost/hanzz");
response->setFrom("user@localhost/resource");
response->setType(Swift::Presence::Unavailable);
@ -243,7 +240,7 @@ class UserTest : public CPPUNIT_NS :: TestFixture, public BasicTest {
// disconnect also from resource
// User is still connected from resource2, so he should not leave the room
response = Swift::Presence::create();
response->setTo("#room@localhost/hanzz");
response->setTo("room@localhost/hanzz");
response->setFrom("user@localhost/resource2");
response->setType(Swift::Presence::Unavailable);
@ -255,7 +252,7 @@ class UserTest : public CPPUNIT_NS :: TestFixture, public BasicTest {
CPPUNIT_ASSERT_EQUAL(0, (int) received.size());
CPPUNIT_ASSERT_EQUAL(std::string("#room"), room);
CPPUNIT_ASSERT_EQUAL(std::string("room"), room);
CPPUNIT_ASSERT_EQUAL(std::string(""), roomNickname);
CPPUNIT_ASSERT_EQUAL(std::string(""), roomPassword);
}
@ -278,7 +275,7 @@ class UserTest : public CPPUNIT_NS :: TestFixture, public BasicTest {
CPPUNIT_ASSERT_EQUAL(std::string(""), roomNickname);
CPPUNIT_ASSERT_EQUAL(std::string(""), roomPassword);
Conversation *conv = user->getConversationManager()->getConversation("#room");
Conversation *conv = user->getConversationManager()->getConversation("room");
CPPUNIT_ASSERT_EQUAL(1, (int) conv->getJIDs().size());
CPPUNIT_ASSERT_EQUAL(Swift::JID("user@localhost/resource2"), conv->getJIDs().front());
}
@ -287,7 +284,7 @@ class UserTest : public CPPUNIT_NS :: TestFixture, public BasicTest {
User *user = userManager->getUser("user@localhost");
user->addUserSetting("stay_connected", "1");
Swift::Presence::ref response = Swift::Presence::create();
response->setTo("#room@localhost/hanzz");
response->setTo("room@localhost/hanzz");
response->setFrom("user@localhost/resource");
response->setType(Swift::Presence::Unavailable);
@ -312,7 +309,7 @@ class UserTest : public CPPUNIT_NS :: TestFixture, public BasicTest {
// User is still connected from resource2, so he should not leave the room
Swift::Presence::ref response = Swift::Presence::create();
response->setTo("#room@localhost/hanzz");
response->setTo("room@localhost/hanzz");
response->setFrom("user@localhost/resource");
response->setType(Swift::Presence::Unavailable);
@ -332,7 +329,7 @@ class UserTest : public CPPUNIT_NS :: TestFixture, public BasicTest {
// disconnect also from resource
// User is still connected from resource2, so he should not leave the room
response = Swift::Presence::create();
response->setTo("#room@localhost/hanzz");
response->setTo("room@localhost/hanzz");
response->setFrom("user@localhost/resource2");
response->setType(Swift::Presence::Unavailable);
@ -368,7 +365,7 @@ class UserTest : public CPPUNIT_NS :: TestFixture, public BasicTest {
CPPUNIT_ASSERT_EQUAL(std::string(""), roomNickname);
CPPUNIT_ASSERT_EQUAL(std::string(""), roomPassword);
Conversation *conv = user->getConversationManager()->getConversation("#room");
Conversation *conv = user->getConversationManager()->getConversation("room");
CPPUNIT_ASSERT_EQUAL(1, (int) conv->getJIDs().size());
CPPUNIT_ASSERT_EQUAL(Swift::JID("user@localhost/resource2"), conv->getJIDs().front());
}
@ -377,12 +374,12 @@ class UserTest : public CPPUNIT_NS :: TestFixture, public BasicTest {
User *user = userManager->getUser("user@localhost");
handlePresenceJoinRoom();
CPPUNIT_ASSERT(user->getConversationManager()->getConversation("#room"));
CPPUNIT_ASSERT(user->getConversationManager()->getConversation("room"));
received.clear();
handlePresenceLeaveRoom();
CPPUNIT_ASSERT(!user->getConversationManager()->getConversation("#room"));
CPPUNIT_ASSERT(!user->getConversationManager()->getConversation("room"));
}
void handleDisconnected() {
@ -427,7 +424,7 @@ class UserTest : public CPPUNIT_NS :: TestFixture, public BasicTest {
user->setConnected(false);
Swift::Presence::ref response = Swift::Presence::create();
response->setTo("#room@localhost/hanzz");
response->setTo("room@localhost/hanzz");
response->setFrom("user@localhost/resource");
Swift::MUCPayload *payload = new Swift::MUCPayload();
@ -436,16 +433,13 @@ class UserTest : public CPPUNIT_NS :: TestFixture, public BasicTest {
injectPresence(response);
loop->processEvents();
// no presence received in server mode, just disco#info
CPPUNIT_ASSERT_EQUAL(1, (int) received.size());
CPPUNIT_ASSERT(getStanza(received[0])->getPayload<Swift::DiscoInfo>());
CPPUNIT_ASSERT_EQUAL(0, (int) received.size());
CPPUNIT_ASSERT_EQUAL(std::string(""), room);
CPPUNIT_ASSERT_EQUAL(std::string(""), roomNickname);
CPPUNIT_ASSERT_EQUAL(std::string(""), roomPassword);
user->setConnected(true);
CPPUNIT_ASSERT_EQUAL(std::string("#room"), room);
CPPUNIT_ASSERT_EQUAL(std::string("room"), room);
CPPUNIT_ASSERT_EQUAL(std::string("hanzz"), roomNickname);
CPPUNIT_ASSERT_EQUAL(std::string("password"), roomPassword);
}
@ -460,7 +454,7 @@ class UserTest : public CPPUNIT_NS :: TestFixture, public BasicTest {
received.clear();
user->setConnected(true);
CPPUNIT_ASSERT_EQUAL(std::string("#room"), room);
CPPUNIT_ASSERT_EQUAL(std::string("room"), room);
CPPUNIT_ASSERT_EQUAL(std::string("hanzz"), roomNickname);
CPPUNIT_ASSERT_EQUAL(std::string("password"), roomPassword);
CPPUNIT_ASSERT_EQUAL(0, (int) received.size());

View file

@ -6,6 +6,8 @@
#include <Swiften/Server/Server.h>
#include <Swiften/Network/DummyNetworkFactories.h>
#include <Swiften/Network/DummyConnectionServer.h>
#include <Swiften/Network/ConnectionFactory.h>
#include <Swiften/Network/DummyTimerFactory.h>
using namespace Transport;
@ -34,7 +36,7 @@ class UserRegistryTest : public CPPUNIT_NS :: TestFixture {
userRegistry->onConnectUser.connect(bind(&UserRegistryTest::handleConnectUser, this, _1));
userRegistry->onDisconnectUser.connect(bind(&UserRegistryTest::handleDisconnectUser, this, _1));
server = new Swift::Server(loop, factories, userRegistry, "localhost", 5222);
server = new Swift::Server(loop, factories, userRegistry, "localhost", "0.0.0.0", 5222);
server->start();
connectionServer = server->getConnectionServer();

View file

@ -51,7 +51,10 @@
#include "transport/BlockSerializer.h"
#include "Swiften/Parser/PayloadParsers/InvisibleParser.h"
#include "Swiften/Serializer/PayloadSerializers/InvisibleSerializer.h"
#include "Swiften/Swiften.h"
#include "Swiften/Parser/GenericPayloadParserFactory.h"
#include "Swiften/Queries/IQRouter.h"
#include "Swiften/Elements/RosterPayload.h"
#include "Swiften/Elements/InBandRegistrationPayload.h"
using namespace Swift;
using namespace boost;
@ -67,9 +70,11 @@ Component::Component(Swift::EventLoop *loop, Swift::NetworkFactories *factories,
m_server = NULL;
m_reconnectCount = 0;
m_config = config;
m_config->onBackendConfigUpdated.connect(boost::bind(&Component::handleBackendConfigChanged, this));
m_factory = factory;
m_loop = loop;
m_userRegistry = userRegistry;
m_rawXML = false;
m_jid = Swift::JID(CONFIG_STRING(m_config, "service.jid"));
@ -80,7 +85,7 @@ Component::Component(Swift::EventLoop *loop, Swift::NetworkFactories *factories,
if (CONFIG_BOOL(m_config, "service.server_mode")) {
LOG4CXX_INFO(logger, "Creating component in server mode on port " << CONFIG_INT(m_config, "service.port"));
m_server = new Swift::Server(loop, m_factories, m_userRegistry, m_jid, CONFIG_INT(m_config, "service.port"));
m_server = new Swift::Server(loop, m_factories, m_userRegistry, m_jid, CONFIG_STRING(m_config, "service.server"), CONFIG_INT(m_config, "service.port"));
if (!CONFIG_STRING(m_config, "service.cert").empty()) {
#ifndef _WIN32
//TODO: fix
@ -177,6 +182,30 @@ Component::~Component() {
}
}
bool Component::handleIQ(boost::shared_ptr<Swift::IQ> iq) {
if (!m_rawXML) {
return false;
}
if (iq->getPayload<Swift::RosterPayload>() != NULL) { return false; }
if (iq->getPayload<Swift::InBandRegistrationPayload>() != NULL) { return false; }
if (iq->getPayload<Swift::StatsPayload>() != NULL) { return false; }
if (iq->getTo().getNode().empty()) {
return false;
}
onRawIQReceived(iq);
return true;
}
void Component::handleBackendConfigChanged() {
if (!m_rawXML && CONFIG_BOOL_DEFAULTED(m_config, "features.rawxml", false)) {
m_rawXML = true;
m_iqRouter->addHandler(this);
}
}
Swift::StanzaChannel *Component::getStanzaChannel() {
return m_stanzaChannel;
}
@ -275,9 +304,8 @@ void Component::handleDataWritten(const Swift::SafeByteArray &data) {
}
void Component::handlePresence(Swift::Presence::ref presence) {
bool isMUC = presence->getPayload<MUCPayload>() != NULL || *presence->getTo().getNode().c_str() == '#';
// filter out login/logout presence spam
if (!presence->getTo().getNode().empty() && isMUC == false)
if (!presence->getTo().getNode().empty())
return;
// filter out bad presences

View file

@ -26,7 +26,6 @@
#include "transport/conversationmanager.h"
#include "transport/presenceoracle.h"
#include "transport/logging.h"
#include "Swiften/Swiften.h"
#include "Swiften/Server/ServerStanzaChannel.h"
#include "Swiften/Elements/StreamError.h"
#include "Swiften/Elements/MUCPayload.h"
@ -195,11 +194,6 @@ void User::setCacheMessages(bool cacheMessages) {
}
void User::handlePresence(Swift::Presence::ref presence, bool forceJoin) {
int currentResourcesCount = m_presenceOracle->getAllPresence(m_jid).size();
m_conversationManager->resetResources();
LOG4CXX_INFO(logger, "PRESENCE " << presence->getFrom().toString() << " " << presence->getTo().toString());
if (!m_connected) {
// we are not connected to legacy network, so we should do it when disco#info arrive :)
@ -231,8 +225,9 @@ void User::handlePresence(Swift::Presence::ref presence, bool forceJoin) {
}
}
bool isMUC = presence->getPayload<Swift::MUCPayload>() != NULL || *presence->getTo().getNode().c_str() == '#';
if (isMUC) {
if (!presence->getTo().getNode().empty()) {
bool isMUC = presence->getPayload<Swift::MUCPayload>() != NULL || *presence->getTo().getNode().c_str() == '#';
if (presence->getType() == Swift::Presence::Unavailable) {
std::string room = Buddy::JIDToLegacyName(presence->getTo());
Conversation *conv = m_conversationManager->getConversation(room);
@ -242,9 +237,13 @@ void User::handlePresence(Swift::Presence::ref presence, bool forceJoin) {
return;
}
}
else {
return;
}
if (getUserSetting("stay_connected") != "1") {
LOG4CXX_INFO(logger, m_jid.toString() << ": Going to left room " << room);
onRawPresenceReceived(presence);
onRoomLeft(room);
BOOST_FOREACH(Swift::Presence::ref &p, m_joinedRooms) {
@ -260,7 +259,7 @@ void User::handlePresence(Swift::Presence::ref presence, bool forceJoin) {
}
}
}
else {
else if (isMUC) {
// force connection to legacy network to let backend to handle auto-join on connect.
if (!m_readyForConnect) {
LOG4CXX_INFO(logger, m_jid.toString() << ": Ready to be connected to legacy network");
@ -286,6 +285,7 @@ void User::handlePresence(Swift::Presence::ref presence, bool forceJoin) {
}
if (forceJoin) {
onRawPresenceReceived(presence);
onRoomJoined(presence->getFrom(), room, presence->getTo().getResource(), password);
}
return;
@ -313,13 +313,34 @@ void User::handlePresence(Swift::Presence::ref presence, bool forceJoin) {
conv->setNickname(presence->getTo().getResource());
conv->addJID(presence->getFrom());
onRawPresenceReceived(presence);
onRoomJoined(presence->getFrom(), room, presence->getTo().getResource(), password);
}
return;
}
int currentResourcesCount = m_presenceOracle->getAllPresence(m_jid).size();
m_conversationManager->resetResources();
if (presence->getType() == Swift::Presence::Unavailable) {
m_conversationManager->removeJID(presence->getFrom());
std::string presences;
std::vector<Swift::Presence::ref> ps = m_presenceOracle->getAllPresence(m_jid);
BOOST_FOREACH(Swift::Presence::ref p, ps) {
if (p != presence) {
presences += p->getFrom().toString() + " ";
}
};
if (!presences.empty()) {
LOG4CXX_INFO(logger, m_jid.toString() << ": User is still connected from following clients: " << presences);
}
else {
LOG4CXX_INFO(logger, m_jid.toString() << ": Last client disconnected");
}
}

View file

@ -29,12 +29,15 @@
#include "transport/discoitemsresponder.h"
#include "storageresponder.h"
#include "Swiften/Swiften.h"
#include "Swiften/Server/ServerStanzaChannel.h"
#include "Swiften/Elements/StreamError.h"
#ifndef __FreeBSD__
#include "Swiften/Elements/MUCPayload.h"
#include "Swiften/Elements/ChatState.h"
#ifndef __FreeBSD__
#ifndef __MACH__
#include "malloc.h"
#endif
#endif
// #include "valgrind/memcheck.h"
namespace Transport {
@ -129,9 +132,11 @@ void UserManager::removeUser(User *user, bool onUserBehalf) {
delete user;
#ifndef WIN32
#ifndef __FreeBSD__
#ifndef __MACH__
malloc_trim(0);
#endif
#endif
#endif
// VALGRIND_DO_LEAK_CHECK;
}
@ -229,7 +234,7 @@ void UserManager::handlePresence(Swift::Presence::ref presence) {
res.password = "";
res.uin = presence->getFrom().getNode();
res.jid = userkey;
if (res.uin.find_last_of("%") != std::string::npos) { // OK
while (res.uin.find_last_of("%") != std::string::npos) { // OK
res.uin.replace(res.uin.find_last_of("%"), 1, "@"); // OK
}
if (m_storageBackend) {
@ -247,8 +252,8 @@ void UserManager::handlePresence(Swift::Presence::ref presence) {
// We allow auto_register feature in gateway-mode. This allows IRC user to register
// the transport just by joining the room.
if (!m_component->inServerMode()) {
if (!registered && (CONFIG_BOOL(m_component->getConfig(), "registration.auto_register") ||
!CONFIG_BOOL_DEFAULTED(m_component->getConfig(), "registration.needRegistration", true))) {
if (!registered && (CONFIG_BOOL(m_component->getConfig(), "registration.auto_register")
/*!CONFIG_BOOL_DEFAULTED(m_component->getConfig(), "registration.needRegistration", true)*/)) {
res.password = "";
res.jid = userkey;
@ -399,6 +404,7 @@ void UserManager::handleGeneralPresenceReceived(Swift::Presence::ref presence) {
break;
case Swift::Presence::Available:
case Swift::Presence::Unavailable:
handleMUCPresence(presence);
break;
case Swift::Presence::Probe:
handleProbePresence(presence);
@ -411,6 +417,24 @@ void UserManager::handleGeneralPresenceReceived(Swift::Presence::ref presence) {
};
}
void UserManager::handleMUCPresence(Swift::Presence::ref presence) {
// Don't let RosterManager to handle presences for us
if (presence->getTo().getNode().empty()) {
return;
}
if (presence->getType() == Swift::Presence::Available) {
handlePresence(presence);
}
else if (presence->getType() == Swift::Presence::Unavailable) {
std::string userkey = presence->getFrom().toBare().toString();
User *user = getUser(userkey);
if (user) {
user->handlePresence(presence);
}
}
}
void UserManager::handleProbePresence(Swift::Presence::ref presence) {
// Don't let RosterManager to handle presences for us
if (presence->getTo().getNode().empty()) {

View file

@ -26,6 +26,9 @@
#include "transport/user.h"
#include "transport/logging.h"
#include "Swiften/Elements/ErrorPayload.h"
#include "Swiften/EventLoop/SimpleEventLoop.h"
#include "Swiften/Network/BoostNetworkFactories.h"
#include "Swiften/Client/Client.h"
#include <boost/shared_ptr.hpp>
#include <boost/thread.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>

View file

@ -20,7 +20,6 @@
#include <string>
#include <map>
#include "Swiften/Swiften.h"
#include "Swiften/Server/UserRegistry.h"
#include "transport/userregistry.h"
#include "transport/logging.h"
@ -79,9 +78,6 @@ void UserRegistry::stopLogin(const Swift::JID& user, Swift::ServerFromClientSess
LOG4CXX_WARN(logger, key << ": Stopping login process (user probably disconnected while logging in), but this is not active session");
}
}
else {
LOG4CXX_WARN(logger, key << ": Stopping login process (user probably disconnected while logging in) for invalid user");
}
// ::removeLater can be called only by libtransport, not by Swift and libtransport
// takes care about user disconnecting itself, so don't call our signal.
@ -96,21 +92,15 @@ void UserRegistry::onPasswordValid(const Swift::JID &user) {
users[key].session->handlePasswordValid();
users.erase(key);
}
else {
LOG4CXX_INFO(logger, key << ": onPasswordValid called for invalid user");
}
}
void UserRegistry::onPasswordInvalid(const Swift::JID &user, const std::string &error) {
std::string key = user.toBare().toString();
if (users.find(key) != users.end()) {
LOG4CXX_INFO(logger, key << ": Password is invalid");
LOG4CXX_INFO(logger, key << ": Password is invalid or there was an error when connecting the legacy network");
users[key].session->handlePasswordInvalid(error);
users.erase(key);
}
else {
LOG4CXX_INFO(logger, key << ": onPasswordInvalid called for invalid user");
}
}
void UserRegistry::handleRemoveTimeout(const Swift::JID &user) {

View file

@ -23,11 +23,12 @@
#include <iostream>
#include <boost/bind.hpp>
#include "Swiften/Queries/IQRouter.h"
#include "Swiften/Swiften.h"
#include "transport/storagebackend.h"
#include "transport/transport.h"
#include "transport/logging.h"
#include "Swiften/Network/NetworkFactories.h"
using namespace Swift;
using namespace boost;

View file

@ -23,7 +23,6 @@
#include <iostream>
#include <boost/bind.hpp>
#include "Swiften/Queries/IQRouter.h"
#include "Swiften/Swiften.h"
#include "transport/user.h"
#include "transport/usermanager.h"
#include "transport/rostermanager.h"