diff --git a/CMakeLists.txt b/CMakeLists.txt index 16700072..d7cb5928 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,8 +44,8 @@ message( STATUS "Found Boost: ${Boost_LIBRARIES}, ${Boost_INCLUDE_DIR}") set(Protobuf_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") find_package(Protobuf REQUIRED) -set(IRCClientQt_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") -find_package(IRCClientQt) +set(Communi_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") +find_package(Communi) set(log4cxx_DIR "${CMAKE_SOURCE_DIR}/cmake_modules") find_package(log4cxx) @@ -64,6 +64,25 @@ ADD_DEFINITIONS(-DBOOST_FILESYSTEM_VERSION=2) message(" Supported features") message("-----------------------") + +if (SPECTRUM_VERSION) + ADD_DEFINITIONS(-DSPECTRUM_VERSION="${SPECTRUM_VERSION}") +else (SPECTRUM_VERSION) + if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.git) + execute_process(COMMAND git "--git-dir=${CMAKE_CURRENT_SOURCE_DIR}/.git" rev-parse --short HEAD + OUTPUT_VARIABLE GIT_REVISION + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + set(SPECTRUM_VERSION 2.0.0-beta-git-${GIT_REVISION}) + ADD_DEFINITIONS(-DSPECTRUM_VERSION="${SPECTRUM_VERSION}") + else (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.git) + set(SPECTRUM_VERSION 2.0.0-alpha) + ADD_DEFINITIONS(-DSPECTRUM_VERSION="${SPECTRUM_VERSION}") + endif (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/.git) +endif (SPECTRUM_VERSION) + +message("Version : " ${SPECTRUM_VERSION}) + if (SQLITE3_FOUND) ADD_DEFINITIONS(-DWITH_SQLITE) include_directories(${SQLITE3_INCLUDE_DIR}) @@ -104,13 +123,13 @@ if (PROTOBUF_FOUND) endif() if(IRC_FOUND) - ADD_DEFINITIONS(-DIRC_SHARED) + ADD_DEFINITIONS(-DCOMMUNI_SHARED) message("IRC plugin : yes") include_directories(${QT_QTNETWORK_INCLUDE_DIR}) include_directories(${IRC_INCLUDE_DIR}) include(${QT_USE_FILE}) else() - message("IRC plugin : no (install libircclient-qt and libprotobuf-dev)") + message("IRC plugin : no (install libCommuni and libprotobuf-dev)") endif() message("Frotz plugin : yes") @@ -170,10 +189,8 @@ include_directories(${OPENSSL_INCLUDE_DIR}) ADD_SUBDIRECTORY(src) ADD_SUBDIRECTORY(plugin) ADD_SUBDIRECTORY(include) -#ADD_SUBDIRECTORY(examples) ADD_SUBDIRECTORY(spectrum) ADD_SUBDIRECTORY(backends) -#ADD_SUBDIRECTORY(tests) if (NOT WIN32) ADD_SUBDIRECTORY(spectrum_manager) endif() diff --git a/README b/README index e69de29b..cd912fb4 100644 --- a/README +++ b/README @@ -0,0 +1,6 @@ +Spectrum is an XMPP transport/gateway. It allows XMPP users to communicate with +their friends who are using one of the supported networks. It supports a wide +range of different networks such as ICQ, XMPP (Jabber, GTalk), AIM, MSN, +Facebook, Twitter, Gadu-Gadu, IRC and SIMPLE. Spectrum is written in C++ +and uses the Swiften XMPP library and libpurple for “legacy networks”. +Spectrum is open source and released under the GNU GPL. diff --git a/backends/CMakeLists.txt b/backends/CMakeLists.txt index 5a758c95..9ecb3b5c 100644 --- a/backends/CMakeLists.txt +++ b/backends/CMakeLists.txt @@ -4,7 +4,7 @@ if (PROTOBUF_FOUND) endif() if (IRC_FOUND) - ADD_SUBDIRECTORY(libircclient-qt) + ADD_SUBDIRECTORY(libcommuni) endif() if (NOT WIN32) diff --git a/backends/libcommuni/CMakeLists.txt b/backends/libcommuni/CMakeLists.txt new file mode 100644 index 00000000..a2a14730 --- /dev/null +++ b/backends/libcommuni/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 2.6) +FILE(GLOB SRC *.cpp) +FILE(GLOB HEADERS *.h) +QT4_WRAP_CPP(SRC ${HEADERS}) +ADD_EXECUTABLE(spectrum2_libcommuni_backend ${SRC}) + +target_link_libraries(spectrum2_libcommuni_backend ${IRC_LIBRARY} ${QT_LIBRARIES} transport-plugin transport pthread) + +INSTALL(TARGETS spectrum2_libcommuni_backend RUNTIME DESTINATION bin) + diff --git a/backends/libircclient-qt/ircnetworkplugin.cpp b/backends/libcommuni/ircnetworkplugin.cpp similarity index 79% rename from backends/libircclient-qt/ircnetworkplugin.cpp rename to backends/libcommuni/ircnetworkplugin.cpp index 2ebe91d9..d7c9ae9e 100644 --- a/backends/libircclient-qt/ircnetworkplugin.cpp +++ b/backends/libcommuni/ircnetworkplugin.cpp @@ -1,4 +1,6 @@ #include "ircnetworkplugin.h" +#include +#include IRCNetworkPlugin::IRCNetworkPlugin(Config *config, Swift::QtEventLoop *loop, const std::string &host, int port) { this->config = config; @@ -26,8 +28,10 @@ void IRCNetworkPlugin::handleLoginRequest(const std::string &user, const std::st if (CONFIG_BOOL(config, "service.server_mode")) { MyIrcSession *session = new MyIrcSession(user, this); std::string h = user.substr(0, user.find("@")); - session->setNick(QString::fromStdString(h.substr(0, h.find("%")))); - session->connectToServer(QString::fromStdString(h.substr(h.find("%") + 1)), 6667); + session->setNickName(QString::fromStdString(h.substr(0, h.find("%")))); + session->setHost(QString::fromStdString(h.substr(h.find("%") + 1))); + session->setPort(6667); + session->open(); std::cout << "CONNECTING IRC NETWORK " << h.substr(h.find("%") + 1) << "\n"; m_sessions[user] = session; } @@ -39,7 +43,7 @@ void IRCNetworkPlugin::handleLoginRequest(const std::string &user, const std::st void IRCNetworkPlugin::handleLogoutRequest(const std::string &user, const std::string &legacyName) { if (m_sessions[user] == NULL) return; - m_sessions[user]->disconnectFromServer(); + m_sessions[user]->close(); m_sessions[user]->deleteLater(); m_sessions.erase(user); } @@ -68,7 +72,7 @@ void IRCNetworkPlugin::handleMessageSendRequest(const std::string &user, const s } } std::cout << "MESSAGE " << u << " " << r << "\n"; - m_sessions[u]->message(QString::fromStdString(r), QString::fromStdString(message)); + m_sessions[u]->sendCommand(IrcCommand::createMessage(QString::fromStdString(r), QString::fromStdString(message))); std::cout << "SENT\n"; } @@ -85,8 +89,10 @@ void IRCNetworkPlugin::handleJoinRoomRequest(const std::string &user, const std: 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->setNick(QString::fromStdString(nickname)); - session->connectToServer(QString::fromStdString(room.substr(room.find("@") + 1)), 6667); + session->setNickName(QString::fromStdString(nickname)); + session->setHost(QString::fromStdString(room.substr(room.find("@") + 1))); + session->setPort(6667); + session->open(); std::cout << "CONNECTING IRC NETWORK " << room.substr(room.find("@") + 1) << "\n"; std::cout << "SUFFIX " << room.substr(room.find("@")) << "\n"; m_sessions[u] = session; @@ -96,11 +102,11 @@ void IRCNetworkPlugin::handleJoinRoomRequest(const std::string &user, const std: } } std::cout << "JOINING " << r << "\n"; - m_sessions[u]->addAutoJoinChannel(QString::fromStdString(r)); - m_sessions[u]->join(QString::fromStdString(r), QString::fromStdString(password)); + m_sessions[u]->addAutoJoinChannel(r); + m_sessions[u]->sendCommand(IrcCommand::createJoin(QString::fromStdString(r), QString::fromStdString(password))); m_sessions[u]->rooms += 1; // update nickname, because we have nickname per session, no nickname per room. - handleRoomNicknameChanged(user, r, m_sessions[u]->nick().toStdString()); + handleRoomNicknameChanged(user, r, m_sessions[u]->nickName().toStdString()); } void IRCNetworkPlugin::handleLeaveRoomRequest(const std::string &user, const std::string &room) { @@ -114,12 +120,12 @@ void IRCNetworkPlugin::handleLeaveRoomRequest(const std::string &user, const std if (m_sessions[u] == NULL) return; - m_sessions[u]->part(QString::fromStdString(r)); - m_sessions[u]->removeAutoJoinChannel(QString::fromStdString(r)); + m_sessions[u]->sendCommand(IrcCommand::createPart(QString::fromStdString(r))); + m_sessions[u]->removeAutoJoinChannel(r); m_sessions[u]->rooms -= 1; if (m_sessions[u]->rooms <= 0) { - m_sessions[u]->disconnectFromServer(); + m_sessions[u]->close(); m_sessions[u]->deleteLater(); m_sessions.erase(u); } diff --git a/backends/libircclient-qt/ircnetworkplugin.h b/backends/libcommuni/ircnetworkplugin.h similarity index 100% rename from backends/libircclient-qt/ircnetworkplugin.h rename to backends/libcommuni/ircnetworkplugin.h diff --git a/backends/libircclient-qt/main.cpp b/backends/libcommuni/main.cpp similarity index 55% rename from backends/libircclient-qt/main.cpp rename to backends/libcommuni/main.cpp index 83723532..f5af84fb 100644 --- a/backends/libircclient-qt/main.cpp +++ b/backends/libcommuni/main.cpp @@ -15,11 +15,22 @@ #include #include "Swiften/EventLoop/Qt/QtEventLoop.h" #include "ircnetworkplugin.h" +#include "singleircnetworkplugin.h" + +#include "log4cxx/logger.h" +#include "log4cxx/consoleappender.h" +#include "log4cxx/patternlayout.h" +#include "log4cxx/propertyconfigurator.h" +#include "log4cxx/helpers/properties.h" +#include "log4cxx/helpers/fileinputstream.h" +#include "log4cxx/helpers/transcoder.h" using namespace boost::program_options; using namespace Transport; -IRCNetworkPlugin * np = NULL; +using namespace log4cxx; + +NetworkPlugin * np = NULL; int main (int argc, char* argv[]) { std::string host; @@ -72,8 +83,39 @@ int main (int argc, char* argv[]) { } QCoreApplication app(argc, argv); + if (CONFIG_STRING(&config, "logging.backend_config").empty()) { + LoggerPtr root = log4cxx::Logger::getRootLogger(); +#ifndef _MSC_VER + root->addAppender(new ConsoleAppender(new PatternLayout("%d %-5p %c: %m%n"))); +#else + root->addAppender(new ConsoleAppender(new PatternLayout(L"%d %-5p %c: %m%n"))); +#endif + } + else { + log4cxx::helpers::Properties p; + log4cxx::helpers::FileInputStream *istream = new log4cxx::helpers::FileInputStream(CONFIG_STRING(&config, "logging.backend_config")); + p.load(istream); + LogString pid, jid; + log4cxx::helpers::Transcoder::decode(boost::lexical_cast(getpid()), pid); + log4cxx::helpers::Transcoder::decode(CONFIG_STRING(&config, "service.jid"), jid); +#ifdef _MSC_VER + p.setProperty(L"pid", pid); + p.setProperty(L"jid", jid); +#else + p.setProperty("pid", pid); + p.setProperty("jid", jid); +#endif + log4cxx::PropertyConfigurator::configure(p); + } + Swift::QtEventLoop eventLoop; - np = new IRCNetworkPlugin(&config, &eventLoop, host, port); + + if (config.getUnregistered().find("service.irc_server") == config.getUnregistered().end()) { + np = new IRCNetworkPlugin(&config, &eventLoop, host, port); + } + else { + np = new SingleIRCNetworkPlugin(&config, &eventLoop, host, port); + } return app.exec(); } diff --git a/backends/libcommuni/session.cpp b/backends/libcommuni/session.cpp new file mode 100644 index 00000000..088ebd1a --- /dev/null +++ b/backends/libcommuni/session.cpp @@ -0,0 +1,212 @@ +/* + * Copyright (C) 2008-2009 J-P Nurmi jpnurmi@gmail.com + * + * 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. + */ + +#include "session.h" +#include +#include +#include "Swiften/Elements/StatusShow.h" +#include +#include + +#include "log4cxx/logger.h" + +using namespace log4cxx; + +static LoggerPtr logger = log4cxx::Logger::getLogger("IRCSession"); + +MyIrcSession::MyIrcSession(const std::string &user, NetworkPlugin *np, const std::string &suffix, QObject* parent) : IrcSession(parent) +{ + this->np = np; + this->user = user; + this->suffix = suffix; + rooms = 0; + connect(this, SIGNAL(disconnected()), SLOT(on_disconnected())); + connect(this, SIGNAL(connected()), SLOT(on_connected())); + connect(this, SIGNAL(messageReceived(IrcMessage*)), this, SLOT(onMessageReceived(IrcMessage*))); +} + +void MyIrcSession::on_connected() { + if (suffix.empty()) { + np->handleConnected(user); + } + + for(std::list::const_iterator it = m_autoJoin.begin(); it != m_autoJoin.end(); it++) { + sendCommand(IrcCommand::createJoin(QString::fromStdString(*it))); + } + + if (getIdentify().find(" ") != std::string::npos) { + std::string to = getIdentify().substr(0, getIdentify().find(" ")); + std::string what = getIdentify().substr(getIdentify().find(" ") + 1); + sendCommand(IrcCommand::createMessage(QString::fromStdString(to), QString::fromStdString(what))); + } +} + +void MyIrcSession::on_disconnected() { + if (suffix.empty()) + np->handleDisconnected(user, 0, ""); +} + +bool MyIrcSession::correctNickname(std::string &nickname) { + bool flags = 0; + switch(nickname.at(0)) { + case '@': nickname = nickname.substr(1); flags = 1; break; + case '+': nickname = nickname.substr(1); break; + default: break; + } + return flags; +} + +void MyIrcSession::on_joined(IrcMessage *message) { + IrcJoinMessage *m = (IrcJoinMessage *) message; + bool flags = 0; + std::string nickname = m->sender().name().toStdString(); + flags = correctNickname(nickname); + np->handleParticipantChanged(user, nickname, m->channel().toStdString() + suffix, (int) flags, pbnetwork::STATUS_ONLINE); + LOG4CXX_INFO(logger, user << ": Joined " << m->parameters()[0].toStdString()); +} + + +void MyIrcSession::on_parted(IrcMessage *message) { + IrcPartMessage *m = (IrcPartMessage *) message; + bool flags = 0; + std::string nickname = m->sender().name().toStdString(); + flags = correctNickname(nickname); + LOG4CXX_INFO(logger, user << ": " << nickname << " parted " << m->channel().toStdString() + suffix); + np->handleParticipantChanged(user, nickname, m->channel().toStdString() + suffix,(int) flags, pbnetwork::STATUS_NONE, m->reason().toStdString()); +} + +void MyIrcSession::on_quit(IrcMessage *message) { + IrcQuitMessage *m = (IrcQuitMessage *) message; + for(std::list::const_iterator it = m_autoJoin.begin(); it != m_autoJoin.end(); it++) { + bool flags = 0; + std::string nickname = m->sender().name().toStdString(); + flags = correctNickname(nickname); + LOG4CXX_INFO(logger, user << ": " << nickname << " quit " << (*it) + suffix); + np->handleParticipantChanged(user, nickname, (*it) + suffix,(int) flags, pbnetwork::STATUS_NONE, m->reason().toStdString()); + } +} + +void MyIrcSession::on_nickChanged(IrcMessage *message) { + IrcNickMessage *m = (IrcNickMessage *) message; + + for(std::list::const_iterator it = m_autoJoin.begin(); it != m_autoJoin.end(); it++) { + std::string nickname = m->sender().name().toStdString(); + bool flags = m_modes[(*it) + nickname]; + LOG4CXX_INFO(logger, user << ": " << nickname << " changed nickname to " << m->nick().toStdString()); + np->handleParticipantChanged(user, nickname, (*it) + suffix,(int) flags, pbnetwork::STATUS_ONLINE, "", m->nick().toStdString()); + } +} + +void MyIrcSession::on_modeChanged(IrcMessage *message) { + IrcModeMessage *m = (IrcModeMessage *) message; + + // mode changed: "#testik" "HanzZ" "+o" "hanzz_k" + std::string nickname = m->argument().toStdString(); + std::string mode = m->mode().toStdString(); + if (nickname.empty()) + return; + LOG4CXX_INFO(logger, user << ": " << nickname << " changed mode to " << mode); + for(std::list::const_iterator it = m_autoJoin.begin(); it != m_autoJoin.end(); it++) { + if (mode == "+o") { + m_modes[(*it) + nickname] = 1; + } + else { + m_modes[(*it) + nickname] = 0; + } + bool flags = m_modes[(*it) + nickname]; + np->handleParticipantChanged(user, nickname, (*it) + suffix,(int) flags, pbnetwork::STATUS_ONLINE, ""); + } +} + +void MyIrcSession::on_topicChanged(IrcMessage *message) { + IrcTopicMessage *m = (IrcTopicMessage *) message; + + bool flags = 0; + std::string nickname = m->sender().name().toStdString(); + flags = correctNickname(nickname); + + LOG4CXX_INFO(logger, user << ": " << nickname << " topic changed to " << m->topic().toStdString()); + np->handleSubject(user, m->channel().toStdString() + suffix, m->topic().toStdString(), nickname); +} + +void MyIrcSession::on_messageReceived(IrcMessage *message) { + IrcPrivateMessage *m = (IrcPrivateMessage *) message; + + std::string target = m->target().toStdString(); + LOG4CXX_INFO(logger, user << ": Message from " << target); + if (target.find("#") == 0) { + bool flags = 0; + std::string nickname = m->sender().name().toStdString(); + flags = correctNickname(nickname); + np->handleMessage(user, target + suffix, m->message().toStdString(), nickname); + } + else { + bool flags = 0; + std::string nickname = m->sender().name().toStdString(); + flags = correctNickname(nickname); + np->handleMessage(user, nickname, m->message().toStdString()); + } +} + +void MyIrcSession::on_numericMessageReceived(IrcMessage *message) { + IrcNumericMessage *m = (IrcNumericMessage *) message; + switch (m->code()) { + case 332: + m_topicData = m->parameters().value(2).toStdString(); + break; + case 333: + np->handleSubject(user, m->parameters().value(1).toStdString() + suffix, m_topicData, m->parameters().value(2).toStdString()); + break; + case 353: + QString channel = m->parameters().value(2); + QStringList members = m->parameters().value(3).split(" "); + + for (int i = 0; i < members.size(); i++) { + bool flags = 0; + std::string nickname = members.at(i).toStdString(); + flags = correctNickname(nickname); + m_modes[channel.toStdString() + nickname] = flags; + np->handleParticipantChanged(user, nickname, channel.toStdString() + suffix,(int) flags, pbnetwork::STATUS_ONLINE); + } + break; + } + + //qDebug() << "numeric message received:" << receiver() << origin << code << params; +} + +void MyIrcSession::onMessageReceived(IrcMessage *message) { + LOG4CXX_INFO(logger, user << ": " << message->toString().toStdString()); + switch (message->type()) { + case IrcMessage::Join: + on_joined(message); + break; + case IrcMessage::Part: + on_parted(message); + break; + case IrcMessage::Quit: + on_quit(message); + break; + case IrcMessage::Nick: + on_nickChanged(message); + break; + case IrcMessage::Mode: + on_modeChanged(message); + break; + case IrcMessage::Topic: + on_topicChanged(message); + break; + case IrcMessage::Private: + on_messageReceived(message); + break; + case IrcMessage::Numeric: + on_numericMessageReceived(message); + break; + } +} diff --git a/backends/libcommuni/session.h b/backends/libcommuni/session.h new file mode 100644 index 00000000..3589dcb4 --- /dev/null +++ b/backends/libcommuni/session.h @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2008-2009 J-P Nurmi jpnurmi@gmail.com + * + * 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. + */ + +#ifndef SESSION_H +#define SESSION_H + +#include +#include + +using namespace Transport; + +class MyIrcSession : public IrcSession +{ + Q_OBJECT + +public: + MyIrcSession(const std::string &user, NetworkPlugin *np, const std::string &suffix = "", QObject* parent = 0); + std::map m_modes; + std::string suffix; + int rooms; + + void addAutoJoinChannel(const std::string &channel) { + m_autoJoin.push_back(channel); + } + + void removeAutoJoinChannel(const std::string &channel) { + m_autoJoin.remove(channel); + } + + void setIdentify(const std::string &identify) { + m_identify = identify; + } + + const std::string &getIdentify() { + return m_identify; + } + + bool correctNickname(std::string &nickname); + + void on_joined(IrcMessage *message); + void on_parted(IrcMessage *message); + void on_quit(IrcMessage *message); + void on_nickChanged(IrcMessage *message); + void on_modeChanged(IrcMessage *message); + void on_topicChanged(IrcMessage *message); + void on_messageReceived(IrcMessage *message); + void on_numericMessageReceived(IrcMessage *message); + +protected Q_SLOTS: + void on_connected(); + void on_disconnected(); + + void onMessageReceived(IrcMessage* message); + +protected: + NetworkPlugin *np; + std::string user; + std::string m_identify; + std::list m_autoJoin; + std::string m_topicData; +}; + +//class MyIrcBuffer : public Irc::Buffer +//{ +// Q_OBJECT + +//public: +// MyIrcBuffer(const QString& receiver, const std::string &user, NetworkPlugin *np, const std::string &suffix, Irc::Session* parent); +// NetworkPlugin *np; +// std::string user; +// MyIrcSession *p; +// std::string m_topicData; +// std::string suffix; + +//protected Q_SLOTS: +// void on_receiverChanged(const QString& receiver); +// void on_joined(const QString& origin); +// void on_parted(const QString& origin, const QString& message); +// void on_quit(const QString& origin, const QString& message); +// void on_nickChanged(const QString& origin, const QString& nick); +// void on_modeChanged(const QString& origin, const QString& mode, const QString& args); +// void on_topicChanged(const QString& origin, const QString& topic); +// void on_invited(const QString& origin, const QString& receiver, const QString& channel); +// void on_kicked(const QString& origin, const QString& nick, const QString& message); +// void on_messageReceived(const QString& origin, const QString& message, Irc::Buffer::MessageFlags flags); +// void on_noticeReceived(const QString& origin, const QString& notice, Irc::Buffer::MessageFlags flags); +// void on_ctcpRequestReceived(const QString& origin, const QString& request, Irc::Buffer::MessageFlags flags); +// void on_ctcpReplyReceived(const QString& origin, const QString& reply, Irc::Buffer::MessageFlags flags); +// void on_ctcpActionReceived(const QString& origin, const QString& action, Irc::Buffer::MessageFlags flags); +// void on_numericMessageReceived(const QString& origin, uint code, const QStringList& params); +// void on_unknownMessageReceived(const QString& origin, const QStringList& params); + +// bool correctNickname(std::string &nickname); +//}; + +#endif // SESSION_H diff --git a/backends/libcommuni/singleircnetworkplugin.cpp b/backends/libcommuni/singleircnetworkplugin.cpp new file mode 100644 index 00000000..06bd378c --- /dev/null +++ b/backends/libcommuni/singleircnetworkplugin.cpp @@ -0,0 +1,128 @@ +#include "singleircnetworkplugin.h" +#include "log4cxx/logger.h" +#include +#include + +using namespace log4cxx; + +static LoggerPtr logger = log4cxx::Logger::getLogger("SingleIRCNetworkPlugin"); + +SingleIRCNetworkPlugin::SingleIRCNetworkPlugin(Config *config, Swift::QtEventLoop *loop, const std::string &host, int port) { + this->config = config; + m_server = config->getUnregistered().find("service.irc_server")->second; + m_socket = new QTcpSocket(); + m_socket->connectToHost(QString::fromStdString(host), port); + connect(m_socket, SIGNAL(readyRead()), this, SLOT(readData())); + + if (config->getUnregistered().find("service.irc_identify") != config->getUnregistered().end()) { + m_identify = config->getUnregistered().find("service.irc_identify")->second; + } + 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(QString::fromStdString(legacyName)); + session->setNickName(QString::fromStdString(legacyName)); + session->setRealName(QString::fromStdString(legacyName)); + session->setHost(QString::fromStdString(m_server)); + session->setPort(6667); + + 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(QString::fromStdString(r), QString::fromStdString(message))); + + if (r.find("#") == 0) { + handleMessage(user, legacyName, message, m_sessions[user]->nickName().toStdString()); + } +} + +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); + m_sessions[user]->sendCommand(IrcCommand::createJoin(QString::fromStdString(room), QString::fromStdString(password))); + m_sessions[user]->rooms += 1; + + // update nickname, because we have nickname per session, no nickname per room. + handleRoomNicknameChanged(user, room, m_sessions[user]->userName().toStdString()); +} + +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(QString::fromStdString(r))); + m_sessions[u]->removeAutoJoinChannel(r); + m_sessions[u]->rooms -= 1; +} diff --git a/backends/libcommuni/singleircnetworkplugin.h b/backends/libcommuni/singleircnetworkplugin.h new file mode 100644 index 00000000..e80dd151 --- /dev/null +++ b/backends/libcommuni/singleircnetworkplugin.h @@ -0,0 +1,40 @@ + +#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/libircclient-qt/CMakeLists.txt b/backends/libircclient-qt/CMakeLists.txt deleted file mode 100644 index 61cf35d9..00000000 --- a/backends/libircclient-qt/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -cmake_minimum_required(VERSION 2.6) -FILE(GLOB SRC *.cpp) -FILE(GLOB HEADERS *.h) -QT4_WRAP_CPP(SRC ${HEADERS}) -ADD_EXECUTABLE(spectrum2_libircclient-qt_backend ${SRC}) - -target_link_libraries(spectrum2_libircclient-qt_backend ${IRC_LIBRARY} ${QT_LIBRARIES} transport-plugin transport pthread) - -INSTALL(TARGETS spectrum2_libircclient-qt_backend RUNTIME DESTINATION bin) - diff --git a/backends/libircclient-qt/session.cpp b/backends/libircclient-qt/session.cpp deleted file mode 100644 index 792bceed..00000000 --- a/backends/libircclient-qt/session.cpp +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Copyright (C) 2008-2009 J-P Nurmi jpnurmi@gmail.com - * - * 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. - */ - -#include "session.h" -#include -#include -#include "Swiften/Elements/StatusShow.h" - -MyIrcSession::MyIrcSession(const std::string &user, NetworkPlugin *np, const std::string &suffix, QObject* parent) : Irc::Session(parent) -{ - this->np = np; - this->user = user; - this->suffix = suffix; - rooms = 0; - connect(this, SIGNAL(disconnected()), SLOT(on_disconnected())); -} - -void MyIrcSession::on_connected(){ - std::cout << "connected:\n"; -} - -void MyIrcSession::on_disconnected() -{ - std::cout << "disconnected:\n"; - if (suffix.empty()) - np->handleDisconnected(user, 0, ""); -} - -void MyIrcSession::on_bufferAdded(Irc::Buffer* buffer) -{ - qDebug() << "buffer added:" << buffer->receiver(); -} - -void MyIrcSession::on_bufferRemoved(Irc::Buffer* buffer) -{ - qDebug() << "buffer removed:" << buffer->receiver(); -} - -Irc::Buffer* MyIrcSession::createBuffer(const QString& receiver) -{ - return new MyIrcBuffer(receiver, user, np, suffix, this); -} - -MyIrcBuffer::MyIrcBuffer(const QString& receiver, const std::string &user, NetworkPlugin *np, const std::string &suffix, Irc::Session* parent) - : Irc::Buffer(receiver, parent) -{ - this->np = np; - this->user = user; - this->suffix = suffix; - p = (MyIrcSession *) parent; - connect(this, SIGNAL(receiverChanged(QString)), SLOT(on_receiverChanged(QString))); - connect(this, SIGNAL(joined(QString)), SLOT(on_joined(QString))); - connect(this, SIGNAL(parted(QString, QString)), SLOT(on_parted(QString, QString))); - connect(this, SIGNAL(quit(QString, QString)), SLOT(on_quit(QString, QString))); - connect(this, SIGNAL(nickChanged(QString, QString)), SLOT(on_nickChanged(QString, QString))); - connect(this, SIGNAL(modeChanged(QString, QString, QString)), SLOT(on_modeChanged(QString, QString, QString))); - connect(this, SIGNAL(topicChanged(QString, QString)), SLOT(on_topicChanged(QString, QString))); - connect(this, SIGNAL(invited(QString, QString, QString)), SLOT(on_invited(QString, QString, QString))); - connect(this, SIGNAL(kicked(QString, QString, QString)), SLOT(on_kicked(QString, QString, QString))); - connect(this, SIGNAL(messageReceived(QString, QString, Irc::Buffer::MessageFlags)), - SLOT(on_messageReceived(QString, QString, Irc::Buffer::MessageFlags))); - connect(this, SIGNAL(noticeReceived(QString, QString, Irc::Buffer::MessageFlags)), - SLOT(on_noticeReceived(QString, QString, Irc::Buffer::MessageFlags))); - connect(this, SIGNAL(ctcpRequestReceived(QString, QString, Irc::Buffer::MessageFlags)), - SLOT(on_ctcpRequestReceived(QString, QString, Irc::Buffer::MessageFlags))); - connect(this, SIGNAL(ctcpReplyReceived(QString, QString, Irc::Buffer::MessageFlags)), - SLOT(on_ctcpReplyReceived(QString, QString, Irc::Buffer::MessageFlags))); - connect(this, SIGNAL(ctcpActionReceived(QString, QString, Irc::Buffer::MessageFlags)), - SLOT(on_ctcpActionReceived(QString, QString, Irc::Buffer::MessageFlags))); - connect(this, SIGNAL(numericMessageReceived(QString, uint, QStringList)), SLOT(on_numericMessageReceived(QString, uint, QStringList))); - connect(this, SIGNAL(unknownMessageReceived(QString, QStringList)), SLOT(on_unknownMessageReceived(QString, QStringList))); -} - -void MyIrcBuffer::on_receiverChanged(const QString& receiver) -{ - qDebug() << "receiver changed:" << receiver; -} - -bool MyIrcBuffer::correctNickname(std::string &nickname) { - bool flags = 0; - switch(nickname.at(0)) { - case '@': nickname = nickname.substr(1); flags = 1; break; - case '+': nickname = nickname.substr(1); break; - default: break; - } - return flags; -} - -void MyIrcBuffer::on_joined(const QString& origin) { - qDebug() << "joined:" << receiver() << origin; - bool flags = 0; - std::string nickname = origin.toStdString(); - flags = correctNickname(nickname); - np->handleParticipantChanged(user, origin.toStdString(), receiver().toStdString() + suffix, (int) flags, pbnetwork::STATUS_ONLINE); -} - -void MyIrcBuffer::on_parted(const QString& origin, const QString& message) { - qDebug() << "parted:" << receiver() << origin << message; - bool flags = 0; - std::string nickname = origin.toStdString(); - flags = correctNickname(nickname); - np->handleParticipantChanged(user, nickname, receiver().toStdString() + suffix,(int) flags, pbnetwork::STATUS_NONE, message.toStdString()); -} - -void MyIrcBuffer::on_quit(const QString& origin, const QString& message) -{ - qDebug() << "quit:" << receiver() << origin << message; - on_parted(origin, message); -} - -void MyIrcBuffer::on_nickChanged(const QString& origin, const QString& nick) { - qDebug() << "nick changed:" << receiver() << origin << nick; - std::string nickname = origin.toStdString(); - bool flags = p->m_modes[receiver().toStdString() + nickname]; -// std::cout << receiver().toStdString() + nickname << " " << flags << "\n"; - np->handleParticipantChanged(user, nickname, receiver().toStdString() + suffix,(int) flags, pbnetwork::STATUS_ONLINE, "", nick.toStdString()); -} - -void MyIrcBuffer::on_modeChanged(const QString& origin, const QString& mode, const QString& args) { - // mode changed: "#testik" "HanzZ" "+o" "hanzz_k" - qDebug() << "mode changed:" << receiver() << origin << mode << args; - std::string nickname = args.toStdString(); - if (nickname.empty()) - return; - if (mode == "+o") { - p->m_modes[receiver().toStdString() + nickname] = 1; - } - else { - p->m_modes[receiver().toStdString() + nickname] = 0; - } - bool flags = p->m_modes[receiver().toStdString() + nickname]; - np->handleParticipantChanged(user, nickname, receiver().toStdString() + suffix,(int) flags, pbnetwork::STATUS_ONLINE, ""); -} - -void MyIrcBuffer::on_topicChanged(const QString& origin, const QString& topic) { - //topic changed: "#testik" "HanzZ" "test" - qDebug() << "topic changed:" << receiver() << origin << topic; - np->handleSubject(user, receiver().toStdString() + suffix, topic.toStdString(), origin.toStdString()); -} - -void MyIrcBuffer::on_invited(const QString& origin, const QString& receiver, const QString& channel) -{ - qDebug() << "invited:" << Irc::Buffer::receiver() << origin << receiver << channel; -} - -void MyIrcBuffer::on_kicked(const QString& origin, const QString& nick, const QString& message) -{ - qDebug() << "kicked:" << receiver() << origin << nick << message; -} - -void MyIrcBuffer::on_messageReceived(const QString& origin, const QString& message, Irc::Buffer::MessageFlags flags) { - qDebug() << "message received:" << receiver() << origin << message << (flags & Irc::Buffer::IdentifiedFlag ? "(identified!)" : "(not identified)"); - if (!receiver().startsWith("#") && (flags & Irc::Buffer::EchoFlag)) - return; - std::string r = receiver().toStdString(); -// if (!suffix.empty()) { -// r = receiver().replace('@', '%').toStdString(); -// } - - if (r.find("#") == 0) { - np->handleMessage(user, r + suffix, message.toStdString(), origin.toStdString()); - } - else { - np->handleMessage(user, r + suffix, message.toStdString()); - } -} - -void MyIrcBuffer::on_noticeReceived(const QString& origin, const QString& notice, Irc::Buffer::MessageFlags flags) -{ - qDebug() << "notice received:" << receiver() << origin << notice - << (flags & Irc::Buffer::IdentifiedFlag ? "(identified!)" : "(not identified)"); -} - -void MyIrcBuffer::on_ctcpRequestReceived(const QString& origin, const QString& request, Irc::Buffer::MessageFlags flags) -{ - qDebug() << "ctcp request received:" << receiver() << origin << request - << (flags & Irc::Buffer::IdentifiedFlag ? "(identified!)" : "(not identified)"); -} - -void MyIrcBuffer::on_ctcpReplyReceived(const QString& origin, const QString& reply, Irc::Buffer::MessageFlags flags) -{ - qDebug() << "ctcp reply received:" << receiver() << origin << reply - << (flags & Irc::Buffer::IdentifiedFlag ? "(identified!)" : "(not identified)"); -} - -void MyIrcBuffer::on_ctcpActionReceived(const QString& origin, const QString& action, Irc::Buffer::MessageFlags flags) -{ - qDebug() << "ctcp action received:" << receiver() << origin << action - << (flags & Irc::Buffer::IdentifiedFlag ? "(identified!)" : "(not identified)"); -} - -void MyIrcBuffer::on_numericMessageReceived(const QString& origin, uint code, const QStringList& params) -{ - switch (code) { - case 251: - if (suffix.empty()) - np->handleConnected(user); - break; - case 332: - m_topicData = params.value(2).toStdString(); - break; - case 333: - np->handleSubject(user, params.value(1).toStdString() + suffix, m_topicData, params.value(2).toStdString()); - break; - case 353: - QString channel = params.value(2); - QStringList members = params.value(3).split(" "); - - for (int i = 0; i < members.size(); i++) { - bool flags = 0; - std::string nickname = members.at(i).toStdString(); - flags = correctNickname(nickname); - p->m_modes[channel.toStdString() + nickname] = flags; - std::cout << channel.toStdString() + suffix << " " << flags << "\n"; - np->handleParticipantChanged(user, nickname, channel.toStdString() + suffix,(int) flags, pbnetwork::STATUS_ONLINE); - } - break; - } - qDebug() << "numeric message received:" << receiver() << origin << code << params; -} - -void MyIrcBuffer::on_unknownMessageReceived(const QString& origin, const QStringList& params) -{ - qDebug() << "unknown message received:" << receiver() << origin << params; -} diff --git a/backends/libircclient-qt/session.h b/backends/libircclient-qt/session.h deleted file mode 100644 index 326ba4b4..00000000 --- a/backends/libircclient-qt/session.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2008-2009 J-P Nurmi jpnurmi@gmail.com - * - * 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. - */ - -#ifndef SESSION_H -#define SESSION_H - -#include -#include -#include - -using namespace Transport; - -class MyIrcSession : public Irc::Session -{ - Q_OBJECT - -public: - MyIrcSession(const std::string &user, NetworkPlugin *np, const std::string &suffix = "", QObject* parent = 0); - std::map m_modes; - std::string suffix; - int rooms; - -protected Q_SLOTS: - void on_connected(); - void on_disconnected(); - - void on_bufferAdded(Irc::Buffer* buffer); - void on_bufferRemoved(Irc::Buffer* buffer); - -protected: - NetworkPlugin *np; - std::string user; - - virtual Irc::Buffer* createBuffer(const QString& receiver); -}; - -class MyIrcBuffer : public Irc::Buffer -{ - Q_OBJECT - -public: - MyIrcBuffer(const QString& receiver, const std::string &user, NetworkPlugin *np, const std::string &suffix, Irc::Session* parent); - NetworkPlugin *np; - std::string user; - MyIrcSession *p; - std::string m_topicData; - std::string suffix; - -protected Q_SLOTS: - void on_receiverChanged(const QString& receiver); - void on_joined(const QString& origin); - void on_parted(const QString& origin, const QString& message); - void on_quit(const QString& origin, const QString& message); - void on_nickChanged(const QString& origin, const QString& nick); - void on_modeChanged(const QString& origin, const QString& mode, const QString& args); - void on_topicChanged(const QString& origin, const QString& topic); - void on_invited(const QString& origin, const QString& receiver, const QString& channel); - void on_kicked(const QString& origin, const QString& nick, const QString& message); - void on_messageReceived(const QString& origin, const QString& message, Irc::Buffer::MessageFlags flags); - void on_noticeReceived(const QString& origin, const QString& notice, Irc::Buffer::MessageFlags flags); - void on_ctcpRequestReceived(const QString& origin, const QString& request, Irc::Buffer::MessageFlags flags); - void on_ctcpReplyReceived(const QString& origin, const QString& reply, Irc::Buffer::MessageFlags flags); - void on_ctcpActionReceived(const QString& origin, const QString& action, Irc::Buffer::MessageFlags flags); - void on_numericMessageReceived(const QString& origin, uint code, const QStringList& params); - void on_unknownMessageReceived(const QString& origin, const QStringList& params); - - bool correctNickname(std::string &nickname); -}; - -#endif // SESSION_H diff --git a/backends/libpurple/main.cpp b/backends/libpurple/main.cpp index 51943171..65d719e3 100644 --- a/backends/libpurple/main.cpp +++ b/backends/libpurple/main.cpp @@ -69,6 +69,20 @@ class SpectrumNetworkPlugin; GKeyFile *keyfile; SpectrumNetworkPlugin *np; +std::string replaceAll( + std::string result, + const std::string& replaceWhat, + const std::string& replaceWithWhat) +{ + while(1) + { + const int pos = result.find(replaceWhat); + if (pos==-1) break; + result.replace(pos,replaceWhat.size(),replaceWithWhat); + } + return result; +} + static std::string KEYFILE_STRING(const std::string &cat, const std::string &key, const std::string &def = "") { gchar *str = g_key_file_get_string(keyfile, cat.c_str(), key.c_str(), 0); if (!str) { @@ -83,6 +97,11 @@ static std::string KEYFILE_STRING(const std::string &cat, const std::string &key ret.erase(ret.end() - 1); } } + + if (ret.find("$jid") != std::string::npos) { + std::string jid = KEYFILE_STRING("service", "jid"); + ret = replaceAll(ret, "$jid", jid); + } return ret; } @@ -523,6 +542,10 @@ class SpectrumNetworkPlugin : public NetworkPlugin { while (keys && keys[i] != NULL) { std::string key = keys[i]; + if (key == "fb_api_key" || key == "fb_api_secret") { + purple_account_set_bool(account, "auth_fb", TRUE); + } + PurplePlugin *plugin = purple_find_prpl(purple_account_get_protocol_id(account)); PurplePluginProtocolInfo *prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(plugin); bool found = false; @@ -570,11 +593,13 @@ class SpectrumNetworkPlugin : public NetworkPlugin { getProtocolAndName(legacyName, name, protocol); if (password.empty()) { + LOG4CXX_INFO(logger, name.c_str() << ": Empty password"); np->handleDisconnected(user, 0, "Empty password."); return; } if (!purple_find_prpl(protocol.c_str())) { + LOG4CXX_INFO(logger, name.c_str() << ": Invalid protocol '" << protocol << "'"); np->handleDisconnected(user, 0, "Invalid protocol " + protocol); return; } @@ -1205,6 +1230,11 @@ static gboolean disconnectMe(void *data) { return FALSE; } +static gboolean pingTimeout(void *data) { + np->checkPing(); + return TRUE; +} + static void connection_report_disconnect(PurpleConnection *gc, PurpleConnectionError reason, const char *text){ PurpleAccount *account = purple_connection_get_account(gc); np->handleDisconnected(np->m_accounts[account], (int) reason, text ? text : ""); @@ -1550,11 +1580,78 @@ static void transport_core_ui_init(void) // #endif } +/***** Core Ui Ops *****/ +static void +spectrum_glib_log_handler(const gchar *domain, + GLogLevelFlags flags, + const gchar *message, + gpointer user_data) +{ + const char *level; + char *new_msg = NULL; + char *new_domain = NULL; + + if ((flags & G_LOG_LEVEL_ERROR) == G_LOG_LEVEL_ERROR) + level = "ERROR"; + else if ((flags & G_LOG_LEVEL_CRITICAL) == G_LOG_LEVEL_CRITICAL) + level = "CRITICAL"; + else if ((flags & G_LOG_LEVEL_WARNING) == G_LOG_LEVEL_WARNING) + level = "WARNING"; + else if ((flags & G_LOG_LEVEL_MESSAGE) == G_LOG_LEVEL_MESSAGE) + level = "MESSAGE"; + else if ((flags & G_LOG_LEVEL_INFO) == G_LOG_LEVEL_INFO) + level = "INFO"; + else if ((flags & G_LOG_LEVEL_DEBUG) == G_LOG_LEVEL_DEBUG) + level = "DEBUG"; + else { + LOG4CXX_ERROR(logger, "Unknown glib logging level in " << (guint)flags); + level = "UNKNOWN"; /* This will never happen. */ + } + + if (message != NULL) + new_msg = purple_utf8_try_convert(message); + + if (domain != NULL) + new_domain = purple_utf8_try_convert(domain); + + if (new_msg != NULL) { + std::string area("glib"); + area.push_back('/'); + area.append(level); + + std::string message(new_domain ? new_domain : "g_log"); + message.push_back(' '); + message.append(new_msg); + + LOG4CXX_ERROR(logger, message); + g_free(new_msg); + } + + g_free(new_domain); +} + +static void +debug_init(void) +{ +#define REGISTER_G_LOG_HANDLER(name) \ + g_log_set_handler((name), \ + (GLogLevelFlags)(G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL \ + | G_LOG_FLAG_RECURSION), \ + spectrum_glib_log_handler, NULL) + + REGISTER_G_LOG_HANDLER(NULL); + REGISTER_G_LOG_HANDLER("GLib"); + REGISTER_G_LOG_HANDLER("GModule"); + REGISTER_G_LOG_HANDLER("GLib-GObject"); + REGISTER_G_LOG_HANDLER("GThread"); + +#undef REGISTER_G_LOD_HANDLER +} + static PurpleCoreUiOps coreUiOps = { NULL, -// debug_init, - NULL, + debug_init, transport_core_ui_init, NULL, spectrum_ui_get_info, @@ -1852,7 +1949,7 @@ int main(int argc, char **argv) { p.load(istream); LogString pid, jid; log4cxx::helpers::Transcoder::decode(stringOf(getpid()), pid); - log4cxx::helpers::Transcoder::decode(KEYFILE_STRING("service", "service.jid"), jid); + log4cxx::helpers::Transcoder::decode(KEYFILE_STRING("service", "jid"), jid); #ifdef _MSC_VER p.setProperty(L"pid", pid); p.setProperty(L"jid", jid); @@ -1868,6 +1965,7 @@ int main(int argc, char **argv) { m_sock = create_socket(host, port); purple_input_add(m_sock, PURPLE_INPUT_READ, &transportDataReceived, NULL); + purple_timeout_add_seconds(30, pingTimeout, NULL); np = new SpectrumNetworkPlugin(host, port); bool libev = KEYFILE_STRING("service", "eventloop") == "libev"; diff --git a/cmake_modules/CommuniConfig.cmake b/cmake_modules/CommuniConfig.cmake new file mode 100644 index 00000000..7f803847 --- /dev/null +++ b/cmake_modules/CommuniConfig.cmake @@ -0,0 +1,10 @@ +FIND_LIBRARY(IRC_LIBRARY NAMES Communi) +FIND_PATH(IRC_INCLUDE_DIR NAMES "ircglobal.h" PATH_SUFFIXES Communi qt4/Communi ) + +# message( STATUS ${IRC_LIBRARY}) +if( IRC_LIBRARY AND IRC_INCLUDE_DIR ) + message( STATUS "Found libCommuni ${IRC_LIBRARY}, ${IRC_INCLUDE_DIR}") + set( IRC_FOUND 1 ) +else() + message( STATUS "Could NOT find libCommuni" ) +endif() diff --git a/cmake_modules/IRCClientQtConfig.cmake b/cmake_modules/IRCClientQtConfig.cmake deleted file mode 100644 index 58118610..00000000 --- a/cmake_modules/IRCClientQtConfig.cmake +++ /dev/null @@ -1,10 +0,0 @@ -FIND_LIBRARY(IRC_LIBRARY NAMES ircclient-qt) -FIND_PATH(IRC_INCLUDE_DIR NAMES "ircglobal.h" PATH_SUFFIXES ircclient-qt qt4/ircclient-qt ) - -# message( STATUS ${IRC_LIBRARY}) -if( IRC_LIBRARY AND IRC_INCLUDE_DIR ) - message( STATUS "Found libircclient-qt: ${IRC_LIBRARY}, ${IRC_INCLUDE_DIR}") - set( IRC_FOUND 1 ) -else() - message( STATUS "Could NOT find libircclient-qt" ) -endif() diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt deleted file mode 100644 index 8d793b28..00000000 --- a/examples/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -ADD_SUBDIRECTORY(server_connect) -ADD_SUBDIRECTORY(usermanager) - -if (PROTOBUF_FOUND) - ADD_SUBDIRECTORY(external_network_plugin) -endif() diff --git a/examples/external_network_plugin/CMakeLists.txt b/examples/external_network_plugin/CMakeLists.txt deleted file mode 100644 index e3c24cf4..00000000 --- a/examples/external_network_plugin/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -FILE(GLOB SRC *.cpp) - -add_custom_command( - OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/pbnetwork_pb2.py" - COMMAND ${PROTOBUF_PROTOC_EXECUTABLE} - ARGS --python_out ${CMAKE_CURRENT_BINARY_DIR} pbnetwork.proto - COMMENT "Running Py protocol buffer compiler on pbnetwork.proto" - VERBATIM ) - -ADD_EXECUTABLE(external_network_plugin ${SRC} ${CMAKE_CURRENT_BINARY_DIR}/pbnetwork_pb2.py) - -TARGET_LINK_LIBRARIES(external_network_plugin transport ${SWIFTEN_LIBRARIES}) - diff --git a/examples/external_network_plugin/main.cpp b/examples/external_network_plugin/main.cpp deleted file mode 100644 index 6edd0e76..00000000 --- a/examples/external_network_plugin/main.cpp +++ /dev/null @@ -1,39 +0,0 @@ -#include "transport/config.h" -#include "transport/transport.h" -#include "transport/usermanager.h" -#include "transport/logger.h" -#include "transport/sqlite3backend.h" -#include "transport/userregistration.h" -#include "transport/networkpluginserver.h" -#include "Swiften/EventLoop/SimpleEventLoop.h" - -using namespace Transport; - -int main(void) -{ - Config config; - if (!config.load("sample.cfg")) { - std::cout << "Can't open sample.cfg configuration file.\n"; - return 1; - } - - Swift::SimpleEventLoop eventLoop; - Component transport(&eventLoop, &config, NULL); - Logger logger(&transport); - - SQLite3Backend sql(&config); - logger.setStorageBackend(&sql); - if (!sql.connect()) { - std::cout << "Can't connect to database.\n"; - } - - UserManager userManager(&transport, &sql); - UserRegistration userRegistration(&transport, &userManager, &sql); - logger.setUserRegistration(&userRegistration); - logger.setUserManager(&userManager); - - NetworkPluginServer plugin(&transport, &config, &userManager); - - transport.connect(); - eventLoop.run(); -} diff --git a/examples/external_network_plugin/pbnetwork.proto b/examples/external_network_plugin/pbnetwork.proto deleted file mode 100644 index d3c2231a..00000000 --- a/examples/external_network_plugin/pbnetwork.proto +++ /dev/null @@ -1,29 +0,0 @@ -package pbnetwork; - -message Connected { - required string name = 1; -} - -message Disconnected { - required string name = 1; - required int32 error = 2; - optional string message = 3; -} - -message Login { - required string protocol = 1; - required string legacyName = 2; - required string password = 3; -} - -message WrapperMessage { - enum Type { - TYPE_CONNECTED = 1; - TYPE_DISCONNECTED = 2; - TYPE_LOGIN = 3; - TYPE_LOGOUT = 4; - } - required Type type = 1; - required bytes payload = 2; -} -; \ No newline at end of file diff --git a/examples/external_network_plugin/plugin.py b/examples/external_network_plugin/plugin.py deleted file mode 100644 index 51a5c58e..00000000 --- a/examples/external_network_plugin/plugin.py +++ /dev/null @@ -1,29 +0,0 @@ -#! /usr/bin/python - -from pbnetwork_pb2 import * -import time -import sys -import socket -import struct - - -connected = Connected() -connected.name = "hanzz.k@gmail.com" - -wrapper = WrapperMessage() -wrapper.type = WrapperMessage.TYPE_CONNECTED -wrapper.payload = connected.SerializeToString() - -sck = socket.socket() -sck.connect(("localhost", 10000)) - -message = wrapper.SerializeToString(); -header = struct.pack(">L", len(message)) -print [header] -sck.send(header + message+header + message); -sck.send(header + message); -sck.send(header + message); -sck.send(header + message); -sck.send(header + message); - -print sck.recv(255) diff --git a/examples/server_connect/CMakeLists.txt b/examples/server_connect/CMakeLists.txt deleted file mode 100644 index d71144af..00000000 --- a/examples/server_connect/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -FILE(GLOB SRC *.cpp) - -ADD_EXECUTABLE(transport_server_connect ${SRC}) - -TARGET_LINK_LIBRARIES(transport_server_connect transport ${SWIFTEN_LIBRARIES}) - diff --git a/examples/server_connect/main.cpp b/examples/server_connect/main.cpp deleted file mode 100644 index 0682e128..00000000 --- a/examples/server_connect/main.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include "transport/config.h" -#include "transport/transport.h" -#include "transport/logger.h" -#include "Swiften/EventLoop/SimpleEventLoop.h" -#include "Swiften/Network/BoostTimerFactory.h" -#include "Swiften/Network/BoostIOServiceThread.h" -#include "Swiften/Network/BoostNetworkFactories.h" -#include "Swiften/Server/UserRegistry.h" -#include "Swiften/Server/Server.h" -#include "Swiften/Swiften.h" - -using namespace Transport; - -class DummyUserRegistry : public Swift::UserRegistry { - public: - DummyUserRegistry() {} - - virtual bool isValidUserPassword(const Swift::JID&user, const Swift::SafeByteArray&) const { - onPasswordValid(user.toString()); - return true; - } -}; - -int main(void) -{ - Swift::logging = true; - - Swift::SimpleEventLoop loop; - - Swift::BoostNetworkFactories *m_factories = new Swift::BoostNetworkFactories(&loop); - DummyUserRegistry dummyregistry; - Swift::Server server(&loop, m_factories, &dummyregistry, "localhost", 5222); - server.start(); - - loop.run(); -} diff --git a/examples/server_connect/sample.cfg b/examples/server_connect/sample.cfg deleted file mode 100644 index 5db19bbe..00000000 --- a/examples/server_connect/sample.cfg +++ /dev/null @@ -1,6 +0,0 @@ -[service] -jid = icq.localhost -password = secret -server = 127.0.0.1 -port = 5222 -server_mode = 1 diff --git a/examples/usermanager/CMakeLists.txt b/examples/usermanager/CMakeLists.txt deleted file mode 100644 index 1438b57d..00000000 --- a/examples/usermanager/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -FILE(GLOB SRC *.cpp) - -ADD_EXECUTABLE(transport_usermanager ${SRC}) - -TARGET_LINK_LIBRARIES(transport_usermanager transport ${SWIFTEN_LIBRARIES}) - diff --git a/examples/usermanager/main.cpp b/examples/usermanager/main.cpp deleted file mode 100644 index c181334e..00000000 --- a/examples/usermanager/main.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include "transport/config.h" -#include "transport/transport.h" -#include "transport/usermanager.h" -#include "transport/logger.h" -#include "transport/sqlite3backend.h" -#include "transport/userregistration.h" -#include "Swiften/EventLoop/SimpleEventLoop.h" - -using namespace Transport; - -int main(void) -{ - Config config; - if (!config.load("sample.cfg")) { - std::cout << "Can't open sample.cfg configuration file.\n"; - return 1; - } - - Swift::SimpleEventLoop eventLoop; - Component transport(&eventLoop, &config, NULL); - Logger logger(&transport); - - SQLite3Backend sql(&config); - logger.setStorageBackend(&sql); - if (!sql.connect()) { - std::cout << "Can't connect to database.\n"; - } - - UserManager userManager(&transport, &sql); - UserRegistration userRegistration(&transport, &userManager, &sql); - logger.setUserRegistration(&userRegistration); - logger.setUserManager(&userManager); - - transport.connect(); - eventLoop.run(); -} diff --git a/examples/usermanager/sample.cfg b/examples/usermanager/sample.cfg deleted file mode 100644 index fc90fdf8..00000000 --- a/examples/usermanager/sample.cfg +++ /dev/null @@ -1,9 +0,0 @@ -[service] -jid = icq.localhost -password = secret -server = 127.0.0.1 -port = 8888 - -[database] -database = test.sql -prefix=icq diff --git a/include/Swiften/Elements/StatsPayload.cpp b/include/Swiften/Elements/StatsPayload.cpp new file mode 100644 index 00000000..e3718721 --- /dev/null +++ b/include/Swiften/Elements/StatsPayload.cpp @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2011 Jan Kaluza + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include + +namespace Swift { + +StatsPayload::StatsPayload() { +} + +} diff --git a/include/Swiften/Elements/StatsPayload.h b/include/Swiften/Elements/StatsPayload.h new file mode 100644 index 00000000..aa1619fe --- /dev/null +++ b/include/Swiften/Elements/StatsPayload.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2011 Jan Kaluza + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include + +#include +#include + +namespace Swift { + class StatsPayload : public Payload { + public: + class Item { + public: + Item(const std::string &name = "", const std::string &units = "", const std::string &value = "") : + name(name), units(units), value(value) { } + + void setName(const std::string &name) { + this->name = name; + } + + const std::string &getName() const { + return name; + } + + void setUnits(const std::string &units) { + this->units = units; + } + + const std::string &getUnits() const { + return units; + } + + void setValue(const std::string &value) { + this->value = value; + } + + const std::string &getValue() const { + return value; + } + + private: + std::string name; + std::string units; + std::string value; + }; + + typedef std::vector StatsPayloadItems; + + StatsPayload(); + + void addItem(const StatsPayload::Item &item) { + items.push_back(item); + } + + const StatsPayloadItems &getItems() const { + return items; + } + + private: + StatsPayloadItems items; + }; +} diff --git a/include/Swiften/Parser/PayloadParsers/StatsParser.cpp b/include/Swiften/Parser/PayloadParsers/StatsParser.cpp new file mode 100644 index 00000000..1c795a8a --- /dev/null +++ b/include/Swiften/Parser/PayloadParsers/StatsParser.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2011 Jan Kaluza + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include +#include + +namespace Swift { + +StatsParser::StatsParser() : level_(TopLevel), inItem_(false) { +} + +void StatsParser::handleStartElement(const std::string& element, const std::string& /*ns*/, const AttributeMap& attributes) { + if (level_ == PayloadLevel) { + if (element == "stat") { + inItem_ = true; + + currentItem_ = StatsPayload::Item(); + + currentItem_.setName(attributes.getAttribute("name")); + currentItem_.setValue(attributes.getAttribute("value")); + currentItem_.setUnits(attributes.getAttribute("units")); + } + } + ++level_; +} + +void StatsParser::handleEndElement(const std::string& element, const std::string& /*ns*/) { + --level_; + if (level_ == PayloadLevel) { + if (inItem_) { + getPayloadInternal()->addItem(currentItem_); + inItem_ = false; + } + } +} + +void StatsParser::handleCharacterData(const std::string& data) { +} + +} diff --git a/include/Swiften/Parser/PayloadParsers/StatsParser.h b/include/Swiften/Parser/PayloadParsers/StatsParser.h new file mode 100644 index 00000000..53b0c97c --- /dev/null +++ b/include/Swiften/Parser/PayloadParsers/StatsParser.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2011 Jan Kaluza + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include +#include + +namespace Swift { + class SerializingParser; + + class StatsParser : public GenericPayloadParser { + public: + StatsParser(); + + virtual void handleStartElement(const std::string& element, const std::string&, const AttributeMap& attributes); + virtual void handleEndElement(const std::string& element, const std::string&); + virtual void handleCharacterData(const std::string& data); + + private: + enum Level { + TopLevel = 0, + PayloadLevel = 1, + ItemLevel = 2 + }; + int level_; + bool inItem_; + StatsPayload::Item currentItem_; + }; +} diff --git a/include/Swiften/Serializer/PayloadSerializers/StatsSerializer.cpp b/include/Swiften/Serializer/PayloadSerializers/StatsSerializer.cpp new file mode 100644 index 00000000..0bd85f5f --- /dev/null +++ b/include/Swiften/Serializer/PayloadSerializers/StatsSerializer.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2011 Jan Kaluza + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#include + +#include + +#include +#include +#include +#include + +namespace Swift { + +StatsSerializer::StatsSerializer() : GenericPayloadSerializer() { +} + +std::string StatsSerializer::serializePayload(boost::shared_ptr stats) const { + XMLElement queryElement("query", "http://jabber.org/protocol/stats"); + foreach(const StatsPayload::Item& item, stats->getItems()) { + boost::shared_ptr statElement(new XMLElement("stat")); + statElement->setAttribute("name", item.getName()); + if (!item.getUnits().empty()) { + statElement->setAttribute("units", item.getUnits()); + } + if (!item.getValue().empty()) { + statElement->setAttribute("value", item.getValue()); + } + + queryElement.addNode(statElement); + } + + return queryElement.serialize(); +} + +} diff --git a/include/Swiften/Serializer/PayloadSerializers/StatsSerializer.h b/include/Swiften/Serializer/PayloadSerializers/StatsSerializer.h new file mode 100644 index 00000000..cf45941d --- /dev/null +++ b/include/Swiften/Serializer/PayloadSerializers/StatsSerializer.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2011 Jan Kaluza + * Licensed under the Simplified BSD license. + * See Documentation/Licenses/BSD-simplified.txt for more information. + */ + +#pragma once + +#include +#include + +namespace Swift { + class StatsSerializer : public GenericPayloadSerializer { + public: + StatsSerializer(); + + virtual std::string serializePayload(boost::shared_ptr) const; + }; +} diff --git a/include/transport/config.h b/include/transport/config.h index c0ad64e5..4ff5b8fa 100644 --- a/include/transport/config.h +++ b/include/transport/config.h @@ -34,6 +34,7 @@ #define CONFIG_BOOL(PTR, KEY) (*PTR)[KEY].as() #define CONFIG_LIST(PTR, KEY) (*PTR)[KEY].as >() #define CONFIG_VECTOR(PTR, KEY) (*PTR)[KEY].as >() +#define CONFIG_HAS_KEY(PTR, KEY) (*PTR).hasKey(KEY) namespace Transport { @@ -60,9 +61,9 @@ class Config { /// the parser using opts parameter. /// \param configfile path to config file /// \param opts extra options which will be recognized by a parser - bool load(const std::string &configfile, boost::program_options::options_description &opts); + bool load(const std::string &configfile, boost::program_options::options_description &opts, const std::string &jid = ""); - bool load(std::istream &ifs, boost::program_options::options_description &opts); + bool load(std::istream &ifs, boost::program_options::options_description &opts, const std::string &jid = ""); bool load(std::istream &ifs); @@ -71,10 +72,14 @@ class Config { /// This function loads only config variables needed by libtransport. /// \see load(const std::string &, boost::program_options::options_description &) /// \param configfile path to config file - bool load(const std::string &configfile); + bool load(const std::string &configfile, const std::string &jid = ""); bool reload(); + bool hasKey(const std::string &key) { + return m_variables.find(key) != m_variables.end(); + } + /// Returns value of variable defined by key. /// For variables in sections you can use "section.variable" key format. diff --git a/include/transport/conversation.h b/include/transport/conversation.h index 8af86993..d3999d28 100644 --- a/include/transport/conversation.h +++ b/include/transport/conversation.h @@ -74,6 +74,14 @@ class Conversation { m_nickname = nickname; } + const std::string &getNickname() { + return m_nickname; + } + + void setJID(const Swift::JID &jid) { + m_jid = jid; + } + /// Sends message to Legacy network. /// \param message Message. @@ -112,6 +120,7 @@ class Conversation { std::string m_nickname; std::string m_room; bool m_muc; + Swift::JID m_jid; }; } diff --git a/include/transport/conversationmanager.h b/include/transport/conversationmanager.h index 487e000a..17ca1467 100644 --- a/include/transport/conversationmanager.h +++ b/include/transport/conversationmanager.h @@ -73,6 +73,8 @@ class ConversationManager { /// \param conv Conversation. void removeConversation(Conversation *conv); + void resetResources(); + private: void handleMessageReceived(Swift::Message::ref message); diff --git a/include/transport/networkplugin.h b/include/transport/networkplugin.h index 334fab8b..8bfbf221 100644 --- a/include/transport/networkplugin.h +++ b/include/transport/networkplugin.h @@ -214,7 +214,8 @@ class NetworkPlugin { virtual void handleExitRequest() { exit(1); } void handleDataRead(std::string &data); virtual void sendData(const std::string &string) {} - + + void checkPing(); private: void handleLoginPayload(const std::string &payload); diff --git a/include/transport/networkpluginserver.h b/include/transport/networkpluginserver.h index a678c247..c04ae834 100644 --- a/include/transport/networkpluginserver.h +++ b/include/transport/networkpluginserver.h @@ -96,7 +96,7 @@ class NetworkPluginServer { void handleFTDataPayload(Backend *b ,const std::string &payload); void handleUserCreated(User *user); - void handleRoomJoined(User *user, const std::string &room, const std::string &nickname, const std::string &password); + void handleRoomJoined(User *user, const Swift::JID &who, const std::string &room, const std::string &nickname, const std::string &password); void handleRoomLeft(User *user, const std::string &room); void handleUserReadyToConnect(User *user); void handleUserPresenceChanged(User *user, Swift::Presence::ref presence); diff --git a/include/transport/protocol.proto b/include/transport/protocol.proto index 8370e617..336915a0 100644 --- a/include/transport/protocol.proto +++ b/include/transport/protocol.proto @@ -1,5 +1,25 @@ package pbnetwork; +enum ConnectionError { + CONNECTION_ERROR_NETWORK_ERROR = 0; + CONNECTION_ERROR_INVALID_USERNAME = 1; + CONNECTION_ERROR_AUTHENTICATION_FAILED = 2; + CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE = 3; + CONNECTION_ERROR_NO_SSL_SUPPORT = 4; + CONNECTION_ERROR_ENCRYPTION_ERROR = 5; + CONNECTION_ERROR_NAME_IN_USE = 6; + CONNECTION_ERROR_INVALID_SETTINGS = 7; + CONNECTION_ERROR_CERT_NOT_PROVIDED = 8; + CONNECTION_ERROR_CERT_UNTRUSTED = 9; + CONNECTION_ERROR_CERT_EXPIRED = 10; + CONNECTION_ERROR_CERT_NOT_ACTIVATED = 11; + CONNECTION_ERROR_CERT_HOSTNAME_MISMATCH = 12; + CONNECTION_ERROR_CERT_FINGERPRINT_MISMATCH = 13; + CONNECTION_ERROR_CERT_SELF_SIGNED = 14; + CONNECTION_ERROR_CERT_OTHER_ERROR = 15; + CONNECTION_ERROR_OTHER_ERROR = 16; +} + enum StatusType { STATUS_ONLINE = 0; STATUS_AWAY = 1; @@ -135,4 +155,4 @@ message WrapperMessage { required Type type = 1; optional bytes payload = 2; } -; \ No newline at end of file +; diff --git a/include/transport/rostermanager.h b/include/transport/rostermanager.h index 2e67532a..64f05fa7 100644 --- a/include/transport/rostermanager.h +++ b/include/transport/rostermanager.h @@ -49,6 +49,7 @@ class AddressedRosterRequest : public Swift::GenericRequest, boost::pool_allocator< std::pair > > BuddiesMap; /// Creates new RosterManager. /// \param user User associated with this RosterManager. /// \param component Transport instance associated with this roster. @@ -80,6 +81,10 @@ class RosterManager { /// \return User User *getUser() { return m_user; } + const BuddiesMap &getBuddies() { + return m_buddies; + } + bool isRemoteRosterSupported() { return m_supportRemoteRoster; } @@ -125,6 +130,7 @@ class RosterManager { Swift::Timer::ref m_RIETimer; std::list m_requests; bool m_supportRemoteRoster; + AddressedRosterRequest::ref m_remoteRosterRequest; }; } diff --git a/include/transport/statsresponder.h b/include/transport/statsresponder.h new file mode 100644 index 00000000..ecdd331f --- /dev/null +++ b/include/transport/statsresponder.h @@ -0,0 +1,53 @@ +/** + * libtransport -- C++ library for easy XMPP Transports development + * + * Copyright (C) 2011, Jan Kaluza + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#pragma once + +#include +#include "Swiften/Swiften.h" +#include "Swiften/Queries/SetResponder.h" +#include "Swiften/Elements/StatsPayload.h" + +namespace Transport { + +class Component; +class UserManager; +class NetworkPluginServer; +class StorageBackend; + +class StatsResponder : public Swift::Responder { + public: + StatsResponder(Component *component, UserManager *userManager, NetworkPluginServer *server, StorageBackend *storageBackend); + ~StatsResponder(); + + private: + virtual bool handleGetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr payload); + virtual bool handleSetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr payload); + + unsigned long usedMemory(); + + Component *m_component; + UserManager *m_userManager; + NetworkPluginServer *m_server; + StorageBackend *m_storageBackend; + time_t m_start; +}; + +} diff --git a/include/transport/user.h b/include/transport/user.h index fe06277a..952bb3b4 100644 --- a/include/transport/user.h +++ b/include/transport/user.h @@ -111,7 +111,7 @@ class User : public Swift::EntityCapsProvider { boost::signal onReadyToConnect; boost::signal onPresenceChanged; - boost::signal onRoomJoined; + boost::signal onRoomJoined; boost::signal onRoomLeft; boost::signal onDisconnected; @@ -135,6 +135,7 @@ class User : public Swift::EntityCapsProvider { time_t m_lastActivity; std::map m_legacyCaps; std::vector > m_filetransfers; + int m_resources; }; } diff --git a/include/transport/util.h b/include/transport/util.h index e5fc086f..79d52a47 100644 --- a/include/transport/util.h +++ b/include/transport/util.h @@ -24,6 +24,7 @@ #include #include #include +#include "Swiften/StringCodecs/Base64.h" namespace Transport { @@ -31,6 +32,10 @@ namespace Util { void removeEverythingOlderThan(const std::vector &dirs, time_t t); +std::string encryptPassword(const std::string &password, const std::string &key); + +std::string decryptPassword(std::string &encrypted, const std::string &key); + } } diff --git a/plugin/src/networkplugin.cpp b/plugin/src/networkplugin.cpp index 82aa3bb4..fb5d58fb 100644 --- a/plugin/src/networkplugin.cpp +++ b/plugin/src/networkplugin.cpp @@ -553,6 +553,13 @@ void NetworkPlugin::send(const std::string &data) { sendData(std::string(header, 4) + data); } +void NetworkPlugin::checkPing() { + if (m_pingReceived == false) { + handleExitRequest(); + } + m_pingReceived = false; +} + void NetworkPlugin::sendPong() { m_pingReceived = true; std::string message; diff --git a/spectrum/src/CMakeLists.txt b/spectrum/src/CMakeLists.txt index 5a78327f..6c20b757 100644 --- a/spectrum/src/CMakeLists.txt +++ b/spectrum/src/CMakeLists.txt @@ -13,7 +13,7 @@ INSTALL(TARGETS spectrum2 RUNTIME DESTINATION bin) INSTALL(FILES sample2.cfg RENAME spectrum.cfg.example - DESTINATION /etc/spectrum2 + DESTINATION /etc/spectrum2/transports ) INSTALL(FILES diff --git a/spectrum/src/main.cpp b/spectrum/src/main.cpp index 5037e6d0..fb52de18 100644 --- a/spectrum/src/main.cpp +++ b/spectrum/src/main.cpp @@ -8,9 +8,11 @@ #include "transport/userregistration.h" #include "transport/networkpluginserver.h" #include "transport/admininterface.h" +#include "transport/statsresponder.h" #include "transport/util.h" #include "Swiften/EventLoop/SimpleEventLoop.h" #include +#include #ifndef WIN32 #include "sys/signal.h" #include @@ -122,6 +124,7 @@ int main(int argc, char **argv) boost::program_options::variables_map vm; bool no_daemon = false; std::string config_file; + std::string jid; #ifndef WIN32 @@ -135,11 +138,14 @@ int main(int argc, char **argv) return -1; } #endif - boost::program_options::options_description desc("Usage: spectrum [OPTIONS] \nAllowed options"); + boost::program_options::options_description desc(std::string("Spectrum version: ") + SPECTRUM_VERSION + "\nUsage: spectrum [OPTIONS] \nAllowed options"); desc.add_options() ("help,h", "help") ("no-daemonize,n", "Do not run spectrum as daemon") + ("no-debug,d", "Create coredumps on crash") + ("jid,j", boost::program_options::value(&jid)->default_value(""), "Specify JID of transport manually") ("config", boost::program_options::value(&config_file)->default_value(""), "Config file") + ("version,v", "Shows Spectrum version") ; try { @@ -149,7 +155,10 @@ int main(int argc, char **argv) options(desc).positional(p).run(), vm); boost::program_options::notify(vm); - + if (vm.count("version")) { + std::cout << SPECTRUM_VERSION << "\n"; + return 0; + } if(vm.count("help")) { @@ -177,31 +186,46 @@ int main(int argc, char **argv) return 1; } - if (!config.load(vm["config"].as())) { + if (!config.load(vm["config"].as(), jid)) { std::cerr << "Can't load configuration file.\n"; return 1; } + // create directories + try { + boost::filesystem::create_directories( + boost::filesystem::path(CONFIG_STRING(&config, "service.pidfile")).parent_path().string() + ); + } + catch (...) { + std::cerr << "Can't create service.pidfile directory " << boost::filesystem::path(CONFIG_STRING(&config, "service.pidfile")).parent_path().string() << ".\n"; + return 1; + } + // create directories + try { + boost::filesystem::create_directories(CONFIG_STRING(&config, "service.working_dir")); + } + catch (...) { + std::cerr << "Can't create service.working_dir directory " << CONFIG_STRING(&config, "service.working_dir") << ".\n"; + return 1; + } + + if (!CONFIG_STRING(&config, "service.group").empty() ||!CONFIG_STRING(&config, "service.user").empty() ) { + struct group *gr; + if ((gr = getgrnam(CONFIG_STRING(&config, "service.group").c_str())) == NULL) { + std::cerr << "Invalid service.group name " << CONFIG_STRING(&config, "service.group") << "\n"; + return 1; + } + struct passwd *pw; + if ((pw = getpwnam(CONFIG_STRING(&config, "service.user").c_str())) == NULL) { + std::cerr << "Invalid service.user name " << CONFIG_STRING(&config, "service.user") << "\n"; + return 1; + } + chown(CONFIG_STRING(&config, "service.working_dir").c_str(), pw->pw_uid, gr->gr_gid); + } + #ifndef WIN32 if (!no_daemon) { - // create directories - try { - boost::filesystem::create_directories(CONFIG_STRING(&config, "service.working_dir")); - } - catch (...) { - std::cerr << "Can't create service.working_dir directory " << CONFIG_STRING(&config, "service.working_dir") << ".\n"; - return 1; - } - try { - boost::filesystem::create_directories( - boost::filesystem::path(CONFIG_STRING(&config, "service.pidfile")).parent_path().string() - ); - } - catch (...) { - std::cerr << "Can't create service.pidfile directory " << boost::filesystem::path(CONFIG_STRING(&config, "service.pidfile")).parent_path().string() << ".\n"; - return 1; - } - // daemonize daemonize(CONFIG_STRING(&config, "service.working_dir").c_str(), CONFIG_STRING(&config, "service.pidfile").c_str()); // removeOldIcons(CONFIG_STRING(&config, "service.working_dir") + "/icons"); @@ -231,6 +255,46 @@ int main(int argc, char **argv) p.setProperty("pid", pid); p.setProperty("jid", jid); #endif + + std::string dir; + BOOST_FOREACH(const log4cxx::LogString &prop, p.propertyNames()) { + if (boost::ends_with(prop, ".File")) { + dir = p.get(prop); + boost::replace_all(dir, "${jid}", jid); + break; + } + } + + if (!dir.empty()) { + // create directories + try { + boost::filesystem::create_directories( + boost::filesystem::path(dir).parent_path().string() + ); + } + catch (...) { + std::cerr << "Can't create logging directory directory " << boost::filesystem::path(dir).parent_path().string() << ".\n"; + return 1; + } + +#ifndef WIN32 + if (!CONFIG_STRING(&config, "service.group").empty() && !CONFIG_STRING(&config, "service.user").empty()) { + struct group *gr; + if ((gr = getgrnam(CONFIG_STRING(&config, "service.group").c_str())) == NULL) { + std::cerr << "Invalid service.group name " << CONFIG_STRING(&config, "service.group") << "\n"; + return 1; + } + struct passwd *pw; + if ((pw = getpwnam(CONFIG_STRING(&config, "service.user").c_str())) == NULL) { + std::cerr << "Invalid service.user name " << CONFIG_STRING(&config, "service.user") << "\n"; + return 1; + } + chown(dir.c_str(), pw->pw_uid, gr->gr_gid); + } + +#endif + } + log4cxx::PropertyConfigurator::configure(p); } @@ -242,12 +306,12 @@ int main(int argc, char **argv) if (!CONFIG_STRING(&config, "service.group").empty()) { struct group *gr; if ((gr = getgrnam(CONFIG_STRING(&config, "service.group").c_str())) == NULL) { - LOG4CXX_ERROR(logger, "Invalid service.group name " << CONFIG_STRING(&config, "service.group")); + std::cerr << "Invalid service.group name " << CONFIG_STRING(&config, "service.group") << "\n"; return 1; } if (((setgid(gr->gr_gid)) != 0) || (initgroups(CONFIG_STRING(&config, "service.user").c_str(), gr->gr_gid) != 0)) { - LOG4CXX_ERROR(logger, "Failed to set service.group name " << CONFIG_STRING(&config, "service.group") << " - " << gr->gr_gid << ":" << strerror(errno)); + std::cerr << "Failed to set service.group name " << CONFIG_STRING(&config, "service.group") << " - " << gr->gr_gid << ":" << strerror(errno) << "\n"; return 1; } } @@ -255,17 +319,22 @@ int main(int argc, char **argv) if (!CONFIG_STRING(&config, "service.user").empty()) { struct passwd *pw; if ((pw = getpwnam(CONFIG_STRING(&config, "service.user").c_str())) == NULL) { - LOG4CXX_ERROR(logger, "Invalid service.user name " << CONFIG_STRING(&config, "service.user")); + std::cerr << "Invalid service.user name " << CONFIG_STRING(&config, "service.user") << "\n"; return 1; } if ((setuid(pw->pw_uid)) != 0) { - LOG4CXX_ERROR(logger, "Failed to set service.user name " << CONFIG_STRING(&config, "service.user") << " - " << pw->pw_uid << ":" << strerror(errno)); + std::cerr << "Failed to set service.user name " << CONFIG_STRING(&config, "service.user") << " - " << pw->pw_uid << ":" << strerror(errno) << "\n"; return 1; } } setrlimit(RLIMIT_CORE, &limit); } + + struct rlimit limit; + limit.rlim_max = RLIM_INFINITY; + limit.rlim_cur = RLIM_INFINITY; + setrlimit(RLIMIT_CORE, &limit); #endif Swift::SimpleEventLoop eventLoop; @@ -313,6 +382,8 @@ int main(int argc, char **argv) NetworkPluginServer plugin(&transport, &config, &userManager, &ftManager); AdminInterface adminInterface(&transport, &userManager, &plugin, storageBackend); + StatsResponder statsResponder(&transport, &userManager, &plugin, storageBackend); + statsResponder.start(); eventLoop_ = &eventLoop; diff --git a/spectrum/src/sample.cfg b/spectrum/src/sample.cfg index 71b1d23b..3fdca1d1 100644 --- a/spectrum/src/sample.cfg +++ b/spectrum/src/sample.cfg @@ -5,6 +5,7 @@ server = 127.0.0.1 port = 5222 server_mode = 1 backend_host=localhost +pidfile=./test.pid # < this option doesn't work yet backend_port=10001 admin_username=admin @@ -12,13 +13,14 @@ admin_password=test #cert=server.pfx #patch to PKCS#12 certificate #cert_password=test #password to that certificate if any users_per_backend=10 -backend=/home/hanzz/code/libtransport/backends/libpurple/spectrum2_libpurple_backend +#backend=/home/hanzz/code/libtransport/backends/libpurple/spectrum2_libpurple_backend #backend=/usr/bin/mono /home/hanzz/code/networkplugin-csharp/msnp-sharp-backend/bin/Debug/msnp-sharp-backend.exe #backend=/home/hanzz/code/libtransport/backends/frotz/spectrum2_frotz_backend -#backend=/home/hanzz/code/libtransport/backends/libircclient-qt/spectrum2_libircclient-qt_backend +backend=/home/hanzz/code/libtransport/backends/libircclient-qt/spectrum2_libircclient-qt_backend #protocol=prpl-msn protocol=any #protocol=prpl-icq +irc_server=irc.freenode.org [backend] #default_avatar=catmelonhead.jpg diff --git a/spectrum/src/sample2.cfg b/spectrum/src/sample2.cfg index ca499f66..0482e4f8 100644 --- a/spectrum/src/sample2.cfg +++ b/spectrum/src/sample2.cfg @@ -36,8 +36,8 @@ backend_port=10001 users_per_backend=10 # Full path to backend binary. -backend=/usr/bin/spectrum_libpurple_backend -#backend=/usr/bin/spectrum_libircclient-qt_backend +backend=/usr/bin/spectrum2_libpurple_backend +#backend=/usr/bin/spectrum2_libircclient-qt_backend # Libpurple protocol-id for spectrum_libpurple_backend protocol=prpl-jabber @@ -62,18 +62,19 @@ type=xmpp [logging] # log4cxx/log4j logging configuration file in ini format used for main spectrum2 instance. -config = /etc/spectrum2/logging.cfg +config = /etc/spectrum2/logging.cfg # log4cxx/log4j logging configuration file in ini format used for backends. -backend_config = /etc/spectrum2/backend-logging.cfg # log4cxx/log4j logging configuration file for backends +backend_config = /etc/spectrum2/backend-logging.cfg [database] # Database backend type # "sqlite3", "mysql" or "none" without database backend -type = none +type = none # For SQLite3: Full path to database # For MySQL: name of database +# database = /var/lib/spectrum2/$jid/database.sql database = jabber_transport # Server. diff --git a/spectrum_manager/src/CMakeLists.txt b/spectrum_manager/src/CMakeLists.txt index 56543501..5024e70c 100644 --- a/spectrum_manager/src/CMakeLists.txt +++ b/spectrum_manager/src/CMakeLists.txt @@ -1,9 +1,9 @@ cmake_minimum_required(VERSION 2.6) FILE(GLOB SRC *.cpp) -ADD_EXECUTABLE(spectrum2_manager ${SRC}) +ADD_EXECUTABLE(spectrum2_manager ${SRC} ../../src/config.cpp) -target_link_libraries(spectrum2_manager transport) +target_link_libraries(spectrum2_manager ${SWIFTEN_LIBRARY}) INSTALL(TARGETS spectrum2_manager RUNTIME DESTINATION bin) diff --git a/spectrum_manager/src/main.cpp b/spectrum_manager/src/main.cpp index 5353bf59..227dc835 100644 --- a/spectrum_manager/src/main.cpp +++ b/spectrum_manager/src/main.cpp @@ -1,10 +1,6 @@ #include "managerconfig.h" -#include "transport/transport.h" -#include "transport/usermanager.h" -#include "transport/logger.h" -#include "transport/sqlite3backend.h" -#include "transport/userregistration.h" -#include "transport/networkpluginserver.h" +#include "transport/config.h" +#include "Swiften/Swiften.h" #include "Swiften/EventLoop/SimpleEventLoop.h" #include @@ -86,12 +82,17 @@ static std::string searchForBinary(const std::string &binary) { } // Executes new backend -static unsigned long exec_(std::string path, std::string config) { +static unsigned long exec_(std::string path, std::string config, std::string jid = "") { // fork and exec pid_t pid = fork(); if ( pid == 0 ) { // child process - exit(execl(path.c_str(), path.c_str(), config.c_str(), NULL)); + if (jid.empty()) { + exit(execl(path.c_str(), path.c_str(), config.c_str(), NULL)); + } + else { + exit(execl(path.c_str(), path.c_str(), "-j", jid.c_str(), config.c_str(), NULL)); + } } else if ( pid < 0 ) { // fork failed } @@ -147,15 +148,30 @@ static void start_all_instances(ManagerConfig *config) { Config cfg; if (cfg.load(itr->path().string()) == false) { std::cerr << "Can't load config file " << itr->path().string() << ". Skipping...\n"; + continue; } - int pid = isRunning(CONFIG_STRING(&cfg, "service.pidfile")); - if (pid == 0) { - std::cout << "Starting " << itr->path() << ": OK\n"; - exec_(spectrum2_binary, itr->path().string()); - } - else { - std::cout << "Starting " << itr->path() << ": Already started (PID=" << pid << ")\n"; + + std::vector vhosts; + if (CONFIG_HAS_KEY(&cfg, "vhosts.vhost")) + vhosts = CONFIG_VECTOR(&cfg, "vhosts.vhost"); + vhosts.push_back(CONFIG_STRING(&cfg, "service.jid")); + + BOOST_FOREACH(std::string &vhost, vhosts) { + Config vhostCfg; + if (vhostCfg.load(itr->path().string(), vhost) == false) { + std::cerr << "Can't load config file " << itr->path().string() << ". Skipping...\n"; + continue; + } + + int pid = isRunning(CONFIG_STRING(&vhostCfg, "service.pidfile")); + if (pid == 0) { + std::cout << "Starting " << itr->path() << ": OK\n"; + exec_(spectrum2_binary, itr->path().string(), vhost); + } + else { + std::cout << "Starting " << itr->path() << ": Already started (PID=" << pid << ")\n"; + } } } } @@ -188,13 +204,26 @@ static void stop_all_instances(ManagerConfig *config) { std::cerr << "Can't load config file " << itr->path().string() << ". Skipping...\n"; } - int pid = isRunning(CONFIG_STRING(&cfg, "service.pidfile")); - if (pid) { - std::cout << "Stopping " << itr->path() << ": OK\n"; - kill(pid, SIGTERM); - } - else { - std::cout << "Stopping " << itr->path() << ": Not running\n"; + std::vector vhosts; + if (CONFIG_HAS_KEY(&cfg, "vhosts.vhost")) + vhosts = CONFIG_VECTOR(&cfg, "vhosts.vhost"); + vhosts.push_back(CONFIG_STRING(&cfg, "service.jid")); + + BOOST_FOREACH(std::string &vhost, vhosts) { + Config vhostCfg; + if (vhostCfg.load(itr->path().string(), vhost) == false) { + std::cerr << "Can't load config file " << itr->path().string() << ". Skipping...\n"; + continue; + } + + int pid = isRunning(CONFIG_STRING(&vhostCfg, "service.pidfile")); + if (pid) { + std::cout << "Stopping " << itr->path() << ": OK\n"; + kill(pid, SIGTERM); + } + else { + std::cout << "Stopping " << itr->path() << ": Not running\n"; + } } } } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 346a4294..7a258e7a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -35,9 +35,9 @@ if (CMAKE_COMPILER_IS_GNUCXX) endif() if (WIN32) - TARGET_LINK_LIBRARIES(transport ${Boost_LIBRARIES} ${SQLITE3_LIBRARIES} ${MYSQL_LIBRARIES} ${SWIFTEN_LIBRARY} ${PROTOBUF_LIBRARIES} ${LOG4CXX_LIBRARIES}) + TARGET_LINK_LIBRARIES(transport ${SQLITE3_LIBRARIES} ${MYSQL_LIBRARIES} ${SWIFTEN_LIBRARY} ${PROTOBUF_LIBRARIES} ${LOG4CXX_LIBRARIES}) else (WIN32) - TARGET_LINK_LIBRARIES(transport ${Boost_LIBRARIES} ${SQLITE3_LIBRARIES} ${MYSQL_LIBRARIES} ${SWIFTEN_LIBRARY} ${PROTOBUF_LIBRARIES} ${LOG4CXX_LIBRARIES} ${POPT_LIBRARY}) + TARGET_LINK_LIBRARIES(transport ${SQLITE3_LIBRARIES} ${MYSQL_LIBRARIES} ${SWIFTEN_LIBRARY} ${PROTOBUF_LIBRARIES} ${LOG4CXX_LIBRARIES} ${POPT_LIBRARY}) endif(WIN32) SET_TARGET_PROPERTIES(transport PROPERTIES diff --git a/src/admininterface.cpp b/src/admininterface.cpp index 3af032d4..7df84772 100644 --- a/src/admininterface.cpp +++ b/src/admininterface.cpp @@ -66,6 +66,11 @@ void AdminInterface::handleMessageReceived(Swift::Message::ref message) { return; } + // Ignore empty messages + if (message->getBody().empty()) { + return; + } + LOG4CXX_INFO(logger, "Message from admin received"); message->setTo(message->getFrom()); message->setFrom(m_component->getJID()); diff --git a/src/config.cpp b/src/config.cpp index 05473249..b72caa86 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -31,13 +31,13 @@ using namespace boost::program_options; namespace Transport { -bool Config::load(const std::string &configfile, boost::program_options::options_description &opts) { +bool Config::load(const std::string &configfile, boost::program_options::options_description &opts, const std::string &jid) { std::ifstream ifs(configfile.c_str()); if (!ifs.is_open()) return false; m_file = configfile; - bool ret = load(ifs, opts); + bool ret = load(ifs, opts, jid); ifs.close(); char path[PATH_MAX] = ""; @@ -49,7 +49,7 @@ bool Config::load(const std::string &configfile, boost::program_options::options return ret; } -bool Config::load(std::istream &ifs, boost::program_options::options_description &opts) { +bool Config::load(std::istream &ifs, boost::program_options::options_description &opts, const std::string &_jid) { m_unregistered.clear(); opts.add_options() ("service.jid", value()->default_value(""), "Transport Jabber ID") @@ -60,13 +60,13 @@ bool Config::load(std::istream &ifs, boost::program_options::options_description ("service.group", value()->default_value(""), "The name of group Spectrum runs as.") ("service.backend", value()->default_value("libpurple_backend"), "Backend") ("service.protocol", value()->default_value(""), "Protocol") - ("service.pidfile", value()->default_value("/var/run/spectrum2/spectrum2.pid"), "Full path to pid file") - ("service.working_dir", value()->default_value("/var/lib/spectrum2"), "Working dir") + ("service.pidfile", value()->default_value("/var/run/spectrum2/$jid.pid"), "Full path to pid file") + ("service.working_dir", value()->default_value("/var/lib/spectrum2/$jid"), "Working dir") ("service.allowed_servers", value()->default_value(""), "Only users from these servers can connect") ("service.server_mode", value()->default_value(false), "True if Spectrum should behave as server") ("service.users_per_backend", value()->default_value(100), "Number of users per one legacy network backend") ("service.backend_host", value()->default_value("localhost"), "Host to bind backend server to") - ("service.backend_port", value()->default_value("10000"), "Port to bind backend server to") + ("service.backend_port", value()->default_value("0"), "Port to bind backend server to") ("service.cert", value()->default_value(""), "PKCS#12 Certificate.") ("service.cert_password", value()->default_value(""), "PKCS#12 Certificate password.") ("service.admin_jid", value()->default_value(""), "Administrator jid.") @@ -76,15 +76,16 @@ bool Config::load(std::istream &ifs, boost::program_options::options_description ("service.memory_collector_time", value()->default_value(0), "Time in seconds after which backend with most memory is set to die.") ("service.more_resources", value()->default_value(false), "Allow more resources to be connected in server mode at the same time.") ("service.enable_privacy_lists", value()->default_value(true), "") + ("vhosts.vhost", value >()->multitoken(), "") ("identity.name", value()->default_value("Spectrum 2 Transport"), "Name showed in service discovery.") ("identity.category", value()->default_value("gateway"), "Disco#info identity category. 'gateway' by default.") ("identity.type", value()->default_value(""), "Type of transport ('icq','msn','gg','irc', ...)") ("registration.enable_public_registration", value()->default_value(true), "True if users should be able to register.") ("registration.language", value()->default_value("en"), "Default language for registration form") - ("registration.instructions", value()->default_value(""), "Instructions showed to user in registration form") - ("registration.username_field", value()->default_value(""), "Label for username field") + ("registration.instructions", value()->default_value("Enter your legacy network username and password."), "Instructions showed to user in registration form") + ("registration.username_label", value()->default_value("Legacy network username:"), "Label for username field") ("registration.username_mask", value()->default_value(""), "Username mask") - ("registration.encoding", value()->default_value("en"), "Default encoding in registration form") + ("registration.encoding", value()->default_value("utf8"), "Default encoding in registration form") ("database.type", value()->default_value("none"), "Database type.") ("database.database", value()->default_value(""), "Database used to store data") ("database.server", value()->default_value("localhost"), "Database server.") @@ -92,6 +93,7 @@ bool Config::load(std::istream &ifs, boost::program_options::options_description ("database.password", value()->default_value(""), "Database Password.") ("database.port", value()->default_value(0), "Database port.") ("database.prefix", value()->default_value(""), "Prefix of tables in database") + ("database.encryption_key", value()->default_value(""), "Encryption key.") ("logging.config", value()->default_value(""), "Path to log4cxx config file which is used for Spectrum 2 instance") ("logging.backend_config", value()->default_value(""), "Path to log4cxx config file which is used for backends") ("backend.default_avatar", value()->default_value(""), "Full path to default avatar") @@ -101,10 +103,69 @@ bool Config::load(std::istream &ifs, boost::program_options::options_description parsed_options parsed = parse_config_file(ifs, opts, true); - BOOST_FOREACH(option opt, parsed.options) { + bool found_working = false; + bool found_pidfile = false; + bool found_backend_port = false; + std::string jid = ""; + BOOST_FOREACH(option &opt, parsed.options) { + if (opt.string_key == "service.jid") { + if (_jid.empty()) { + jid = opt.value[0]; + } + else { + opt.value[0] = _jid; + jid = _jid; + } + } + else if (opt.string_key == "service.backend_port") { + found_backend_port = true; + if (opt.value[0] == "0") { + unsigned long r = 0; + BOOST_FOREACH(char c, _jid) { + r += (int) c; + } + srand(time(NULL) + r); + int randomPort = 30000 + rand() % 10000; + opt.value[0] = boost::lexical_cast(randomPort); + } + } + else if (opt.string_key == "service.working_dir") { + found_working = true; + } + else if (opt.string_key == "service.pidfile") { + found_pidfile = true; + } + } + + if (!found_working) { + std::vector value; + value.push_back("/var/lib/spectrum2/$jid"); + parsed.options.push_back(boost::program_options::basic_option("service.working_dir", value)); + } + if (!found_pidfile) { + std::vector value; + value.push_back("/var/run/spectrum2/$jid.pid"); + parsed.options.push_back(boost::program_options::basic_option("service.pidfile", value)); + } + if (!found_backend_port) { + unsigned long r = 0; + BOOST_FOREACH(char c, _jid) { + r += (int) c; + } + srand(time(NULL) + r); + int randomPort = 30000 + rand() % 10000; + std::vector value; + value.push_back(boost::lexical_cast(randomPort)); + parsed.options.push_back(boost::program_options::basic_option("service.backend_port", value)); + } + + BOOST_FOREACH(option &opt, parsed.options) { if (opt.unregistered) { m_unregistered[opt.string_key] = opt.value[0]; } + else if (opt.value[0].find("$jid") != std::string::npos) { + boost::replace_all(opt.value[0], "$jid", jid); + } } store(parsed, m_variables); @@ -120,9 +181,9 @@ bool Config::load(std::istream &ifs) { return load(ifs, opts); } -bool Config::load(const std::string &configfile) { +bool Config::load(const std::string &configfile, const std::string &jid) { options_description opts("Transport options"); - return load(configfile, opts); + return load(configfile, opts, jid); } bool Config::reload() { diff --git a/src/conversation.cpp b/src/conversation.cpp index 81b1654d..d903e320 100644 --- a/src/conversation.cpp +++ b/src/conversation.cpp @@ -37,6 +37,7 @@ Conversation::Conversation(ConversationManager *conversationManager, const std:: m_legacyName = legacyName; m_conversationManager->addConversation(this); m_muc = isMUC; + m_jid = m_conversationManager->getUser()->getJID().toBare(); } Conversation::~Conversation() { @@ -54,11 +55,16 @@ void Conversation::handleMessage(boost::shared_ptr &message, con else { message->setType(Swift::Message::Chat); } + + std::string n = nickname; + if (n.empty() && !m_room.empty() && !m_muc) { + n = m_nickname; + } + if (message->getType() != Swift::Message::Groupchat) { - - message->setTo(m_conversationManager->getUser()->getJID().toBare()); + message->setTo(m_jid); // normal message - if (nickname.empty()) { + if (n.empty()) { Buddy *buddy = m_conversationManager->getUser()->getRosterManager()->getBuddy(m_legacyName); if (buddy) { message->setFrom(buddy->getJID()); @@ -70,10 +76,10 @@ void Conversation::handleMessage(boost::shared_ptr &message, con // PM message else { if (m_room.empty()) { - message->setFrom(Swift::JID(nickname, m_conversationManager->getComponent()->getJID().toBare(), "user")); + message->setFrom(Swift::JID(n, m_conversationManager->getComponent()->getJID().toBare(), "user")); } else { - message->setFrom(Swift::JID(m_room, m_conversationManager->getComponent()->getJID().toBare(), nickname)); + message->setFrom(Swift::JID(m_room, m_conversationManager->getComponent()->getJID().toBare(), n)); } } m_conversationManager->getComponent()->getStanzaChannel()->sendMessage(message); @@ -83,7 +89,7 @@ void Conversation::handleMessage(boost::shared_ptr &message, con if (legacyName.find_last_of("@") != std::string::npos) { legacyName.replace(legacyName.find_last_of("@"), 1, "%"); // OK } - message->setTo(m_conversationManager->getUser()->getJID().toString()); + message->setTo(m_jid); message->setFrom(Swift::JID(legacyName, m_conversationManager->getComponent()->getJID().toBare(), nickname)); m_conversationManager->getComponent()->getStanzaChannel()->sendMessage(message); } @@ -99,7 +105,7 @@ void Conversation::handleParticipantChanged(const std::string &nick, int flag, i } } presence->setFrom(Swift::JID(legacyName, m_conversationManager->getComponent()->getJID().toBare(), nickname)); - presence->setTo(m_conversationManager->getUser()->getJID().toString()); + presence->setTo(m_jid); presence->setType(Swift::Presence::Available); if (!statusMessage.empty()) diff --git a/src/conversationmanager.cpp b/src/conversationmanager.cpp index 2f422ff3..bc08d7a7 100644 --- a/src/conversationmanager.cpp +++ b/src/conversationmanager.cpp @@ -62,6 +62,15 @@ void ConversationManager::removeConversation(Conversation *conv) { m_convs.erase(conv->getLegacyName()); } +void ConversationManager::resetResources() { + for (std::map::const_iterator it = m_convs.begin(); it != m_convs.end(); it++) { + if ((*it).second->isMUC()) { + continue; + } + (*it).second->setJID(m_user->getJID().toBare()); + } +} + void ConversationManager::handleMessageReceived(Swift::Message::ref message) { // std::string name = message->getTo().getUnescapedNode(); // if (name.find_last_of("%") != std::string::npos) { // OK when commented @@ -84,10 +93,13 @@ void ConversationManager::handleMessageReceived(Swift::Message::ref message) { if (!m_convs[name]) { m_convs[name] = m_component->getFactory()->createConversation(this, name); m_convs[name]->setRoom(room_name); + m_convs[name]->setNickname(name); } } + // update resource and send the message + m_convs[name]->setJID(message->getFrom()); m_convs[name]->sendMessage(message); } -} \ No newline at end of file +} diff --git a/src/mysqlbackend.cpp b/src/mysqlbackend.cpp index a10d89ec..60ac8b47 100644 --- a/src/mysqlbackend.cpp +++ b/src/mysqlbackend.cpp @@ -21,6 +21,7 @@ #ifdef WITH_MYSQL #include "transport/mysqlbackend.h" +#include "transport/util.h" #include #include "log4cxx/logger.h" @@ -409,7 +410,11 @@ bool MySQLBackend::exec(const std::string &query) { } void MySQLBackend::setUser(const UserInfo &user) { - *m_setUser << user.jid << user.uin << user.password << user.language << user.encoding << user.vip << user.password; + std::string encrypted = user.password; + if (!CONFIG_STRING(m_config, "database.encryption_key").empty()) { + encrypted = Util::encryptPassword(encrypted, CONFIG_STRING(m_config, "database.encryption_key")); + } + *m_setUser << user.jid << user.uin << encrypted << user.language << user.encoding << user.vip << user.password; EXEC(m_setUser, setUser(user)); } @@ -423,6 +428,10 @@ bool MySQLBackend::getUser(const std::string &barejid, UserInfo &user) { while (m_getUser->fetch() == 0) { ret = true; *m_getUser >> user.id >> user.jid >> user.uin >> user.password >> user.encoding >> user.language >> user.vip; + + if (!CONFIG_STRING(m_config, "database.encryption_key").empty()) { + user.password = Util::decryptPassword(user.password, CONFIG_STRING(m_config, "database.encryption_key")); + } } return ret; diff --git a/src/networkpluginserver.cpp b/src/networkpluginserver.cpp index 4da583eb..b284390c 100644 --- a/src/networkpluginserver.cpp +++ b/src/networkpluginserver.cpp @@ -257,6 +257,17 @@ NetworkPluginServer::NetworkPluginServer(Component *component, Config *config, U } NetworkPluginServer::~NetworkPluginServer() { + for (std::list::const_iterator it = m_clients.begin(); it != m_clients.end(); it++) { + LOG4CXX_INFO(logger, "Stopping backend " << *it); + std::string message; + pbnetwork::WrapperMessage wrap; + wrap.set_type(pbnetwork::WrapperMessage_Type_TYPE_EXIT); + wrap.SerializeToString(&message); + + Backend *c = (Backend *) *it; + send(c->connection, message); + } + m_pingTimer->stop(); m_server->stop(); m_server.reset(); @@ -924,7 +935,7 @@ void NetworkPluginServer::handleUserCreated(User *user) { // UserInfo userInfo = user->getUserInfo(); user->onReadyToConnect.connect(boost::bind(&NetworkPluginServer::handleUserReadyToConnect, this, user)); user->onPresenceChanged.connect(boost::bind(&NetworkPluginServer::handleUserPresenceChanged, this, user, _1)); - user->onRoomJoined.connect(boost::bind(&NetworkPluginServer::handleRoomJoined, this, user, _1, _2, _3)); + user->onRoomJoined.connect(boost::bind(&NetworkPluginServer::handleRoomJoined, this, user, _1, _2, _3, _4)); user->onRoomLeft.connect(boost::bind(&NetworkPluginServer::handleRoomLeft, this, user, _1)); } @@ -980,7 +991,7 @@ void NetworkPluginServer::handleUserPresenceChanged(User *user, Swift::Presence: send(c->connection, message); } -void NetworkPluginServer::handleRoomJoined(User *user, const std::string &r, const std::string &nickname, const std::string &password) { +void NetworkPluginServer::handleRoomJoined(User *user, const Swift::JID &who, const std::string &r, const std::string &nickname, const std::string &password) { UserInfo userInfo = user->getUserInfo(); pbnetwork::Room room; @@ -1003,6 +1014,7 @@ void NetworkPluginServer::handleRoomJoined(User *user, const std::string &r, con NetworkConversation *conv = new NetworkConversation(user->getConversationManager(), r, true); conv->onMessageToSend.connect(boost::bind(&NetworkPluginServer::handleMessageReceived, this, _1, _2)); conv->setNickname(nickname); + conv->setJID(who); } void NetworkPluginServer::handleRoomLeft(User *user, const std::string &r) { @@ -1036,7 +1048,6 @@ void NetworkPluginServer::handleRoomLeft(User *user, const std::string &r) { } void NetworkPluginServer::handleUserDestroyed(User *user) { - std::cout << "HANDLE_DESTROYED\n"; m_waitingUsers.remove(user); UserInfo userInfo = user->getUserInfo(); diff --git a/src/rostermanager.cpp b/src/rostermanager.cpp index dc024b7a..db888909 100644 --- a/src/rostermanager.cpp +++ b/src/rostermanager.cpp @@ -52,9 +52,9 @@ RosterManager::RosterManager(User *user, Component *component){ m_supportRemoteRoster = false; if (!m_component->inServerMode()) { - AddressedRosterRequest::ref request = AddressedRosterRequest::ref(new AddressedRosterRequest(m_component->getIQRouter(), m_user->getJID().toBare())); - request->onResponse.connect(boost::bind(&RosterManager::handleRemoteRosterResponse, this, _1, _2)); - request->send(); + m_remoteRosterRequest = AddressedRosterRequest::ref(new AddressedRosterRequest(m_component->getIQRouter(), m_user->getJID().toBare())); + m_remoteRosterRequest->onResponse.connect(boost::bind(&RosterManager::handleRemoteRosterResponse, this, _1, _2)); + m_remoteRosterRequest->send(); } } @@ -67,6 +67,11 @@ RosterManager::~RosterManager() { sendUnavailablePresences(m_user->getJID().toBare()); + if (m_remoteRosterRequest) { + m_remoteRosterRequest->onResponse.disconnect_all_slots(); + m_component->getIQRouter()->removeHandler(m_remoteRosterRequest); + } + for (std::map, boost::pool_allocator< std::pair > >::iterator it = m_buddies.begin(); it != m_buddies.end(); it++) { Buddy *buddy = (*it).second; if (!buddy) { @@ -136,8 +141,6 @@ void RosterManager::handleBuddyChanged(Buddy *buddy) { } void RosterManager::setBuddyCallback(Buddy *buddy) { - m_setBuddyTimer->onTick.disconnect(boost::bind(&RosterManager::setBuddyCallback, this, buddy)); - LOG4CXX_INFO(logger, "Associating buddy " << buddy->getName() << " with " << m_user->getJID().toString()); m_buddies[buddy->getName()] = buddy; onBuddySet(buddy); @@ -148,21 +151,17 @@ void RosterManager::setBuddyCallback(Buddy *buddy) { sendBuddyRosterPush(buddy); } else { - if (m_setBuddyTimer->onTick.empty()) { - m_setBuddyTimer->stop(); - - if (m_supportRemoteRoster) { - sendBuddyRosterPush(buddy); + if (m_supportRemoteRoster) { + sendBuddyRosterPush(buddy); + } + else { + // Send RIE only if there's resource which supports it. + Swift::JID jidWithRIE = m_user->getJIDWithFeature("http://jabber.org/protocol/rosterx"); + if (jidWithRIE.isValid()) { + m_RIETimer->start(); } else { - // Send RIE only if there's resource which supports it. - Swift::JID jidWithRIE = m_user->getJIDWithFeature("http://jabber.org/protocol/rosterx"); - if (jidWithRIE.isValid()) { - m_RIETimer->start(); - } - else { - sendBuddySubscribePresence(buddy); - } + sendBuddySubscribePresence(buddy); } } } @@ -198,6 +197,7 @@ void RosterManager::handleBuddyRosterPushResponse(Swift::ErrorPayload::ref error } void RosterManager::handleRemoteRosterResponse(boost::shared_ptr payload, Swift::ErrorPayload::ref error) { + m_remoteRosterRequest.reset(); if (error) { m_supportRemoteRoster = false; LOG4CXX_INFO(logger, m_user->getJID().toString() << ": This server does not support remote roster protoXEP"); diff --git a/src/statsresponder.cpp b/src/statsresponder.cpp new file mode 100644 index 00000000..802a1bbd --- /dev/null +++ b/src/statsresponder.cpp @@ -0,0 +1,142 @@ +/** + * XMPP - libpurple transport + * + * Copyright (C) 2009, 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/statsresponder.h" + +#include +#include +#include "Swiften/Queries/IQRouter.h" +#include "transport/BlockPayload.h" +#include "Swiften/Swiften.h" +#include "transport/usermanager.h" +#include "transport/user.h" +#include "transport/buddy.h" +#include "transport/rostermanager.h" +#include "transport/memoryusage.h" +#include "transport/conversationmanager.h" +#include "transport/rostermanager.h" +#include "transport/usermanager.h" +#include "transport/networkpluginserver.h" +#include "log4cxx/logger.h" + +using namespace log4cxx; + +using namespace Swift; +using namespace boost; + +namespace Transport { + +static LoggerPtr logger = Logger::getLogger("StatsResponder"); + +StatsResponder::StatsResponder(Component *component, UserManager *userManager, NetworkPluginServer *server, StorageBackend *storageBackend) : Swift::Responder(component->getIQRouter()) { + m_component = component; + m_userManager = userManager; + m_server = server; + m_storageBackend = storageBackend; + m_start = time(0); +} + +StatsResponder::~StatsResponder() { + +} + +unsigned long StatsResponder::usedMemory() { + double shared = 0; + double rss = 0; +#ifndef WIN32 + process_mem_usage(shared, rss); +#endif + rss -= shared; + + const std::list &backends = m_server->getBackends(); + BOOST_FOREACH(NetworkPluginServer::Backend * backend, backends) { + rss += backend->res - backend->shared; + } + + return (unsigned long) rss; +} + +bool StatsResponder::handleGetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr stats) { + boost::shared_ptr response(new StatsPayload()); + + if (stats->getItems().empty()) { + response->addItem(StatsPayload::Item("uptime")); + response->addItem(StatsPayload::Item("users/online")); + response->addItem(StatsPayload::Item("contacts/online")); + response->addItem(StatsPayload::Item("contacts/total")); + response->addItem(StatsPayload::Item("backends")); + response->addItem(StatsPayload::Item("memory-usage")); + } + else { + unsigned long contactsOnline = 0; + unsigned long contactsTotal = 0; + + Swift::StatusShow s; + std::string statusMessage; + for (std::map::const_iterator it = m_userManager->getUsers().begin(); it != m_userManager->getUsers().end(); it++) { + if (!(*it).second) { + continue; + } + const RosterManager::BuddiesMap &buddies = (*it).second->getRosterManager()->getBuddies(); + contactsTotal += buddies.size(); + for(RosterManager::BuddiesMap::const_iterator bt = buddies.begin(); bt != buddies.end(); bt++) { + if (!(*bt).second) { + continue; + } + if (!(*bt).second->getStatus(s, statusMessage)) + continue; + if (s.getType() != Swift::StatusShow::None) { + contactsOnline++; + } + } + } + + BOOST_FOREACH(const StatsPayload::Item &item, stats->getItems()) { + if (item.getName() == "uptime") { + response->addItem(StatsPayload::Item("uptime", "seconds", boost::lexical_cast(time(0) - m_start))); + } + else if (item.getName() == "users/online") { + response->addItem(StatsPayload::Item("users/online", "users", boost::lexical_cast(m_userManager->getUserCount()))); + } + else if (item.getName() == "backends") { + response->addItem(StatsPayload::Item("backends", "backends", boost::lexical_cast(m_server->getBackendCount()))); + } + else if (item.getName() == "memory-usage") { + response->addItem(StatsPayload::Item("memory-usage", "KB", boost::lexical_cast(usedMemory()))); + } + else if (item.getName() == "contacts/online") { + response->addItem(StatsPayload::Item("contacts/online", "contacts", boost::lexical_cast(contactsOnline))); + } + else if (item.getName() == "contacts/total") { + response->addItem(StatsPayload::Item("contacts/total", "contacts", boost::lexical_cast(contactsTotal))); + } + } + } + + sendResponse(from, id, response); + + return true; +} + +bool StatsResponder::handleSetRequest(const Swift::JID& from, const Swift::JID& to, const std::string& id, boost::shared_ptr stats) { + return false; +} + +} diff --git a/src/tests/rostermanager.cpp b/src/tests/rostermanager.cpp index 85238a97..c9f3bf35 100644 --- a/src/tests/rostermanager.cpp +++ b/src/tests/rostermanager.cpp @@ -24,6 +24,7 @@ class RosterManagerTest : public CPPUNIT_NS :: TestFixture, public BasicTest { CPPUNIT_TEST_SUITE(RosterManagerTest); CPPUNIT_TEST(setBuddy); CPPUNIT_TEST(sendCurrentPresences); + CPPUNIT_TEST(sendCurrentPresence); CPPUNIT_TEST_SUITE_END(); public: @@ -132,13 +133,32 @@ class RosterManagerTest : public CPPUNIT_NS :: TestFixture, public BasicTest { user->getRosterManager()->sendCurrentPresences("user@localhost/resource"); CPPUNIT_ASSERT_EQUAL(2, (int) received.size()); -// CPPUNIT_ASSERT(dynamic_cast(getStanza(received[2]))); -// CPPUNIT_ASSERT_EQUAL(Swift::StatusShow::Away, dynamic_cast(getStanza(received[2]))->getShow()); -// CPPUNIT_ASSERT_EQUAL(std::string("status1"), dynamic_cast(getStanza(received[2]))->getStatus()); -// -// CPPUNIT_ASSERT(dynamic_cast(getStanza(received[3]))); -// CPPUNIT_ASSERT_EQUAL(Swift::StatusShow::Away, dynamic_cast(getStanza(received[3]))->getShow()); -// CPPUNIT_ASSERT_EQUAL(std::string("status2"), dynamic_cast(getStanza(received[3]))->getStatus()); + + for (int i = 0; i < 2; i++) { + CPPUNIT_ASSERT(dynamic_cast(getStanza(received[i]))); + CPPUNIT_ASSERT_EQUAL(Swift::StatusShow::Away, dynamic_cast(getStanza(received[i]))->getShow()); + CPPUNIT_ASSERT_EQUAL(std::string("user@localhost/resource"), dynamic_cast(getStanza(received[i]))->getTo().toString()); + } + } + + void sendCurrentPresence() { + setBuddy(); + received.clear(); + + User *user = userManager->getUser("user@localhost"); + user->getRosterManager()->sendCurrentPresence("buddy1@localhost", "user@localhost/resource"); + + CPPUNIT_ASSERT_EQUAL(1, (int) received.size()); + CPPUNIT_ASSERT(dynamic_cast(getStanza(received[0]))); + CPPUNIT_ASSERT_EQUAL(Swift::StatusShow::Away, dynamic_cast(getStanza(received[0]))->getShow()); + CPPUNIT_ASSERT_EQUAL(std::string("user@localhost/resource"), dynamic_cast(getStanza(received[0]))->getTo().toString()); + + received.clear(); + user->getRosterManager()->sendCurrentPresence("buddy_unknown@localhost", "user@localhost/resource"); + CPPUNIT_ASSERT_EQUAL(1, (int) received.size()); + CPPUNIT_ASSERT(dynamic_cast(getStanza(received[0]))); + CPPUNIT_ASSERT_EQUAL(Swift::Presence::Unavailable, dynamic_cast(getStanza(received[0]))->getType()); + CPPUNIT_ASSERT_EQUAL(std::string("user@localhost/resource"), dynamic_cast(getStanza(received[0]))->getTo().toString()); } void disconnectUser() { diff --git a/src/tests/util.cpp b/src/tests/util.cpp new file mode 100644 index 00000000..f5c66819 --- /dev/null +++ b/src/tests/util.cpp @@ -0,0 +1,45 @@ +#include "transport/userregistry.h" +#include "transport/config.h" +#include "transport/storagebackend.h" +#include "transport/user.h" +#include "transport/transport.h" +#include "transport/conversation.h" +#include "transport/usermanager.h" +#include "transport/localbuddy.h" +#include +#include +#include +#include +#include +#include +#include +#include "Swiften/Server/ServerStanzaChannel.h" +#include "Swiften/Server/ServerFromClientSession.h" +#include "Swiften/Parser/PayloadParsers/FullPayloadParserFactoryCollection.h" +#include "basictest.h" + +#include "transport/util.h" + +using namespace Transport; + +class UtilTest : public CPPUNIT_NS :: TestFixture{ + CPPUNIT_TEST_SUITE(UtilTest); + CPPUNIT_TEST(encryptDecryptPassword); + CPPUNIT_TEST_SUITE_END(); + + public: + void setUp (void) { + } + + void tearDown (void) { + + } + + void encryptDecryptPassword() { + std::string encrypted = Util::encryptPassword("password", "key"); + CPPUNIT_ASSERT_EQUAL(std::string("password"), Util::decryptPassword(encrypted, "key")); + } + +}; + +CPPUNIT_TEST_SUITE_REGISTRATION (UtilTest); diff --git a/src/transport.cpp b/src/transport.cpp index e9d87cb5..7d3677f1 100644 --- a/src/transport.cpp +++ b/src/transport.cpp @@ -34,6 +34,8 @@ #include "Swiften/Serializer/PayloadSerializers/AttentionSerializer.h" #include "Swiften/Parser/PayloadParsers/XHTMLIMParser.h" #include "Swiften/Serializer/PayloadSerializers/XHTMLIMSerializer.h" +#include "Swiften/Parser/PayloadParsers/StatsParser.h" +#include "Swiften/Serializer/PayloadSerializers/StatsSerializer.h" #include "transport/BlockParser.h" #include "transport/BlockSerializer.h" #include "Swiften/Parser/PayloadParsers/InvisibleParser.h" @@ -90,11 +92,13 @@ Component::Component(Swift::EventLoop *loop, Swift::NetworkFactories *factories, m_server->addPayloadParserFactory(new GenericPayloadParserFactory("html", "http://jabber.org/protocol/xhtml-im")); m_server->addPayloadParserFactory(new GenericPayloadParserFactory("block", "urn:xmpp:block:0")); m_server->addPayloadParserFactory(new GenericPayloadParserFactory("invisible", "urn:xmpp:invisible:0")); + m_server->addPayloadParserFactory(new GenericPayloadParserFactory("query", "http://jabber.org/protocol/stats")); m_server->addPayloadSerializer(new Swift::AttentionSerializer()); m_server->addPayloadSerializer(new Swift::XHTMLIMSerializer()); m_server->addPayloadSerializer(new Transport::BlockSerializer()); m_server->addPayloadSerializer(new Swift::InvisibleSerializer()); + m_server->addPayloadSerializer(new Swift::StatsSerializer()); m_server->onDataRead.connect(boost::bind(&Component::handleDataRead, this, _1)); m_server->onDataWritten.connect(boost::bind(&Component::handleDataWritten, this, _1)); @@ -113,11 +117,13 @@ Component::Component(Swift::EventLoop *loop, Swift::NetworkFactories *factories, m_component->addPayloadParserFactory(new GenericPayloadParserFactory("html", "http://jabber.org/protocol/xhtml-im")); m_component->addPayloadParserFactory(new GenericPayloadParserFactory("block", "urn:xmpp:block:0")); m_component->addPayloadParserFactory(new GenericPayloadParserFactory("invisible", "urn:xmpp:invisible:0")); + m_component->addPayloadParserFactory(new GenericPayloadParserFactory("query", "http://jabber.org/protocol/stats")); m_component->addPayloadSerializer(new Swift::AttentionSerializer()); m_component->addPayloadSerializer(new Swift::XHTMLIMSerializer()); m_component->addPayloadSerializer(new Transport::BlockSerializer()); m_component->addPayloadSerializer(new Swift::InvisibleSerializer()); + m_component->addPayloadSerializer(new Swift::StatsSerializer()); m_stanzaChannel = m_component->getStanzaChannel(); m_iqRouter = m_component->getIQRouter(); diff --git a/src/user.cpp b/src/user.cpp index c66da595..9b1d7fd6 100644 --- a/src/user.cpp +++ b/src/user.cpp @@ -43,7 +43,7 @@ namespace Transport { static LoggerPtr logger = Logger::getLogger("User"); User::User(const Swift::JID &jid, UserInfo &userInfo, Component *component, UserManager *userManager) { - m_jid = jid; + m_jid = jid.toBare(); m_data = NULL; m_component = component; @@ -54,6 +54,7 @@ User::User(const Swift::JID &jid, UserInfo &userInfo, Component *component, User m_connected = false; m_readyForConnect = false; m_ignoreDisconnect = false; + m_resources = 0; m_reconnectTimer = m_component->getNetworkFactories()->getTimerFactory()->createTimer(10000); m_reconnectTimer->onTick.connect(boost::bind(&User::onConnectingTimeout, this)); @@ -96,6 +97,10 @@ Swift::JID User::getJIDWithFeature(const std::string &feature) { else { continue; } + + if (!discoInfo) { + continue; + } #else continue; #endif @@ -129,30 +134,37 @@ void User::sendCurrentPresence() { return; } - if (m_connected) { - Swift::Presence::ref highest = m_presenceOracle->getHighestPriorityPresence(m_jid.toBare()); - if (highest) { - Swift::Presence::ref response = Swift::Presence::create(highest); - response->setTo(m_jid); - response->setFrom(m_component->getJID()); - m_component->getStanzaChannel()->sendPresence(response); + std::vector presences = m_presenceOracle->getAllPresence(m_jid); + foreach(Swift::Presence::ref presence, presences) { + if (presence->getType() == Swift::Presence::Unavailable) { + continue; + } + + if (m_connected) { + Swift::Presence::ref highest = m_presenceOracle->getHighestPriorityPresence(m_jid.toBare()); + if (highest) { + Swift::Presence::ref response = Swift::Presence::create(highest); + response->setTo(presence->getFrom()); + response->setFrom(m_component->getJID()); + m_component->getStanzaChannel()->sendPresence(response); + } + else { + Swift::Presence::ref response = Swift::Presence::create(); + response->setTo(presence->getFrom()); + response->setFrom(m_component->getJID()); + response->setType(Swift::Presence::Unavailable); + m_component->getStanzaChannel()->sendPresence(response); + } } else { Swift::Presence::ref response = Swift::Presence::create(); - response->setTo(m_jid.toBare()); + response->setTo(presence->getFrom()); response->setFrom(m_component->getJID()); response->setType(Swift::Presence::Unavailable); + response->setStatus("Connecting"); m_component->getStanzaChannel()->sendPresence(response); } } - else { - Swift::Presence::ref response = Swift::Presence::create(); - response->setTo(m_jid.toBare()); - response->setFrom(m_component->getJID()); - response->setType(Swift::Presence::Unavailable); - response->setStatus("Connecting"); - m_component->getStanzaChannel()->sendPresence(response); - } } void User::setConnected(bool connected) { @@ -164,6 +176,10 @@ void User::setConnected(bool connected) { } void User::handlePresence(Swift::Presence::ref presence) { + int currentResourcesCount = m_presenceOracle->getAllPresence(m_jid).size(); + + m_conversationManager->resetResources(); + if (!m_connected) { // we are not connected to legacy network, so we should do it when disco#info arrive :) if (m_readyForConnect == false) { @@ -210,12 +226,35 @@ void User::handlePresence(Swift::Presence::ref presence) { if (presence->getPayload() != NULL) { password = presence->getPayload()->getPassword() ? *presence->getPayload()->getPassword() : ""; } - onRoomJoined(room, presence->getTo().getResource(), password); + onRoomJoined(presence->getFrom(), room, presence->getTo().getResource(), password); } return; } - sendCurrentPresence(); + + // User wants to disconnect this resource + if (!m_component->inServerMode()) { + if (presence->getType() == Swift::Presence::Unavailable) { + // Send unavailable presences for online contacts + m_rosterManager->sendUnavailablePresences(presence->getFrom()); + + // Send unavailable presence for transport contact itself + Swift::Presence::ref response = Swift::Presence::create(); + response->setTo(presence->getFrom()); + response->setFrom(m_component->getJID()); + response->setType(Swift::Presence::Unavailable); + m_component->getStanzaChannel()->sendPresence(response); + } + else { + sendCurrentPresence(); + // This resource is new, so we have to send buddies presences + if (currentResourcesCount != m_resources) { + m_rosterManager->sendCurrentPresences(presence->getFrom()); + } + } + } + + m_resources = currentResourcesCount; // Change legacy network presence diff --git a/src/usermanager.cpp b/src/usermanager.cpp index 6110c45e..f4d7867e 100644 --- a/src/usermanager.cpp +++ b/src/usermanager.cpp @@ -149,7 +149,7 @@ void UserManager::handlePresence(Swift::Presence::ref presence) { // Create user class if it's not there if (!user) { // Admin user is not legacy network user, so do not create User class instance for him - if (CONFIG_STRING(m_component->getConfig(), "service.admin_username") == presence->getFrom().getNode()) { + if (m_component->inServerMode() && CONFIG_STRING(m_component->getConfig(), "service.admin_jid") == presence->getFrom().toBare().toString()) { return; } @@ -319,6 +319,11 @@ void UserManager::handleSubscription(Swift::Presence::ref presence) { return; } + // Don't let RosterManager to handle presences for us + if (presence->getTo().getNode().empty()) { + return; + } + User *user = getUser(presence->getFrom().toBare().toString()); if (user) { diff --git a/src/userregistration.cpp b/src/userregistration.cpp index 7b05216f..646722ed 100644 --- a/src/userregistration.cpp +++ b/src/userregistration.cpp @@ -181,7 +181,7 @@ bool UserRegistration::handleGetRequest(const Swift::JID& from, const Swift::JID bool registered = m_storageBackend->getUser(barejid, res); std::string instructions = CONFIG_STRING(m_config, "registration.instructions"); - std::string usernameField = CONFIG_STRING(m_config, "registration.username_field"); + std::string usernameField = CONFIG_STRING(m_config, "registration.username_label"); // normal jabber:iq:register reg->setInstructions(instructions); @@ -219,20 +219,21 @@ bool UserRegistration::handleGetRequest(const Swift::JID& from, const Swift::JID ListSingleFormField::ref language = ListSingleFormField::create(); language->setName("language"); language->setLabel((("Language"))); + language->addOption(Swift::FormField::Option(CONFIG_STRING(m_config, "registration.language"), CONFIG_STRING(m_config, "registration.language"))); if (registered) language->setValue(res.language); else language->setValue(CONFIG_STRING(m_config, "registration.language")); form->addField(language); - TextSingleFormField::ref encoding = TextSingleFormField::create(); - encoding->setName("encoding"); - encoding->setLabel((("Encoding"))); - if (registered) - encoding->setValue(res.encoding); - else - encoding->setValue(CONFIG_STRING(m_config, "registration.encoding")); - form->addField(encoding); +// TextSingleFormField::ref encoding = TextSingleFormField::create(); +// encoding->setName("encoding"); +// encoding->setLabel((("Encoding"))); +// if (registered) +// encoding->setValue(res.encoding); +// else +// encoding->setValue(CONFIG_STRING(m_config, "registration.encoding")); +// form->addField(encoding); if (registered) { BooleanFormField::ref boolean = BooleanFormField::create(); @@ -328,10 +329,7 @@ bool UserRegistration::handleSetRequest(const Swift::JID& from, const Swift::JID } // Register or change password - if (payload->getUsername()->empty() || - (payload->getPassword()->empty() && CONFIG_STRING(m_config, "service.protocol") != "twitter" && CONFIG_STRING(m_config, "service.protocol") != "bonjour") - ) - { + if (payload->getUsername()->empty()) { sendError(from, id, ErrorPayload::NotAcceptable, ErrorPayload::Modify); return true; } diff --git a/src/util.cpp b/src/util.cpp index 322d06ad..0a5f3a04 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -75,6 +75,34 @@ void removeEverythingOlderThan(const std::vector &dirs, time_t t) { } } +std::string encryptPassword(const std::string &password, const std::string &key) { + std::string encrypted; + encrypted.resize(password.size()); + for (int i = 0; i < password.size(); i++) { + char c = password[i]; + char keychar = key[i % key.size()]; + c += keychar; + encrypted[i] = c; + } + + encrypted = Swift::Base64::encode(Swift::createByteArray(encrypted)); + return encrypted; +} + +std::string decryptPassword(std::string &encrypted, const std::string &key) { + encrypted = Swift::byteArrayToString(Swift::Base64::decode(encrypted)); + std::string password; + password.resize(encrypted.size()); + for (int i = 0; i < encrypted.size(); i++) { + char c = encrypted[i]; + char keychar = key[i % key.size()]; + c -= keychar; + password[i] = c; + } + + return password; +} + } } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt deleted file mode 100644 index 0688a10f..00000000 --- a/tests/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -ADD_SUBDIRECTORY(login) -ADD_SUBDIRECTORY(login_bad_name) -ADD_SUBDIRECTORY(login_bad_name2) - -add_custom_target(tests python runtests.py WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) diff --git a/tests/login/CMakeLists.txt b/tests/login/CMakeLists.txt deleted file mode 100644 index d153150f..00000000 --- a/tests/login/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -FILE(GLOB SRC *.cpp) - -ADD_EXECUTABLE(login_test ${SRC}) - - TARGET_LINK_LIBRARIES(login_test transport ${SWIFTEN_LIBRARIES}) - diff --git a/tests/login/main.cpp b/tests/login/main.cpp deleted file mode 100644 index 4ca5c246..00000000 --- a/tests/login/main.cpp +++ /dev/null @@ -1,44 +0,0 @@ -#include -#include - -#include -#include - -using namespace Swift; -using namespace boost; - -Client* client; - -static void handleDisconnected(const boost::optional &) { - exit(1); -} - -static void handleConnected() { - exit(0); -} - -static void handleMessageReceived(Message::ref message) { - // Echo back the incoming message - message->setTo(message->getFrom()); - message->setFrom(JID()); - client->sendMessage(message); -} - -int main(int, char **argv) { - SimpleEventLoop eventLoop; - BoostNetworkFactories networkFactories(&eventLoop); - - client = new Client(argv[1], argv[2], &networkFactories); - client->setAlwaysTrustCertificates(); - client->onConnected.connect(&handleConnected); - client->onDisconnected.connect(bind(&handleDisconnected, _1)); - client->onMessageReceived.connect(bind(&handleMessageReceived, _1)); - Swift::ClientOptions opt; - opt.allowPLAINWithoutTLS = true; - client->connect(opt); - - eventLoop.run(); - - delete client; - return 0; -} diff --git a/tests/login_bad_name/CMakeLists.txt b/tests/login_bad_name/CMakeLists.txt deleted file mode 100644 index 5536b9f7..00000000 --- a/tests/login_bad_name/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -FILE(GLOB SRC *.cpp) - -ADD_EXECUTABLE(login_bad_name_test ${SRC}) - -TARGET_LINK_LIBRARIES(login_bad_name_test transport ${SWIFTEN_LIBRARIES}) - diff --git a/tests/login_bad_name/main.cpp b/tests/login_bad_name/main.cpp deleted file mode 100644 index 652fb61a..00000000 --- a/tests/login_bad_name/main.cpp +++ /dev/null @@ -1,45 +0,0 @@ -#include -#include - -#include -#include - -using namespace Swift; -using namespace boost; - -Client* client; - -static void handleDisconnected(const boost::optional &error) { - exit(error->getType() != ClientError::AuthenticationFailedError); -} - -static void handleConnected() { - exit(1); -} - -static void handleMessageReceived(Message::ref message) { - // Echo back the incoming message - message->setTo(message->getFrom()); - message->setFrom(JID()); - client->sendMessage(message); -} - -int main(int, char **argv) { - SimpleEventLoop eventLoop; - BoostNetworkFactories networkFactories(&eventLoop); - - JID jid(JID(argv[1]).getNode() + "something", JID(argv[1]).getDomain()); - client = new Client(jid, argv[2], &networkFactories); - client->setAlwaysTrustCertificates(); - client->onConnected.connect(&handleConnected); - client->onDisconnected.connect(bind(&handleDisconnected, _1)); - client->onMessageReceived.connect(bind(&handleMessageReceived, _1)); - Swift::ClientOptions opt; - opt.allowPLAINWithoutTLS = true; - client->connect(opt); - - eventLoop.run(); - - delete client; - return 0; -} diff --git a/tests/login_bad_name2/.nolibircclient-qt b/tests/login_bad_name2/.nolibircclient-qt deleted file mode 100644 index e69de29b..00000000 diff --git a/tests/login_bad_name2/CMakeLists.txt b/tests/login_bad_name2/CMakeLists.txt deleted file mode 100644 index 41d4f73c..00000000 --- a/tests/login_bad_name2/CMakeLists.txt +++ /dev/null @@ -1,6 +0,0 @@ -FILE(GLOB SRC *.cpp) - -ADD_EXECUTABLE(login_bad_name2_test ${SRC}) - -TARGET_LINK_LIBRARIES(login_bad_name2_test transport ${SWIFTEN_LIBRARIES}) - diff --git a/tests/login_bad_name2/main.cpp b/tests/login_bad_name2/main.cpp deleted file mode 100644 index b2e232e5..00000000 --- a/tests/login_bad_name2/main.cpp +++ /dev/null @@ -1,45 +0,0 @@ -#include -#include - -#include -#include - -using namespace Swift; -using namespace boost; - -Client* client; - -static void handleDisconnected(const boost::optional &error) { - exit(error->getType() != ClientError::AuthenticationFailedError); -} - -static void handleConnected() { - exit(1); -} - -static void handleMessageReceived(Message::ref message) { - // Echo back the incoming message - message->setTo(message->getFrom()); - message->setFrom(JID()); - client->sendMessage(message); -} - -int main(int, char **argv) { - SimpleEventLoop eventLoop; - BoostNetworkFactories networkFactories(&eventLoop); - - JID jid(std::string("something") + JID(argv[1]).getNode(), JID(argv[1]).getDomain()); - client = new Client(jid, argv[2], &networkFactories); - client->setAlwaysTrustCertificates(); - client->onConnected.connect(&handleConnected); - client->onDisconnected.connect(bind(&handleDisconnected, _1)); - client->onMessageReceived.connect(bind(&handleMessageReceived, _1)); - Swift::ClientOptions opt; - opt.allowPLAINWithoutTLS = true; - client->connect(opt); - - eventLoop.run(); - - delete client; - return 0; -} diff --git a/tests/runtests.py b/tests/runtests.py deleted file mode 100644 index 391a1497..00000000 --- a/tests/runtests.py +++ /dev/null @@ -1,70 +0,0 @@ -import os -import sys -from subprocess import * -import time - -def run_spectrum(backend, test): - os.system("rm test.sql") - f = open("sample.cfg", "w") - f.write("\ - [service]\n\ - jid = localhost\n\ - password = secret\n\ - server = 127.0.0.1\n\ - port = 5222\n\ - server_mode = 1\n\ - backend=../backends/%s/%s_backend\n\ - protocol=prpl-jabber\n\ -\ - [database]\n\ - database = test.sql\n\ - prefix=icq\n\ - " % (backend, backend) - ) - f.close() - p = Popen("../spectrum/src/spectrum sample.cfg > " + backend + "_" + test + ".log 2>&1", shell=True) - time.sleep(4) - return p - -def one_test_run(): - os.system("killall spectrum 2> /dev/null") - os.system("rm *.log") - - for backend in os.listdir("../backends"): - if not os.path.isdir("../backends/" + backend) or backend == "CMakeFiles": - continue - - for d in os.listdir("."): - binary = d + "/" + d + "_test" - if not os.path.exists(binary): - continue - - if os.path.exists(d + "/.no" + backend): - continue - - p = run_spectrum(backend, d) - - if backend.find("purple") >= 0: - p = Popen(binary + " pyjim%jabber.cz@localhost test", shell=True) - else: - p = Popen(binary + " testnickname%irc.freenode.net@localhost test", shell=True) - - seconds = 0 - while p.poll() is None and seconds < 20: - time.sleep(1) - seconds += 1 - - if p.returncode == 0 and seconds < 20: - print "[ PASS ]", backend, binary - - else: - if seconds == 20: - print "[ TIME ]", backend, binary - else: - print "[ FAIL ]", backend, binary - - os.system("killall spectrum 2> /dev/null") - -one_test_run() - -