From 892af5dc57c2ed20634723b0979944a14c35457e 2012-05-22 09:03:57 From: Vitaly Takmazov Date: 2012-05-22 09:03:57 Subject: [PATCH] Merge branch 'master' of https://github.com/hanzz/libtransport --- diff --git a/backends/CMakeLists.txt b/backends/CMakeLists.txt index 9cda44dc39cae14210ec8715f6fc31264cc099a4..761249d7cf982642cd824b02408d4c4fe4aa1ca1 100644 --- a/backends/CMakeLists.txt +++ b/backends/CMakeLists.txt @@ -7,6 +7,10 @@ if (PROTOBUF_FOUND) ADD_SUBDIRECTORY(libcommuni) endif() + ADD_SUBDIRECTORY(smstools3) + + ADD_SUBDIRECTORY(swiften) + ADD_SUBDIRECTORY(template) if (NOT WIN32) diff --git a/backends/libpurple/main.cpp b/backends/libpurple/main.cpp index d01c21388fa3d3d167db06446bbfe96e68dbdae3..e06fdb2bb159769b40d46ca58f46947e7cce2785 100644 --- a/backends/libpurple/main.cpp +++ b/backends/libpurple/main.cpp @@ -50,6 +50,21 @@ template std::string stringOf(T object) { return (os.str()); } +static std::vector &split(const std::string &s, char delim, std::vector &elems) { + std::stringstream ss(s); + std::string item; + while(std::getline(ss, item, delim)) { + elems.push_back(item); + } + return elems; +} + + +static std::vector split(const std::string &s, char delim) { + std::vector elems; + return split(s, delim, elems); +} + static void transportDataReceived(gpointer data, gint source, PurpleInputCondition cond); class SpectrumNetworkPlugin; @@ -296,6 +311,17 @@ class SpectrumNetworkPlugin : public NetworkPlugin { if (ret) { purple_account_set_int(account, "version", fromString(std::string(contents, length))); } + + + if (KEYFILE_STRING("service", "protocol") == "prpl-novell") { + std::string username(purple_account_get_username(account)); + std::vector u = split(username, '@'); + purple_account_set_username(account, (const char*) u.front().c_str()); + std::vector s = split(u.back(), ':'); + purple_account_set_string(account, "server", s.front().c_str()); + purple_account_set_int(account, "port", atoi(s.back().c_str())); + } + } void handleLoginRequest(const std::string &user, const std::string &legacyName, const std::string &password) { diff --git a/backends/swiften/CMakeLists.txt b/backends/swiften/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..d8861e057f001a5586a13424ba78569cc9d8aa95 --- /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 0000000000000000000000000000000000000000..642930fca39ba11b78acf76241d61a4d86f75967 --- /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/Updated 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/include/transport/config.h b/include/transport/config.h index 7d01eaa04e2c9a248d3b88b5aebd7b70dba93a0b..903480e953056b23a3106b3d4ee2ca7abb89a56c 100644 --- a/include/transport/config.h +++ b/include/transport/config.h @@ -28,13 +28,13 @@ #include #include - +#define CONFIG_HAS_KEY(PTR, KEY) (*PTR).hasKey(KEY) #define CONFIG_STRING(PTR, KEY) (*PTR)[KEY].as() #define CONFIG_INT(PTR, KEY) (*PTR)[KEY].as() #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) +#define CONFIG_VECTOR(PTR, KEY) ((*PTR).hasKey(KEY) ? (*PTR)[KEY].as >() : std::vector()) + namespace Transport { diff --git a/plugin/cpp/networkplugin.cpp b/plugin/cpp/networkplugin.cpp index c6d52eb7e6aa519a15e1f272e874be10a6388c5c..43d82653731d0f0810c56af488c11fb75aaa3ee9 100644 --- a/plugin/cpp/networkplugin.cpp +++ b/plugin/cpp/networkplugin.cpp @@ -26,6 +26,8 @@ #ifndef WIN32 #include +#include +#include #else #include #include diff --git a/spectrum/src/main.cpp b/spectrum/src/main.cpp index 6f7446d04f15a7192cec9199ffbfa191f2762cb8..abff2382dfab4b72e8ae554dca33079f36ab601e 100644 --- a/spectrum/src/main.cpp +++ b/spectrum/src/main.cpp @@ -148,7 +148,7 @@ int main(int argc, char **argv) boost::program_options::positional_options_description p; p.add("config", -1); boost::program_options::store(boost::program_options::command_line_parser(argc, argv). - options(desc).positional(p).run(), vm); + options(desc).positional(p).allow_unregistered().run(), vm); boost::program_options::notify(vm); if (vm.count("version")) { diff --git a/spectrum/src/sample.cfg b/spectrum/src/sample.cfg index 9097e3b42ebe2a37ef9a0542f6166dcbf54b8d51..4409be640ca1093f577eb86b35438e984ff675c4 100644 --- a/spectrum/src/sample.cfg +++ b/spectrum/src/sample.cfg @@ -13,10 +13,10 @@ 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 +#protocol=prpl-jabber +protocol=prpl-msn #protocol=any #protocol=prpl-icq working_dir=./ diff --git a/spectrum_manager/src/main.cpp b/spectrum_manager/src/main.cpp index 1229cdb1165c5dfe9f3b1dc13652a5fbbb840d7b..0ab409801ef99b67c5e517f6373b9d8411de3e0d 100644 --- a/spectrum_manager/src/main.cpp +++ b/spectrum_manager/src/main.cpp @@ -308,10 +308,12 @@ static void ask_local_servers(ManagerConfig *config, Swift::BoostNetworkFactorie Config cfg; if (cfg.load(itr->path().string()) == false) { std::cerr << "Can't load config file " << itr->path().string() << ". Skipping...\n"; + continue; } if (CONFIG_VECTOR(&cfg, "service.admin_jid").empty() || CONFIG_STRING(&cfg, "service.admin_password").empty()) { std::cerr << itr->path().string() << ": service.admin_jid or service.admin_password empty. This server can't be queried over XMPP.\n"; + continue; } finished++; diff --git a/spectrum_manager/src/managerconfig.h b/spectrum_manager/src/managerconfig.h index 933843c383e0344839ffbe515c5588e55aeae765..48ca15b81007b64ede87466f51fa740d7ce50051 100644 --- a/spectrum_manager/src/managerconfig.h +++ b/spectrum_manager/src/managerconfig.h @@ -68,6 +68,10 @@ class ManagerConfig { return m_variables[key]; } + bool hasKey(const std::string &key) { + return m_variables.find(key) != m_variables.end(); + } + /// Returns path to config file from which data were loaded. const std::string &getManagerConfigFile() { return m_file; } diff --git a/src/config.cpp b/src/config.cpp index d123c7b080a0d6fb97b52abfff862d7296c56292..d33d7d514b8581ed70ca37debbf88bfae68846b4 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -124,6 +124,13 @@ bool Config::load(std::istream &ifs, boost::program_options::options_description ("backend.no_vcard_fetch", value()->default_value(false), "True if VCards for buddies should not be fetched. Only avatars will be forwarded.") ; + // Load configs passed by command line + if (m_argc != 0 && m_argv) { + basic_command_line_parser parser = command_line_parser(m_argc, m_argv).options(opts).allow_unregistered(); + parsed_options parsed = parser.run(); + store(parsed, m_variables); + } + parsed_options parsed = parse_config_file(ifs, opts, true); bool found_working = false; @@ -189,11 +196,6 @@ bool Config::load(std::istream &ifs, boost::program_options::options_description } } - // Load configs passed by command line - if (m_argc != 0 && m_argv) { - store(parse_command_line(m_argc, m_argv, opts), m_variables); - } - store(parsed, m_variables); notify(m_variables); diff --git a/src/userregistration.cpp b/src/userregistration.cpp index 2be05fb24cb315fae538fb8e02882e4a22616c51..7d083c2e602a910c22bd436e0ef6e7dcac539fca 100644 --- a/src/userregistration.cpp +++ b/src/userregistration.cpp @@ -189,7 +189,7 @@ bool UserRegistration::handleGetRequest(const Swift::JID& from, const Swift::JID reg->setRegistered(registered); reg->setUsername(res.uin); if (CONFIG_STRING(m_config, "service.protocol") != "twitter" && CONFIG_STRING(m_config, "service.protocol") != "bonjour") - reg->setPassword(res.password); + reg->setPassword(""); // form