diff --git a/CMakeLists.txt b/CMakeLists.txt index 6a6ba6f3..7b7ae408 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -97,7 +97,12 @@ else(WIN32) endif() endif(WIN32) -message( STATUS "Found Boost: ${Boost_LIBRARIES}, ${Boost_INCLUDE_DIR}") +message( STATUS "Found Boost: ${Boost_VERSION}, ${Boost_LIBRARIES}, ${Boost_INCLUDE_DIR}") + +if (${Boost_VERSION} GREATER 104999) + message( STATUS "Using BOOST_FILESYSTEM_VERSION=3") + add_definitions(-DBOOST_FILESYSTEM_VERSION=3) +endif() # FIND POPT if (NOT WIN32) diff --git a/backends/CMakeLists.txt b/backends/CMakeLists.txt index 6dcfb900..6bb81a77 100644 --- a/backends/CMakeLists.txt +++ b/backends/CMakeLists.txt @@ -9,7 +9,6 @@ if (PROTOBUF_FOUND) if (ENABLE_SWIFTEN) ADD_SUBDIRECTORY(swiften) - ADD_SUBDIRECTORY(swiften_raw) endif() ADD_SUBDIRECTORY(template) diff --git a/backends/libcommuni/backports.h b/backends/libcommuni/backports.h new file mode 100644 index 00000000..525e4e69 --- /dev/null +++ b/backends/libcommuni/backports.h @@ -0,0 +1,70 @@ +#include +#include +#include +#include + +namespace CommuniBackport { + +bool parseColors(const QString& message, int pos, int* len, int* fg = 0, int* bg = 0) +{ + // fg(,bg) + *len = 0; + if (fg) + *fg = -1; + if (bg) + *bg = -1; + QRegExp rx(QLatin1String("(\\d{1,2})(?:,(\\d{1,2}))?")); + int idx = rx.indexIn(message, pos); + if (idx == pos) { + *len = rx.matchedLength(); + if (fg) + *fg = rx.cap(1).toInt(); + if (bg) { + bool ok = false; + int tmp = rx.cap(2).toInt(&ok); + if (ok) + *bg = tmp; + } + } + return *len > 0; +} + +/*! +Converts \a text to plain text. This function parses the text and +strips away IRC-style formatting like colors, bold and underline. + +\sa toHtml() +*/ +void toPlainText(QString& processed) +{ + + int pos = 0; + int len = 0; + while (pos < processed.size()) { + switch (processed.at(pos).unicode()) { + case '\x02': // bold + case '\x0f': // none + case '\x13': // strike-through + case '\x15': // underline + case '\x16': // inverse + case '\x1d': // italic + case '\x1f': // underline + processed.remove(pos, 1); + break; + + case '\x03': // color + if (parseColors(processed, pos + 1, &len)) + processed.remove(pos, len + 1); + else + processed.remove(pos, 1); + break; + + default: + ++pos; + break; + } + } + +} + +} diff --git a/backends/libcommuni/ircnetworkplugin.cpp b/backends/libcommuni/ircnetworkplugin.cpp index 4996a874..c5f3c2cb 100644 --- a/backends/libcommuni/ircnetworkplugin.cpp +++ b/backends/libcommuni/ircnetworkplugin.cpp @@ -1,3 +1,23 @@ +/** + * XMPP - libpurple transport + * + * Copyright (C) 2013, Jan Kaluza + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + #include "ircnetworkplugin.h" #include #include @@ -53,12 +73,13 @@ void IRCNetworkPlugin::readData() { m_firstPing = false; // Users can join the network without registering if we allow // one user to connect multiple IRC networks. + NetworkPlugin::PluginConfig cfg; if (m_servers.empty()) { - NetworkPlugin::PluginConfig cfg; cfg.setNeedRegistration(false); - cfg.setSupportMUC(true); - sendConfig(cfg); } + cfg.setSupportMUC(true); + cfg.disableJIDEscaping(); + sendConfig(cfg); } std::string d = std::string(m_socket->readAll().data(), availableBytes); @@ -94,7 +115,7 @@ MyIrcSession *IRCNetworkPlugin::createSession(const std::string &user, const std void IRCNetworkPlugin::handleLoginRequest(const std::string &user, const std::string &legacyName, const std::string &password) { if (!m_servers.empty()) { - // legacy name is users nickname + // legacy name is user's nickname if (m_sessions[user] != NULL) { LOG4CXX_WARN(logger, user << ": Already logged in."); return; @@ -153,6 +174,13 @@ void IRCNetworkPlugin::handleMessageSendRequest(const std::string &user, const s } std::string target = getTargetName(legacyName); + // We are sending PM message. On XMPP side, user is sending PM using the particular channel, + // for example #room@irc.freenode.org/hanzz. On IRC side, we are forwarding this message + // just to "hanzz". Therefore we have to somewhere store, that message from "hanzz" should + // be mapped to #room@irc.freenode.org/hanzz. + if (legacyName.find("/") != std::string::npos) { + m_sessions[session]->addPM(target, legacyName.substr(0, legacyName.find("@"))); + } LOG4CXX_INFO(logger, user << ": Session name: " << session << ", message to " << target); diff --git a/backends/libcommuni/ircnetworkplugin.h b/backends/libcommuni/ircnetworkplugin.h index 9c4cdca7..527b2ed5 100644 --- a/backends/libcommuni/ircnetworkplugin.h +++ b/backends/libcommuni/ircnetworkplugin.h @@ -1,3 +1,22 @@ +/** + * XMPP - libpurple transport + * + * Copyright (C) 2013, Jan Kaluza + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ #pragma once diff --git a/backends/libcommuni/main.cpp b/backends/libcommuni/main.cpp index d2013b50..423ab27c 100644 --- a/backends/libcommuni/main.cpp +++ b/backends/libcommuni/main.cpp @@ -1,11 +1,21 @@ -/* - * Copyright (C) 2008-2009 J-P Nurmi jpnurmi@gmail.com +/** + * XMPP - libpurple transport * - * This example is free, and not covered by LGPL license. There is no - * restriction applied to their modification, redistribution, using and so on. - * You can study them, modify them, use them in your own program - either - * completely or partially. By using it you may give me some credits in your - * program, but you don't have to. + * Copyright (C) 2013, Jan Kaluza + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ #include "transport/config.h" diff --git a/backends/libcommuni/session.cpp b/backends/libcommuni/session.cpp index c13297ff..b6e9f79d 100644 --- a/backends/libcommuni/session.cpp +++ b/backends/libcommuni/session.cpp @@ -1,11 +1,21 @@ -/* - * Copyright (C) 2008-2009 J-P Nurmi jpnurmi@gmail.com +/** + * XMPP - libpurple transport * - * This example is free, and not covered by LGPL license. There is no - * restriction applied to their modification, redistribution, using and so on. - * You can study them, modify them, use them in your own program - either - * completely or partially. By using it you may give me some credits in your - * program, but you don't have to. + * Copyright (C) 2013, Jan Kaluza + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ #include "session.h" @@ -13,6 +23,9 @@ #include #include #include +#include + +#include "backports.h" #include "ircnetworkplugin.h" @@ -65,12 +78,37 @@ void MyIrcSession::on_connected() { if (getIdentify().find(" ") != std::string::npos) { std::string to = getIdentify().substr(0, getIdentify().find(" ")); std::string what = getIdentify().substr(getIdentify().find(" ") + 1); + LOG4CXX_INFO(logger, user << ": Sending IDENTIFY message to " << to); sendCommand(IrcCommand::createMessage(FROM_UTF8(to), FROM_UTF8(what))); } } void MyIrcSession::on_socketError(QAbstractSocket::SocketError error) { - on_disconnected(); + std::string reason; + switch(error) { + case QAbstractSocket::ConnectionRefusedError: reason = "The connection was refused by the peer (or timed out)."; break; + case QAbstractSocket::RemoteHostClosedError: reason = "The remote host closed the connection."; break; + case QAbstractSocket::HostNotFoundError: reason = "The host address was not found."; break; + case QAbstractSocket::SocketAccessError: reason = "The socket operation failed because the application lacked the required privileges."; break; + case QAbstractSocket::SocketResourceError: reason = "The local system ran out of resources."; break; + case QAbstractSocket::SocketTimeoutError: reason = "The socket operation timed out."; break; + case QAbstractSocket::DatagramTooLargeError: reason = "The datagram was larger than the operating system's limit."; break; + case QAbstractSocket::NetworkError: reason = "An error occurred with the network."; break; + case QAbstractSocket::SslHandshakeFailedError: reason = "The SSL/TLS handshake failed, so the connection was closed"; break; + case QAbstractSocket::UnknownSocketError: reason = "An unidentified error occurred."; break; + default: reason= "Unknown error."; break; + }; + + if (!suffix.empty()) { + 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_ROOM_NOT_FOUND, pbnetwork::STATUS_NONE, reason); + } + } + else { + np->handleDisconnected(user, 0, reason); + np->tryNextServer(); + } + m_connected = false; } void MyIrcSession::on_disconnected() { @@ -96,9 +134,8 @@ bool MyIrcSession::correctNickname(std::string &nickname) { void MyIrcSession::on_joined(IrcMessage *message) { IrcJoinMessage *m = (IrcJoinMessage *) message; - bool op = 0; std::string nickname = TO_UTF8(m->sender().name()); - op = correctNickname(nickname); + bool op = correctNickname(nickname); 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 +144,8 @@ void MyIrcSession::on_joined(IrcMessage *message) { void MyIrcSession::on_parted(IrcMessage *message) { IrcPartMessage *m = (IrcPartMessage *) message; - bool op = 0; std::string nickname = TO_UTF8(m->sender().name()); - op = correctNickname(nickname); + bool op = correctNickname(nickname); 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())); @@ -117,13 +153,13 @@ void MyIrcSession::on_parted(IrcMessage *message) { void MyIrcSession::on_quit(IrcMessage *message) { IrcQuitMessage *m = (IrcQuitMessage *) message; + std::string nickname = TO_UTF8(m->sender().name()); + bool op = correctNickname(nickname); + for(AutoJoinMap::iterator it = m_autoJoin.begin(); it != m_autoJoin.end(); it++) { - bool op = 0; - std::string nickname = TO_UTF8(m->sender().name()); if (!hasIRCBuddy(it->second->getChannel(), nickname)) { continue; } - op = correctNickname(nickname); removeIRCBuddy(it->second->getChannel(), nickname); LOG4CXX_INFO(logger, user << ": " << nickname << " quit " << it->second->getChannel() + suffix); np->handleParticipantChanged(user, nickname, it->second->getChannel() + suffix, op, pbnetwork::STATUS_NONE, TO_UTF8(m->reason())); @@ -132,9 +168,10 @@ void MyIrcSession::on_quit(IrcMessage *message) { void MyIrcSession::on_nickChanged(IrcMessage *message) { IrcNickMessage *m = (IrcNickMessage *) message; + std::string nickname = TO_UTF8(m->sender().name()); + correctNickname(nickname); for(AutoJoinMap::iterator it = m_autoJoin.begin(); it != m_autoJoin.end(); it++) { - std::string nickname = TO_UTF8(m->sender().name()); if (!hasIRCBuddy(it->second->getChannel(), nickname)) { continue; } @@ -152,20 +189,23 @@ void MyIrcSession::on_modeChanged(IrcMessage *message) { std::string mode = TO_UTF8(m->mode()); if (nickname.empty()) return; - LOG4CXX_INFO(logger, user << ": " << nickname << " changed mode to " << mode); - for(AutoJoinMap::iterator it = m_autoJoin.begin(); it != m_autoJoin.end(); it++) { - if (!hasIRCBuddy(it->second->getChannel(), nickname)) { - continue; - } - IRCBuddy &buddy = getIRCBuddy(it->second->getChannel(), nickname); - if (mode == "+o") { - buddy.setOp(true); - } - else { - buddy.setOp(false); - } - np->handleParticipantChanged(user, nickname, it->second->getChannel() + suffix,(int) buddy.isOp(), pbnetwork::STATUS_ONLINE, ""); + + correctNickname(nickname); + + if (!hasIRCBuddy(TO_UTF8(m->target().toLower()), nickname)) { + return; } + IRCBuddy &buddy = getIRCBuddy(TO_UTF8(m->target().toLower()), nickname); + if (mode == "+o") { + buddy.setOp(true); + } + else { + buddy.setOp(false); + } + + np->handleParticipantChanged(user, nickname, TO_UTF8(m->target().toLower()) + suffix,(int) buddy.isOp(), pbnetwork::STATUS_ONLINE, ""); + + LOG4CXX_INFO(logger, user << ": " << nickname << " changed mode to " << mode << " in " << TO_UTF8(m->target().toLower())); } void MyIrcSession::on_topicChanged(IrcMessage *message) { @@ -192,19 +232,43 @@ void MyIrcSession::on_messageReceived(IrcMessage *message) { if (m->isAction()) { msg = QString("/me ") + msg; } + QString html = "";//msg; + CommuniBackport::toPlainText(msg); + + // TODO: Communi produces invalid html now... +// if (html == msg) { +// html = ""; +// } +// else { +// html = IrcUtil::messageToHtml(html); +// } 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()); correctNickname(nickname); - np->handleMessage(user, target + suffix, TO_UTF8(msg), nickname); + np->handleMessage(user, target + suffix, TO_UTF8(msg), nickname, TO_UTF8(html)); } else { std::string nickname = TO_UTF8(m->sender().name()); correctNickname(nickname); - LOG4CXX_INFO(logger, nickname + suffix); - np->handleMessage(user, nickname + suffix, TO_UTF8(msg)); + if (m_pms.find(nickname) != m_pms.end()) { + if (hasIRCBuddy(m_pms[nickname], nickname)) { + LOG4CXX_INFO(logger, nickname); + np->handleMessage(user, m_pms[nickname] + suffix, TO_UTF8(msg), nickname, TO_UTF8(html), "", false, true); + return; + } + else { + nickname = nickname + suffix; + } + } + else { + nickname = nickname + suffix; + } + + LOG4CXX_INFO(logger, nickname); + np->handleMessage(user, nickname, TO_UTF8(msg), "", TO_UTF8(html)); } } @@ -313,8 +377,6 @@ void MyIrcSession::on_numericMessageReceived(IrcMessage *message) { 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; } void MyIrcSession::awayTimeout() { @@ -326,6 +388,44 @@ void MyIrcSession::awayTimeout() { } } +void MyIrcSession::on_noticeMessageReceived(IrcMessage *message) { + IrcNoticeMessage *m = (IrcNoticeMessage *) message; + LOG4CXX_INFO(logger, user << ": NOTICE " << TO_UTF8(m->message())); + + QString msg = m->message(); + CommuniBackport::toPlainText(msg); + + std::string target = TO_UTF8(m->target().toLower()); + if (target.find("#") == 0) { + std::string nickname = TO_UTF8(m->sender().name()); + correctNickname(nickname); + np->handleMessage(user, target + suffix, TO_UTF8(msg), nickname); + } + else { + std::string nickname = TO_UTF8(m->sender().name()); + correctNickname(nickname); + if (nickname.find(".") != std::string::npos) { + return; + } + if (m_pms.find(nickname) != m_pms.end()) { + if (hasIRCBuddy(m_pms[nickname], nickname)) { + LOG4CXX_INFO(logger, nickname); + np->handleMessage(user, m_pms[nickname] + suffix, TO_UTF8(msg), nickname, "", "", false, true); + return; + } + else { + nickname = nickname + suffix; + } + } + else { + nickname = nickname + suffix; + } + + LOG4CXX_INFO(logger, nickname); + np->handleMessage(user, nickname, TO_UTF8(msg), ""); + } +} + void MyIrcSession::onMessageReceived(IrcMessage *message) { // LOG4CXX_INFO(logger, user << ": " << TO_UTF8(message->toString())); switch (message->type()) { @@ -353,6 +453,9 @@ void MyIrcSession::onMessageReceived(IrcMessage *message) { case IrcMessage::Numeric: on_numericMessageReceived(message); break; + case IrcMessage::Notice: + on_noticeMessageReceived(message); + break; default:break; } } diff --git a/backends/libcommuni/session.h b/backends/libcommuni/session.h index 029905a7..e4122d43 100644 --- a/backends/libcommuni/session.h +++ b/backends/libcommuni/session.h @@ -1,11 +1,21 @@ -/* - * Copyright (C) 2008-2009 J-P Nurmi jpnurmi@gmail.com +/** + * XMPP - libpurple transport * - * This example is free, and not covered by LGPL license. There is no - * restriction applied to their modification, redistribution, using and so on. - * You can study them, modify them, use them in your own program - either - * completely or partially. By using it you may give me some credits in your - * program, but you don't have to. + * Copyright (C) 2013, Jan Kaluza + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */ #ifndef SESSION_H @@ -69,8 +79,6 @@ public: MyIrcSession(const std::string &user, IRCNetworkPlugin *np, const std::string &suffix = "", QObject* parent = 0); virtual ~MyIrcSession(); - std::string suffix; - int rooms; void addAutoJoinChannel(const std::string &channel, const std::string &password) { m_autoJoin[channel] = boost::make_shared(channel, password, 12 + m_autoJoin.size()); @@ -81,6 +89,14 @@ public: removeIRCBuddies(channel); } + // We are sending PM message. On XMPP side, user is sending PM using the particular channel, + // for example #room@irc.freenode.org/hanzz. On IRC side, we are forwarding this message + // just to "hanzz". Therefore we have to somewhere store, that message from "hanzz" should + // be mapped to #room@irc.freenode.org/hanzz. + void addPM(const std::string &name, const std::string &room) { + m_pms[name] = room; + } + void setIdentify(const std::string &identify) { m_identify = identify; } @@ -115,6 +131,10 @@ public: void on_topicChanged(IrcMessage *message); void on_messageReceived(IrcMessage *message); void on_numericMessageReceived(IrcMessage *message); + void on_noticeMessageReceived(IrcMessage *message); + + std::string suffix; + int rooms; protected Q_SLOTS: void on_connected(); @@ -133,6 +153,7 @@ protected: bool m_connected; std::list m_rooms; std::list m_names; + std::map m_pms; IRCBuddyMap m_buddies; QTimer *m_awayTimer; }; diff --git a/backends/libpurple/main.cpp b/backends/libpurple/main.cpp index e8004dfb..182e21c2 100644 --- a/backends/libpurple/main.cpp +++ b/backends/libpurple/main.cpp @@ -298,6 +298,39 @@ class SpectrumNetworkPlugin : public NetworkPlugin { purple_account_set_int_wrapped(account, "port", atoi(s.back().c_str())); } + if (!CONFIG_STRING_DEFAULTED(config, "proxy.type", "").empty()) { + PurpleProxyInfo *info = purple_proxy_info_new(); + if (CONFIG_STRING_DEFAULTED(config, "proxy.type", "") == "http") { + purple_proxy_info_set_type(info, PURPLE_PROXY_HTTP); + } + else if (CONFIG_STRING_DEFAULTED(config, "proxy.type", "") == "socks4") { + purple_proxy_info_set_type(info, PURPLE_PROXY_SOCKS4); + } + else if (CONFIG_STRING_DEFAULTED(config, "proxy.type", "") == "socks5") { + purple_proxy_info_set_type(info, PURPLE_PROXY_SOCKS5); + } + else { + LOG4CXX_ERROR(logger, "Unknown proxy.type " << CONFIG_STRING_DEFAULTED(config, "proxy.type", "")); + } + + info->username = NULL; + info->password = NULL; + + purple_proxy_info_set_type(info, PURPLE_PROXY_SOCKS5); + purple_proxy_info_set_host(info, CONFIG_STRING_DEFAULTED(config, "proxy.host", "").c_str()); + if (CONFIG_INT_DEFAULTED(config, "proxy.port", 0)) { + purple_proxy_info_set_port(info, CONFIG_INT_DEFAULTED(config, "proxy.port", 0)); + } + if (!CONFIG_STRING_DEFAULTED(config, "proxy.username", "").empty()) { + purple_proxy_info_set_username(info, CONFIG_STRING_DEFAULTED(config, "proxy.username", "").c_str()); + } + + if (!CONFIG_STRING_DEFAULTED(config, "proxy.password", "").empty()) { + purple_proxy_info_set_password(info, CONFIG_STRING_DEFAULTED(config, "proxy.password", "").c_str()); + } + + purple_account_set_proxy_info(account, info); + } } void handleLoginRequest(const std::string &user, const std::string &legacyName, const std::string &password) { @@ -940,16 +973,15 @@ static void NodeRemoved(PurpleBlistNode *node, void *data) { } static void buddyListSaveNode(PurpleBlistNode *node) { - if (!PURPLE_BLIST_NODE_IS_BUDDY(node)) + if (!PURPLE_BLIST_NODE_IS_BUDDY_WRAPPED(node)) return; - } static void buddyListSaveAccount(PurpleAccount *account) { } static void buddyListRemoveNode(PurpleBlistNode *node) { - if (!PURPLE_BLIST_NODE_IS_BUDDY(node)) + if (!PURPLE_BLIST_NODE_IS_BUDDY_WRAPPED(node)) return; } diff --git a/backends/skype/skype.cpp b/backends/skype/skype.cpp index 9f054efe..95f1390c 100644 --- a/backends/skype/skype.cpp +++ b/backends/skype/skype.cpp @@ -20,6 +20,7 @@ #include "skype.h" #include "skypeplugin.h" +#include "skypedb.h" #include "transport/config.h" #include "transport/logging.h" @@ -95,14 +96,15 @@ void Skype::login() { return; } - std::string db_path = createSkypeDirectory(); + m_db = createSkypeDirectory(); - bool spawned = spawnSkype(db_path); + bool spawned = spawnSkype(m_db); if (!spawned) { m_np->handleDisconnected(m_user, pbnetwork::CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE, "Error spawning the Skype instance."); return; } + m_db += "/" + getUsername() + "/main.db"; if (m_connection == NULL) { LOG4CXX_INFO(logger, "Creating DBUS connection."); @@ -229,51 +231,55 @@ bool Skype::loadSkypeBuddies() { } } - std::string friends = send_command("GET AUTH_CONTACTS_PROFILES"); + // Try to load skype buddies from database, if it fails + // fallback to old method. + if (!SkypeDB::loadBuddies(m_np, m_db, m_user, group_map)) { + 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) + char **full_friends_list = g_strsplit((strchr(friends.c_str(), ' ')+1), ";", 0); + if (full_friends_list && full_friends_list[0]) { - std::string buddy = full_friends_list[i]; + //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) - if (buddy[0] == ',') { - buddy.erase(buddy.begin()); + for (int i=0; full_friends_list[i] && full_friends_list[i+1] && *full_friends_list[i] != '\0'; i+=8) + { + std::string buddy = full_friends_list[i]; + + if (buddy[0] == ',') { + buddy.erase(buddy.begin()); + } + + if (buddy.rfind(",") != std::string::npos) { + buddy = buddy.substr(buddy.rfind(",")); + } + + if (buddy[0] == ',') { + buddy.erase(buddy.begin()); + } + + LOG4CXX_INFO(logger, "Got buddy " << buddy); + std::string st = full_friends_list[i + 5]; + + pbnetwork::StatusType status = getStatus(st); + + std::string alias = full_friends_list[i + 6]; + + std::string mood_text = ""; + if (full_friends_list[i + 8] && *full_friends_list[i + 8] != '\0' && *full_friends_list[i + 8] != ',') { + mood_text = full_friends_list[i + 8]; + } + + std::vector groups; + if (group_map.find(buddy) != group_map.end()) { + groups.push_back(group_map[buddy]); + } + m_np->handleBuddyChanged(m_user, buddy, alias, groups, status, mood_text); } - - if (buddy.rfind(",") != std::string::npos) { - buddy = buddy.substr(buddy.rfind(",")); - } - - if (buddy[0] == ',') { - buddy.erase(buddy.begin()); - } - - LOG4CXX_INFO(logger, "Got buddy " << buddy); - std::string st = full_friends_list[i + 5]; - - pbnetwork::StatusType status = getStatus(st); - - std::string alias = full_friends_list[i + 6]; - - std::string mood_text = ""; - if (full_friends_list[i + 8] && *full_friends_list[i + 8] != '\0' && *full_friends_list[i + 8] != ',') { - mood_text = full_friends_list[i + 8]; - } - - std::vector groups; - if (group_map.find(buddy) != group_map.end()) { - groups.push_back(group_map[buddy]); - } - m_np->handleBuddyChanged(m_user, buddy, alias, groups, status, mood_text); } + g_strfreev(full_friends_list); } - g_strfreev(full_friends_list); send_command("SET AUTOAWAY OFF"); send_command("SET USERSTATUS ONLINE"); diff --git a/backends/skype/skype.h b/backends/skype/skype.h index 9b321bc5..5230b4d8 100644 --- a/backends/skype/skype.h +++ b/backends/skype/skype.h @@ -87,5 +87,6 @@ class Skype { int fd_output; std::map m_groups; SkypePlugin *m_np; + std::string m_db; }; diff --git a/backends/skype/skypedb.cpp b/backends/skype/skypedb.cpp index 4daf44ee..7e24a644 100644 --- a/backends/skype/skypedb.cpp +++ b/backends/skype/skypedb.cpp @@ -40,6 +40,8 @@ #include "malloc.h" #endif +#include "skypeplugin.h" + // Prepare the SQL statement #define PREP_STMT(sql, str) \ if(sqlite3_prepare_v2(db, std::string(str).c_str(), -1, &sql, NULL)) { \ @@ -104,7 +106,57 @@ bool getAvatar(const std::string &db_path, const std::string &name, std::string FINALIZE_STMT(stmt); } else { - LOG4CXX_ERROR(logger, "Can't created prepared statement"); + LOG4CXX_ERROR(logger, "Can't create prepared statement"); + LOG4CXX_ERROR(logger, (sqlite3_errmsg(db) == NULL ? "" : sqlite3_errmsg(db))); + } + sqlite3_close(db); + } + return ret; +} + +bool loadBuddies(SkypePlugin *np, const std::string &db_path, std::string &user, std::map &group_map) { + 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; +// aliases, fullname, + PREP_STMT(stmt, "select skypename, displayname, mood_text from Contacts;"); + if (stmt) { + BEGIN(stmt); + int ret2; + while((ret2 = sqlite3_step(stmt)) == SQLITE_ROW) { + const char *d; + d = (const char *) sqlite3_column_text(stmt, 0); + if (!d) { + continue; + } + + ret = true; + + std::string buddy = d; + d = (const char *) sqlite3_column_text(stmt, 1); + std::string alias = d ? d : buddy; + d = (const char *) sqlite3_column_text(stmt, 2); + std::string mood_text = d ? d : ""; + + std::vector groups; + if (group_map.find(buddy) != group_map.end()) { + groups.push_back(group_map[buddy]); + } + np->handleBuddyChanged(user, buddy, alias, groups, pbnetwork::STATUS_NONE, mood_text); + } + if (ret2 != SQLITE_DONE) { + FINALIZE_STMT(stmt); + ret = false; + } + } + else { + LOG4CXX_ERROR(logger, "Can't create prepared statement"); LOG4CXX_ERROR(logger, (sqlite3_errmsg(db) == NULL ? "" : sqlite3_errmsg(db))); } sqlite3_close(db); diff --git a/backends/skype/skypedb.h b/backends/skype/skypedb.h index d0ba9f27..d3490b00 100644 --- a/backends/skype/skypedb.h +++ b/backends/skype/skypedb.h @@ -26,8 +26,10 @@ #include #include +class SkypePlugin; + namespace SkypeDB { bool getAvatar(const std::string &db, const std::string &name, std::string &avatar); - + bool loadBuddies(SkypePlugin *np, const std::string &db, std::string &user, std::map &group_map); } diff --git a/backends/skype/skypeplugin.cpp b/backends/skype/skypeplugin.cpp index 61a38c5c..9cd7ea58 100644 --- a/backends/skype/skypeplugin.cpp +++ b/backends/skype/skypeplugin.cpp @@ -42,34 +42,6 @@ #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"); @@ -84,6 +56,7 @@ static gboolean transportDataReceived(GIOChannel *source, GIOCondition condition if (n <= 0) { LOG4CXX_INFO(logger, "Diconnecting from spectrum2 server"); Logging::shutdownLogging(); + google::protobuf::ShutdownProtobufLibrary(); exit(errno); } std::string d = std::string(buffer, n); @@ -104,6 +77,7 @@ static int create_socket(const char *host, int portno) { // strerror() will not work for gethostbyname() and hstrerror() // is supposedly obsolete Logging::shutdownLogging(); + google::protobuf::ShutdownProtobufLibrary(); exit(1); } serv_addr.sin_addr.s_addr = *((unsigned long *) hos->h_addr_list[0]); @@ -121,6 +95,7 @@ static int create_socket(const char *host, int portno) { static void io_destroy(gpointer data) { Logging::shutdownLogging(); + google::protobuf::ShutdownProtobufLibrary(); exit(1); } @@ -176,6 +151,7 @@ void SkypePlugin::handleLogoutRequest(const std::string &user, const std::string LOG4CXX_INFO(logger, "User wants to logout, logging out"); skype->logout(); Logging::shutdownLogging(); + google::protobuf::ShutdownProtobufLibrary(); exit(1); } } diff --git a/backends/swiften/main.cpp b/backends/swiften/main.cpp index 3ccc632e..5a2c1f37 100644 --- a/backends/swiften/main.cpp +++ b/backends/swiften/main.cpp @@ -30,6 +30,7 @@ using namespace boost::program_options; using namespace Transport; DEFINE_LOGGER(logger, "Swiften"); +DEFINE_LOGGER(logger_xml, "backend.xml"); // eventloop Swift::SimpleEventLoop *loop_; @@ -37,96 +38,60 @@ Swift::SimpleEventLoop *loop_; // Plugins class SwiftenPlugin; NetworkPlugin *np = NULL; +Swift::XMPPSerializer *serializer; -class MUCController { +class ForwardIQHandler : public Swift::IQHandler { public: - MUCController(const std::string &user, boost::shared_ptr client, const std::string &room, const std::string &nickname, const std::string &password) { + std::map m_id2resource; + + ForwardIQHandler(NetworkPlugin *np, const std::string &user) { + m_np = np; m_user = user; - m_room = room; - muc = client->getMUCManager()->createMUC(room); - if (!password.empty()) { - muc->setPassword(password); + } + + bool handleIQ(boost::shared_ptr iq) { + if (iq->getPayload() != NULL) { + return false; + } + if (iq->getType() == Swift::IQ::Get) { + m_id2resource[iq->getID()] = iq->getFrom().getResource(); } - 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 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 error) { - - } - - void part() { - muc->part(); + iq->setTo(m_user); + std::string xml = safeByteArrayToString(serializer->serializeElement(iq)); + m_np->sendRawXML(xml); + return true; } private: - Swift::MUC::ref muc; + NetworkPlugin *m_np; std::string m_user; - std::string m_room; - std::string m_nick; + }; -class SwiftenPlugin : public NetworkPlugin { +class SwiftenPlugin : public NetworkPlugin, Swift::XMPPParserClient { public: Swift::BoostNetworkFactories *m_factories; Swift::BoostIOServiceThread m_boostIOServiceThread; boost::shared_ptr 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(""); + LOG4CXX_INFO(logger, "Starting the plugin."); } @@ -137,10 +102,61 @@ class SwiftenPlugin : public NetworkPlugin { // This method has to call handleDataRead with all received data from network plugin server void _handleDataRead(boost::shared_ptr 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 element) { + boost::shared_ptr stanza = boost::dynamic_pointer_cast(element); + if (!stanza) { + return; + } + + std::string user = stanza->getFrom().toBare(); + + boost::shared_ptr client = m_users[user]; + if (!client) + return; + + stanza->setFrom(client->getJID()); + + boost::shared_ptr message = boost::dynamic_pointer_cast(stanza); + if (message) { + client->sendMessage(message); + return; + } + + boost::shared_ptr presence = boost::dynamic_pointer_cast(stanza); + if (presence) { + client->sendPresence(presence); + return; + } + + boost::shared_ptr iq = boost::dynamic_pointer_cast(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 &error) { std::string message = ""; bool reconnect = false; @@ -186,7 +202,7 @@ 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); + m_handlers.erase(user); } #ifndef WIN32 @@ -219,67 +235,53 @@ class SwiftenPlugin : public NetworkPlugin { } void handleSwiftPresenceChanged(const std::string &user, Swift::Presence::ref presence) { - boost::shared_ptr client = m_users[user]; - if (client->getMUCRegistry()->isMUC(presence->getFrom().toBare())) { - return; - } - - if (presence->getPayload() != NULL || presence->getPayload() != NULL) { - return; - } - - LOG4CXX_INFO(logger, user << ": " << presence->getFrom().toBare().toString() << " presence changed"); - - std::string message = presence->getStatus(); - std::string photo = ""; - - boost::shared_ptr update = presence->getPayload(); - if (update) { - photo = update->getPhotoHash(); - } - - boost::optional 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 groups; - handleBuddyChanged(user, presence->getFrom().toBare().toString(), presence->getFrom().toBare(), groups, (pbnetwork::StatusType) presence->getShow(), message, photo); - } +// boost::shared_ptr client = m_users[user]; +// if (client->getMUCRegistry()->isMUC(presence->getFrom().toBare())) { +// return; +// } +// +// if (presence->getPayload() != NULL || presence->getPayload() != NULL) { +// return; +// } +// +// LOG4CXX_INFO(logger, user << ": " << presence->getFrom().toBare().toString() << " presence changed"); +// +// std::string message = presence->getStatus(); +// std::string photo = ""; +// +// boost::shared_ptr update = presence->getPayload(); +// if (update) { +// photo = update->getPhotoHash(); +// } +// +// boost::optional 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 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) { - std::string body = message->getBody(); - boost::shared_ptr client = m_users[user]; - if (client) { - if (message->getType() == Swift::Message::Groupchat) { - boost::shared_ptr delay = message->getPayload(); - 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, "", ""); - } - } + 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, "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)); + client->getSubscriptionManager()->onPresenceSubscriptionRequest.connect(boost::bind(&SwiftenPlugin::handleSubscriptionRequest, this, user, _1, _2, _3)); + client->getSubscriptionManager()->onPresenceSubscriptionRevoked.connect(boost::bind(&SwiftenPlugin::handleSubscriptionRevoked, this, user, _1, _2)); Swift::ClientOptions opt; opt.allowPLAINWithoutTLS = true; client->connect(opt); + + boost::shared_ptr handler = boost::make_shared(this, user); + client->getIQRouter()->addHandler(handler); + m_handlers[user] = handler; + } + + void handleSubscriptionRequest(const std::string &user, const Swift::JID& jid, const std::string& message, Swift::Presence::ref presence) { + handleSwiftPresenceChanged(user, presence); + } + + void handleSubscriptionRevoked(const std::string &user, const Swift::JID& jid, const std::string& message) { + Swift::Presence::ref presence = Swift::Presence::create(); + presence->setTo(user); + presence->setFrom(jid); + presence->setType(Swift::Presence::Unsubscribe); + handleSwiftPresenceChanged(user, presence); } void handleLogoutRequest(const std::string &user, const std::string &legacyName) { @@ -306,43 +328,20 @@ 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); } } void handleMessageSendRequest(const std::string &user, const std::string &legacyName, const std::string &msg, const std::string &xhtml = "", const std::string &id = "") { - LOG4CXX_INFO(logger, "Sending message from " << user << " to " << legacyName << "."); - boost::shared_ptr client = m_users[user]; - if (client) { - boost::shared_ptr message(new Swift::Message()); - 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 muc = m_mucs[user][legacyName]; -// handleMessage(user, legacyName, msg, muc->getNickname(), xhtml); - } - - client->sendMessage(message); - } } void handleVCardRequest(const std::string &user, const std::string &legacyName, unsigned int id) { - boost::shared_ptr client = m_users[user]; - if (client) { - LOG4CXX_INFO(logger, user << ": fetching VCard of " << legacyName << " id=" << id); - Swift::GetVCardRequest::ref request = Swift::GetVCardRequest::create(Swift::JID(legacyName), client->getIQRouter()); - request->onResponse.connect(boost::bind(&SwiftenPlugin::handleSwiftVCardReceived, this, user, id, _1, _2)); - request->send(); - } } void handleBuddyUpdatedRequest(const std::string &user, const std::string &buddyName, const std::string &alias, const std::vector &groups) { boost::shared_ptr client = m_users[user]; if (client) { LOG4CXX_INFO(logger, user << ": Added/Updated buddy " << buddyName << "."); - if (!client->getRoster()->containsJID(buddyName)) { + if (!client->getRoster()->containsJID(buddyName) || client->getRoster()->getSubscriptionStateForJID(buddyName) != Swift::RosterItemPayload::Both) { Swift::RosterItemPayload item; item.setName(alias); item.setJID(buddyName); @@ -381,39 +380,17 @@ class SwiftenPlugin : public NetworkPlugin { } void handleJoinRoomRequest(const std::string &user, const std::string &room, const std::string &nickname, const std::string &password) { - boost::shared_ptr client = m_users[user]; - if (client) { - if (client->getMUCRegistry()->isMUC(room)) { - return; - } - boost::shared_ptr muc = boost::shared_ptr( 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 client = m_users[user]; - if (client) { - if (!client->getMUCRegistry()->isMUC(room)) { - return; - } - boost::shared_ptr 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 > m_users; - std::map > > m_mucs; + std::map > m_handlers; }; #ifndef WIN32 diff --git a/backends/swiften_raw/CMakeLists.txt b/backends/swiften_raw/CMakeLists.txt deleted file mode 100644 index ccd50e8e..00000000 --- a/backends/swiften_raw/CMakeLists.txt +++ /dev/null @@ -1,14 +0,0 @@ -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) - diff --git a/backends/swiften_raw/main.cpp b/backends/swiften_raw/main.cpp deleted file mode 100644 index 9f164a81..00000000 --- a/backends/swiften_raw/main.cpp +++ /dev/null @@ -1,427 +0,0 @@ -// 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 -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 m_id2resource; - - ForwardIQHandler(NetworkPlugin *np, const std::string &user) { - m_np = np; - m_user = user; - } - - bool handleIQ(boost::shared_ptr iq) { - if (iq->getPayload() != 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 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(""); - - LOG4CXX_INFO(logger, "Starting the plugin."); - } - - // NetworkPlugin uses this method to send the data to networkplugin server - void sendData(const std::string &string) { - m_conn->write(Swift::createSafeByteArray(string)); - } - - // This method has to call handleDataRead with all received data from network plugin server - void _handleDataRead(boost::shared_ptr data) { - 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 element) { - boost::shared_ptr stanza = boost::dynamic_pointer_cast(element); - if (!stanza) { - return; - } - - std::string user = stanza->getFrom().toBare(); - - boost::shared_ptr client = m_users[user]; - if (!client) - return; - - stanza->setFrom(client->getJID()); - - boost::shared_ptr message = boost::dynamic_pointer_cast(stanza); - if (message) { - client->sendMessage(message); - return; - } - - boost::shared_ptr presence = boost::dynamic_pointer_cast(stanza); - if (presence) { - client->sendPresence(presence); - return; - } - - boost::shared_ptr iq = boost::dynamic_pointer_cast(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 &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 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 client = m_users[user]; -// if (client->getMUCRegistry()->isMUC(presence->getFrom().toBare())) { -// return; -// } -// -// if (presence->getPayload() != NULL || presence->getPayload() != NULL) { -// return; -// } -// -// LOG4CXX_INFO(logger, user << ": " << presence->getFrom().toBare().toString() << " presence changed"); -// -// std::string message = presence->getStatus(); -// std::string photo = ""; -// -// boost::shared_ptr update = presence->getPayload(); -// if (update) { -// photo = update->getPhotoHash(); -// } -// -// boost::optional 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 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, " client = boost::make_shared(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 handler = boost::make_shared(this, user); - client->getIQRouter()->addHandler(handler); - m_handlers[user] = handler; - } - - void handleLogoutRequest(const std::string &user, const std::string &legacyName) { - boost::shared_ptr 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 &groups) { - boost::shared_ptr 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 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 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 &groups) { - boost::shared_ptr client = m_users[user]; - if (client) { - Swift::RosterItemPayload item(buddyName, "", Swift::RosterItemPayload::Remove); - boost::shared_ptr 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 > m_users; - std::map > 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; -} diff --git a/backends/twitter/TwitterResponseParser.cpp b/backends/twitter/TwitterResponseParser.cpp index a6d919a9..f8ef061e 100644 --- a/backends/twitter/TwitterResponseParser.cpp +++ b/backends/twitter/TwitterResponseParser.cpp @@ -137,11 +137,13 @@ std::vector getTimeline(std::string &xml) if(rootElement == NULL) { LOG4CXX_ERROR(logger, "Error while parsing XML") + LOG4CXX_ERROR(logger, xml) return statuses; } if(rootElement->getName() != "statuses") { - LOG4CXX_ERROR(logger, "XML doesn't correspond to timeline") + LOG4CXX_ERROR(logger, "XML doesn't correspond to timeline:") + LOG4CXX_ERROR(logger, xml) return statuses; } @@ -162,11 +164,13 @@ std::vector getDirectMessages(std::string &xml) if(rootElement == NULL) { LOG4CXX_ERROR(logger, "Error while parsing XML") + LOG4CXX_ERROR(logger, xml) return DMs; } if(rootElement->getName() != TwitterReponseTypes::directmessages) { - LOG4CXX_ERROR(logger, "XML doesn't correspond to direct-messages") + LOG4CXX_ERROR(logger, "XML doesn't correspond to direct-messages:") + LOG4CXX_ERROR(logger, xml) return DMs; } @@ -187,11 +191,13 @@ std::vector getUsers(std::string &xml) if(rootElement == NULL) { LOG4CXX_ERROR(logger, "Error while parsing XML") + LOG4CXX_ERROR(logger, xml) return users; } if(rootElement->getName() != TwitterReponseTypes::users) { - LOG4CXX_ERROR(logger, "XML doesn't correspond to user list") + LOG4CXX_ERROR(logger, "XML doesn't correspond to user list:") + LOG4CXX_ERROR(logger, xml) return users; } @@ -212,11 +218,13 @@ User getUser(std::string &xml) if(rootElement == NULL) { LOG4CXX_ERROR(logger, "Error while parsing XML") + LOG4CXX_ERROR(logger, xml) return user; } if(rootElement->getName() != TwitterReponseTypes::user) { LOG4CXX_ERROR(logger, "XML doesn't correspond to user object") + LOG4CXX_ERROR(logger, xml) return user; } @@ -231,11 +239,13 @@ std::vector getIDs(std::string &xml) if(rootElement == NULL) { LOG4CXX_ERROR(logger, "Error while parsing XML") + LOG4CXX_ERROR(logger, xml) return IDs; } if(rootElement->getName() != TwitterReponseTypes::id_list) { LOG4CXX_ERROR(logger, "XML doesn't correspond to id_list"); + LOG4CXX_ERROR(logger, xml) return IDs; } @@ -258,6 +268,7 @@ Error getErrorMessage(std::string &xml) if(rootElement == NULL) { LOG4CXX_ERROR(logger, "Error while parsing XML"); + LOG4CXX_ERROR(logger, xml) return resp; } diff --git a/backends/twitter/libtwitcurl/twitcurl.cpp b/backends/twitter/libtwitcurl/twitcurl.cpp index 6d2b2577..d8682ddf 100644 --- a/backends/twitter/libtwitcurl/twitcurl.cpp +++ b/backends/twitter/libtwitcurl/twitcurl.cpp @@ -28,7 +28,7 @@ m_curlProxyParamsSet( false ), m_curlLoginParamsSet( false ), m_curlCallbackParamsSet( false ), m_eApiFormatType( twitCurlTypes::eTwitCurlApiFormatXml ), -m_eProtocolType( twitCurlTypes::eTwitCurlProtocolHttp ) +m_eProtocolType( twitCurlTypes::eTwitCurlProtocolHttps ) { /* Clear callback buffers */ clearCurlCallbackBuffers(); @@ -41,6 +41,7 @@ m_eProtocolType( twitCurlTypes::eTwitCurlProtocolHttp ) getLastCurlError( dummyStr ); } curl_easy_setopt(m_curlHandle, CURLOPT_VERBOSE, 1); + curl_easy_setopt(m_curlHandle, CURLOPT_FOLLOWLOCATION, 1); curl_easy_setopt(m_curlHandle, CURLOPT_DEBUGFUNCTION, myDebugCallback); } diff --git a/include/transport/buddy.h b/include/transport/buddy.h index 06792dc8..9d47cf6f 100644 --- a/include/transport/buddy.h +++ b/include/transport/buddy.h @@ -80,7 +80,7 @@ class Buddy { /// \param only_new if True, this function returns Presence stanza only if it's different /// than the previously generated one. /// \return Presence stanza or NULL. - Swift::Presence::ref generatePresenceStanza(int features, bool only_new = false); + std::vector &generatePresenceStanzas(int features, bool only_new = false); void setBlocked(bool block) { if (block) @@ -124,6 +124,8 @@ class Buddy { void sendPresence(); + void handleRawPresence(Swift::Presence::ref); + /// Handles VCard from legacy network and forwards it to XMPP user. /// \param id ID used in IQ-result. @@ -157,6 +159,8 @@ class Buddy { /// \return avatar hash or empty string. virtual std::string getIconHash() = 0; + virtual bool isAvailable() = 0; + /// Returns legacy name of buddy from JID. /// \param jid Jabber ID. @@ -167,10 +171,10 @@ class Buddy { protected: void generateJID(); Swift::JID m_jid; + std::vector m_presences; private: long m_id; -// Swift::Presence::ref m_lastPresence; BuddyFlag m_flags; RosterManager *m_rosterManager; Subscription m_subscription; diff --git a/include/transport/config.h b/include/transport/config.h index 8f760fea..97044a1d 100644 --- a/include/transport/config.h +++ b/include/transport/config.h @@ -52,6 +52,7 @@ const myType &safeAs(const boost::program_options::variable_value &var, const my #define CONFIG_STRING_DEFAULTED(PTR, KEY, DEF) ((*PTR).hasKey(KEY) ? Transport::safeAs((*PTR)[KEY], DEF) : DEF) #define CONFIG_BOOL_DEFAULTED(PTR, KEY, DEF) ((*PTR).hasKey(KEY) ? Transport::safeAs((*PTR)[KEY], DEF) : DEF) #define CONFIG_LIST_DEFAULTED(PTR, KEY, DEF) ((*PTR).hasKey(KEY) ? Transport::safeAs >((*PTR)[KEY], DEF) : DEF) +#define CONFIG_INT_DEFAULTED(PTR, KEY, DEF) ((*PTR).hasKey(KEY) ? Transport::safeAs((*PTR)[KEY], DEF) : DEF) namespace Transport { diff --git a/include/transport/conversation.h b/include/transport/conversation.h index 78aa5266..241f6662 100644 --- a/include/transport/conversation.h +++ b/include/transport/conversation.h @@ -40,7 +40,8 @@ class Conversation { PARTICIPANT_FLAG_BANNED = 4, PARTICIPANT_FLAG_NOT_AUTHORIZED = 8, PARTICIPANT_FLAG_ME = 16, - PARTICIPANT_FLAG_KICKED = 32 + PARTICIPANT_FLAG_KICKED = 32, + PARTICIPANT_FLAG_ROOM_NOT_FOUD = 64 } ParticipantFlag; typedef struct _Participant { @@ -71,6 +72,7 @@ class Conversation { void handleMessage(boost::shared_ptr &message, const std::string &nickname = ""); void handleRawMessage(boost::shared_ptr &message); + void handleRawPresence(Swift::Presence::ref presence); /// Handles participant change in MUC. @@ -84,9 +86,7 @@ class Conversation { /// Sets XMPP user nickname in MUC rooms. /// \param nickname XMPP user nickname in MUC rooms. - void setNickname(const std::string &nickname) { - m_nickname = nickname; - } + void setNickname(const std::string &nickname); const std::string &getNickname() { return m_nickname; @@ -152,6 +152,7 @@ class Conversation { private: Swift::Presence::ref generatePresence(const std::string &nick, int flag, int status, const std::string &statusMessage, const std::string &newname = ""); + void cacheMessage(boost::shared_ptr &message); private: ConversationManager *m_conversationManager; @@ -161,10 +162,17 @@ class Conversation { bool m_muc; Swift::JID m_jid; std::list m_jids; - std::map m_participants; - boost::shared_ptr m_subject; bool m_sentInitialPresence; + bool m_nicknameChanged; + + // TODO: Move this to some extra class to cache the most used + // rooms across different accounts. Just now if we have 10 users + // connected to single room, we store all those things 10 times. + // It would be also great to store last 100 messages per room + // every time, so we can get history messages for IRC for example. + boost::shared_ptr m_subject; std::list > m_cachedMessages; + std::map m_participants; }; } diff --git a/include/transport/localbuddy.h b/include/transport/localbuddy.h index 69c5c153..ebe2c945 100644 --- a/include/transport/localbuddy.h +++ b/include/transport/localbuddy.h @@ -39,10 +39,10 @@ class LocalBuddy : public Buddy { std::string getName() { return m_name; } bool setName(const std::string &name); - bool getStatus(Swift::StatusShow &status, std::string &statusMessage) { - status = m_status; - statusMessage = m_statusMessage; - return true; + bool getStatus(Swift::StatusShow &status, std::string &statusMessage); + + bool isAvailable() { + return m_status.getType() != Swift::StatusShow::None; } void setStatus(const Swift::StatusShow &status, const std::string &statusMessage); diff --git a/include/transport/networkplugin.h b/include/transport/networkplugin.h index 7afe3358..3230ddb6 100644 --- a/include/transport/networkplugin.h +++ b/include/transport/networkplugin.h @@ -39,7 +39,8 @@ class NetworkPlugin { class PluginConfig { public: - PluginConfig() : m_needPassword(true), m_needRegistration(false), m_supportMUC(false), m_rawXML(false) {} + PluginConfig() : m_needPassword(true), m_needRegistration(false), m_supportMUC(false), m_rawXML(false), + m_disableJIDEscaping(false) {} virtual ~PluginConfig() {} void setNeedRegistration(bool needRegistration = false) { m_needRegistration = needRegistration; } @@ -47,12 +48,14 @@ class NetworkPlugin { void setSupportMUC(bool supportMUC = true) { m_supportMUC = supportMUC; } void setExtraFields(const std::vector &fields) { m_extraFields = fields; } void setRawXML(bool rawXML = false) { m_rawXML = rawXML; } + void disableJIDEscaping() { m_disableJIDEscaping = true; } private: bool m_needPassword; bool m_needRegistration; bool m_supportMUC; bool m_rawXML; + bool m_disableJIDEscaping; std::vector m_extraFields; friend class NetworkPlugin; diff --git a/include/transport/protocol.proto b/include/transport/protocol.proto index 4a5b8f00..8d48386d 100644 --- a/include/transport/protocol.proto +++ b/include/transport/protocol.proto @@ -95,6 +95,7 @@ enum ParticipantFlag { PARTICIPANT_FLAG_NOT_AUTHORIZED = 8; PARTICIPANT_FLAG_ME = 16; PARTICIPANT_FLAG_KICKED = 32; + PARTICIPANT_FLAG_ROOM_NOT_FOUND = 64; } message Participant { diff --git a/include/transport/transport.h b/include/transport/transport.h index 51f69018..318ec673 100644 --- a/include/transport/transport.h +++ b/include/transport/transport.h @@ -155,6 +155,10 @@ namespace Transport { boost::signal)> onRawIQReceived; + bool isRawXMLEnabled() { + return m_rawXML; + } + private: void handleConnected(); void handleConnectionError(const Swift::ComponentError &error); diff --git a/src/utf8.h b/include/transport/utf8.h similarity index 100% rename from src/utf8.h rename to include/transport/utf8.h diff --git a/src/utf8/checked.h b/include/transport/utf8/checked.h similarity index 90% rename from src/utf8/checked.h rename to include/transport/utf8/checked.h index c534e2a0..12b15317 100644 --- a/src/utf8/checked.h +++ b/include/transport/utf8/checked.h @@ -94,6 +94,37 @@ namespace utf8 return result; } + template + output_iterator remove_invalid(octet_iterator start, octet_iterator end, output_iterator out) + { + while (start != end) { + octet_iterator sequence_start = start; + internal::utf_error err_code = internal::validate_next(start, end); + switch (err_code) { + case internal::OK : + for (octet_iterator it = sequence_start; it != start; ++it) + *out++ = *it; + break; + case internal::NOT_ENOUGH_ROOM: + throw not_enough_room(); + case internal::INVALID_LEAD: +// append (replacement, out); + ++start; + break; + case internal::INCOMPLETE_SEQUENCE: + case internal::OVERLONG_SEQUENCE: + case internal::INVALID_CODE_POINT: +// append (replacement, out); + ++start; + // just one replacement mark for the sequence + while (internal::is_trail(*start) && start != end) + ++start; + break; + } + } + return out; + } + template output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out, uint32_t replacement) { diff --git a/src/utf8/core.h b/include/transport/utf8/core.h similarity index 100% rename from src/utf8/core.h rename to include/transport/utf8/core.h diff --git a/src/utf8/unchecked.h b/include/transport/utf8/unchecked.h similarity index 100% rename from src/utf8/unchecked.h rename to include/transport/utf8/unchecked.h diff --git a/plugin/cpp/networkplugin.cpp b/plugin/cpp/networkplugin.cpp index fb660b1f..978d3e56 100644 --- a/plugin/cpp/networkplugin.cpp +++ b/plugin/cpp/networkplugin.cpp @@ -74,6 +74,8 @@ void NetworkPlugin::sendConfig(const PluginConfig &cfg) { data += "[features]\n"; data += std::string("muc=") + (cfg.m_supportMUC ? "1" : "0") + "\n"; data += std::string("rawxml=") + (cfg.m_rawXML ? "1" : "0") + "\n"; + data += std::string("disable_jid_escaping=") + (cfg.m_disableJIDEscaping ? "1" : "0") + "\n"; + pbnetwork::BackendConfig m; m.set_config(data); diff --git a/spectrum/src/CMakeLists.txt b/spectrum/src/CMakeLists.txt index f401f2b4..005fbb32 100644 --- a/spectrum/src/CMakeLists.txt +++ b/spectrum/src/CMakeLists.txt @@ -24,11 +24,17 @@ endif() INSTALL(TARGETS spectrum2 RUNTIME DESTINATION bin) INSTALL(FILES - sample2.cfg + sample2_gateway.cfg RENAME spectrum.cfg.example DESTINATION /etc/spectrum2/transports ) +INSTALL(FILES + sample2.cfg + RENAME spectrum_server_mode.cfg.example + DESTINATION /etc/spectrum2/transports + ) + INSTALL(FILES backend-logging.cfg DESTINATION /etc/spectrum2 diff --git a/spectrum/src/sample.cfg b/spectrum/src/sample.cfg index 75735b97..edb5af77 100644 --- a/spectrum/src/sample.cfg +++ b/spectrum/src/sample.cfg @@ -14,8 +14,8 @@ admin_password=test #cert_password=test #password to that certificate if any users_per_backend=10 #backend=../..//backends/swiften/spectrum2_swiften_backend -#backend=../../backends/twitter/spectrum2_twitter_backend -backend=/home/hanzz/code/libtransport/backends/libcommuni/spectrum2_libcommuni_backend +backend=../../backends/twitter/spectrum2_twitter_backend +#backend=/home/hanzz/code/libtransport/backends/libcommuni/spectrum2_libcommuni_backend protocol=prpl-jabber #protocol=prpl-msn #protocol=any diff --git a/spectrum/src/sample2.cfg b/spectrum/src/sample2.cfg index 5a91d5b8..5d96d391 100644 --- a/spectrum/src/sample2.cfg +++ b/spectrum/src/sample2.cfg @@ -6,14 +6,9 @@ server_mode = 1 #user=spectrum #group=spectrum -# JID of Spectrum instance. +# Hostname (JID) of this Spectrum 2 server mode instance. jid = localhost -# Password used to connect the XMPP server in gateway mode. -# In server mode, this option is ignored. -password = secret - -# XMPP server to which Spectrum connects in gateway mode. # To bind to all ipv4 interfaces, use server=0.0.0.0 server = 127.0.0.1 diff --git a/spectrum/src/sample2_gateway.cfg b/spectrum/src/sample2_gateway.cfg new file mode 100644 index 00000000..9ec4e60b --- /dev/null +++ b/spectrum/src/sample2_gateway.cfg @@ -0,0 +1,107 @@ +[service] +# The name of user/group Spectrum runs as. +#user=spectrum +#group=spectrum + +# JID of Spectrum instance. +jid = icq.domain.tld + +# Password used to connect the XMPP server. +password = secret + +# XMPP server to which Spectrum connects in gateway mode. +server = 127.0.0.1 + +# XMPP server port. +port = 5347 + +# Interface on which Spectrum listens for backends. +backend_host = localhost + +# Port on which Spectrum listens for backends. +# By default Spectrum chooses random backend port and there's +# no need to change it normally +#backend_port=10001 + +# Number of users per one legacy network backend. +users_per_backend=10 + +# Full path to backend binary. +backend=/usr/bin/spectrum2_libpurple_backend +#backend=/usr/bin/spectrum2_libcommuni_backend +# For skype: +#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 +#protocol=prpl-msn +#protocol=prpl-icq + +# prpl-any means that user sets his protocol in his JID which has to be +# in following format: protocol.username@domain.tld +# So for example: prpl-jabber.hanzz.k%gmail.com@domain.tld +#protocol=prpl-any + +[identity] +# Name of Spectrum instance in service discovery +name=Spectrum Jabber Transport + +# Type of transport ("msn", "icq", "xmpp"). +# Check http://xmpp.org/registrar/disco-categories.html#gateway +type=xmpp + +# Category of transport, default is "gateway +#category=gateway + +[logging] +# log4cxx/log4j logging configuration file in ini format used for main spectrum2 instance. +config = /etc/spectrum2/logging.cfg + +# log4cxx/log4j logging configuration file in ini format used for backends. +backend_config = /etc/spectrum2/backend-logging.cfg + +[database] +# Database backend type +# "sqlite3", "mysql", "pqxx", or "none" without database backend +type = sqlite3 + +# For SQLite3: Full path to database +# For MySQL and PostgreSQL: name of database +# default database = /var/lib/spectrum2/$jid/database.sql +#database = jabber_transport + +# Server. +#server = localhost + +# Port. +#port = 0 + +# User. +#user = spectrum + +# Paasword. +#password = secret + +# Prefix used for tables +#prefix = jabber_ + +# Connection string (for pqxx only!) +# If you set this, it ignores every other database option except for type and prefix. +#connectionstring = host=localhost user=specturm password=secret + +[registration] +# Enable public registrations +enable_public_registration=1 + +# Text to display upon user registration form +#username_label=Jabber JID (e.g. user@server.tld): +#instructions=Enter your remote jabber JID and password as well as your local username and password + +# If True a local jabber account on is needed +# for transport registration, the idea is to enable public registration +# from other servers, but only for users, who have already local accounts +#require_local_account=1 +#local_username_label=Local username (without @server.tld): +#local_account_server=localhost +#local_account_server_timeout=10000 + diff --git a/src/admininterface.cpp b/src/admininterface.cpp index d8591c18..d961a0a3 100644 --- a/src/admininterface.cpp +++ b/src/admininterface.cpp @@ -176,7 +176,9 @@ void AdminInterface::handleQuery(Swift::Message::ref message) { unsigned long per_user = 0; const std::list &backends = m_server->getBackends(); BOOST_FOREACH(NetworkPluginServer::Backend * backend, backends) { - per_user += (backend->res - backend->init_res); + if (backend->res >= backend->init_res) { + per_user += (backend->res - backend->init_res); + } } message->setBody(boost::lexical_cast(per_user / m_userManager->getUserCount())); @@ -220,7 +222,7 @@ void AdminInterface::handleQuery(Swift::Message::ref message) { int id = 1; const std::list &backends = m_server->getBackends(); BOOST_FOREACH(NetworkPluginServer::Backend * backend, backends) { - if (backend->users.size() == 0) { + if (backend->users.size() == 0 || backend->res < backend->init_res) { lst += "Backend " + boost::lexical_cast(id) + " (ID=" + backend->id + "): 0\n"; } else { diff --git a/src/buddy.cpp b/src/buddy.cpp index 4c112052..00aea73c 100644 --- a/src/buddy.cpp +++ b/src/buddy.cpp @@ -40,8 +40,8 @@ Buddy::~Buddy() { } void Buddy::sendPresence() { - Swift::Presence::ref presence = generatePresenceStanza(255); - if (presence) { + std::vector &presences = generatePresenceStanzas(255); + BOOST_FOREACH(Swift::Presence::ref presence, presences) { m_rosterManager->getUser()->getComponent()->getStanzaChannel()->sendPresence(presence); } } @@ -89,33 +89,46 @@ Buddy::Subscription Buddy::getSubscription() { return m_subscription; } -Swift::Presence::ref Buddy::generatePresenceStanza(int features, bool only_new) { - std::string alias = getAlias(); - std::string name = getSafeName(); +void Buddy::handleRawPresence(Swift::Presence::ref presence) { + for (std::vector::iterator it = m_presences.begin(); it != m_presences.end(); it++) { + if ((*it)->getFrom() == presence->getFrom()) { + m_presences.erase(it); + break; + } + } - Swift::StatusShow s; - std::string statusMessage; - if (!getStatus(s, statusMessage)) - return Swift::Presence::ref(); + m_presences.push_back(presence); + m_rosterManager->getUser()->getComponent()->getStanzaChannel()->sendPresence(presence); +} +std::vector &Buddy::generatePresenceStanzas(int features, bool only_new) { if (m_jid.getNode().empty()) { generateJID(); } + Swift::StatusShow s; + std::string statusMessage; + if (!getStatus(s, statusMessage)) { + for (std::vector::iterator it = m_presences.begin(); it != m_presences.end(); it++) { + if ((*it)->getFrom() == m_jid) { + m_presences.erase(it); + break; + } + } + return m_presences; + } + Swift::Presence::ref presence = Swift::Presence::create(); presence->setTo(m_rosterManager->getUser()->getJID().toBare()); + presence->setFrom(m_jid); 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) { @@ -131,6 +144,15 @@ Swift::Presence::ref Buddy::generatePresenceStanza(int features, bool only_new) } } + BOOST_FOREACH(Swift::Presence::ref &p, m_presences) { + if (p->getFrom() == presence->getFrom()) { + p = presence; + return m_presences; + } + } + + m_presences.push_back(presence); + // if (only_new) { // if (m_lastPresence) // m_lastPresence->setTo(Swift::JID("")); @@ -140,7 +162,7 @@ Swift::Presence::ref Buddy::generatePresenceStanza(int features, bool only_new) // m_lastPresence = presence; // } - return presence; + return m_presences; } std::string Buddy::getSafeName() { diff --git a/src/config.cpp b/src/config.cpp index f1a476ae..7df60def 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -316,6 +316,7 @@ void Config::updateBackendConfig(const std::string &backendConfig) { ("features.receipts", value()->default_value(false), "") ("features.muc", value()->default_value(false), "") ("features.rawxml", value()->default_value(false), "") + ("features.disable_jid_escaping", value()->default_value(false), "") ; std::stringstream ifs(backendConfig); @@ -325,6 +326,12 @@ void Config::updateBackendConfig(const std::string &backendConfig) { notify(m_backendConfig); onBackendConfigUpdated(); + + if (CONFIG_BOOL_DEFAULTED(this, "features.disable_jid_escaping", false)) { + Variables::iterator it(m_variables.find("service.jid_escaping")); + boost::program_options::variable_value& vx(it->second); + vx.value() = false; + } } Config *Config::createFromArgs(int argc, char **argv, std::string &error, std::string &host, int &port) { diff --git a/src/conversation.cpp b/src/conversation.cpp index 7f4fe73c..387af4ce 100644 --- a/src/conversation.cpp +++ b/src/conversation.cpp @@ -39,6 +39,7 @@ Conversation::Conversation(ConversationManager *conversationManager, const std:: m_muc = isMUC; m_jid = m_conversationManager->getUser()->getJID().toBare(); m_sentInitialPresence = false; + m_nicknameChanged = false; if (CONFIG_BOOL_DEFAULTED(conversationManager->getComponent()->getConfig(), "features.rawxml", false)) { m_sentInitialPresence = true; @@ -86,17 +87,21 @@ void Conversation::setRoom(const std::string &room) { m_legacyName = m_room + "/" + m_legacyName; } +void Conversation::cacheMessage(boost::shared_ptr &message) { + boost::posix_time::ptime timestamp = boost::posix_time::second_clock::universal_time(); + boost::shared_ptr delay(boost::make_shared()); + delay->setStamp(timestamp); + message->addPayload(delay); + m_cachedMessages.push_back(message); + if (m_cachedMessages.size() > 100) { + m_cachedMessages.pop_front(); + } +} + void Conversation::handleRawMessage(boost::shared_ptr &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 delay(boost::make_shared()); - delay->setStamp(timestamp); - message->addPayload(delay); - m_cachedMessages.push_back(message); - if (m_cachedMessages.size() > 100) { - m_cachedMessages.pop_front(); - } + cacheMessage(message); } else { m_conversationManager->getComponent()->getStanzaChannel()->sendMessage(message); @@ -104,14 +109,7 @@ void Conversation::handleRawMessage(boost::shared_ptr &message) } else { if (m_jids.empty()) { - boost::posix_time::ptime timestamp = boost::posix_time::second_clock::universal_time(); - boost::shared_ptr delay(boost::make_shared()); - delay->setStamp(timestamp); - message->addPayload(delay); - m_cachedMessages.push_back(message); - if (m_cachedMessages.size() > 100) { - m_cachedMessages.pop_front(); - } + cacheMessage(message); } else { BOOST_FOREACH(const Swift::JID &jid, m_jids) { @@ -201,10 +199,9 @@ void Conversation::handleMessage(boost::shared_ptr &message, con } void Conversation::sendParticipants(const Swift::JID &to) { - for (std::map::iterator it = m_participants.begin(); it != m_participants.end(); it++) { - Swift::Presence::ref presence = generatePresence(it->first, it->second.flag, it->second.status, it->second.statusMessage, ""); - presence->setTo(to); - m_conversationManager->getComponent()->getStanzaChannel()->sendPresence(presence); + for (std::map::iterator it = m_participants.begin(); it != m_participants.end(); it++) { + (*it).second->setTo(to); + m_conversationManager->getComponent()->getStanzaChannel()->sendPresence((*it).second); } } @@ -218,6 +215,17 @@ void Conversation::sendCachedMessages(const Swift::JID &to) { } m_conversationManager->getComponent()->getStanzaChannel()->sendMessage(*it); } + + if (m_subject) { + if (to.isValid()) { + m_subject->setTo(to); + } + else { + m_subject->setTo(m_jid.toBare()); + } + m_conversationManager->getComponent()->getStanzaChannel()->sendMessage(m_subject); + } + m_cachedMessages.clear(); } @@ -257,13 +265,25 @@ Swift::Presence::ref Conversation::generatePresence(const std::string &nick, int delete p; presence->setType(Swift::Presence::Error); presence->addPayload(boost::shared_ptr(new Swift::MUCPayload())); - presence->addPayload(boost::shared_ptr(new Swift::ErrorPayload(Swift::ErrorPayload::NotAuthorized, Swift::ErrorPayload::Auth))); + presence->addPayload(boost::shared_ptr(new Swift::ErrorPayload(Swift::ErrorPayload::NotAuthorized, Swift::ErrorPayload::Auth, statusMessage))); + return presence; + } + else if (flag & PARTICIPANT_FLAG_ROOM_NOT_FOUD) { + delete p; + presence->setType(Swift::Presence::Error); + presence->addPayload(boost::shared_ptr(new Swift::MUCPayload())); + presence->addPayload(boost::shared_ptr(new Swift::ErrorPayload(Swift::ErrorPayload::ItemNotFound, Swift::ErrorPayload::Cancel, statusMessage))); return presence; } else { Swift::MUCUserPayload::StatusCode c; c.code = 110; p->addStatusCode(c); + if (m_nicknameChanged) { + Swift::MUCUserPayload::StatusCode c; + c.code = 210; + p->addStatusCode(c); + } m_sentInitialPresence = true; } } @@ -292,6 +312,20 @@ Swift::Presence::ref Conversation::generatePresence(const std::string &nick, int return presence; } + +void Conversation::setNickname(const std::string &nickname) { + if (!nickname.empty() && m_nickname != nickname) { + m_nicknameChanged = true; + } + m_nickname = nickname; +} + +void Conversation::handleRawPresence(Swift::Presence::ref presence) { + // TODO: Detect nickname change. + m_conversationManager->getComponent()->getStanzaChannel()->sendPresence(presence); + m_participants[presence->getFrom().getResource()] = presence; +} + 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); @@ -299,11 +333,7 @@ void Conversation::handleParticipantChanged(const std::string &nick, Conversatio m_participants.erase(nick); } else { - Participant p; - p.flag = flag; - p.status = status; - p.statusMessage = statusMessage; - m_participants[nick] = p; + m_participants[nick] = presence; } diff --git a/src/localbuddy.cpp b/src/localbuddy.cpp index 47fe2b7f..ef695e17 100644 --- a/src/localbuddy.cpp +++ b/src/localbuddy.cpp @@ -102,4 +102,13 @@ void LocalBuddy::setGroups(const std::vector &groups) { } } +bool LocalBuddy::getStatus(Swift::StatusShow &status, std::string &statusMessage) { + if (getRosterManager()->getUser()->getComponent()->isRawXMLEnabled()) { + return false; + } + status = m_status; + statusMessage = m_statusMessage; + return true; +} + } diff --git a/src/logging.cpp b/src/logging.cpp index 947eb2bb..61c584b0 100644 --- a/src/logging.cpp +++ b/src/logging.cpp @@ -147,9 +147,9 @@ static void initLogging(Config *config, std::string key, bool only_create_dir = dirs.push_back(dir); } } +#ifndef WIN32 mode_t old_cmask; // create directories -#ifndef WIN32 old_cmask = umask(0007); #endif diff --git a/src/networkpluginserver.cpp b/src/networkpluginserver.cpp index 03e1c1a6..4ea242e6 100644 --- a/src/networkpluginserver.cpp +++ b/src/networkpluginserver.cpp @@ -52,7 +52,7 @@ #include "boost/date_time/posix_time/posix_time.hpp" #include "boost/signal.hpp" -#include "utf8.h" +#include "transport/utf8.h" #include #include @@ -246,8 +246,8 @@ static void handleBuddyPayload(LocalBuddy *buddy, const pbnetwork::Buddy &payloa // Change groups if it's not empty. The same as above... std::vector groups; for (int i = 0; i < payload.group_size(); i++) { - std::string group = payload.group(i); - utf8::replace_invalid(payload.group(i).begin(), payload.group(i).end(), group.begin(), '_'); + std::string group; + utf8::replace_invalid(payload.group(i).begin(), payload.group(i).end(), std::back_inserter(group), '_'); groups.push_back(group); } if (!groups.empty()) { @@ -490,16 +490,16 @@ void NetworkPluginServer::handleVCardPayload(const std::string &data) { // TODO: ERROR return; } - std::string field = payload.fullname(); + std::string field; boost::shared_ptr vcard(new Swift::VCard()); - utf8::replace_invalid(payload.fullname().begin(), payload.fullname().end(), field.begin(), '_'); + utf8::replace_invalid(payload.fullname().begin(), payload.fullname().end(), std::back_inserter(field), '_'); vcard->setFullName(field); - field = payload.nickname(); + field.clear(); - utf8::replace_invalid(payload.nickname().begin(), payload.nickname().end(), field.begin(), '_'); + utf8::replace_invalid(payload.nickname().begin(), payload.nickname().end(), std::back_inserter(field), '_'); vcard->setNickname(field); vcard->setPhoto(Swift::createByteArray(payload.photo())); @@ -1053,10 +1053,17 @@ void NetworkPluginServer::handleElement(boost::shared_ptr elemen boost::shared_ptr presence = boost::dynamic_pointer_cast(stanza); if (presence) { - m_component->getStanzaChannel()->sendPresence(presence); if (buddy) { - buddy->m_statusMessage = presence->getStatus(); - buddy->m_status = Swift::StatusShow(presence->getShow()); + if (!buddy->isAvailable() && presence->getType() != Swift::Presence::Unavailable) { + buddy->m_status.setType(Swift::StatusShow::Online); + } + buddy->handleRawPresence(presence); + } + else if (conv) { + conv->handleRawPresence(presence); + } + else { + m_component->getStanzaChannel()->sendPresence(presence); } return; @@ -1446,6 +1453,8 @@ void NetworkPluginServer::handleUserPresenceChanged(User *user, Swift::Presence: if (presence->getShow() == Swift::StatusShow::None) return; + handleRawPresenceReceived(presence); + UserInfo userInfo = user->getUserInfo(); pbnetwork::Status status; diff --git a/src/pqxxbackend.cpp b/src/pqxxbackend.cpp index f9b399d3..73748d08 100644 --- a/src/pqxxbackend.cpp +++ b/src/pqxxbackend.cpp @@ -141,7 +141,7 @@ bool PQXXBackend::createDatabase() { "UNIQUE (ver)" ");"); - exec("INSERT INTO db_version (ver) VALUES ('1');"); + exec("INSERT INTO " + m_prefix + "db_version (ver) VALUES ('1');"); } return true; @@ -295,12 +295,12 @@ bool PQXXBackend::getBuddies(long id, std::list &roster) { BuddyInfo b; std::string group; - b.id = r[0][0].as(); - b.legacyName = r[0][1].as(); - b.subscription = r[0][2].as(); - b.alias = r[0][3].as(); - group = r[0][4].as(); - b.flags = r[0][5].as(); + b.id = (*it)[0].as(); + b.legacyName = (*it)[1].as(); + b.subscription = (*it)[2].as(); + b.alias = (*it)[3].as(); + group = (*it)[4].as(); + b.flags = (*it)[5].as(); if (!group.empty()) { b.groups = StorageBackend::deserializeGroups(group); @@ -317,10 +317,10 @@ bool PQXXBackend::getBuddies(long id, std::list &roster) { std::string key; std::string val; - buddy_id = r[0][0].as(); - var.type = r[0][1].as(); - key = r[0][2].as(); - val = r[0][3].as(); + buddy_id = (*it)[0].as(); + var.type = (*it)[1].as(); + key = (*it)[2].as(); + val = (*it)[3].as(); switch (var.type) { case TYPE_BOOLEAN: var.b = atoi(val.c_str()); @@ -353,10 +353,10 @@ bool PQXXBackend::getBuddies(long id, std::list &roster) { bool PQXXBackend::removeUser(long id) { try { pqxx::nontransaction txn(*m_conn); - txn.exec("DELETE FROM " + m_prefix + "users SET id=" + pqxx::to_string(id)); - txn.exec("DELETE FROM " + m_prefix + "buddies SET user_id=" + pqxx::to_string(id)); - txn.exec("DELETE FROM " + m_prefix + "user_settings SET user_id=" + pqxx::to_string(id)); - txn.exec("DELETE FROM " + m_prefix + "buddies_settings SET user_id=" + pqxx::to_string(id)); + txn.exec("DELETE FROM " + m_prefix + "users WHERE id=" + pqxx::to_string(id)); + txn.exec("DELETE FROM " + m_prefix + "buddies WHERE user_id=" + pqxx::to_string(id)); + txn.exec("DELETE FROM " + m_prefix + "users_settings WHERE user_id=" + pqxx::to_string(id)); + txn.exec("DELETE FROM " + m_prefix + "buddies_settings WHERE user_id=" + pqxx::to_string(id)); return true; } diff --git a/src/rostermanager.cpp b/src/rostermanager.cpp index f9c47e77..b91a9e43 100644 --- a/src/rostermanager.cpp +++ b/src/rostermanager.cpp @@ -109,7 +109,7 @@ void RosterManager::removeBuddy(const std::string &name) { return; } - if (m_component->inServerMode() || m_remoteRosterRequest) { + if (m_component->inServerMode() || m_supportRemoteRoster) { sendBuddyRosterRemove(buddy); } else { @@ -200,7 +200,7 @@ void RosterManager::sendBuddyUnsubscribePresence(Buddy *buddy) { void RosterManager::sendBuddySubscribePresence(Buddy *buddy) { Swift::Presence::ref response = Swift::Presence::create(); response->setTo(m_user->getJID()); - response->setFrom(buddy->getJID()); + response->setFrom(buddy->getJID().toBare()); response->setType(Swift::Presence::Subscribe); if (!buddy->getAlias().empty()) { response->addPayload(boost::make_shared(buddy->getAlias())); @@ -255,9 +255,11 @@ void RosterManager::storeBuddy(Buddy *buddy) { void RosterManager::handleBuddyRosterPushResponse(Swift::ErrorPayload::ref error, Swift::SetRosterRequest::ref request, const std::string &key) { LOG4CXX_INFO(logger, "handleBuddyRosterPushResponse called for buddy " << key); if (m_buddies[key] != NULL) { - Swift::Presence::ref presence = m_buddies[key]->generatePresenceStanza(255); - if (presence && presence->getType() == Swift::Presence::Available) { - m_component->getStanzaChannel()->sendPresence(presence); + if (m_buddies[key]->isAvailable()) { + std::vector &presences = m_buddies[key]->generatePresenceStanzas(255); + BOOST_FOREACH(Swift::Presence::ref &presence, presences) { + m_component->getStanzaChannel()->sendPresence(presence); + } } } else { @@ -440,13 +442,13 @@ void RosterManager::handleSubscription(Swift::Presence::ref presence) { Buddy *buddy = getBuddy(Buddy::JIDToLegacyName(presence->getTo())); if (buddy) { + std::vector &presences = buddy->generatePresenceStanzas(255); switch (presence->getType()) { // buddy is already there, so nothing to do, just answer case Swift::Presence::Subscribe: onBuddyAdded(buddy); response->setType(Swift::Presence::Subscribed); - currentPresence = buddy->generatePresenceStanza(255); - if (currentPresence) { + BOOST_FOREACH(Swift::Presence::ref ¤tPresence, presences) { currentPresence->setTo(presence->getFrom()); m_component->getStanzaChannel()->sendPresence(currentPresence); } @@ -587,8 +589,11 @@ void RosterManager::sendCurrentPresences(const Swift::JID &to) { if (!buddy) { continue; } - Swift::Presence::ref presence = buddy->generatePresenceStanza(255); - if (presence && presence->getType() == Swift::Presence::Available) { + if (!buddy->isAvailable()) { + continue; + } + std::vector &presences = buddy->generatePresenceStanzas(255); + BOOST_FOREACH(Swift::Presence::ref &presence, presences) { presence->setTo(to); m_component->getStanzaChannel()->sendPresence(presence); } @@ -598,8 +603,8 @@ void RosterManager::sendCurrentPresences(const Swift::JID &to) { void RosterManager::sendCurrentPresence(const Swift::JID &from, const Swift::JID &to) { Buddy *buddy = getBuddy(Buddy::JIDToLegacyName(from)); if (buddy) { - Swift::Presence::ref presence = buddy->generatePresenceStanza(255); - if (presence) { + std::vector &presences = buddy->generatePresenceStanzas(255); + BOOST_FOREACH(Swift::Presence::ref &presence, presences) { presence->setTo(to); m_component->getStanzaChannel()->sendPresence(presence); } @@ -619,11 +624,18 @@ void RosterManager::sendUnavailablePresences(const Swift::JID &to) { if (!buddy) { continue; } - Swift::Presence::ref presence = buddy->generatePresenceStanza(255); - if (presence && presence->getType() == Swift::Presence::Available) { + + if (!buddy->isAvailable()) { + continue; + } + + std::vector &presences = buddy->generatePresenceStanzas(255); + BOOST_FOREACH(Swift::Presence::ref &presence, presences) { + Swift::Presence::Type type = presence->getType(); presence->setTo(to); presence->setType(Swift::Presence::Unavailable); m_component->getStanzaChannel()->sendPresence(presence); + presence->setType(type); } } diff --git a/src/tests/basictest.cpp b/src/tests/basictest.cpp index 6f3545f6..f1df8288 100644 --- a/src/tests/basictest.cpp +++ b/src/tests/basictest.cpp @@ -241,6 +241,9 @@ void BasicTest::disconnectUser() { if (user) { user->addUserSetting("stay_connected", "0"); } + else { + return; + } received.clear(); userManager->disconnectUser("user@localhost"); dynamic_cast(factories->getTimerFactory())->setTime(100); diff --git a/src/tests/config.cpp b/src/tests/config.cpp index 24776a74..a53fc11f 100644 --- a/src/tests/config.cpp +++ b/src/tests/config.cpp @@ -26,6 +26,7 @@ class ConfigTest : public CPPUNIT_NS :: TestFixture{ CPPUNIT_TEST_SUITE(ConfigTest); CPPUNIT_TEST(setStringTwice); CPPUNIT_TEST(updateBackendConfig); + CPPUNIT_TEST(updateBackendConfigJIDEscaping); CPPUNIT_TEST(unregisteredList); CPPUNIT_TEST(unregisteredString); CPPUNIT_TEST(unregisteredListAsString); @@ -57,6 +58,16 @@ class ConfigTest : public CPPUNIT_NS :: TestFixture{ CPPUNIT_ASSERT_EQUAL(false, CONFIG_BOOL(&cfg, "registration.needPassword")); } + void updateBackendConfigJIDEscaping() { + Config cfg; + std::istringstream ifs("service.jids = irc.freenode.org\n"); + cfg.load(ifs); + CPPUNIT_ASSERT_EQUAL(true, CONFIG_BOOL(&cfg, "service.jid_escaping")); + + cfg.updateBackendConfig("[features]\ndisable_jid_escaping=1\n"); + CPPUNIT_ASSERT_EQUAL(false, CONFIG_BOOL(&cfg, "service.jid_escaping")); + } + void unregisteredList() { Config cfg; std::istringstream ifs("service.irc_server = irc.freenode.org\nservice.irc_server=localhost\n"); diff --git a/src/tests/conversationmanager.cpp b/src/tests/conversationmanager.cpp index b8f2eb4e..27e8b3c8 100644 --- a/src/tests/conversationmanager.cpp +++ b/src/tests/conversationmanager.cpp @@ -23,6 +23,7 @@ using namespace Transport; class ConversationManagerTest : public CPPUNIT_NS :: TestFixture, public BasicTest { CPPUNIT_TEST_SUITE(ConversationManagerTest); + CPPUNIT_TEST(conversationSize); CPPUNIT_TEST(handleNormalMessages); CPPUNIT_TEST(handleNormalMessagesHeadline); CPPUNIT_TEST(handleGroupchatMessages); @@ -37,6 +38,7 @@ class ConversationManagerTest : public CPPUNIT_NS :: TestFixture, public BasicTe CPPUNIT_TEST(handleGroupchatRemoved); CPPUNIT_TEST(handleNicknameConflict); CPPUNIT_TEST(handleNotAuthorized); + CPPUNIT_TEST(handleSetNickname); CPPUNIT_TEST_SUITE_END(); public: @@ -60,6 +62,10 @@ class ConversationManagerTest : public CPPUNIT_NS :: TestFixture, public BasicTe tearMeDown(); } + void conversationSize() { + std::cout << " = " << sizeof(Conversation) << " B"; + } + void handleMessageReceived(TestingConversation *_conv, boost::shared_ptr &_msg) { m_conv = _conv; m_msg = _msg; @@ -308,6 +314,10 @@ class ConversationManagerTest : public CPPUNIT_NS :: TestFixture, public BasicTe conv->setNickname("nickname"); conv->addJID("user@localhost/resource"); + boost::shared_ptr msg0(new Swift::Message()); + msg0->setSubject("subject"); + conv->handleMessage(msg0, "anotheruser"); + CPPUNIT_ASSERT(!user->shouldCacheMessages()); // disconnectUser @@ -345,7 +355,7 @@ class ConversationManagerTest : public CPPUNIT_NS :: TestFixture, public BasicTe injectPresence(response); loop->processEvents(); - CPPUNIT_ASSERT_EQUAL(3, (int) received.size()); + CPPUNIT_ASSERT_EQUAL(4, (int) received.size()); CPPUNIT_ASSERT(dynamic_cast(getStanza(received[1]))); CPPUNIT_ASSERT_EQUAL(std::string("hi there!"), dynamic_cast(getStanza(received[1]))->getBody()); CPPUNIT_ASSERT_EQUAL(std::string("user@localhost/resource"), dynamic_cast(getStanza(received[1]))->getTo().toString()); @@ -356,6 +366,10 @@ class ConversationManagerTest : public CPPUNIT_NS :: TestFixture, public BasicTe CPPUNIT_ASSERT_EQUAL(std::string("user@localhost/resource"), dynamic_cast(getStanza(received[2]))->getTo().toString()); CPPUNIT_ASSERT_EQUAL(std::string("#room@localhost/anotheruser"), dynamic_cast(getStanza(received[2]))->getFrom().toString()); + CPPUNIT_ASSERT(dynamic_cast(getStanza(received[3]))); + CPPUNIT_ASSERT_EQUAL(std::string("subject"), dynamic_cast(getStanza(received[3]))->getSubject()); + CPPUNIT_ASSERT_EQUAL(std::string("user@localhost/resource"), dynamic_cast(getStanza(received[3]))->getTo().toString()); + CPPUNIT_ASSERT_EQUAL(std::string("#room@localhost/anotheruser"), dynamic_cast(getStanza(received[3]))->getFrom().toString()); } void handleGroupchatMessagesBouncerLeave() { @@ -633,6 +647,25 @@ class ConversationManagerTest : public CPPUNIT_NS :: TestFixture, public BasicTe CPPUNIT_ASSERT_EQUAL(Swift::ErrorPayload::NotAuthorized, getStanza(received[0])->getPayload()->getCondition()); } + void handleSetNickname() { + 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"); + loop->processEvents(); + + conv->setNickname("nickname2"); + conv->handleParticipantChanged("nickname2", Conversation::PARTICIPANT_FLAG_NONE, Swift::StatusShow::Away, "my status message"); + loop->processEvents(); + + CPPUNIT_ASSERT_EQUAL(1, (int) received.size()); + CPPUNIT_ASSERT(dynamic_cast(getStanza(received[0]))); + CPPUNIT_ASSERT_EQUAL(110, getStanza(received[0])->getPayload()->getStatusCodes()[0].code); + CPPUNIT_ASSERT_EQUAL(210, getStanza(received[0])->getPayload()->getStatusCodes()[1].code); + } + }; CPPUNIT_TEST_SUITE_REGISTRATION (ConversationManagerTest); diff --git a/src/tests/localbuddy.cpp b/src/tests/localbuddy.cpp index 9300c290..9fe00bdc 100644 --- a/src/tests/localbuddy.cpp +++ b/src/tests/localbuddy.cpp @@ -22,6 +22,7 @@ using namespace Transport; class LocalBuddyTest : public CPPUNIT_NS :: TestFixture, public BasicTest { CPPUNIT_TEST_SUITE(LocalBuddyTest); + CPPUNIT_TEST(localBuddySize); CPPUNIT_TEST(createWithInvalidName); CPPUNIT_TEST(buddyFlagsFromJID); CPPUNIT_TEST(JIDToLegacyName); @@ -43,6 +44,10 @@ class LocalBuddyTest : public CPPUNIT_NS :: TestFixture, public BasicTest { tearMeDown(); } + void localBuddySize() { + std::cout << " = " << sizeof(LocalBuddy) << " B"; + } + void createWithInvalidName() { User *user = userManager->getUser("user@localhost"); CPPUNIT_ASSERT(user); diff --git a/src/tests/main.cpp b/src/tests/main.cpp index 218d66fb..9847222e 100644 --- a/src/tests/main.cpp +++ b/src/tests/main.cpp @@ -19,7 +19,11 @@ int main (int argc, char* argv[]) { #ifdef WITH_LOG4CXX LoggerPtr root = Logger::getRootLogger(); +#ifndef _MSC_VER root->addAppender(new FileAppender(new PatternLayout("%d %-5p %c: %m%n"), "libtransport_test.log", false)); +#else + root->addAppender(new FileAppender(new PatternLayout(L"%d %-5p %c: %m%n"), L"libtransport_test.log", false)); +#endif #endif std::vector testsToRun; diff --git a/src/tests/networkpluginserver.cpp b/src/tests/networkpluginserver.cpp index 3b2fd5f8..8690ce35 100644 --- a/src/tests/networkpluginserver.cpp +++ b/src/tests/networkpluginserver.cpp @@ -25,6 +25,7 @@ #include #include #include // for clock() +#include using namespace Transport; @@ -54,8 +55,10 @@ class NetworkPluginServerTest : public CPPUNIT_NS :: TestFixture, public BasicTe CPPUNIT_TEST(handleMessageHeadline); CPPUNIT_TEST(handleConvMessageAckPayload); CPPUNIT_TEST(handleRawXML); + CPPUNIT_TEST(handleRawXMLSplit); CPPUNIT_TEST(benchmarkHandleBuddyChangedPayload); + CPPUNIT_TEST(benchmarkSendUnavailablePresence); CPPUNIT_TEST_SUITE_END(); public: @@ -140,6 +143,49 @@ class NetworkPluginServerTest : public CPPUNIT_NS :: TestFixture, public BasicTe std::cerr << " " << clk.elapsedTime() << " s"; } + void benchmarkSendUnavailablePresence() { + Clock clk; + std::vector lst; + for (int i = 0; i < 1000; i++) { + pbnetwork::Buddy buddy; + buddy.set_username("user@localhost"); + buddy.set_buddyname("buddy" + boost::lexical_cast(i) + "@test"); + buddy.set_status((pbnetwork::StatusType) 5); + + std::string message; + buddy.SerializeToString(&message); + lst.push_back(message); + } + + std::vector lst2; + for (int i = 0; i < 1000; i++) { + pbnetwork::Buddy buddy; + buddy.set_username("user@localhost"); + buddy.set_buddyname("buddy" + boost::lexical_cast(1000+i) + "@test"); + buddy.set_status((pbnetwork::StatusType) 2); + + std::string message; + buddy.SerializeToString(&message); + lst2.push_back(message); + } + + + for (int i = 0; i < 1000; i++) { + serv->handleBuddyChangedPayload(lst[i]); + received.clear(); + } + for (int i = 0; i < 1000; i++) { + serv->handleBuddyChangedPayload(lst2[i]); + received.clear(); + } + + User *user = userManager->getUser("user@localhost"); + clk.start(); + user->getRosterManager()->sendUnavailablePresences("user@localhost"); + clk.end(); + std::cerr << " " << clk.elapsedTime() << " s"; + } + void handleBuddyChangedPayload() { User *user = userManager->getUser("user@localhost"); @@ -198,14 +244,36 @@ class NetworkPluginServerTest : public CPPUNIT_NS :: TestFixture, public BasicTe } void handleRawXML() { + cfg->updateBackendConfig("[features]\nrawxml=1\n"); User *user = userManager->getUser("user@localhost"); + std::vector grp; + grp.push_back("group1"); + LocalBuddy *buddy = new LocalBuddy(user->getRosterManager(), -1, "buddy1@domain.tld", "Buddy 1", grp, BUDDY_JID_ESCAPING); + user->getRosterManager()->setBuddy(buddy); + received.clear(); - std::string xml = ""; - + std::string xml = ""; serv->handleRawXML(xml); - CPPUNIT_ASSERT_EQUAL(1, (int) received.size()); + + std::string xml2 = ""; + serv->handleRawXML(xml2); + + CPPUNIT_ASSERT_EQUAL(2, (int) received.size()); CPPUNIT_ASSERT(dynamic_cast(getStanza(received[0]))); - CPPUNIT_ASSERT_EQUAL(std::string("buddy1\\40domain.tld@localhost"), dynamic_cast(getStanza(received[0]))->getFrom().toString()); + CPPUNIT_ASSERT_EQUAL(std::string("buddy1\\40domain.tld@localhost/res"), dynamic_cast(getStanza(received[0]))->getFrom().toString()); + CPPUNIT_ASSERT(dynamic_cast(getStanza(received[1]))); + CPPUNIT_ASSERT_EQUAL(std::string("buddy1\\40domain.tld@localhost/res2"), dynamic_cast(getStanza(received[1]))->getFrom().toString()); + + received.clear(); + user->getRosterManager()->sendUnavailablePresences("user@localhost"); + + CPPUNIT_ASSERT_EQUAL(3, (int) received.size()); + CPPUNIT_ASSERT(dynamic_cast(getStanza(received[0]))); + CPPUNIT_ASSERT_EQUAL(std::string("buddy1\\40domain.tld@localhost/res"), dynamic_cast(getStanza(received[0]))->getFrom().toString()); + CPPUNIT_ASSERT_EQUAL(Swift::Presence::Unavailable, dynamic_cast(getStanza(received[0]))->getType()); + CPPUNIT_ASSERT(dynamic_cast(getStanza(received[1]))); + CPPUNIT_ASSERT_EQUAL(std::string("buddy1\\40domain.tld@localhost/res2"), dynamic_cast(getStanza(received[1]))->getFrom().toString()); + CPPUNIT_ASSERT_EQUAL(Swift::Presence::Unavailable, dynamic_cast(getStanza(received[1]))->getType()); } void handleMessageHeadline() { @@ -235,6 +303,26 @@ class NetworkPluginServerTest : public CPPUNIT_NS :: TestFixture, public BasicTe CPPUNIT_ASSERT(dynamic_cast(getStanza(received[0]))); CPPUNIT_ASSERT_EQUAL(Swift::Message::Headline, dynamic_cast(getStanza(received[0]))->getType()); } + + void handleRawXMLSplit() { + cfg->updateBackendConfig("[features]\nrawxml=1\n"); + User *user = userManager->getUser("user@localhost"); + std::vector grp; + grp.push_back("group1"); + LocalBuddy *buddy = new LocalBuddy(user->getRosterManager(), -1, "buddy1@domain.tld", "Buddy 1", grp, BUDDY_JID_ESCAPING); + user->getRosterManager()->setBuddy(buddy); + received.clear(); + + std::string xml = "handleRawXML(xml); + + std::string xml2 = " to='user@localhost'/>"; + serv->handleRawXML(xml2); + + CPPUNIT_ASSERT_EQUAL(1, (int) received.size()); + CPPUNIT_ASSERT(dynamic_cast(getStanza(received[0]))); + CPPUNIT_ASSERT_EQUAL(std::string("buddy1\\40domain.tld@localhost/res"), dynamic_cast(getStanza(received[0]))->getFrom().toString()); + } }; CPPUNIT_TEST_SUITE_REGISTRATION (NetworkPluginServerTest); diff --git a/src/tests/rostermanager.cpp b/src/tests/rostermanager.cpp index b9df7a0f..5b58a722 100644 --- a/src/tests/rostermanager.cpp +++ b/src/tests/rostermanager.cpp @@ -70,6 +70,7 @@ class RosterManagerTest : public CPPUNIT_NS :: TestFixture, public BasicTest { CPPUNIT_ASSERT_EQUAL(1, (int) received.size()); CPPUNIT_ASSERT(getStanza(received[0])->getPayload()); CPPUNIT_ASSERT_EQUAL(std::string("Buddy 1"), getStanza(received[0])->getPayload()->getNickname()); + CPPUNIT_ASSERT_EQUAL(std::string("buddy1@localhost"), getStanza(received[0])->getFrom().toString()); } diff --git a/src/tests/util.cpp b/src/tests/util.cpp index c590e81a..280dcd06 100644 --- a/src/tests/util.cpp +++ b/src/tests/util.cpp @@ -18,6 +18,7 @@ #include "Swiften/Server/ServerFromClientSession.h" #include "Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.h" #include "basictest.h" +#include "transport/utf8.h" #include "transport/util.h" @@ -27,6 +28,7 @@ class UtilTest : public CPPUNIT_NS :: TestFixture{ CPPUNIT_TEST_SUITE(UtilTest); CPPUNIT_TEST(encryptDecryptPassword); CPPUNIT_TEST(serializeGroups); + CPPUNIT_TEST(replaceInvalid); CPPUNIT_TEST_SUITE_END(); public: @@ -63,6 +65,18 @@ class UtilTest : public CPPUNIT_NS :: TestFixture{ CPPUNIT_ASSERT_EQUAL(std::string("Buddies2"), StorageBackend::deserializeGroups(g)[1]); } + void replaceInvalid() { + std::string x("test\x80\xe0\xa0\xc0\xaf\xed\xa0\x80test"); + std::string a; + CPPUNIT_ASSERT(x.end() != utf8::find_invalid(x.begin(), x.end())); + utf8::replace_invalid(x.begin(), x.end(), std::back_inserter(a), '_'); + CPPUNIT_ASSERT_EQUAL(std::string("test____test"), a); + + a = ""; + utf8::remove_invalid(x.begin(), x.end(), std::back_inserter(a)); + CPPUNIT_ASSERT_EQUAL(std::string("testtest"), a); + } + }; CPPUNIT_TEST_SUITE_REGISTRATION (UtilTest); diff --git a/src/user.cpp b/src/user.cpp index 9afab31a..301031b8 100644 --- a/src/user.cpp +++ b/src/user.cpp @@ -258,6 +258,8 @@ void User::handlePresence(Swift::Presence::ref presence, bool forceJoin) { delete conv; } } + + return; } else if (isMUC) { // force connection to legacy network to let backend to handle auto-join on connect. @@ -315,8 +317,11 @@ void User::handlePresence(Swift::Presence::ref presence, bool forceJoin) { onRawPresenceReceived(presence); onRoomJoined(presence->getFrom(), room, presence->getTo().getResource(), password); + + return; } - return; + + onRawPresenceReceived(presence); } int currentResourcesCount = m_presenceOracle->getAllPresence(m_jid).size();