From d6cad830d2249494445ed5f9aeef3a22068dbbbb Mon Sep 17 00:00:00 2001 From: HanzZ Date: Sun, 20 May 2012 11:21:17 +0200 Subject: [PATCH] Added Swiften backend --- backends/CMakeLists.txt | 2 + backends/swiften/CMakeLists.txt | 10 ++ backends/swiften/main.cpp | 301 ++++++++++++++++++++++++++++++++ spectrum/src/sample.cfg | 2 +- 4 files changed, 314 insertions(+), 1 deletion(-) create mode 100644 backends/swiften/CMakeLists.txt create mode 100644 backends/swiften/main.cpp diff --git a/backends/CMakeLists.txt b/backends/CMakeLists.txt index cdb2696f..19475c60 100644 --- a/backends/CMakeLists.txt +++ b/backends/CMakeLists.txt @@ -9,6 +9,8 @@ if (PROTOBUF_FOUND) ADD_SUBDIRECTORY(smstools3) + ADD_SUBDIRECTORY(swiften) + ADD_SUBDIRECTORY(template) if (NOT WIN32) diff --git a/backends/swiften/CMakeLists.txt b/backends/swiften/CMakeLists.txt new file mode 100644 index 00000000..d8861e05 --- /dev/null +++ b/backends/swiften/CMakeLists.txt @@ -0,0 +1,10 @@ +cmake_minimum_required(VERSION 2.6) + +FILE(GLOB SRC *.cpp) + +ADD_EXECUTABLE(spectrum2_swiften_backend ${SRC}) + +target_link_libraries(spectrum2_swiften_backend transport pthread ${Boost_LIBRARIES} ${SWIFTEN_LIBRARY} ${LOG4CXX_LIBRARIES}) + +INSTALL(TARGETS spectrum2_swiften_backend RUNTIME DESTINATION bin) + diff --git a/backends/swiften/main.cpp b/backends/swiften/main.cpp new file mode 100644 index 00000000..cedfd42e --- /dev/null +++ b/backends/swiften/main.cpp @@ -0,0 +1,301 @@ +// Transport includes +#include "transport/config.h" +#include "transport/networkplugin.h" +#include "transport/logging.h" + +// Swiften +#include "Swiften/Swiften.h" + +// for signal handler +#include "unistd.h" +#include "signal.h" +#include "sys/wait.h" +#include "sys/signal.h" + +// malloc_trim +#include "malloc.h" + +// Boost +#include +using namespace boost::filesystem; +using namespace boost::program_options; +using namespace Transport; + +DEFINE_LOGGER(logger, "Swiften"); + +// eventloop +Swift::SimpleEventLoop *loop_; + +// Plugins +class SwiftenPlugin; +SwiftenPlugin *np = NULL; + +class SwiftenPlugin : public NetworkPlugin { + public: + Swift::BoostNetworkFactories *m_factories; + Swift::BoostIOServiceThread m_boostIOServiceThread; + boost::shared_ptr m_conn; + + SwiftenPlugin(Config *config, Swift::SimpleEventLoop *loop, const std::string &host, int port) : NetworkPlugin() { + this->config = config; + m_factories = new Swift::BoostNetworkFactories(loop); + m_conn = m_factories->getConnectionFactory()->createConnection(); + m_conn->onDataRead.connect(boost::bind(&SwiftenPlugin::_handleDataRead, this, _1)); + m_conn->connect(Swift::HostAddressPort(Swift::HostAddress(host), port)); + + LOG4CXX_INFO(logger, "Starting the plugin."); + } + + // NetworkPlugin uses this method to send the data to networkplugin server + void sendData(const std::string &string) { + m_conn->write(Swift::createSafeByteArray(string)); + } + + // This method has to call handleDataRead with all received data from network plugin server + void _handleDataRead(boost::shared_ptr data) { + std::string d(data->begin(), data->end()); + handleDataRead(d); + } + + void handleSwiftDisconnected(const std::string &user, const boost::optional &error) { + std::string message = ""; + if (error) { + switch(error->getType()) { + case Swift::ClientError::UnknownError: message = ("Unknown Error"); break; + case Swift::ClientError::DomainNameResolveError: message = ("Unable to find server"); break; + case Swift::ClientError::ConnectionError: message = ("Error connecting to server"); break; + case Swift::ClientError::ConnectionReadError: message = ("Error while receiving server data"); break; + case Swift::ClientError::ConnectionWriteError: message = ("Error while sending data to the server"); break; + case Swift::ClientError::XMLError: message = ("Error parsing server data"); break; + case Swift::ClientError::AuthenticationFailedError: message = ("Login/password invalid"); break; + case Swift::ClientError::CompressionFailedError: message = ("Error while compressing stream"); break; + case Swift::ClientError::ServerVerificationFailedError: message = ("Server verification failed"); break; + case Swift::ClientError::NoSupportedAuthMechanismsError: message = ("Authentication mechanisms not supported"); break; + case Swift::ClientError::UnexpectedElementError: message = ("Unexpected response"); break; + case Swift::ClientError::ResourceBindError: message = ("Error binding resource"); break; + case Swift::ClientError::SessionStartError: message = ("Error starting session"); break; + case Swift::ClientError::StreamError: message = ("Stream error"); break; + case Swift::ClientError::TLSError: message = ("Encryption error"); break; + case Swift::ClientError::ClientCertificateLoadError: message = ("Error loading certificate (Invalid password?)"); break; + case Swift::ClientError::ClientCertificateError: message = ("Certificate not authorized"); break; + + case Swift::ClientError::UnknownCertificateError: message = ("Unknown certificate"); break; + case Swift::ClientError::CertificateExpiredError: message = ("Certificate has expired"); break; + case Swift::ClientError::CertificateNotYetValidError: message = ("Certificate is not yet valid"); break; + case Swift::ClientError::CertificateSelfSignedError: message = ("Certificate is self-signed"); break; + case Swift::ClientError::CertificateRejectedError: message = ("Certificate has been rejected"); break; + case Swift::ClientError::CertificateUntrustedError: message = ("Certificate is not trusted"); break; + case Swift::ClientError::InvalidCertificatePurposeError: message = ("Certificate cannot be used for encrypting your connection"); break; + case Swift::ClientError::CertificatePathLengthExceededError: message = ("Certificate path length constraint exceeded"); break; + case Swift::ClientError::InvalidCertificateSignatureError: message = ("Invalid certificate signature"); break; + case Swift::ClientError::InvalidCAError: message = ("Invalid Certificate Authority"); break; + case Swift::ClientError::InvalidServerIdentityError: message = ("Certificate does not match the host identity"); break; + } + } + LOG4CXX_INFO(logger, user << ": Disconnected " << message); + handleDisconnected(user, 3, message); + + boost::shared_ptr client = m_users[user]; + if (client) { + client->onConnected.disconnect(boost::bind(&SwiftenPlugin::handleSwiftConnected, this, user)); + client->onDisconnected.disconnect(boost::bind(&SwiftenPlugin::handleSwiftDisconnected, this, user, _1)); + client->onMessageReceived.disconnect(boost::bind(&SwiftenPlugin::handleSwiftMessageReceived, this, user, _1)); + m_users.erase(user); + } + +#ifndef WIN32 + // force returning of memory chunks allocated by libxml2 to kernel + malloc_trim(0); +#endif + } + + void handleSwiftConnected(const std::string &user) { + handleConnected(user); + m_users[user]->requestRoster(); + Swift::Presence::ref response = Swift::Presence::create(); + response->setFrom(m_users[user]->getJID()); + m_users[user]->sendPresence(response); + } + + void handleSwiftRosterReceived(const std::string &user) { + Swift::PresenceOracle *oracle = m_users[user]->getPresenceOracle(); + BOOST_FOREACH(const Swift::XMPPRosterItem &item, m_users[user]->getRoster()->getItems()) { + Swift::Presence::ref lastPresence = oracle->getLastPresence(item.getJID()); + pbnetwork::StatusType status = lastPresence ? ((pbnetwork::StatusType) lastPresence->getShow()) : pbnetwork::STATUS_NONE; + handleBuddyChanged(user, item.getJID().toBare().toString(), + item.getName(), item.getGroups(), status); + } + } + + void handleSwiftPresenceChanged(const std::string &user, Swift::Presence::ref presence) { + LOG4CXX_INFO(logger, user << ": " << presence->getFrom().toBare().toString() << " presence changed"); + + std::string message = presence->getStatus(); + std::string photo = ""; + + boost::shared_ptr update = presence->getPayload(); + if (update) { + photo = update->getPhotoHash(); + } + + boost::optional item = m_users[user]->getRoster()->getItem(presence->getFrom()); + if (item) { + handleBuddyChanged(user, presence->getFrom().toBare().toString(), item->getName(), item->getGroups(), (pbnetwork::StatusType) presence->getShow(), message, photo); + } + else { + std::vector groups; + handleBuddyChanged(user, presence->getFrom().toBare().toString(), presence->getFrom().toBare(), groups, (pbnetwork::StatusType) presence->getShow(), message, photo); + } + } + + void handleSwiftMessageReceived(const std::string &user, Swift::Message::ref message) { + std::string body = message->getBody(); + boost::shared_ptr client = m_users[user]; + if (client) { + handleMessage(user, message->getFrom().toBare().toString(), body, "", ""); + } + } + + void handleSwiftVCardReceived(const std::string &user, unsigned int id, Swift::VCard::ref vcard, Swift::ErrorPayload::ref error) { + if (error || !vcard) { + LOG4CXX_INFO(logger, user << ": error fetching VCard with id=" << id); + handleVCard(user, id, "", "", "", ""); + return; + } + LOG4CXX_INFO(logger, user << ": VCard fetched - id=" << id); + std::string photo((const char *)&vcard->getPhoto()[0], vcard->getPhoto().size()); + handleVCard(user, id, vcard->getFullName(), vcard->getFullName(), vcard->getNickname(), photo); + } + + void handleLoginRequest(const std::string &user, const std::string &legacyName, const std::string &password) { + LOG4CXX_INFO(logger, user << ": connecting as " << legacyName); + boost::shared_ptr client = boost::make_shared(Swift::JID(legacyName), password, m_factories); + m_users[user] = client; + client->setAlwaysTrustCertificates(); + client->onConnected.connect(boost::bind(&SwiftenPlugin::handleSwiftConnected, this, user)); + client->onDisconnected.connect(boost::bind(&SwiftenPlugin::handleSwiftDisconnected, this, user, _1)); + client->onMessageReceived.connect(boost::bind(&SwiftenPlugin::handleSwiftMessageReceived, this, user, _1)); + client->getRoster()->onInitialRosterPopulated.connect(boost::bind(&SwiftenPlugin::handleSwiftRosterReceived, this, user)); + client->getPresenceOracle()->onPresenceChange.connect(boost::bind(&SwiftenPlugin::handleSwiftPresenceChanged, this, user, _1)); + Swift::ClientOptions opt; + opt.allowPLAINWithoutTLS = true; + client->connect(opt); + } + + void handleLogoutRequest(const std::string &user, const std::string &legacyName) { + boost::shared_ptr client = m_users[user]; + if (client) { + client->onConnected.disconnect(boost::bind(&SwiftenPlugin::handleSwiftConnected, this, user)); +// client->onDisconnected.disconnect(boost::bind(&SwiftenPlugin::handleSwiftDisconnected, this, user, _1)); + client->onMessageReceived.disconnect(boost::bind(&SwiftenPlugin::handleSwiftMessageReceived, this, user, _1)); + client->getRoster()->onInitialRosterPopulated.disconnect(boost::bind(&SwiftenPlugin::handleSwiftRosterReceived, this, user)); + client->getPresenceOracle()->onPresenceChange.disconnect(boost::bind(&SwiftenPlugin::handleSwiftPresenceChanged, this, user, _1)); + client->disconnect(); + } + } + + void handleMessageSendRequest(const std::string &user, const std::string &legacyName, const std::string &msg, const std::string &xhtml = "") { + LOG4CXX_INFO(logger, "Sending message from " << user << " to " << legacyName << "."); + boost::shared_ptr client = m_users[user]; + if (client) { + boost::shared_ptr message(new Swift::Message()); + message->setTo(Swift::JID(legacyName)); + message->setFrom(client->getJID()); + message->setBody(msg); + + client->sendMessage(message); + } + } + + void handleVCardRequest(const std::string &user, const std::string &legacyName, unsigned int id) { + boost::shared_ptr client = m_users[user]; + if (client) { + LOG4CXX_INFO(logger, user << ": fetching VCard of " << legacyName << " id=" << id); + Swift::GetVCardRequest::ref request = Swift::GetVCardRequest::create(Swift::JID(legacyName), client->getIQRouter()); + request->onResponse.connect(boost::bind(&SwiftenPlugin::handleSwiftVCardReceived, this, user, id, _1, _2)); + request->send(); + } + } + + void handleBuddyUpdatedRequest(const std::string &user, const std::string &buddyName, const std::string &alias, const std::vector &groups) { +// LOG4CXX_INFO(logger, user << ": Added buddy " << buddyName << "."); +// handleBuddyChanged(user, buddyName, alias, groups, pbnetwork::STATUS_ONLINE); + } + + void handleBuddyRemovedRequest(const std::string &user, const std::string &buddyName, const std::vector &groups) { + + } + + private: + Config *config; + std::map > m_users; +}; + +static void spectrum_sigchld_handler(int sig) +{ + int status; + pid_t pid; + + do { + pid = waitpid(-1, &status, WNOHANG); + } while (pid != 0 && pid != (pid_t)-1); + + if ((pid == (pid_t) - 1) && (errno != ECHILD)) { + char errmsg[BUFSIZ]; + snprintf(errmsg, BUFSIZ, "Warning: waitpid() returned %d", pid); + perror(errmsg); + } +} + + +int main (int argc, char* argv[]) { + std::string host; + int port; + + if (signal(SIGCHLD, spectrum_sigchld_handler) == SIG_ERR) { + std::cout << "SIGCHLD handler can't be set\n"; + return -1; + } + + boost::program_options::options_description desc("Usage: spectrum [OPTIONS] \nAllowed options"); + desc.add_options() + ("host,h", value(&host), "host") + ("port,p", value(&port), "port") + ; + try + { + boost::program_options::variables_map vm; + boost::program_options::store(boost::program_options::parse_command_line(argc, argv, desc), vm); + boost::program_options::notify(vm); + } + catch (std::runtime_error& e) + { + std::cout << desc << "\n"; + exit(1); + } + catch (...) + { + std::cout << desc << "\n"; + exit(1); + } + + + if (argc < 5) { + return 1; + } + + Config config; + if (!config.load(argv[5])) { + std::cerr << "Can't open " << argv[1] << " configuration file.\n"; + return 1; + } + + Logging::initBackendLogging(&config); + + Swift::SimpleEventLoop eventLoop; + loop_ = &eventLoop; + np = new SwiftenPlugin(&config, &eventLoop, host, port); + loop_->run(); + + return 0; +} diff --git a/spectrum/src/sample.cfg b/spectrum/src/sample.cfg index f872dc90..4409be64 100644 --- a/spectrum/src/sample.cfg +++ b/spectrum/src/sample.cfg @@ -13,7 +13,7 @@ admin_password=test #cert=server.pfx #patch to PKCS#12 certificate #cert_password=test #password to that certificate if any users_per_backend=10 -backend=../..//backends/libpurple/spectrum2_libpurple_backend +backend=../..//backends/swiften/spectrum2_swiften_backend #backend=../../backends/template/template_backend.py #protocol=prpl-jabber protocol=prpl-msn