diff --git a/CMakeLists.txt b/CMakeLists.txt index a69a1d3c..7e1c2792 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -145,7 +145,12 @@ else(WIN32) message(STATUS "Using non-multithreaded boost") set(Boost_USE_MULTITHREADED 0) endif(contains) - find_package(Boost COMPONENTS program_options date_time system filesystem regex thread-mt signals REQUIRED) + set(Boost_FIND_QUIETLY ON) + find_package(Boost COMPONENTS program_options date_time system filesystem regex thread-mt signals) + if (NOT Boost_FOUND) + set(Boost_FIND_QUIETLY OFF) + find_package(Boost COMPONENTS program_options date_time system filesystem regex thread signals REQUIRED) + endif() endif(WIN32) message( STATUS "Found Boost: ${Boost_LIBRARIES}, ${Boost_INCLUDE_DIR}") diff --git a/backends/libcommuni/ircnetworkplugin.cpp b/backends/libcommuni/ircnetworkplugin.cpp index 85e55d25..90e4d663 100644 --- a/backends/libcommuni/ircnetworkplugin.cpp +++ b/backends/libcommuni/ircnetworkplugin.cpp @@ -10,9 +10,37 @@ DEFINE_LOGGER(logger, "IRCNetworkPlugin"); IRCNetworkPlugin::IRCNetworkPlugin(Config *config, Swift::QtEventLoop *loop, const std::string &host, int port) { this->config = config; + m_currentServer = 0; m_socket = new QTcpSocket(); m_socket->connectToHost(FROM_UTF8(host), port); connect(m_socket, SIGNAL(readyRead()), this, SLOT(readData())); + + std::string server = CONFIG_STRING_DEFAULTED(config, "service.irc_server", ""); + if (!server.empty()) { + m_servers.push_back(server); + } + else { + + std::list list; + list = CONFIG_LIST_DEFAULTED(config, "service.irc_server", list); + + m_servers.insert(m_servers.begin(), list.begin(), list.end()); + } + + if (CONFIG_HAS_KEY(config, "service.irc_identify")) { + m_identify = CONFIG_STRING(config, "service.irc_identify"); + } + else { + m_identify = "NickServ identify $name $password"; + } +} + +void IRCNetworkPlugin::tryNextServer() { + if (!m_servers.empty()) { + int nextServer = (m_currentServer + 1) % m_servers.size(); + LOG4CXX_INFO(logger, "Server " << m_servers[m_currentServer] << " disconnected user. Next server to try will be " << m_servers[nextServer]); + m_currentServer = nextServer; + } } void IRCNetworkPlugin::readData() { @@ -28,114 +56,156 @@ void IRCNetworkPlugin::sendData(const std::string &string) { m_socket->write(string.c_str(), string.size()); } +MyIrcSession *IRCNetworkPlugin::createSession(const std::string &user, const std::string &hostname, const std::string &nickname, const std::string &password, const std::string &suffix) { + MyIrcSession *session = new MyIrcSession(user, this, suffix); + session->setUserName(FROM_UTF8(nickname)); + session->setNickName(FROM_UTF8(nickname)); + session->setRealName(FROM_UTF8(nickname)); + session->setHost(FROM_UTF8(hostname)); + session->setPort(6667); + session->setEncoding( "utf-8" ); + + if (!password.empty()) { + std::string identify = m_identify; + boost::replace_all(identify, "$password", password); + boost::replace_all(identify, "$name", nickname); + session->setIdentify(identify); + } + + LOG4CXX_INFO(logger, user << ": Connecting " << hostname << " as " << nickname << ", suffix=" << suffix); + + session->open(); + + return session; +} + void IRCNetworkPlugin::handleLoginRequest(const std::string &user, const std::string &legacyName, const std::string &password) { - // In server mode, hostname of the server we want to connect to is stored in "user" JID. - // In component mode we will connect user to the IRC network once he joins the room. - if (CONFIG_BOOL(config, "service.server_mode")) { - MyIrcSession *session = new MyIrcSession(user, this); - std::string h = user.substr(0, user.find("@")); - session->setNickName(FROM_UTF8(h.substr(0, h.find("%")))); - session->setHost(FROM_UTF8(h.substr(h.find("%") + 1))); - session->setPort(6667); - session->open(); - LOG4CXX_INFO(logger, user << ": Connecting IRC network " << h.substr(h.find("%") + 1)); - m_sessions[user] = session; + if (!m_servers.empty()) { + // legacy name is users nickname + if (m_sessions[user] != NULL) { + LOG4CXX_WARN(logger, user << ": Already logged in."); + return; + } + + m_sessions[user] = createSession(user, m_servers[m_currentServer], legacyName, password, ""); } else { + // We are waiting for first room join to connect user to IRC network, because we don't know which + // network he choose... LOG4CXX_INFO(logger, user << ": Ready for connections"); handleConnected(user); } } void IRCNetworkPlugin::handleLogoutRequest(const std::string &user, const std::string &legacyName) { - if (m_sessions[user] == NULL) + if (m_sessions[user] == NULL) { + LOG4CXX_WARN(logger, user << ": Already disconnected."); return; + } + LOG4CXX_INFO(logger, user << ": Disconnecting."); m_sessions[user]->close(); m_sessions[user]->deleteLater(); m_sessions.erase(user); } -void IRCNetworkPlugin::handleMessageSendRequest(const std::string &user, const std::string &legacyName, const std::string &message, const std::string &/*xhtml*/) { +std::string IRCNetworkPlugin::getSessionName(const std::string &user, const std::string &legacyName) { std::string u = user; - if (!CONFIG_BOOL(config, "service.server_mode")) { + if (!CONFIG_BOOL(config, "service.server_mode") && m_servers.empty()) { u = user + legacyName.substr(legacyName.find("@") + 1); if (u.find("/") != std::string::npos) { u = u.substr(0, u.find("/")); } } - if (m_sessions[u] == NULL) { - LOG4CXX_WARN(logger, user << ": Session name: " << u << ", No session for user"); - return; - } + return u; +} +std::string IRCNetworkPlugin::getTargetName(const std::string &legacyName) { std::string r = legacyName; - if (!CONFIG_BOOL(config, "service.server_mode")) { +// if (!CONFIG_BOOL(config, "service.server_mode")) { if (legacyName.find("/") == std::string::npos) { r = legacyName.substr(0, r.find("@")); } else { r = legacyName.substr(legacyName.find("/") + 1); } +// } + return r; +} + +void IRCNetworkPlugin::handleMessageSendRequest(const std::string &user, const std::string &legacyName, const std::string &message, const std::string &/*xhtml*/) { + std::string session = getSessionName(user, legacyName); + if (m_sessions[session] == NULL) { + LOG4CXX_WARN(logger, user << ": Session name: " << session << ", No session for user"); + return; } - LOG4CXX_INFO(logger, user << ": Session name: " << u << ", message to " << r); - m_sessions[u]->sendCommand(IrcCommand::createMessage(FROM_UTF8(r), FROM_UTF8(message))); + + std::string target = getTargetName(legacyName); + + LOG4CXX_INFO(logger, user << ": Session name: " << session << ", message to " << target); + m_sessions[session]->sendCommand(IrcCommand::createMessage(FROM_UTF8(target), FROM_UTF8(message))); + + if (target.find("#") == 0) { + handleMessage(user, legacyName, message, TO_UTF8(m_sessions[session]->nickName())); + } +} + +void IRCNetworkPlugin::handleRoomSubjectChangedRequest(const std::string &user, const std::string &room, const std::string &message) { + std::string session = getSessionName(user, room); + if (m_sessions[session] == NULL) { + LOG4CXX_WARN(logger, user << ": Session name: " << session << ", No session for user"); + return; + } + + std::string target = getTargetName(room); + m_sessions[session]->sendCommand(IrcCommand::createTopic(FROM_UTF8(target), FROM_UTF8(message))); } void IRCNetworkPlugin::handleJoinRoomRequest(const std::string &user, const std::string &room, const std::string &nickname, const std::string &password) { - std::string r = room; - std::string u = user; - if (!CONFIG_BOOL(config, "service.server_mode")) { - u = user + room.substr(room.find("@") + 1); - r = room.substr(0, room.find("@")); - } + std::string session = getSessionName(user, room); + std::string target = getTargetName(room); - LOG4CXX_INFO(logger, user << ": Session name: " << u << ", Joining room " << r); - if (m_sessions[u] == NULL) { - // in gateway mode we want to login this user to network according to legacyName - if (room.find("@") != std::string::npos) { - // suffix is %irc.freenode.net to let MyIrcSession return #room%irc.freenode.net - MyIrcSession *session = new MyIrcSession(user, this, room.substr(room.find("@"))); - session->setNickName(FROM_UTF8(nickname)); - session->setHost(FROM_UTF8(room.substr(room.find("@") + 1))); - session->setPort(6667); - session->open(); - LOG4CXX_INFO(logger, user << ": Connecting IRC network " << room.substr(room.find("@") + 1)); - m_sessions[u] = session; + LOG4CXX_INFO(logger, user << ": Session name: " << session << ", Joining room " << target); + if (m_sessions[session] == NULL) { + if (m_servers.empty()) { + // in gateway mode we want to login this user to network according to legacyName + if (room.find("@") != std::string::npos) { + // suffix is %irc.freenode.net to let MyIrcSession return #room%irc.freenode.net + m_sessions[session] = createSession(user, room.substr(room.find("@") + 1), nickname, "", room.substr(room.find("@"))); + } + else { + LOG4CXX_WARN(logger, user << ": There's no proper server defined in room to which this user wants to join: " << room); + return; + } } else { - LOG4CXX_WARN(logger, user << ": There's no proper server defined in room to which this user wants to join: " << room); + LOG4CXX_WARN(logger, user << ": Join room requested for unconnected user"); return; } } - m_sessions[u]->addAutoJoinChannel(r, password); - m_sessions[u]->sendCommand(IrcCommand::createJoin(FROM_UTF8(r), FROM_UTF8(password))); - m_sessions[u]->rooms += 1; + m_sessions[session]->addAutoJoinChannel(target, password); + m_sessions[session]->sendCommand(IrcCommand::createJoin(FROM_UTF8(target), FROM_UTF8(password))); + m_sessions[session]->rooms += 1; // update nickname, because we have nickname per session, no nickname per room. - handleRoomNicknameChanged(user, r, TO_UTF8(m_sessions[u]->nickName())); + handleRoomNicknameChanged(user, target, TO_UTF8(m_sessions[session]->nickName())); } void IRCNetworkPlugin::handleLeaveRoomRequest(const std::string &user, const std::string &room) { - std::string r = room; - std::string u = user; - if (!CONFIG_BOOL(config, "service.server_mode")) { - r = room.substr(0, room.find("@")); - u = user + room.substr(room.find("@") + 1); - } + std::string session = getSessionName(user, room); + std::string target = getTargetName(room); - if (m_sessions[u] == NULL) + LOG4CXX_INFO(logger, user << ": Session name: " << session << ", Leaving room " << target); + if (m_sessions[session] == NULL) return; - LOG4CXX_INFO(logger, user << ": Session name: " << u << ", Leaving room " << r); + m_sessions[session]->sendCommand(IrcCommand::createPart(FROM_UTF8(target))); + m_sessions[session]->removeAutoJoinChannel(target); + m_sessions[session]->rooms -= 1; - m_sessions[u]->sendCommand(IrcCommand::createPart(FROM_UTF8(r))); - m_sessions[u]->removeAutoJoinChannel(r); - m_sessions[u]->rooms -= 1; - - if (m_sessions[u]->rooms <= 0) { - LOG4CXX_INFO(logger, user << ": Session name: " << u << ", User is not in room, disconnecting from network"); - m_sessions[u]->close(); - m_sessions[u]->deleteLater(); - m_sessions.erase(u); + if (m_sessions[session]->rooms <= 0 && m_servers.empty()) { + LOG4CXX_INFO(logger, user << ": Session name: " << session << ", User is not in any room, disconnecting from network"); + m_sessions[session]->close(); + m_sessions[session]->deleteLater(); + m_sessions.erase(session); } } diff --git a/backends/libcommuni/ircnetworkplugin.h b/backends/libcommuni/ircnetworkplugin.h index ff77d2bc..043089cd 100644 --- a/backends/libcommuni/ircnetworkplugin.h +++ b/backends/libcommuni/ircnetworkplugin.h @@ -26,12 +26,24 @@ class IRCNetworkPlugin : public QObject, public NetworkPlugin { void handleLeaveRoomRequest(const std::string &user, const std::string &room); + void handleRoomSubjectChangedRequest(const std::string &user, const std::string &room, const std::string &message); + + void tryNextServer(); + public slots: void readData(); void sendData(const std::string &string); + private: + MyIrcSession *createSession(const std::string &user, const std::string &hostname, const std::string &nickname, const std::string &password, const std::string &suffix = ""); + std::string getSessionName(const std::string &user, const std::string &legacyName); + std::string getTargetName(const std::string &legacyName); + private: Config *config; QTcpSocket *m_socket; std::map m_sessions; + std::vector m_servers; + int m_currentServer; + std::string m_identify; }; \ No newline at end of file diff --git a/backends/libcommuni/main.cpp b/backends/libcommuni/main.cpp index 4509b9af..99b3bc58 100644 --- a/backends/libcommuni/main.cpp +++ b/backends/libcommuni/main.cpp @@ -16,7 +16,6 @@ #include #include "Swiften/EventLoop/Qt/QtEventLoop.h" #include "ircnetworkplugin.h" -#include "singleircnetworkplugin.h" using namespace boost::program_options; using namespace Transport; @@ -40,12 +39,7 @@ int main (int argc, char* argv[]) { Swift::QtEventLoop eventLoop; - if (!CONFIG_HAS_KEY(cfg, "service.irc_server")) { - np = new IRCNetworkPlugin(cfg, &eventLoop, host, port); - } - else { - np = new SingleIRCNetworkPlugin(cfg, &eventLoop, host, port); - } + np = new IRCNetworkPlugin(cfg, &eventLoop, host, port); return app.exec(); } diff --git a/backends/libcommuni/session.cpp b/backends/libcommuni/session.cpp index 51505fb4..2eabfe96 100644 --- a/backends/libcommuni/session.cpp +++ b/backends/libcommuni/session.cpp @@ -14,6 +14,8 @@ #include #include +#include "ircnetworkplugin.h" + #define FROM_UTF8(WHAT) QString::fromUtf8((WHAT).c_str(), (WHAT).size()) #define TO_UTF8(WHAT) std::string((WHAT).toUtf8().data(), (WHAT).toUtf8().size()) @@ -21,7 +23,7 @@ DEFINE_LOGGER(logger, "IRCSession"); -MyIrcSession::MyIrcSession(const std::string &user, NetworkPlugin *np, const std::string &suffix, QObject* parent) : IrcSession(parent) +MyIrcSession::MyIrcSession(const std::string &user, IRCNetworkPlugin *np, const std::string &suffix, QObject* parent) : IrcSession(parent) { this->np = np; this->user = user; @@ -29,6 +31,7 @@ MyIrcSession::MyIrcSession(const std::string &user, NetworkPlugin *np, const std m_connected = false; rooms = 0; connect(this, SIGNAL(disconnected()), SLOT(on_disconnected())); + connect(this, SIGNAL(socketError(QAbstractSocket::SocketError)), SLOT(on_socketError(QAbstractSocket::SocketError))); connect(this, SIGNAL(connected()), SLOT(on_connected())); connect(this, SIGNAL(messageReceived(IrcMessage*)), this, SLOT(onMessageReceived(IrcMessage*))); } @@ -50,9 +53,15 @@ void MyIrcSession::on_connected() { } } +void MyIrcSession::on_socketError(QAbstractSocket::SocketError error) { + on_disconnected(); +} + void MyIrcSession::on_disconnected() { - if (suffix.empty()) + if (suffix.empty()) { np->handleDisconnected(user, 0, ""); + np->tryNextServer(); + } m_connected = false; } @@ -161,7 +170,8 @@ void MyIrcSession::on_messageReceived(IrcMessage *message) { bool flags = 0; std::string nickname = TO_UTF8(m->sender().name()); flags = correctNickname(nickname); - np->handleMessage(user, nickname, TO_UTF8(m->message())); + LOG4CXX_INFO(logger, nickname + suffix); + np->handleMessage(user, nickname + suffix, TO_UTF8(m->message())); } } @@ -181,6 +191,7 @@ void MyIrcSession::on_numericMessageReceived(IrcMessage *message) { channel = m->parameters().value(2); members = m->parameters().value(3).split(" "); + LOG4CXX_INFO(logger, user << ": Received members for " << TO_UTF8(channel) << suffix); for (int i = 0; i < members.size(); i++) { bool flags = 0; std::string nickname = TO_UTF8(members.at(i)); @@ -190,9 +201,7 @@ void MyIrcSession::on_numericMessageReceived(IrcMessage *message) { } break; case 432: - if (m_connected) { - np->handleDisconnected(user, pbnetwork::CONNECTION_ERROR_INVALID_USERNAME, "Erroneous Nickname"); - } + np->handleDisconnected(user, pbnetwork::CONNECTION_ERROR_INVALID_USERNAME, "Erroneous Nickname"); break; default: break; diff --git a/backends/libcommuni/session.h b/backends/libcommuni/session.h index bff2aee4..507feb93 100644 --- a/backends/libcommuni/session.h +++ b/backends/libcommuni/session.h @@ -18,6 +18,8 @@ using namespace Transport; +class IRCNetworkPlugin; + class MyIrcSession : public IrcSession { Q_OBJECT @@ -37,7 +39,7 @@ public: typedef std::map > AutoJoinMap; - MyIrcSession(const std::string &user, NetworkPlugin *np, const std::string &suffix = "", QObject* parent = 0); + MyIrcSession(const std::string &user, IRCNetworkPlugin *np, const std::string &suffix = "", QObject* parent = 0); std::map m_modes; std::string suffix; int rooms; @@ -72,11 +74,12 @@ public: protected Q_SLOTS: void on_connected(); void on_disconnected(); + void on_socketError(QAbstractSocket::SocketError error); void onMessageReceived(IrcMessage* message); protected: - NetworkPlugin *np; + IRCNetworkPlugin *np; std::string user; std::string m_identify; AutoJoinMap m_autoJoin; diff --git a/backends/libcommuni/singleircnetworkplugin.cpp b/backends/libcommuni/singleircnetworkplugin.cpp deleted file mode 100644 index 439d765b..00000000 --- a/backends/libcommuni/singleircnetworkplugin.cpp +++ /dev/null @@ -1,138 +0,0 @@ -#include "singleircnetworkplugin.h" -#include "transport/logging.h" -#include -#include - -#define FROM_UTF8(WHAT) QString::fromUtf8((WHAT).c_str(), (WHAT).size()) -#define TO_UTF8(WHAT) std::string((WHAT).toUtf8().data(), (WHAT).toUtf8().size()) - -DEFINE_LOGGER(logger, "SingleIRCNetworkPlugin"); - -SingleIRCNetworkPlugin::SingleIRCNetworkPlugin(Config *config, Swift::QtEventLoop *loop, const std::string &host, int port) { - this->config = config; - if (CONFIG_HAS_KEY(config, "service.irc_server")) { - m_server = CONFIG_STRING(config, "service.irc_server"); - } - else { - LOG4CXX_ERROR(logger, "No [service] irc_server defined, exiting..."); - exit(-1); - } - m_socket = new QTcpSocket(); - m_socket->connectToHost(FROM_UTF8(host), port); - connect(m_socket, SIGNAL(readyRead()), this, SLOT(readData())); - - if (CONFIG_HAS_KEY(config, "service.irc_identify")) { - m_identify = CONFIG_STRING(config, "service.irc_identify"); - } - else { - m_identify = "NickServ identify $name $password"; - } - - LOG4CXX_INFO(logger, "SingleIRCNetworkPlugin for server " << m_server << " initialized."); -} - -void SingleIRCNetworkPlugin::readData() { - size_t availableBytes = m_socket->bytesAvailable(); - if (availableBytes == 0) - return; - - std::string d = std::string(m_socket->readAll().data(), availableBytes); - handleDataRead(d); -} - -void SingleIRCNetworkPlugin::sendData(const std::string &string) { - m_socket->write(string.c_str(), string.size()); -} - -void SingleIRCNetworkPlugin::handleLoginRequest(const std::string &user, const std::string &legacyName, const std::string &password) { - // legacy name is users nickname - if (m_sessions[user] != NULL) { - LOG4CXX_WARN(logger, user << ": Already logged in."); - return; - } - LOG4CXX_INFO(logger, user << ": Connecting " << m_server << " as " << legacyName); - - MyIrcSession *session = new MyIrcSession(user, this); - session->setUserName(FROM_UTF8(legacyName)); - session->setNickName(FROM_UTF8(legacyName)); - session->setRealName(FROM_UTF8(legacyName)); - session->setHost(FROM_UTF8(m_server)); - session->setPort(6667); - session->setEncoding( "utf-8" ); - - if (!password.empty()) { - std::string identify = m_identify; - boost::replace_all(identify, "$password", password); - boost::replace_all(identify, "$name", legacyName); - session->setIdentify(identify); - } - - session->open(); - - m_sessions[user] = session; -} - -void SingleIRCNetworkPlugin::handleLogoutRequest(const std::string &user, const std::string &legacyName) { - if (m_sessions[user] == NULL) { - LOG4CXX_WARN(logger, user << ": Already disconnected."); - return; - } - LOG4CXX_INFO(logger, user << ": Disconnecting."); - - m_sessions[user]->close(); - m_sessions[user]->deleteLater(); - m_sessions.erase(user); -} - -void SingleIRCNetworkPlugin::handleMessageSendRequest(const std::string &user, const std::string &legacyName, const std::string &message, const std::string &/*xhtml*/) { - if (m_sessions[user] == NULL) { - LOG4CXX_WARN(logger, user << ": Message received for unconnected user"); - return; - } - - // handle PMs - std::string r = legacyName; - if (legacyName.find("/") == std::string::npos) { - r = legacyName.substr(0, r.find("@")); - } - else { - r = legacyName.substr(legacyName.find("/") + 1); - } - - LOG4CXX_INFO(logger, user << ": Forwarding message to " << r); - m_sessions[user]->sendCommand(IrcCommand::createMessage(FROM_UTF8(r), FROM_UTF8(message))); - - if (r.find("#") == 0) { - handleMessage(user, legacyName, message, TO_UTF8(m_sessions[user]->nickName())); - } -} - -void SingleIRCNetworkPlugin::handleJoinRoomRequest(const std::string &user, const std::string &room, const std::string &nickname, const std::string &password) { - if (m_sessions[user] == NULL) { - LOG4CXX_WARN(logger, user << ": Join room requested for unconnected user"); - return; - } - - LOG4CXX_INFO(logger, user << ": Joining " << room); - m_sessions[user]->addAutoJoinChannel(room, password); - m_sessions[user]->sendCommand(IrcCommand::createJoin(FROM_UTF8(room), FROM_UTF8(password))); - m_sessions[user]->rooms += 1; - - // update nickname, because we have nickname per session, no nickname per room. - handleRoomNicknameChanged(user, room, TO_UTF8(m_sessions[user]->userName())); -} - -void SingleIRCNetworkPlugin::handleLeaveRoomRequest(const std::string &user, const std::string &room) { - std::string r = room; - std::string u = user; - - if (m_sessions[u] == NULL) { - LOG4CXX_WARN(logger, user << ": Leave room requested for unconnected user"); - return; - } - - LOG4CXX_INFO(logger, user << ": Leaving " << room); - m_sessions[u]->sendCommand(IrcCommand::createPart(FROM_UTF8(r))); - m_sessions[u]->removeAutoJoinChannel(r); - m_sessions[u]->rooms -= 1; -} diff --git a/backends/libcommuni/singleircnetworkplugin.h b/backends/libcommuni/singleircnetworkplugin.h deleted file mode 100644 index e80dd151..00000000 --- a/backends/libcommuni/singleircnetworkplugin.h +++ /dev/null @@ -1,40 +0,0 @@ - -#pragma once - -#include "transport/config.h" -#include "transport/networkplugin.h" -#include "session.h" -#include -#include -#include "Swiften/EventLoop/Qt/QtEventLoop.h" -#include "ircnetworkplugin.h" - - -class SingleIRCNetworkPlugin : public QObject, public NetworkPlugin { - Q_OBJECT - - public: - SingleIRCNetworkPlugin(Config *config, Swift::QtEventLoop *loop, const std::string &host, int port); - - void handleLoginRequest(const std::string &user, const std::string &legacyName, const std::string &password); - - void handleLogoutRequest(const std::string &user, const std::string &legacyName); - - void handleMessageSendRequest(const std::string &user, const std::string &legacyName, const std::string &message, const std::string &/*xhtml*/); - - 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); - - std::map m_sessions; - - public slots: - void readData(); - void sendData(const std::string &string); - - private: - Config *config; - QTcpSocket *m_socket; - std::string m_server; - std::string m_identify; -}; diff --git a/backends/libpurple/main.cpp b/backends/libpurple/main.cpp index 0a487ea3..1c3f8c48 100644 --- a/backends/libpurple/main.cpp +++ b/backends/libpurple/main.cpp @@ -17,7 +17,9 @@ #include "geventloop.h" // #include "valgrind/memcheck.h" +#ifndef __FreeBSD__ #include "malloc.h" +#endif #include #include "errno.h" #include @@ -364,8 +366,10 @@ class SpectrumNetworkPlugin : public NetworkPlugin { purple_accounts_delete_wrapped(account); #ifndef WIN32 +#ifndef __FreeBSD__ malloc_trim(0); #endif +#endif // VALGRIND_DO_LEAK_CHECK; } } @@ -1459,8 +1463,10 @@ static void signed_on(PurpleConnection *gc, gpointer unused) { PurpleAccount *account = purple_connection_get_account_wrapped(gc); np->handleConnected(np->m_accounts[account]); #ifndef WIN32 +#ifndef __FreeBSD__ // force returning of memory chunks allocated by libxml2 to kernel malloc_trim(0); +#endif #endif // For prpl-gg @@ -1648,8 +1654,10 @@ static void transportDataReceived(gpointer data, gint source, PurpleInputConditi int main(int argc, char **argv) { #ifndef WIN32 +#ifndef __FreeBSD__ mallopt(M_CHECK_ACTION, 2); mallopt(M_PERTURB, 0xb); +#endif signal(SIGPIPE, SIG_IGN); diff --git a/backends/twitter/userdb.cpp b/backends/twitter/userdb.cpp deleted file mode 100644 index 12719019..00000000 --- a/backends/twitter/userdb.cpp +++ /dev/null @@ -1,80 +0,0 @@ -#include "userdb.h" - -#if 0 - -DEFINE_LOGGER(logger, "Twitter Backend Database"); - -UserDB::UserDB(std::string database): errMsg(0), rc(0), dbOpen(false) -{ - rc = sqlite3_open(database.c_str(), &db); - if( rc ) { - LOG4CXX_ERROR(logger, "Failed to open database" << database); - sqlite3_close(db); - exit(0); - } - - LOG4CXX_INFO(logger, "Checking if table users is present") - if(exe(std::string("select * from users limit 1;")) != SQLITE_OK) { - exe("create table users (user text primarykey, key text, secret text);"); - LOG4CXX_INFO(logger, "Created table users in the database"); - } - dbOpen = true; -} - -int UserDB::exe(std::string s_exe) -{ - data.clear(); - - //LOG4CXX_INFO(logger, "Executing: " << s_exe) - rc = sqlite3_get_table(db, s_exe.c_str(), &result, &nRow, &nCol, &errMsg); - if( rc == SQLITE_OK ) { - int col = nCol; //Skip past the headers - for(int i = 0; i < nRow; ++i) { - std::vector row; - for(int j = 0 ; j < nCol ; j++) row.push_back(result[col++]); - data.push_back(row); - } - } - sqlite3_free_table(result); - return rc; -} - -void UserDB::insert(UserData info) -{ - std::string q = "insert into users (user,key,secret) values ('" + info.user + "','" + info.accessTokenKey + "','" + info.accessTokenSecret + "');"; - if(exe(q) != SQLITE_OK) { - LOG4CXX_ERROR(logger, "Failed to insert into database!"); - exit(0); - } -} - -void UserDB::fetch(std::string user, std::vector &row) -{ - std::string q = "select key,secret from users where user='" + user + "'"; - if(exe(q) != SQLITE_OK) { - LOG4CXX_ERROR(logger, "Failed to fetch data from database!"); - exit(0); - } - row = data[0]; -} - -std::set UserDB::getRegisteredUsers() -{ - std::string q = "select user from users"; - if(exe(q) != SQLITE_OK) { - LOG4CXX_ERROR(logger, "Failed to registered users from database!"); - exit(0); - } - - std::set users; - for(int i=0 ; i -#include -#include -#include -#include "transport/logging.h" - -struct UserData -{ - std::string user; - std::string accessTokenKey; - std::string accessTokenSecret; - UserData(){} - UserData(std::string _user, std::string key, std::string secret) { - user = _user; - accessTokenKey = key; - accessTokenSecret = secret; - } -}; - -class UserDB { - private: - sqlite3 *db; - char *errMsg; - char **result; - int rc; - int nRow,nCol; - bool dbOpen; - std::vector< std::vector > data; - - public: - - UserDB (std::string database); - int exe(std::string s_exe); - void insert(UserData info); - void fetch(std::string user, std::vector &row); - std::set getRegisteredUsers(); - ~UserDB(); -}; - -#endif - - -#endif \ No newline at end of file diff --git a/include/transport/config.h b/include/transport/config.h index de7888ac..aa55cd9a 100644 --- a/include/transport/config.h +++ b/include/transport/config.h @@ -28,6 +28,20 @@ #include #include +namespace Transport { + +template +const myType &safeAs(const boost::program_options::variable_value &var, const myType &def) { + try { + return var.as(); + } + catch(...) { + return def; + } +} + +} + #define CONFIG_HAS_KEY(PTR, KEY) (*PTR).hasKey(KEY) #define CONFIG_STRING(PTR, KEY) (*PTR)[KEY].as() #define CONFIG_INT(PTR, KEY) (*PTR)[KEY].as() @@ -35,9 +49,9 @@ #define CONFIG_LIST(PTR, KEY) (*PTR)[KEY].as >() #define CONFIG_VECTOR(PTR, KEY) ((*PTR).hasKey(KEY) ? (*PTR)[KEY].as >() : std::vector()) -#define CONFIG_STRING_DEFAULTED(PTR, KEY, DEF) ((*PTR).hasKey(KEY) ? (*PTR)[KEY].as() : DEF) -#define CONFIG_BOOL_DEFAULTED(PTR, KEY, DEF) ((*PTR).hasKey(KEY) ? (*PTR)[KEY].as() : DEF) - +#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) namespace Transport { diff --git a/include/transport/conversation.h b/include/transport/conversation.h index d228099f..e137b566 100644 --- a/include/transport/conversation.h +++ b/include/transport/conversation.h @@ -34,6 +34,12 @@ class ConversationManager; /// Represents one XMPP-Legacy network conversation. class Conversation { public: + typedef struct _Participant { + int flag; + int status; + std::string statusMessage; + } Participant; + /// Type of participants in MUC rooms. enum ParticipantFlag {None, Moderator}; @@ -82,6 +88,18 @@ class Conversation { m_jid = jid; } + void addJID(const Swift::JID &jid) { + m_jids.push_back(jid); + } + + void removeJID(const Swift::JID &jid) { + m_jids.remove(jid); + } + + const std::list &getJIDs() { + return m_jids; + } + /// Sends message to Legacy network. /// \param message Message. @@ -116,6 +134,11 @@ class Conversation { void destroyRoom(); + void sendParticipants(const Swift::JID &to); + + private: + Swift::Presence::ref generatePresence(const std::string &nick, int flag, int status, const std::string &statusMessage, const std::string &newname = ""); + private: ConversationManager *m_conversationManager; std::string m_legacyName; @@ -123,6 +146,8 @@ class Conversation { std::string m_room; bool m_muc; Swift::JID m_jid; + std::list m_jids; + std::map m_participants; }; } diff --git a/include/transport/conversationmanager.h b/include/transport/conversationmanager.h index 44c81888..016914c8 100644 --- a/include/transport/conversationmanager.h +++ b/include/transport/conversationmanager.h @@ -70,6 +70,7 @@ class ConversationManager { void removeConversation(Conversation *conv); void resetResources(); + void removeJID(const Swift::JID &jid); private: void handleMessageReceived(Swift::Message::ref message); diff --git a/include/transport/networkplugin.h b/include/transport/networkplugin.h index b565f43a..ea594225 100644 --- a/include/transport/networkplugin.h +++ b/include/transport/networkplugin.h @@ -218,6 +218,7 @@ class NetworkPlugin { /// \param photo Raw photo data. virtual void handleVCardUpdatedRequest(const std::string &/*user*/, const std::string &/*photo*/, const std::string &nickname) {} + virtual void handleRoomSubjectChangedRequest(const std::string &/*user*/, const std::string &/*room*/, const std::string &/*message*/) {} virtual void handleJoinRoomRequest(const std::string &/*user*/, const std::string &/*room*/, const std::string &/*nickname*/, const std::string &/*pasword*/) {} virtual void handleLeaveRoomRequest(const std::string &/*user*/, const std::string &/*room*/) {} @@ -260,6 +261,7 @@ class NetworkPlugin { void handleFTFinishPayload(const std::string &payload); void handleFTPausePayload(const std::string &payload); void handleFTContinuePayload(const std::string &payload); + void handleRoomSubjectChangedPayload(const std::string &payload); void send(const std::string &data); void sendPong(); diff --git a/include/transport/user.h b/include/transport/user.h index 4e98c4a8..11c253b0 100644 --- a/include/transport/user.h +++ b/include/transport/user.h @@ -110,6 +110,10 @@ class User : public Swift::EntityCapsProvider { return m_connected; } + int getResourceCount() { + return m_resources; + } + boost::signal onReadyToConnect; boost::signal onPresenceChanged; boost::signal onRoomJoined; diff --git a/include/transport/vcardresponder.h b/include/transport/vcardresponder.h index 2618a897..156a95e4 100644 --- a/include/transport/vcardresponder.h +++ b/include/transport/vcardresponder.h @@ -59,4 +59,4 @@ class VCardResponder : public Swift::Responder { Swift::Timer::ref m_collectTimer; }; -} \ No newline at end of file +} diff --git a/plugin/cpp/networkplugin.cpp b/plugin/cpp/networkplugin.cpp index 6b289cd5..eba6dcff 100644 --- a/plugin/cpp/networkplugin.cpp +++ b/plugin/cpp/networkplugin.cpp @@ -374,6 +374,16 @@ void NetworkPlugin::handleConvMessagePayload(const std::string &data) { handleMessageSendRequest(payload.username(), payload.buddyname(), payload.message(), payload.xhtml()); } +void NetworkPlugin::handleRoomSubjectChangedPayload(const std::string &data) { + pbnetwork::ConversationMessage payload; + if (payload.ParseFromString(data) == false) { + // TODO: ERROR + return; + } + + handleRoomSubjectChangedRequest(payload.username(), payload.buddyname(), payload.message()); +} + void NetworkPlugin::handleAttentionPayload(const std::string &data) { pbnetwork::ConversationMessage payload; if (payload.ParseFromString(data) == false) { @@ -550,6 +560,9 @@ void NetworkPlugin::handleDataRead(std::string &data) { case pbnetwork::WrapperMessage_Type_TYPE_CONV_MESSAGE: handleConvMessagePayload(wrapper.payload()); break; + case pbnetwork::WrapperMessage_Type_TYPE_ROOM_SUBJECT_CHANGED: + handleRoomSubjectChangedPayload(wrapper.payload()); + break; case pbnetwork::WrapperMessage_Type_TYPE_JOIN_ROOM: handleJoinRoomPayload(wrapper.payload()); break; diff --git a/spectrum_manager/src/methods.cpp b/spectrum_manager/src/methods.cpp index eca476c8..87d463d3 100644 --- a/spectrum_manager/src/methods.cpp +++ b/spectrum_manager/src/methods.cpp @@ -195,7 +195,7 @@ int start_instances(ManagerConfig *config, const std::string &_jid) { } } catch (const filesystem_error& ex) { - std::cerr << "boost filesystem error\n"; + std::cerr << "Filesystem error: " << ex.what() << "\n"; return 6; } return rv; @@ -271,7 +271,7 @@ void stop_instances(ManagerConfig *config, const std::string &_jid) { } } catch (const filesystem_error& ex) { - std::cerr << "boost filesystem error\n"; + std::cerr << "Filesystem error: " << ex.what() << "\n"; exit(5); } } @@ -324,7 +324,7 @@ int show_status(ManagerConfig *config) { } } catch (const filesystem_error& ex) { - std::cerr << "boost filesystem error\n"; + std::cerr << "Filesystem error: " << ex.what() << "\n"; exit(5); } return ret; @@ -460,7 +460,7 @@ void ask_local_server(ManagerConfig *config, Swift::BoostNetworkFactories &netwo } } catch (const filesystem_error& ex) { - std::cerr << "boost filesystem error\n"; + std::cerr << "Filesystem error: " << ex.what() << "\n"; exit(5); } } @@ -498,7 +498,7 @@ std::vector show_list(ManagerConfig *config, bool show) { } } catch (const filesystem_error& ex) { - std::cerr << "boost filesystem error\n"; + std::cerr << "Filesystem error: " << ex.what() << "\n"; } return list; } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 17012906..6c815564 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -38,11 +38,11 @@ else(PROTOBUF_FOUND) ADD_LIBRARY(transport SHARED ${HEADERS} ${SRC} ${SWIFTEN_SRC}) endif(PROTOBUF_FOUND) -if (CMAKE_COMPILER_IS_GNUCXX) +# if (CMAKE_COMPILER_IS_GNUCXX) if (NOT WIN32) ADD_DEFINITIONS(-fPIC) endif() -endif() +# endif() if (WIN32) TARGET_LINK_LIBRARIES(transport transport-plugin sqlite3 ${PQXX_LIBRARY} ${PQ_LIBRARY} ${MYSQL_LIBRARIES} ${SWIFTEN_LIBRARY} ${LOG4CXX_LIBRARIES} ${PROTOBUF_LIBRARY}) diff --git a/src/config.cpp b/src/config.cpp index 432fb86f..ce49f6d6 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -135,18 +135,6 @@ bool Config::load(std::istream &ifs, boost::program_options::options_description ; - // Load configs passed by command line - if (m_argc != 0 && m_argv) { - basic_command_line_parser parser = command_line_parser(m_argc, m_argv).options(opts).allow_unregistered(); - parsed_options parsed = parser.run(); - BOOST_FOREACH(option &opt, parsed.options) { - if (opt.unregistered && !opt.value.empty()) { - m_unregistered[opt.string_key] = variable_value(opt.value[0], false); - } - } - store(parsed, m_variables); - } - parsed_options parsed = parse_config_file(ifs, opts, true); bool found_working = false; @@ -212,15 +200,43 @@ bool Config::load(std::istream &ifs, boost::program_options::options_description parsed.options.push_back(boost::program_options::basic_option("database.database", value)); } + std::list has_key; BOOST_FOREACH(option &opt, parsed.options) { if (opt.unregistered) { - m_unregistered[opt.string_key] = variable_value(opt.value[0], false); + if (std::find(has_key.begin(), has_key.end(), opt.string_key) == has_key.end()) { + has_key.push_back(opt.string_key); + m_unregistered[opt.string_key] = variable_value(opt.value[0], false); + } + else { + std::list list; + try { + list = m_unregistered[opt.string_key].as >(); + } + catch(...) { + list.push_back(m_unregistered[opt.string_key].as()); + } + + list.push_back(opt.value[0]); + m_unregistered[opt.string_key] = variable_value(list, false); + } } else if (opt.value[0].find("$jid") != std::string::npos) { boost::replace_all(opt.value[0], "$jid", jid); } } + // Load configs passed by command line + if (m_argc != 0 && m_argv) { + basic_command_line_parser parser = command_line_parser(m_argc, m_argv).options(opts).allow_unregistered(); + parsed_options parsed = parser.run(); + BOOST_FOREACH(option &opt, parsed.options) { + if (opt.unregistered && !opt.value.empty()) { + m_unregistered[opt.string_key] = variable_value(opt.value[0], false); + } + } + store(parsed, m_variables); + } + store(parsed, m_variables); notify(m_variables); diff --git a/src/conversation.cpp b/src/conversation.cpp index 22799776..f6105461 100644 --- a/src/conversation.cpp +++ b/src/conversation.cpp @@ -55,6 +55,10 @@ void Conversation::destroyRoom() { Swift::MUCUserPayload *p = new Swift::MUCUserPayload (); p->addItem(item); + Swift::MUCUserPayload::StatusCode c; + c.code = 332; + p->addStatusCode(c); + presence->addPayload(boost::shared_ptr(p)); m_conversationManager->getComponent()->getStanzaChannel()->sendPresence(presence); } @@ -111,13 +115,23 @@ void Conversation::handleMessage(boost::shared_ptr &message, con if (n.empty()) { n = " "; } - message->setTo(m_jid); - message->setFrom(Swift::JID(legacyName, m_conversationManager->getComponent()->getJID().toBare(), n)); - m_conversationManager->getComponent()->getStanzaChannel()->sendMessage(message); + BOOST_FOREACH(const Swift::JID &jid, m_jids) { + message->setTo(jid); + message->setFrom(Swift::JID(legacyName, m_conversationManager->getComponent()->getJID().toBare(), n)); + m_conversationManager->getComponent()->getStanzaChannel()->sendMessage(message); + } } } -void Conversation::handleParticipantChanged(const std::string &nick, int flag, int status, const std::string &statusMessage, const std::string &newname) { +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); + } +} + +Swift::Presence::ref Conversation::generatePresence(const std::string &nick, int flag, int status, const std::string &statusMessage, const std::string &newname) { std::string nickname = nick; Swift::Presence::ref presence = Swift::Presence::create(); std::string legacyName = m_legacyName; @@ -127,7 +141,6 @@ void Conversation::handleParticipantChanged(const std::string &nick, int flag, i } } presence->setFrom(Swift::JID(legacyName, m_conversationManager->getComponent()->getJID().toBare(), nickname)); - presence->setTo(m_jid); presence->setType(Swift::Presence::Available); if (!statusMessage.empty()) @@ -168,9 +181,29 @@ void Conversation::handleParticipantChanged(const std::string &nick, int flag, i } p->addItem(item); - presence->addPayload(boost::shared_ptr(p)); - m_conversationManager->getComponent()->getStanzaChannel()->sendPresence(presence); + return presence; +} + +void Conversation::handleParticipantChanged(const std::string &nick, int flag, int status, const std::string &statusMessage, const std::string &newname) { + Swift::Presence::ref presence = generatePresence(nick, flag, status, statusMessage, newname); + + if (presence->getType() == Swift::Presence::Unavailable) { + m_participants.erase(nick); + } + else { + Participant p; + p.flag = flag; + p.status = status; + p.statusMessage = statusMessage; + m_participants[nick] = p; + } + + + BOOST_FOREACH(const Swift::JID &jid, m_jids) { + presence->setTo(jid); + m_conversationManager->getComponent()->getStanzaChannel()->sendPresence(presence); + } if (!newname.empty()) { handleParticipantChanged(newname, flag, status, statusMessage); } diff --git a/src/conversationmanager.cpp b/src/conversationmanager.cpp index 2089238e..4fc23afc 100644 --- a/src/conversationmanager.cpp +++ b/src/conversationmanager.cpp @@ -89,6 +89,12 @@ void ConversationManager::resetResources() { } } +void ConversationManager::removeJID(const Swift::JID &jid) { + for (std::map::const_iterator it = m_convs.begin(); it != m_convs.end(); it++) { + (*it).second->removeJID(jid); + } +} + void ConversationManager::handleMessageReceived(Swift::Message::ref message) { // std::string name = message->getTo().getUnescapedNode(); // if (name.find_last_of("%") != std::string::npos) { // OK when commented diff --git a/src/networkpluginserver.cpp b/src/networkpluginserver.cpp index 0c133a2c..a7efd8fb 100644 --- a/src/networkpluginserver.cpp +++ b/src/networkpluginserver.cpp @@ -56,6 +56,8 @@ #else #include "sys/wait.h" #include "sys/signal.h" +#include +#include #include "popt.h" #endif @@ -1192,7 +1194,7 @@ void NetworkPluginServer::handleRoomJoined(User *user, const Swift::JID &who, co user->getConversationManager()->addConversation(conv); conv->onMessageToSend.connect(boost::bind(&NetworkPluginServer::handleMessageReceived, this, _1, _2)); conv->setNickname(nickname); - conv->setJID(who); + conv->addJID(who); } void NetworkPluginServer::handleRoomLeft(User *user, const std::string &r) { @@ -1301,6 +1303,22 @@ void NetworkPluginServer::handleMessageReceived(NetworkConversation *conv, boost send(c->connection, message); return; } + + if (!msg->getSubject().empty()) { + pbnetwork::ConversationMessage m; + m.set_username(conv->getConversationManager()->getUser()->getJID().toBare()); + m.set_buddyname(conv->getLegacyName()); + m.set_message(msg->getSubject()); + + std::string message; + m.SerializeToString(&message); + + WRAP(message, pbnetwork::WrapperMessage_Type_TYPE_ROOM_SUBJECT_CHANGED); + + Backend *c = (Backend *) conv->getConversationManager()->getUser()->getData(); + send(c->connection, message); + return; + } std::string xhtml; diff --git a/src/tests/basictest.cpp b/src/tests/basictest.cpp index e9779f9c..9a893e7d 100644 --- a/src/tests/basictest.cpp +++ b/src/tests/basictest.cpp @@ -41,7 +41,7 @@ using namespace Transport; void BasicTest::setMeUp (void) { streamEnded = false; - std::istringstream ifs("service.server_mode = 1\nservice.jid=localhost"); + std::istringstream ifs("service.server_mode = 1\nservice.jid=localhost\nservice.more_resources=1\n"); cfg = new Config(); cfg->load(ifs); @@ -86,6 +86,7 @@ void BasicTest::setMeUp (void) { payloadSerializers->addSerializer(new Swift::GatewayPayloadSerializer()); parser = new Swift::XMPPParser(this, payloadParserFactories, factories->getXMLParserFactory()); + parser2 = new Swift::XMPPParser(this, payloadParserFactories, factories->getXMLParserFactory()); serverFromClientSession = boost::shared_ptr(new Swift::ServerFromClientSession("id", factories->getConnectionFactory()->createConnection(), payloadParserFactories, payloadSerializers, userRegistry, factories->getXMLParserFactory(), Swift::JID("user@localhost/resource"))); @@ -95,13 +96,19 @@ void BasicTest::setMeUp (void) { dynamic_cast(component->getStanzaChannel())->addSession(serverFromClientSession); parser->parse(""); + parser2->parse(""); received.clear(); + received2.clear(); receivedData.clear(); loop->processEvents(); } void BasicTest::tearMeDown (void) { dynamic_cast(component->getStanzaChannel())->removeSession(serverFromClientSession); + if (serverFromClientSession2) { + dynamic_cast(component->getStanzaChannel())->removeSession(serverFromClientSession2); + serverFromClientSession2.reset(); + } delete component; delete userRegistry; delete factories; @@ -109,29 +116,48 @@ void BasicTest::tearMeDown (void) { delete loop; delete cfg; delete parser; + delete parser2; delete storage; delete userRegistration; delete itemsResponder; received.clear(); + received2.clear(); receivedData.clear(); + receivedData2.clear(); } void BasicTest::handleDataReceived(const Swift::SafeByteArray &data) { // std::cout << safeByteArrayToString(data) << "\n"; + stream1_active = true; receivedData += safeByteArrayToString(data) + "\n"; parser->parse(safeByteArrayToString(data)); } +void BasicTest::handleDataReceived2(const Swift::SafeByteArray &data) { +// std::cout << safeByteArrayToString(data) << "\n"; + stream1_active = false; + receivedData2 += safeByteArrayToString(data) + "\n"; + parser2->parse(safeByteArrayToString(data)); +} + void BasicTest::handleStreamStart(const Swift::ProtocolHeader&) { } void BasicTest::dumpReceived() { + std::cout << "\nStream1:\n"; std::cout << receivedData << "\n"; + std::cout << "Stream2:\n"; + std::cout << receivedData2 << "\n"; } void BasicTest::handleElement(boost::shared_ptr element) { - received.push_back(element); + if (stream1_active) { + received.push_back(element); + } + else { + received2.push_back(element); + } } void BasicTest::handleStreamEnd() { @@ -159,6 +185,7 @@ Swift::Stanza *BasicTest::getStanza(boost::shared_ptr element) { void BasicTest::connectUser() { CPPUNIT_ASSERT_EQUAL(0, userManager->getUserCount()); userRegistry->isValidUserPassword(Swift::JID("user@localhost/resource"), serverFromClientSession.get(), Swift::createSafeByteArray("password")); + userRegistry->onPasswordValid(Swift::JID("user@localhost/resource")); loop->processEvents(); CPPUNIT_ASSERT_EQUAL(1, userManager->getUserCount()); @@ -173,9 +200,39 @@ void BasicTest::connectUser() { user->setConnected(true); CPPUNIT_ASSERT(user->isConnected() == true); - CPPUNIT_ASSERT_EQUAL(1, (int) received.size()); + CPPUNIT_ASSERT_EQUAL(2, (int) received.size()); CPPUNIT_ASSERT(getStanza(received[0])->getPayload()); received.clear(); + receivedData.clear(); +} + +void BasicTest::connectSecondResource() { + serverFromClientSession2 = boost::shared_ptr(new Swift::ServerFromClientSession("id", factories->getConnectionFactory()->createConnection(), + payloadParserFactories, payloadSerializers, userRegistry, factories->getXMLParserFactory(), Swift::JID("user@localhost/resource2"))); + serverFromClientSession2->startSession(); + + serverFromClientSession2->onDataWritten.connect(boost::bind(&BasicTest::handleDataReceived2, this, _1)); + + dynamic_cast(component->getStanzaChannel())->addSession(serverFromClientSession2); + + userRegistry->isValidUserPassword(Swift::JID("user@localhost/resource2"), serverFromClientSession2.get(), Swift::createSafeByteArray("password")); + userRegistry->onPasswordValid(Swift::JID("user@localhost/resource2")); + + loop->processEvents(); + + Swift::Presence::ref response = Swift::Presence::create(); + response->setTo("localhost"); + response->setFrom("user@localhost/resource2"); + injectPresence(response); + loop->processEvents(); + + CPPUNIT_ASSERT_EQUAL(1, userManager->getUserCount()); + + User *user = userManager->getUser("user@localhost"); + CPPUNIT_ASSERT(user); + CPPUNIT_ASSERT_EQUAL(2, user->getResourceCount()); + + CPPUNIT_ASSERT(getStanza(received2[1])->getPayload()); } void BasicTest::disconnectUser() { diff --git a/src/tests/basictest.h b/src/tests/basictest.h index ee4415cd..554ab5b6 100644 --- a/src/tests/basictest.h +++ b/src/tests/basictest.h @@ -205,6 +205,7 @@ class BasicTest : public Swift::XMPPParserClient { void tearMeDown (void); void handleDataReceived(const Swift::SafeByteArray &data); + void handleDataReceived2(const Swift::SafeByteArray &data); void handleStreamStart(const Swift::ProtocolHeader&); @@ -228,6 +229,7 @@ class BasicTest : public Swift::XMPPParserClient { } void connectUser(); + void connectSecondResource(); void disconnectUser(); void add2Buddies(); @@ -237,9 +239,11 @@ class BasicTest : public Swift::XMPPParserClient { bool streamEnded; UserManager *userManager; boost::shared_ptr serverFromClientSession; + boost::shared_ptr serverFromClientSession2; Swift::FullPayloadSerializerCollection* payloadSerializers; Swift::FullPayloadParserFactoryCollection* payloadParserFactories; Swift::XMPPParser *parser; + Swift::XMPPParser *parser2; UserRegistry *userRegistry; Config *cfg; Swift::Server *server; @@ -248,9 +252,12 @@ class BasicTest : public Swift::XMPPParserClient { TestingFactory *factory; Component *component; std::vector > received; + std::vector > received2; std::string receivedData; + std::string receivedData2; StorageBackend *storage; UserRegistration *userRegistration; DiscoItemsResponder *itemsResponder; + bool stream1_active; }; diff --git a/src/tests/config.cpp b/src/tests/config.cpp index d9e344f0..24776a74 100644 --- a/src/tests/config.cpp +++ b/src/tests/config.cpp @@ -24,7 +24,12 @@ using namespace Transport; class ConfigTest : public CPPUNIT_NS :: TestFixture{ CPPUNIT_TEST_SUITE(ConfigTest); + CPPUNIT_TEST(setStringTwice); CPPUNIT_TEST(updateBackendConfig); + CPPUNIT_TEST(unregisteredList); + CPPUNIT_TEST(unregisteredString); + CPPUNIT_TEST(unregisteredListAsString); + CPPUNIT_TEST(unregisteredStringAsList); CPPUNIT_TEST_SUITE_END(); public: @@ -35,6 +40,14 @@ class ConfigTest : public CPPUNIT_NS :: TestFixture{ } + void setStringTwice() { + char *argv[3] = {"binary", "--service.jids=localhost", NULL}; + Config cfg(2, argv); + std::istringstream ifs("service.jids = irc.freenode.org\n"); + cfg.load(ifs); + CPPUNIT_ASSERT_EQUAL(std::string("localhost"), CONFIG_STRING(&cfg, "service.jids")); + } + void updateBackendConfig() { Config cfg; CPPUNIT_ASSERT(!cfg.hasKey("registration.needPassword")); @@ -44,6 +57,35 @@ class ConfigTest : public CPPUNIT_NS :: TestFixture{ CPPUNIT_ASSERT_EQUAL(false, CONFIG_BOOL(&cfg, "registration.needPassword")); } + void unregisteredList() { + Config cfg; + std::istringstream ifs("service.irc_server = irc.freenode.org\nservice.irc_server=localhost\n"); + cfg.load(ifs); + CPPUNIT_ASSERT_EQUAL(2, (int) CONFIG_LIST(&cfg, "service.irc_server").size()); + } + + void unregisteredString() { + Config cfg; + std::istringstream ifs("service.irc_server = irc.freenode.org"); + cfg.load(ifs); + CPPUNIT_ASSERT_EQUAL(std::string("irc.freenode.org"), CONFIG_STRING(&cfg, "service.irc_server")); + } + + void unregisteredListAsString() { + Config cfg; + std::istringstream ifs("service.irc_server = irc.freenode.orgn\nservice.irc_server = irc2.freenode.org"); + cfg.load(ifs); + CPPUNIT_ASSERT_EQUAL(std::string(""), CONFIG_STRING_DEFAULTED(&cfg, "service.irc_server", "")); + } + + void unregisteredStringAsList() { + Config cfg; + std::istringstream ifs("service.irc_server = irc.freenode.org"); + cfg.load(ifs); + std::list list; + CPPUNIT_ASSERT_EQUAL(0, (int) CONFIG_LIST_DEFAULTED(&cfg, "service.irc_server", list).size()); + } + }; CPPUNIT_TEST_SUITE_REGISTRATION (ConfigTest); diff --git a/src/tests/conversationmanager.cpp b/src/tests/conversationmanager.cpp index 5d517f7c..9aa739fa 100644 --- a/src/tests/conversationmanager.cpp +++ b/src/tests/conversationmanager.cpp @@ -25,8 +25,11 @@ class ConversationManagerTest : public CPPUNIT_NS :: TestFixture, public BasicTe CPPUNIT_TEST_SUITE(ConversationManagerTest); CPPUNIT_TEST(handleNormalMessages); CPPUNIT_TEST(handleGroupchatMessages); + CPPUNIT_TEST(handleGroupchatMessagesTwoResources); CPPUNIT_TEST(handleChatstateMessages); + CPPUNIT_TEST(handleSubjectMessages); CPPUNIT_TEST(handleParticipantChanged); + CPPUNIT_TEST(handleParticipantChangedTwoResources); CPPUNIT_TEST(handlePMFromXMPP); CPPUNIT_TEST(handleGroupchatRemoved); CPPUNIT_TEST_SUITE_END(); @@ -89,6 +92,38 @@ class ConversationManagerTest : public CPPUNIT_NS :: TestFixture, public BasicTe received.clear(); } + void handleSubjectMessages() { + User *user = userManager->getUser("user@localhost"); + + TestingConversation *conv = new TestingConversation(user->getConversationManager(), "buddy1"); + user->getConversationManager()->addConversation(conv); + conv->onMessageToSend.connect(boost::bind(&ConversationManagerTest::handleMessageReceived, this, _1, _2)); + + boost::shared_ptr msg(new Swift::Message()); + msg->setSubject("subject"); + + // Forward it + conv->handleMessage(msg); + loop->processEvents(); + + CPPUNIT_ASSERT_EQUAL(1, (int) received.size()); + CPPUNIT_ASSERT(dynamic_cast(getStanza(received[0]))); + CPPUNIT_ASSERT_EQUAL(std::string("subject"), dynamic_cast(getStanza(received[0]))->getSubject()); + received.clear(); + + // send response + msg->setFrom("user@localhost/resource"); + msg->setTo("buddy1@localhost/bot"); + injectMessage(msg); + loop->processEvents(); + + CPPUNIT_ASSERT_EQUAL(0, (int) received.size()); + CPPUNIT_ASSERT(m_msg); + CPPUNIT_ASSERT_EQUAL(std::string("subject"), m_msg->getSubject()); + + received.clear(); + } + void handleNormalMessages() { User *user = userManager->getUser("user@localhost"); @@ -158,7 +193,7 @@ class ConversationManagerTest : public CPPUNIT_NS :: TestFixture, public BasicTe user->getConversationManager()->addConversation(conv); conv->onMessageToSend.connect(boost::bind(&ConversationManagerTest::handleMessageReceived, this, _1, _2)); conv->setNickname("nickname"); - conv->setJID("user@localhost/resource"); + conv->addJID("user@localhost/resource"); // reset resources should not touch this resource user->getConversationManager()->resetResources(); @@ -191,13 +226,55 @@ class ConversationManagerTest : public CPPUNIT_NS :: TestFixture, public BasicTe CPPUNIT_ASSERT_EQUAL(std::string("response!"), m_msg->getBody()); } + void handleGroupchatMessagesTwoResources() { + connectSecondResource(); + received2.clear(); + User *user = userManager->getUser("user@localhost"); + TestingConversation *conv = new TestingConversation(user->getConversationManager(), "#room", true); + user->getConversationManager()->addConversation(conv); + conv->onMessageToSend.connect(boost::bind(&ConversationManagerTest::handleMessageReceived, this, _1, _2)); + conv->setNickname("nickname"); + conv->addJID("user@localhost/resource"); + conv->addJID("user@localhost/resource2"); + + // reset resources should not touch this resource + user->getConversationManager()->resetResources(); + + boost::shared_ptr msg(new Swift::Message()); + msg->setBody("hi there!"); + + // Forward it + conv->handleMessage(msg, "anotheruser"); + + loop->processEvents(); + CPPUNIT_ASSERT_EQUAL(1, (int) received2.size()); + CPPUNIT_ASSERT(dynamic_cast(getStanza(received2[0]))); + CPPUNIT_ASSERT_EQUAL(std::string("hi there!"), dynamic_cast(getStanza(received2[0]))->getBody()); + CPPUNIT_ASSERT_EQUAL(std::string("user@localhost/resource2"), dynamic_cast(getStanza(received2[0]))->getTo().toString()); + CPPUNIT_ASSERT_EQUAL(std::string("#room@localhost/anotheruser"), dynamic_cast(getStanza(received2[0]))->getFrom().toString()); + + received.clear(); + + // send response + msg->setFrom("user@localhost/resource2"); + msg->setTo("#room@localhost"); + msg->setBody("response!"); + msg->setType(Swift::Message::Groupchat); + injectMessage(msg); + loop->processEvents(); + + CPPUNIT_ASSERT_EQUAL(0, (int) received.size()); + CPPUNIT_ASSERT(m_msg); + CPPUNIT_ASSERT_EQUAL(std::string("response!"), m_msg->getBody()); + } + void handleParticipantChanged() { 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->setJID("user@localhost/resource"); + conv->addJID("user@localhost/resource"); // normal presence conv->handleParticipantChanged("anotheruser", 0, Swift::StatusShow::Away, "my status message"); @@ -246,6 +323,31 @@ class ConversationManagerTest : public CPPUNIT_NS :: TestFixture, public BasicTe CPPUNIT_ASSERT_EQUAL(303, getStanza(received[0])->getPayload()->getStatusCodes()[0].code); } + void handleParticipantChangedTwoResources() { + connectSecondResource(); + received2.clear(); + 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"); + conv->addJID("user@localhost/resource2"); + + // normal presence + conv->handleParticipantChanged("anotheruser", 0, Swift::StatusShow::Away, "my status message"); + loop->processEvents(); + + CPPUNIT_ASSERT_EQUAL(1, (int) received2.size()); + CPPUNIT_ASSERT(dynamic_cast(getStanza(received2[0]))); + CPPUNIT_ASSERT_EQUAL(Swift::StatusShow::Away, dynamic_cast(getStanza(received2[0]))->getShow()); + CPPUNIT_ASSERT_EQUAL(std::string("user@localhost/resource2"), dynamic_cast(getStanza(received2[0]))->getTo().toString()); + CPPUNIT_ASSERT_EQUAL(std::string("#room@localhost/anotheruser"), dynamic_cast(getStanza(received2[0]))->getFrom().toString()); + CPPUNIT_ASSERT(getStanza(received2[0])->getPayload()); + CPPUNIT_ASSERT_EQUAL(Swift::MUCOccupant::Member, *getStanza(received2[0])->getPayload()->getItems()[0].affiliation); + CPPUNIT_ASSERT_EQUAL(Swift::MUCOccupant::Participant, *getStanza(received2[0])->getPayload()->getItems()[0].role); + } + void handlePMFromXMPP() { User *user = userManager->getUser("user@localhost"); TestingConversation *conv = new TestingConversation(user->getConversationManager(), "#room", true); @@ -293,6 +395,7 @@ class ConversationManagerTest : public CPPUNIT_NS :: TestFixture, public BasicTe CPPUNIT_ASSERT_EQUAL(Swift::StatusShow::None, dynamic_cast(getStanza(received[0]))->getShow()); CPPUNIT_ASSERT_EQUAL(std::string("user@localhost/resource"), dynamic_cast(getStanza(received[0]))->getTo().toString()); CPPUNIT_ASSERT_EQUAL(std::string("#room@localhost/nickname"), dynamic_cast(getStanza(received[0]))->getFrom().toString()); + CPPUNIT_ASSERT_EQUAL(332, getStanza(received[0])->getPayload()->getStatusCodes()[0].code); } }; diff --git a/src/tests/user.cpp b/src/tests/user.cpp index 114da87b..bbafedbb 100644 --- a/src/tests/user.cpp +++ b/src/tests/user.cpp @@ -25,7 +25,10 @@ class UserTest : public CPPUNIT_NS :: TestFixture, public BasicTest { CPPUNIT_TEST(sendCurrentPresence); CPPUNIT_TEST(handlePresence); CPPUNIT_TEST(handlePresenceJoinRoom); + CPPUNIT_TEST(handlePresenceJoinRoomTwoResources); CPPUNIT_TEST(handlePresenceLeaveRoom); + CPPUNIT_TEST(handlePresenceLeaveRoomTwoResources); + CPPUNIT_TEST(handlePresenceLeaveRoomTwoResourcesOneDisconnects); CPPUNIT_TEST(leaveJoinedRoom); CPPUNIT_TEST(handleDisconnected); CPPUNIT_TEST(handleDisconnectedReconnect); @@ -139,6 +142,7 @@ class UserTest : public CPPUNIT_NS :: TestFixture, public BasicTest { // simulate that backend joined the room TestingConversation *conv = new TestingConversation(user->getConversationManager(), "#room", true); + conv->addJID("user@localhost/resource"); user->getConversationManager()->addConversation(conv); received.clear(); @@ -154,6 +158,41 @@ class UserTest : public CPPUNIT_NS :: TestFixture, public BasicTest { CPPUNIT_ASSERT_EQUAL(std::string(""), roomPassword); } + void handlePresenceJoinRoomTwoResources() { + handlePresenceJoinRoom(); + User *user = userManager->getUser("user@localhost"); + + // Add 1 participant + Conversation *conv = user->getConversationManager()->getConversation("#room"); + conv->handleParticipantChanged("anotheruser", 0, Swift::StatusShow::Away, "my status message"); + + // Connect 2nd resource + connectSecondResource(); + received2.clear(); + Swift::Presence::ref response = Swift::Presence::create(); + response->setTo("#room@localhost/hanzz"); + response->setFrom("user@localhost/resource2"); + + Swift::MUCPayload *payload = new Swift::MUCPayload(); + payload->setPassword("password"); + response->addPayload(boost::shared_ptr(payload)); + injectPresence(response); + loop->processEvents(); + + CPPUNIT_ASSERT_EQUAL(std::string(""), room); + CPPUNIT_ASSERT_EQUAL(std::string(""), roomNickname); + CPPUNIT_ASSERT_EQUAL(std::string(""), roomPassword); + + CPPUNIT_ASSERT_EQUAL(2, (int) received2.size()); + CPPUNIT_ASSERT(dynamic_cast(getStanza(received2[1]))); + CPPUNIT_ASSERT_EQUAL(Swift::StatusShow::Away, dynamic_cast(getStanza(received2[1]))->getShow()); + CPPUNIT_ASSERT_EQUAL(std::string("user@localhost/resource2"), dynamic_cast(getStanza(received2[1]))->getTo().toString()); + CPPUNIT_ASSERT_EQUAL(std::string("#room@localhost/anotheruser"), dynamic_cast(getStanza(received2[1]))->getFrom().toString()); + CPPUNIT_ASSERT(getStanza(received2[1])->getPayload()); + CPPUNIT_ASSERT_EQUAL(Swift::MUCOccupant::Member, *getStanza(received2[1])->getPayload()->getItems()[0].affiliation); + CPPUNIT_ASSERT_EQUAL(Swift::MUCOccupant::Participant, *getStanza(received2[1])->getPayload()->getItems()[0].role); + } + void handlePresenceLeaveRoom() { Swift::Presence::ref response = Swift::Presence::create(); response->setTo("#room@localhost/hanzz"); @@ -173,6 +212,71 @@ class UserTest : public CPPUNIT_NS :: TestFixture, public BasicTest { CPPUNIT_ASSERT_EQUAL(std::string(""), roomPassword); } + void handlePresenceLeaveRoomTwoResources() { + handlePresenceJoinRoomTwoResources(); + received.clear(); + + // User is still connected from resource2, so he should not leave the room + Swift::Presence::ref response = Swift::Presence::create(); + response->setTo("#room@localhost/hanzz"); + response->setFrom("user@localhost/resource"); + response->setType(Swift::Presence::Unavailable); + + Swift::MUCPayload *payload = new Swift::MUCPayload(); + payload->setPassword("password"); + response->addPayload(boost::shared_ptr(payload)); + injectPresence(response); + loop->processEvents(); + + CPPUNIT_ASSERT_EQUAL(0, (int) received.size()); + + CPPUNIT_ASSERT_EQUAL(std::string(""), room); + CPPUNIT_ASSERT_EQUAL(std::string(""), roomNickname); + CPPUNIT_ASSERT_EQUAL(std::string(""), roomPassword); + + // disconnect also from resource + // User is still connected from resource2, so he should not leave the room + response = Swift::Presence::create(); + response->setTo("#room@localhost/hanzz"); + response->setFrom("user@localhost/resource2"); + response->setType(Swift::Presence::Unavailable); + + payload = new Swift::MUCPayload(); + payload->setPassword("password"); + response->addPayload(boost::shared_ptr(payload)); + injectPresence(response); + loop->processEvents(); + + CPPUNIT_ASSERT_EQUAL(0, (int) received.size()); + + CPPUNIT_ASSERT_EQUAL(std::string("#room"), room); + CPPUNIT_ASSERT_EQUAL(std::string(""), roomNickname); + CPPUNIT_ASSERT_EQUAL(std::string(""), roomPassword); + } + + void handlePresenceLeaveRoomTwoResourcesOneDisconnects() { + handlePresenceJoinRoomTwoResources(); + received.clear(); + User *user = userManager->getUser("user@localhost"); + + // User is still connected from resource2, so he should not leave the room + Swift::Presence::ref response = Swift::Presence::create(); + response->setTo("localhost/hanzz"); + response->setFrom("user@localhost/resource"); + response->setType(Swift::Presence::Unavailable); + injectPresence(response); + loop->processEvents(); + + + CPPUNIT_ASSERT_EQUAL(std::string(""), room); + CPPUNIT_ASSERT_EQUAL(std::string(""), roomNickname); + CPPUNIT_ASSERT_EQUAL(std::string(""), roomPassword); + + Conversation *conv = user->getConversationManager()->getConversation("#room"); + CPPUNIT_ASSERT_EQUAL(1, (int) conv->getJIDs().size()); + CPPUNIT_ASSERT_EQUAL(Swift::JID("user@localhost/resource2"), conv->getJIDs().front()); + } + void leaveJoinedRoom() { User *user = userManager->getUser("user@localhost"); handlePresenceJoinRoom(); diff --git a/src/tests/usermanager.cpp b/src/tests/usermanager.cpp index 80a78cf7..1f03575d 100644 --- a/src/tests/usermanager.cpp +++ b/src/tests/usermanager.cpp @@ -23,6 +23,7 @@ using namespace Transport; class UserManagerTest : public CPPUNIT_NS :: TestFixture, public BasicTest { CPPUNIT_TEST_SUITE(UserManagerTest); CPPUNIT_TEST(connectUser); + CPPUNIT_TEST(connectTwoResources); CPPUNIT_TEST(connectUserTransportDisabled); CPPUNIT_TEST(connectUserRegistrationNeeded); CPPUNIT_TEST(connectUserRegistrationNeededRegistered); @@ -86,6 +87,22 @@ class UserManagerTest : public CPPUNIT_NS :: TestFixture, public BasicTest { CPPUNIT_ASSERT_EQUAL(Swift::Presence::Unavailable, presence->getType()); } + void connectTwoResources() { + connectUser(); + add2Buddies(); + connectSecondResource(); + + // we should get presences + CPPUNIT_ASSERT_EQUAL(4, (int) received2.size()); + CPPUNIT_ASSERT(dynamic_cast(getStanza(received2[2]))); + CPPUNIT_ASSERT_EQUAL(Swift::StatusShow::Away, dynamic_cast(getStanza(received2[2]))->getShow()); + CPPUNIT_ASSERT_EQUAL(std::string("status1"), dynamic_cast(getStanza(received2[2]))->getStatus()); + + CPPUNIT_ASSERT(dynamic_cast(getStanza(received2[3]))); + CPPUNIT_ASSERT_EQUAL(Swift::StatusShow::Away, dynamic_cast(getStanza(received2[3]))->getShow()); + CPPUNIT_ASSERT_EQUAL(std::string("status2"), dynamic_cast(getStanza(received2[3]))->getStatus()); + } + }; CPPUNIT_TEST_SUITE_REGISTRATION (UserManagerTest); diff --git a/src/user.cpp b/src/user.cpp index 56f53f72..f791b992 100644 --- a/src/user.cpp +++ b/src/user.cpp @@ -218,10 +218,17 @@ void User::handlePresence(Swift::Presence::ref presence) { if (isMUC) { if (presence->getType() == Swift::Presence::Unavailable) { std::string room = Buddy::JIDToLegacyName(presence->getTo()); + Conversation *conv = m_conversationManager->getConversation(room); + if (conv) { + conv->removeJID(presence->getFrom()); + if (!conv->getJIDs().empty()) { + return; + } + } + LOG4CXX_INFO(logger, m_jid.toString() << ": Going to left room " << room); onRoomLeft(room); - Conversation *conv = m_conversationManager->getConversation(room); if (conv) { m_conversationManager->removeConversation(conv); delete conv; @@ -235,8 +242,15 @@ void User::handlePresence(Swift::Presence::ref presence) { onReadyToConnect(); } std::string room = Buddy::JIDToLegacyName(presence->getTo()); - if (m_conversationManager->getConversation(room) != NULL) { - LOG4CXX_INFO(logger, m_jid.toString() << ": User has already tried to join room " << room << " as " << presence->getTo().getResource()); + Conversation *conv = m_conversationManager->getConversation(room); + if (conv != NULL) { + if (std::find(conv->getJIDs().begin(), conv->getJIDs().end(), presence->getFrom()) != conv->getJIDs().end()) { + LOG4CXX_INFO(logger, m_jid.toString() << ": User has already tried to join room " << room << " as " << presence->getTo().getResource()); + } + else { + conv->addJID(presence->getFrom()); + conv->sendParticipants(presence->getFrom()); + } return; } @@ -249,6 +263,10 @@ void User::handlePresence(Swift::Presence::ref presence) { } return; } + + if (presence->getType() == Swift::Presence::Unavailable) { + m_conversationManager->removeJID(presence->getFrom()); + } // User wants to disconnect this resource @@ -266,13 +284,14 @@ void User::handlePresence(Swift::Presence::ref presence) { } else { sendCurrentPresence(); - // This resource is new, so we have to send buddies presences - if (currentResourcesCount != m_resources) { - m_rosterManager->sendCurrentPresences(presence->getFrom()); - } } } + // This resource is new, so we have to send buddies presences + if (presence->getType() != Swift::Presence::Unavailable && currentResourcesCount != m_resources) { + m_rosterManager->sendCurrentPresences(presence->getFrom()); + } + m_resources = currentResourcesCount; diff --git a/src/usermanager.cpp b/src/usermanager.cpp index ebbfb2d1..98c75dcf 100644 --- a/src/usermanager.cpp +++ b/src/usermanager.cpp @@ -31,7 +31,9 @@ #include "Swiften/Swiften.h" #include "Swiften/Server/ServerStanzaChannel.h" #include "Swiften/Elements/StreamError.h" +#ifndef __FreeBSD__ #include "malloc.h" +#endif // #include "valgrind/memcheck.h" namespace Transport { @@ -124,8 +126,10 @@ void UserManager::removeUser(User *user, bool onUserBehalf) { onUserDestroyed(user); delete user; #ifndef WIN32 +#ifndef __FreeBSD__ malloc_trim(0); #endif +#endif // VALGRIND_DO_LEAK_CHECK; } @@ -340,7 +344,7 @@ void UserManager::handleMessageReceived(Swift::Message::ref message) { messageToBackendSent(); } - if (message->getBody().empty() && !statePayload) { + if (message->getBody().empty() && !statePayload && message->getSubject().empty()) { return; }