diff --git a/include/transport/Frontend.h b/include/transport/Frontend.h index bed99f2e602a737b0607aca085ae4292c559e427..c989bb26694a290d8adb79e8688001f7eb29885d 100644 --- a/include/transport/Frontend.h +++ b/include/transport/Frontend.h @@ -92,6 +92,7 @@ class Frontend { virtual void addRoomToRoomList(const std::string &handle, const std::string &name) = 0; virtual std::string setOAuth2Code(const std::string &code, const std::string &state) { return "OAuth2 code is not needed for this frontend."; } + virtual std::string getOAuth2URL(const std::vector &args) { return ""; } boost::signal onVCardRequired; boost::signal vcard)> onVCardUpdated; diff --git a/include/transport/HTTPRequest.h b/include/transport/HTTPRequest.h index 667651a2b5a910540cdd65ae4720fe0351e155e5..313dff2b0695f01cedc50e595996936148441fa4 100644 --- a/include/transport/HTTPRequest.h +++ b/include/transport/HTTPRequest.h @@ -1,20 +1,27 @@ -#ifndef HTTPREQ_H -#define HTTPREQ_H + +#pragma once #include "curl/curl.h" #include "transport/Logging.h" +#include "transport/ThreadPool.h" #include #include #include #include "rapidjson/document.h" +#include + namespace Transport { -class HTTPRequest { +class HTTPRequest : public Thread { public: - HTTPRequest(); + typedef enum { Get } Type; + typedef boost::function< void (HTTPRequest *, bool, rapidjson::Document &json, const std::string &data) > Callback; + + HTTPRequest(ThreadPool *tp, Type type, const std::string &url, Callback callback); + HTTPRequest(Type type, const std::string &url); - ~HTTPRequest() { + virtual ~HTTPRequest() { if(curlhandle) { curl_easy_cleanup(curlhandle); curlhandle = NULL; @@ -22,17 +29,32 @@ class HTTPRequest { } void setProxy(std::string, std::string, std::string, std::string); - bool GET(std::string url, std::string &output); - bool GET(std::string url, rapidjson::Document &json); - std::string getCurlError() {return std::string(curl_errorbuffer);} + bool execute(); + bool execute(rapidjson::Document &json); + std::string getError() {return std::string(curl_errorbuffer);} + + void run(); + void finalize(); + + boost::signal onRequestFinished; private: bool init(); + bool GET(std::string url, std::string &output); + bool GET(std::string url, rapidjson::Document &json); + CURL *curlhandle; char curl_errorbuffer[1024]; std::string error; std::string callbackdata; + ThreadPool *m_tp; + std::string m_url; + bool m_ok; + rapidjson::Document m_json; + std::string m_data; + Callback m_callback; + Type m_type; static int curlCallBack(char* data, size_t size, size_t nmemb, HTTPRequest *obj); @@ -40,4 +62,3 @@ class HTTPRequest { } -#endif diff --git a/include/transport/HTTPRequestQueue.h b/include/transport/HTTPRequestQueue.h new file mode 100644 index 0000000000000000000000000000000000000000..a5677dc94c809c89cf482dd878812df87d1f310a --- /dev/null +++ b/include/transport/HTTPRequestQueue.h @@ -0,0 +1,33 @@ + +#pragma once + +#include "curl/curl.h" +#include "transport/Logging.h" +#include "transport/ThreadPool.h" +#include +#include +#include +#include "rapidjson/document.h" + +namespace Transport { + +class HTTPRequest; + +class HTTPRequestQueue { + public: + HTTPRequestQueue(int delayBetweenRequests = 1); + + virtual ~HTTPRequestQueue(); + + void queueRequest(HTTPRequest *req); + + void sendNextRequest(); + + private: + int m_delay; + std::queue m_queue; + bool m_processing; +}; + +} + diff --git a/include/transport/MySQLBackend.h b/include/transport/MySQLBackend.h index ad0f50a1c49b2c29dda832ca27e2da7371d06042..5c9eb19770d9eedaa5c7345ba8b017ee2558627b 100644 --- a/include/transport/MySQLBackend.h +++ b/include/transport/MySQLBackend.h @@ -83,6 +83,8 @@ class MySQLBackend : public StorageBackend bool getOnlineUsers(std::vector &users); + bool getUsers(std::vector &users); + long addBuddy(long userId, const BuddyInfo &buddyInfo); void updateBuddy(long userId, const BuddyInfo &buddyInfo); @@ -156,6 +158,7 @@ class MySQLBackend : public StorageBackend Statement *m_getBuddiesSettings; Statement *m_setUserOnline; Statement *m_getOnlineUsers; + Statement *m_getUsers; }; } diff --git a/include/transport/OAuth2.h b/include/transport/OAuth2.h index 92c7c7a832f26bcf0a9c1fb3358b4e812508e904..c8612d17c1caa3cb8789c2a01f20e35d9ed667ca 100644 --- a/include/transport/OAuth2.h +++ b/include/transport/OAuth2.h @@ -35,7 +35,11 @@ class OAuth2 { std::string generateAuthURL(); - std::string handleOAuth2Code(const std::string &code, const std::string &state); + const std::string &getState() { + return m_state; + } + + std::string requestToken(const std::string &code, std::string &error); private: std::string m_clientId; diff --git a/include/transport/PQXXBackend.h b/include/transport/PQXXBackend.h index de7937cda2f385cfc36f669e330acd2d5cfdbe5d..89ce1e175351be39e871966f2350187afd57d095 100644 --- a/include/transport/PQXXBackend.h +++ b/include/transport/PQXXBackend.h @@ -83,6 +83,8 @@ class PQXXBackend : public StorageBackend bool getOnlineUsers(std::vector &users); + bool getUsers(std::vector &users); + long addBuddy(long userId, const BuddyInfo &buddyInfo); void updateBuddy(long userId, const BuddyInfo &buddyInfo); diff --git a/include/transport/SQLite3Backend.h b/include/transport/SQLite3Backend.h index 7e0dba53f37d848263bf591ca4f7a509b3cd8ea6..237b8c4075d6e5d0dc6acb6a9968bb7404963945 100644 --- a/include/transport/SQLite3Backend.h +++ b/include/transport/SQLite3Backend.h @@ -72,6 +72,8 @@ class SQLite3Backend : public StorageBackend bool getOnlineUsers(std::vector &users); + bool getUsers(std::vector &users); + /// Removes user and all connected data from database. /// \param id id of user - UserInfo.id /// \return true if user has been found in database and removed @@ -124,6 +126,7 @@ class SQLite3Backend : public StorageBackend sqlite3_stmt *m_getBuddiesSettings; sqlite3_stmt *m_setUserOnline; sqlite3_stmt *m_getOnlineUsers; + sqlite3_stmt *m_getUsers; }; } diff --git a/include/transport/StorageBackend.h b/include/transport/StorageBackend.h index a5d101be206572146936bdf8415b4a9690e1b76a..cf2f72386d80cb75e1481894e8b1a531109017c0 100644 --- a/include/transport/StorageBackend.h +++ b/include/transport/StorageBackend.h @@ -123,6 +123,8 @@ class StorageBackend /// getOnlineUsers virtual bool getOnlineUsers(std::vector &users) = 0; + virtual bool getUsers(std::vector &users) = 0; + virtual long addBuddy(long userId, const BuddyInfo &buddyInfo) = 0; virtual void updateBuddy(long userId, const BuddyInfo &buddyInfo) = 0; virtual void removeBuddy(long id) = 0; diff --git a/include/transport/ThreadPool.h b/include/transport/ThreadPool.h index 0758c232403da76851c0a2609e6b0814871f4d1a..95035b4d127d8736ce4790cb36e065fe2dd03db6 100644 --- a/include/transport/ThreadPool.h +++ b/include/transport/ThreadPool.h @@ -1,5 +1,4 @@ -#ifndef THREAD_POOL -#define THREAD_POOL +#pragma once #include #include @@ -8,6 +7,7 @@ #include #include "Swiften/EventLoop/EventLoop.h" +namespace Transport { /* * Thread serves as a base class for any code that has to be excuted as a thread @@ -70,4 +70,4 @@ class ThreadPool void releaseThread(int i); }; -#endif +} diff --git a/spectrum/src/frontends/slack/SlackAPI.cpp b/spectrum/src/frontends/slack/SlackAPI.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5f560f0ed702c9305b87b1f4a8c915edbe361684 --- /dev/null +++ b/spectrum/src/frontends/slack/SlackAPI.cpp @@ -0,0 +1,133 @@ +/** + * 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 "SlackAPI.h" +#include "SlackFrontend.h" +#include "SlackUser.h" +#include "SlackRTM.h" + +#include "transport/Transport.h" +#include "transport/HTTPRequest.h" +#include "transport/Util.h" + +#include +#include +#include +#include + +namespace Transport { + +DEFINE_LOGGER(logger, "SlackAPI"); + +SlackAPI::SlackAPI(Component *component, UserInfo uinfo) : m_uinfo(uinfo) { + m_component = component; +} + +SlackAPI::~SlackAPI() { +} + +void SlackAPI::handleSendMessage(HTTPRequest *req, bool ok, rapidjson::Document &resp, const std::string &data) { + LOG4CXX_INFO(logger, data); +} + +void SlackAPI::sendMessage(const std::string &from, const std::string &to, const std::string &text) { + std::string url = "https://slack.com/api/chat.postMessage?"; + url += "&username=" + Util::urlencode(from); + url += "&channel=" + Util::urlencode(to); + url += "&text=" + Util::urlencode(text); + url += "&token=" + Util::urlencode(m_uinfo.encoding); + + HTTPRequest *req = new HTTPRequest(THREAD_POOL(m_component), HTTPRequest::Get, url, + boost::bind(&SlackAPI::handleSendMessage, this, _1, _2, _3, _4)); + queueRequest(req); +} + +std::string SlackAPI::getChannelId(HTTPRequest *req, bool ok, rapidjson::Document &resp, const std::string &data) { + if (!ok) { + LOG4CXX_ERROR(logger, req->getError()); + LOG4CXX_ERROR(logger, data); + return ""; + } + + rapidjson::Value &channel = resp["channel"]; + if (!channel.IsObject()) { + LOG4CXX_ERROR(logger, "No 'channel' object in the reply."); + LOG4CXX_ERROR(logger, data); + return ""; + } + + rapidjson::Value &id = channel["id"]; + if (!id.IsString()) { + LOG4CXX_ERROR(logger, "No 'id' string in the reply."); + LOG4CXX_ERROR(logger, data); + return ""; + } + + return id.GetString(); +} + +void SlackAPI::imOpen(const std::string &uid, HTTPRequest::Callback callback) { + std::string url = "https://slack.com/api/im.open?user=" + Util::urlencode(uid) + "&token=" + Util::urlencode(m_uinfo.encoding); + HTTPRequest *req = new HTTPRequest(THREAD_POOL(m_component), HTTPRequest::Get, url, callback); + queueRequest(req); +} + +std::string SlackAPI::getOwnerId(HTTPRequest *req, bool ok, rapidjson::Document &resp, const std::string &data) { + if (!ok) { + LOG4CXX_ERROR(logger, req->getError()); + return ""; + } + + rapidjson::Value &members = resp["members"]; + if (!members.IsArray()) { + LOG4CXX_ERROR(logger, "No 'members' object in the reply."); + return ""; + } + + for (int i = 0; i < members.Size(); i++) { + if (!members[i].IsObject()) { + continue; + } + + rapidjson::Value &is_primary_owner = members[i]["is_primary_owner"]; + if (!is_primary_owner.IsBool()) { + continue; + } + + if (is_primary_owner.GetBool()) { + rapidjson::Value &name = members[i]["id"]; + if (!name.IsString()) { + LOG4CXX_ERROR(logger, "No 'name' string in the reply."); + return ""; + } + return name.GetString(); + } + } + + return ""; +} + +void SlackAPI::usersList(HTTPRequest::Callback callback) { + std::string url = "https://slack.com/api/users.list?presence=0&token=" + Util::urlencode(m_uinfo.encoding); + HTTPRequest *req = new HTTPRequest(THREAD_POOL(m_component), HTTPRequest::Get, url, callback); + queueRequest(req); +} + +} diff --git a/spectrum/src/frontends/slack/SlackAPI.h b/spectrum/src/frontends/slack/SlackAPI.h new file mode 100644 index 0000000000000000000000000000000000000000..7037938b21cf2d16257ab75f3bfb2a3d1e123df3 --- /dev/null +++ b/spectrum/src/frontends/slack/SlackAPI.h @@ -0,0 +1,63 @@ +/** + * Spectrum 2 Slack Frontend + * + * Copyright (C) 2015, 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 "transport/HTTPRequestQueue.h" +#include "transport/HTTPRequest.h" +#include "transport/StorageBackend.h" +#include "rapidjson/document.h" + +#include +#include +#include + +#include + +namespace Transport { + +class Component; +class StorageBackend; +class HTTPRequest; +class SlackRTM; + +class SlackAPI : public HTTPRequestQueue { + public: + SlackAPI(Component *component, UserInfo uinfo); + + virtual ~SlackAPI(); + + void usersList(HTTPRequest::Callback callback); + std::string getOwnerId(HTTPRequest *req, bool ok, rapidjson::Document &resp, const std::string &data); + + void imOpen(const std::string &uid, HTTPRequest::Callback callback); + std::string getChannelId(HTTPRequest *req, bool ok, rapidjson::Document &resp, const std::string &data); + + void sendMessage(const std::string &from, const std::string &to, const std::string &text); + + private: + void handleSendMessage(HTTPRequest *req, bool ok, rapidjson::Document &resp, const std::string &data); + + private: + Component *m_component; + UserInfo m_uinfo; +}; + +} diff --git a/spectrum/src/frontends/slack/SlackFrontend.cpp b/spectrum/src/frontends/slack/SlackFrontend.cpp index aae1452e62ebe8b265a38286e0991e4f3cd28c5e..4af86579d697230c5a4434ed5c377fb5087de5a2 100644 --- a/spectrum/src/frontends/slack/SlackFrontend.cpp +++ b/spectrum/src/frontends/slack/SlackFrontend.cpp @@ -29,7 +29,7 @@ #include "transport/Logging.h" #include "transport/Config.h" #include "transport/Transport.h" -#include "transport/OAuth2.h" +#include "transport/ThreadPool.h" #include #include @@ -53,18 +53,10 @@ void SlackFrontend::init(Component *transport, Swift::EventLoop *loop, Swift::Ne m_transport = transport; m_config = transport->getConfig(); m_jid = Swift::JID(CONFIG_STRING(m_config, "service.jid")); - - std::string redirect_url = "http://spectrum.im/slackoauth2/" + CONFIG_STRING(m_config, "service.jid"); - m_oauth2 = new OAuth2(CONFIG_STRING_DEFAULTED(m_config, "service.client_id",""), - CONFIG_STRING_DEFAULTED(m_config, "service.client_secret",""), - "https://slack.com/oauth/authorize", - "https://slack.com/api/oauth.access", - redirect_url, - "channels:read channels:write team:read"); + m_tp = new ThreadPool(loop, 10); } SlackFrontend::~SlackFrontend() { - delete m_oauth2; } void SlackFrontend::clearRoomList() { @@ -97,7 +89,7 @@ boost::shared_ptr SlackFrontend::sendCapabilitiesRequest(Swift } void SlackFrontend::reconnectUser(const std::string &user) { - + return static_cast(m_userManager)->reconnectUser(user); } RosterManager *SlackFrontend::createRosterManager(User *user, Component *component) { @@ -109,20 +101,22 @@ User *SlackFrontend::createUser(const Swift::JID &jid, UserInfo &userInfo, Compo } UserManager *SlackFrontend::createUserManager(Component *component, UserRegistry *userRegistry, StorageBackend *storageBackend) { - return new SlackUserManager(component, userRegistry, storageBackend); + m_userManager = new SlackUserManager(component, userRegistry, storageBackend); + return m_userManager; } void SlackFrontend::connectToServer() { - LOG4CXX_INFO(logger, "Connecting to Slack API server"); - - std::string url = m_oauth2->generateAuthURL(); - LOG4CXX_INFO(logger, url); + LOG4CXX_INFO(logger, "Started."); + m_transport->handleConnected(); } std::string SlackFrontend::setOAuth2Code(const std::string &code, const std::string &state) { - LOG4CXX_INFO(logger, "Using OAuth2 code " << code << " to get the authorization token"); - return m_oauth2->handleOAuth2Code(code, state); + return static_cast(m_userManager)->handleOAuth2Code(code, state); +} + +std::string SlackFrontend::getOAuth2URL(const std::vector &args) { + return static_cast(m_userManager)->getOAuth2URL(args); } void SlackFrontend::disconnectFromServer() { diff --git a/spectrum/src/frontends/slack/SlackFrontend.h b/spectrum/src/frontends/slack/SlackFrontend.h index a3a7544dc7b010c433d757482264c37303271199..c7a50d048419f1fb30b508008b373fa2f62e6f36 100644 --- a/spectrum/src/frontends/slack/SlackFrontend.h +++ b/spectrum/src/frontends/slack/SlackFrontend.h @@ -25,13 +25,15 @@ #include #include +#define THREAD_POOL(X) static_cast(X->getFrontend())->getThreadPool() + namespace Transport { class UserRegistry; class Frontend; class Config; class DiscoItemsResponder; class VCardResponder; - class OAuth2; + class ThreadPool; class SlackFrontend : public Frontend { public: @@ -64,13 +66,19 @@ namespace Transport { virtual void clearRoomList(); virtual void addRoomToRoomList(const std::string &handle, const std::string &name); virtual std::string setOAuth2Code(const std::string &code, const std::string &state); + virtual std::string getOAuth2URL(const std::vector &args); void handleMessage(boost::shared_ptr message); + ThreadPool *getThreadPool() { + return m_tp; + } + private: Config* m_config; Swift::JID m_jid; Component *m_transport; - OAuth2 *m_oauth2; + UserManager *m_userManager; + ThreadPool *m_tp; }; } diff --git a/spectrum/src/frontends/slack/SlackInstallation.cpp b/spectrum/src/frontends/slack/SlackInstallation.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6f3eae73c167bef3c868e4ba676ae20038684c2b --- /dev/null +++ b/spectrum/src/frontends/slack/SlackInstallation.cpp @@ -0,0 +1,80 @@ +/** + * 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 "SlackInstallation.h" +#include "SlackFrontend.h" +#include "SlackUser.h" +#include "SlackRTM.h" +#include "SlackAPI.h" + +#include "transport/Transport.h" +#include "transport/HTTPRequest.h" +#include "transport/Util.h" + +#include +#include +#include +#include + +namespace Transport { + +DEFINE_LOGGER(logger, "SlackInstallation"); + +SlackInstallation::SlackInstallation(Component *component, StorageBackend *storageBackend, UserInfo uinfo) : m_uinfo(uinfo) { + m_component = component; + m_storageBackend = storageBackend; + m_api = new SlackAPI(component, uinfo); + + + m_api->usersList(boost::bind(&SlackInstallation::handleUsersList, this, _1, _2, _3, _4)); +// m_rtm = new SlackRTM(component, storageBackend, uinfo); +} + +SlackInstallation::~SlackInstallation() { +// delete m_rtm; + delete m_api; +} + +void SlackInstallation::handleImOpen(HTTPRequest *req, bool ok, rapidjson::Document &resp, const std::string &data) { + std::string channel = m_api->getChannelId(req, ok, resp, data); + LOG4CXX_INFO(logger, "Opened channel with team owner: " << channel); + + std::string msg; + msg = "Hi, It seems you have authorized Spectrum 2 transport for your team. " + "As a team owner, you should now configure it. You should provide username and " + "password you want to use to connect your team to legacy network of your choice."; + m_api->sendMessage("Spectrum 2", channel, msg); + + msg = "You can do it by typing \".spectrum2 register \". Password may be optional."; + m_api->sendMessage("Spectrum 2", channel, msg); + + msg = "For example to connect the Freenode IRC network, just type \".spectrum2 register irc.freenode.net\"."; + m_api->sendMessage("Spectrum 2", channel, msg); +} + +void SlackInstallation::handleUsersList(HTTPRequest *req, bool ok, rapidjson::Document &resp, const std::string &data) { + std::string ownerId = m_api->getOwnerId(req, ok, resp, data); + LOG4CXX_INFO(logger, "Team owner ID is " << ownerId); + + m_api->imOpen(ownerId, boost::bind(&SlackInstallation::handleImOpen, this, _1, _2, _3, _4)); +} + + +} diff --git a/spectrum/src/frontends/slack/SlackInstallation.h b/spectrum/src/frontends/slack/SlackInstallation.h new file mode 100644 index 0000000000000000000000000000000000000000..73756d927e864b1fda867dd6e4c1784952c7a64f --- /dev/null +++ b/spectrum/src/frontends/slack/SlackInstallation.h @@ -0,0 +1,61 @@ +/** + * Spectrum 2 Slack Frontend + * + * Copyright (C) 2015, 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 "transport/StorageBackend.h" +#include "rapidjson/document.h" + +#include +#include +#include + +#include + +namespace Transport { + +class Component; +class StorageBackend; +class HTTPRequest; +class SlackRTM; +class SlackAPI; + +class SlackInstallation { + public: + SlackInstallation(Component *component, StorageBackend *storageBackend, UserInfo uinfo); + + virtual ~SlackInstallation(); + + boost::signal onInstallationDone; + + private: + void handleUsersList(HTTPRequest *req, bool ok, rapidjson::Document &resp, const std::string &data); + void handleImOpen(HTTPRequest *req, bool ok, rapidjson::Document &resp, const std::string &data); + + private: + Component *m_component; + StorageBackend *m_storageBackend; + UserInfo m_uinfo; + std::string m_ownerName; + SlackRTM *m_rtm; + SlackAPI *m_api; +}; + +} diff --git a/spectrum/src/frontends/slack/SlackRTM.cpp b/spectrum/src/frontends/slack/SlackRTM.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7e12f75475f4b889f865721c736331b2937a4496 --- /dev/null +++ b/spectrum/src/frontends/slack/SlackRTM.cpp @@ -0,0 +1,120 @@ +/** + * 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 "SlackRTM.h" +#include "SlackFrontend.h" +#include "SlackUser.h" + +#include "transport/Transport.h" +#include "transport/HTTPRequest.h" +#include "transport/Util.h" + +#include +#include +#include +#include + +namespace Transport { + +DEFINE_LOGGER(logger, "SlackRTM"); + +SlackRTM::SlackRTM(Component *component, StorageBackend *storageBackend, UserInfo uinfo) : m_uinfo(uinfo) { + m_component = component; + m_storageBackend = storageBackend; + + Swift::TLSOptions o; + Swift::PlatformTLSFactories *m_tlsFactory = new Swift::PlatformTLSFactories(); + m_tlsConnectionFactory = new Swift::TLSConnectionFactory(m_tlsFactory->getTLSContextFactory(), component->getNetworkFactories()->getConnectionFactory(), o); + + + std::string url = "https://slack.com/api/rtm.start?"; + url += "token=" + Util::urlencode(m_uinfo.encoding); + +// HTTPRequest *req = new HTTPRequest(); +// req->GET(THREAD_POOL(m_component), url, +// boost::bind(&SlackRTM::handleRTMStart, this, _1, _2, _3, _4)); +} + +SlackRTM::~SlackRTM() { + +} + +void SlackRTM::handleRTMStart(HTTPRequest *req, bool ok, rapidjson::Document &resp, const std::string &data) { + if (!ok) { + LOG4CXX_ERROR(logger, req->getError()); + LOG4CXX_ERROR(logger, data); + return; + } + + rapidjson::Value &url = resp["url"]; + if (!url.IsString()) { + LOG4CXX_ERROR(logger, "No 'url' object in the reply."); + LOG4CXX_ERROR(logger, data); + return; + } + + std::string u = url.GetString(); + LOG4CXX_INFO(logger, "Started RTM, WebSocket URL is " << u); + + u = u.substr(6); + m_host = u.substr(0, u.find("/")); + m_path = u.substr(u.find("/")); + + LOG4CXX_INFO(logger, "Starting DNS query for " << m_host << " " << m_path); + m_dnsQuery = m_component->getNetworkFactories()->getDomainNameResolver()->createAddressQuery(m_host); + m_dnsQuery->onResult.connect(boost::bind(&SlackRTM::handleDNSResult, this, _1, _2)); + m_dnsQuery->run(); +} + +void SlackRTM::handleDataRead(boost::shared_ptr data) { + LOG4CXX_INFO(logger, "data read"); + std::string d = Swift::safeByteArrayToString(*data); + LOG4CXX_INFO(logger, d); +} + +void SlackRTM::handleConnected(bool error) { + if (error) { + LOG4CXX_ERROR(logger, "Connection to " << m_host << " failed"); + return; + } + + LOG4CXX_INFO(logger, "Connected to " << m_host); + + std::string req = ""; + req += "GET " + m_path + " HTTP/1.1\r\n"; + req += "Host: " + m_host + ":443\r\n"; + req += "Upgrade: websocket\r\n"; + req += "Connection: Upgrade\r\n"; + req += "Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n"; + req += "Sec-WebSocket-Version: 13\r\n"; + req += "\r\n"; + + m_conn->write(Swift::createSafeByteArray(req)); + +} + +void SlackRTM::handleDNSResult(const std::vector &addrs, boost::optional) { + m_conn = m_tlsConnectionFactory->createConnection(); + m_conn->onDataRead.connect(boost::bind(&SlackRTM::handleDataRead, this, _1)); + m_conn->onConnectFinished.connect(boost::bind(&SlackRTM::handleConnected, this, _1)); + m_conn->connect(Swift::HostAddressPort(addrs[0], 443)); +} + +} diff --git a/spectrum/src/frontends/slack/SlackRTM.h b/spectrum/src/frontends/slack/SlackRTM.h new file mode 100644 index 0000000000000000000000000000000000000000..a6722ffbb16f8f2b59927ca8888ed624e8cb23c7 --- /dev/null +++ b/spectrum/src/frontends/slack/SlackRTM.h @@ -0,0 +1,71 @@ +/** + * Spectrum 2 Slack Frontend + * + * Copyright (C) 2015, 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 "transport/StorageBackend.h" +#include "rapidjson/document.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +namespace Transport { + +class Component; +class StorageBackend; +class HTTPRequest; + +class SlackRTM { + public: + SlackRTM(Component *component, StorageBackend *storageBackend, UserInfo uinfo); + + virtual ~SlackRTM(); + + private: + void handleDNSResult(const std::vector&, boost::optional); + void handleDataRead(boost::shared_ptr data); + void handleConnected(bool error); + void handleRTMStart(HTTPRequest *req, bool ok, rapidjson::Document &resp, const std::string &data); + + private: + Component *m_component; + StorageBackend *m_storageBackend; + UserInfo m_uinfo; + boost::shared_ptr m_dnsQuery; + boost::shared_ptr m_conn; + Swift::TLSConnectionFactory *m_tlsConnectionFactory; + std::string m_host; + std::string m_path; +}; + +} diff --git a/spectrum/src/frontends/slack/SlackUserManager.cpp b/spectrum/src/frontends/slack/SlackUserManager.cpp index 7d3e4cbe11dbe0835140495c8bf53057fd853dcd..0276560bfc546a04c23a342593d1038005ee64bb 100644 --- a/spectrum/src/frontends/slack/SlackUserManager.cpp +++ b/spectrum/src/frontends/slack/SlackUserManager.cpp @@ -21,6 +21,7 @@ #include "SlackUserManager.h" #include "SlackUserRegistration.h" #include "SlackFrontend.h" +#include "SlackInstallation.h" #include "transport/User.h" #include "transport/Transport.h" @@ -33,6 +34,7 @@ DEFINE_LOGGER(logger, "SlackUserManager"); SlackUserManager::SlackUserManager(Component *component, UserRegistry *userRegistry, StorageBackend *storageBackend) : UserManager(component, userRegistry, storageBackend) { m_component = component; + m_storageBackend = storageBackend; m_userRegistration = new SlackUserRegistration(component, this, storageBackend); } @@ -40,6 +42,30 @@ SlackUserManager::~SlackUserManager() { delete m_userRegistration; } +void SlackUserManager::reconnectUser(const std::string &user) { + UserInfo uinfo; + if (!m_storageBackend->getUser(user, uinfo)) { + LOG4CXX_ERROR(logger, "User " << user << " tried to reconnect, but he's not registered."); + return; + } + + if (!uinfo.uin.empty()) { + LOG4CXX_INFO(logger, "Reconnecting user " << user); + Swift::Presence::ref response = Swift::Presence::create(); + response->setTo(m_component->getJID()); + response->setFrom(user + "@" + m_component->getJID().toString()); + response->setType(Swift::Presence::Available); + } + else { + LOG4CXX_INFO(logger, "Cannot reconnect user " << user << "," + "because he does not have legacy network configured. " + "Continuing in Installation mode for this user until " + "he configures the legacy network."); + m_installations[user] = new SlackInstallation(m_component, m_storageBackend, uinfo); + m_installations[user]->onInstallationDone.connect(boost::bind(&SlackUserManager::reconnectUser, this, _1)); + } +} + void SlackUserManager::sendVCard(unsigned int id, Swift::VCard::ref vcard) { } @@ -49,4 +75,13 @@ UserRegistration *SlackUserManager::getUserRegistration() { return m_userRegistration; } +std::string SlackUserManager::handleOAuth2Code(const std::string &code, const std::string &state) { + return static_cast(m_userRegistration)->handleOAuth2Code(code, state); +} + +std::string SlackUserManager::getOAuth2URL(const std::vector &args) { + return static_cast(m_userRegistration)->createOAuth2URL(args); +} + + } diff --git a/spectrum/src/frontends/slack/SlackUserManager.h b/spectrum/src/frontends/slack/SlackUserManager.h index 61fafa0eeb1e17d4cfa68656257ae0cf84587bb3..afc8b2c7f7fb1021b4bd41d00af961b19cc20b38 100644 --- a/spectrum/src/frontends/slack/SlackUserManager.h +++ b/spectrum/src/frontends/slack/SlackUserManager.h @@ -39,6 +39,7 @@ class XMPPUserRegistration; class GatewayResponder; class AdHocManager; class SettingsAdHocCommandFactory; +class SlackInstallation; class SlackUserManager : public UserManager { public: @@ -46,13 +47,21 @@ class SlackUserManager : public UserManager { virtual ~SlackUserManager(); + void reconnectUser(const std::string &user); + virtual void sendVCard(unsigned int id, Swift::VCard::ref vcard); UserRegistration *getUserRegistration(); + std::string handleOAuth2Code(const std::string &code, const std::string &state); + + std::string getOAuth2URL(const std::vector &args); + private: Component *m_component; UserRegistration *m_userRegistration; + StorageBackend *m_storageBackend; + std::map m_installations; }; } diff --git a/spectrum/src/frontends/slack/SlackUserRegistration.cpp b/spectrum/src/frontends/slack/SlackUserRegistration.cpp index cd33b8a1d092a179d6f632b0911d9495d0caed85..4f34df74a629556a81e37b037db87a6e65f952f7 100644 --- a/spectrum/src/frontends/slack/SlackUserRegistration.cpp +++ b/spectrum/src/frontends/slack/SlackUserRegistration.cpp @@ -30,6 +30,11 @@ #include "transport/Logging.h" #include "transport/Buddy.h" #include "transport/Config.h" +#include "transport/OAuth2.h" +#include "transport/Util.h" +#include "transport/HTTPRequest.h" + +#include "rapidjson/document.h" #include #include @@ -49,9 +54,97 @@ SlackUserRegistration::SlackUserRegistration(Component *component, UserManager * m_config = m_component->getConfig(); m_storageBackend = storageBackend; m_userManager = userManager; + } SlackUserRegistration::~SlackUserRegistration(){ + +} + +std::string SlackUserRegistration::createOAuth2URL(const std::vector &args) { + std::string redirect_url = "http://slack.spectrum.im/auth/" + CONFIG_STRING(m_config, "service.jid"); + OAuth2 *oauth2 = new OAuth2(CONFIG_STRING_DEFAULTED(m_config, "service.client_id",""), + CONFIG_STRING_DEFAULTED(m_config, "service.client_secret",""), + "https://slack.com/oauth/authorize", + "https://slack.com/api/oauth.access", + redirect_url, + "channels:read channels:write team:read im:read im:write chat:write:bot"); + std::string url = oauth2->generateAuthURL(); + + m_auths[oauth2->getState()] = oauth2; + m_authsData[oauth2->getState()] = args; + + return url; +} + +std::string SlackUserRegistration::getTeamDomain(const std::string &token) { + std::string url = "https://slack.com/api/team.info?token=" + Util::urlencode(token); + + rapidjson::Document resp; + HTTPRequest req(HTTPRequest::Get, url); + if (!req.execute(resp)) { + LOG4CXX_ERROR(logger, req.getError()); + return ""; + } + + rapidjson::Value &team = resp["team"]; + if (!team.IsObject()) { + LOG4CXX_ERROR(logger, "No 'team' object in the reply."); + return ""; + } + + rapidjson::Value &domain = team["domain"]; + if (!domain.IsString()) { + LOG4CXX_ERROR(logger, "No 'domain' string in the reply."); + return ""; + } + + return domain.GetString(); +} + +std::string SlackUserRegistration::handleOAuth2Code(const std::string &code, const std::string &state) { + OAuth2 *oauth2 = NULL; + std::vector data; + if (m_auths.find(state) != m_auths.end()) { + oauth2 = m_auths[state]; + data = m_authsData[state]; + } + else { + return "Received state code '" + state + "' not found in state codes list."; + } + + std::string token; + std::string error = oauth2->requestToken(code, token); + if (!error.empty()) { + return error; + } + + UserInfo user; + user.jid = getTeamDomain(token); + user.uin = ""; + user.password = ""; + user.language = "en"; + user.encoding = token; // Use encoding as a token handler... it's BAD, but easy... + user.vip = 0; + user.id = 0; + registerUser(user); + + m_storageBackend->getUser(user.jid, user); + + std::string value = data[2]; + int type = (int) TYPE_STRING; + m_storageBackend->getUserSetting(user.id, "bot_token", type, value); + + LOG4CXX_INFO(logger, "Registered Slack user " << user.jid); + + m_auths.erase(state); + delete oauth2; + + m_authsData.erase(state); + + m_component->getFrontend()->reconnectUser(user.jid); + + return ""; } bool SlackUserRegistration::doUserRegistration(const UserInfo &row) { diff --git a/spectrum/src/frontends/slack/SlackUserRegistration.h b/spectrum/src/frontends/slack/SlackUserRegistration.h index 5b6e8b2901743e28346fb648f246cd941034c380..a0369f8bcf04e925bb8e407f1fd6d61b69137081 100644 --- a/spectrum/src/frontends/slack/SlackUserRegistration.h +++ b/spectrum/src/frontends/slack/SlackUserRegistration.h @@ -31,6 +31,7 @@ class Component; class StorageBackend; class UserManager; class Config; +class OAuth2; class SlackUserRegistration : public UserRegistration { public: @@ -38,6 +39,12 @@ class SlackUserRegistration : public UserRegistration { ~SlackUserRegistration(); + std::string createOAuth2URL(const std::vector &args); + + std::string getTeamDomain(const std::string &token); + + std::string handleOAuth2Code(const std::string &code, const std::string &state); + virtual bool doUserRegistration(const UserInfo &userInfo); virtual bool doUserUnregistration(const UserInfo &userInfo); @@ -47,6 +54,8 @@ class SlackUserRegistration : public UserRegistration { StorageBackend *m_storageBackend; UserManager *m_userManager; Config *m_config; + std::map m_auths; + std::map > m_authsData; }; diff --git a/src/AdminInterface.cpp b/src/AdminInterface.cpp index 1714a421618c37a1138c784baed86c8b09db1537..80bb4e75feadec890a9f85458e130d1921a0ff41 100644 --- a/src/AdminInterface.cpp +++ b/src/AdminInterface.cpp @@ -313,6 +313,18 @@ void AdminInterface::handleQuery(Swift::Message::ref message) { message->setBody("Bad argument count. See 'help'."); } } + else if (message->getBody().find("get_oauth2_url ") == 0) { + std::string body = message->getBody(); + std::vector args; + boost::split(args, body, boost::is_any_of(" ")); + if (args.size() == 3) { + std::string url = m_component->getFrontend()->getOAuth2URL(args); + message->setBody(url); + } + else { + message->setBody("Bad argument count. See 'help'."); + } + } else if (message->getBody().find("help") == 0) { std::string help; help += "General:\n"; diff --git a/src/Config.cpp b/src/Config.cpp index 5d328d54a1513d712928931ea09c19acbc8ec2cb..cea8408ba6e26aca095fac9d1057535bb65b7f97 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -103,6 +103,7 @@ bool Config::load(std::istream &ifs, boost::program_options::options_description ("service.jid_escaping", value()->default_value(true), "") ("service.vip_only", value()->default_value(false), "") ("service.vip_message", value()->default_value(""), "") + ("service.reconnect_all_users", value()->default_value(false), "") ("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.") diff --git a/src/HTTPRequest.cpp b/src/HTTPRequest.cpp index abaafd8cd120003aab4580243e00b92d948eead9..e63bf0ef206805790ecd6ef3c06f3596a98a7abf 100644 --- a/src/HTTPRequest.cpp +++ b/src/HTTPRequest.cpp @@ -4,7 +4,20 @@ namespace Transport { DEFINE_LOGGER(logger, "HTTPRequest") -HTTPRequest::HTTPRequest() : curlhandle(NULL) { +HTTPRequest::HTTPRequest(ThreadPool *tp, Type type, const std::string &url, Callback callback) { + m_type = type; + m_url = url; + m_tp = tp; + m_callback = callback; + + init(); +} + +HTTPRequest::HTTPRequest(Type type, const std::string &url) { + m_type = type; + m_url = url; + m_tp = NULL; + init(); } @@ -60,7 +73,7 @@ bool HTTPRequest::GET(std::string url, std::string &data) { /* Set http request and url */ curl_easy_setopt(curlhandle, CURLOPT_HTTPGET, 1); - curl_easy_setopt(curlhandle, CURLOPT_VERBOSE, 1); + curl_easy_setopt(curlhandle, CURLOPT_VERBOSE, 0); curl_easy_setopt(curlhandle, CURLOPT_URL, url.c_str()); /* Send http request and return status*/ @@ -76,18 +89,50 @@ bool HTTPRequest::GET(std::string url, std::string &data) { } bool HTTPRequest::GET(std::string url, rapidjson::Document &json) { - std::string data; - if (!GET(url, data)) { + if (!GET(url, m_data)) { return false; } - if(json.Parse<0>(data.c_str()).HasParseError()) { - LOG4CXX_ERROR(logger, "Error while parsing JSON") - LOG4CXX_ERROR(logger, data) + if(json.Parse<0>(m_data.c_str()).HasParseError()) { + LOG4CXX_ERROR(logger, "Error while parsing JSON"); + LOG4CXX_ERROR(logger, m_data); + strcpy(curl_errorbuffer, "Error while parsing JSON"); + return false; + } + + return true; +} + +void HTTPRequest::run() { + switch (m_type) { + case Get: + m_ok = GET(m_url, m_json); + break; + } +} + +void HTTPRequest::finalize() { + m_callback(this, m_ok, m_json, m_data); + onRequestFinished(); +} + +bool HTTPRequest::execute() { + if (!m_tp) { return false; } + m_tp->runAsThread(this); return true; } +bool HTTPRequest::execute(rapidjson::Document &json) { + switch (m_type) { + case Get: + m_ok = GET(m_url, json); + break; + } + + return m_ok; +} + } diff --git a/src/HTTPRequestQueue.cpp b/src/HTTPRequestQueue.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f886525eb344e49434e70376e7c54c1e8ca49388 --- /dev/null +++ b/src/HTTPRequestQueue.cpp @@ -0,0 +1,42 @@ +#include "transport/HTTPRequestQueue.h" +#include "transport/HTTPRequest.h" + +namespace Transport { + +DEFINE_LOGGER(logger, "HTTPRequestQueue") + +HTTPRequestQueue::HTTPRequestQueue(int delay) { + m_delay = delay; + m_processing = false; +} + +HTTPRequestQueue::~HTTPRequestQueue() { + +} + +void HTTPRequestQueue::sendNextRequest() { + if (m_queue.empty()) { + m_processing = false; + return; + } + + if (m_processing) { + return; + } + + HTTPRequest *req = m_queue.front(); + m_queue.pop(); + req->onRequestFinished.connect(boost::bind(&HTTPRequestQueue::sendNextRequest, this)); + req->execute(); +} + +void HTTPRequestQueue::queueRequest(HTTPRequest *req) { + m_queue.push(req); + + if (!m_processing) { + sendNextRequest(); + } +} + + +} diff --git a/src/MySQLBackend.cpp b/src/MySQLBackend.cpp index 590305cbb939ed03a048bc0440dafbb78a5d2ce2..da20d8922cf79e6316531cf8f82cbb518a1ae964 100644 --- a/src/MySQLBackend.cpp +++ b/src/MySQLBackend.cpp @@ -301,6 +301,7 @@ void MySQLBackend::disconnect() { delete m_getBuddySetting; delete m_setUserOnline; delete m_getOnlineUsers; + delete m_getUsers; mysql_close(&m_conn); } @@ -348,6 +349,7 @@ bool MySQLBackend::connect() { m_setUserOnline = new Statement(&m_conn, "bi", "UPDATE " + m_prefix + "users SET online=?, last_login=NOW() WHERE id=?"); m_getOnlineUsers = new Statement(&m_conn, "|s", "SELECT jid FROM " + m_prefix + "users WHERE online=1"); + m_getUsers = new Statement(&m_conn, "|s", "SELECT jid FROM " + m_prefix + "users"); return true; } @@ -482,6 +484,20 @@ bool MySQLBackend::getOnlineUsers(std::vector &users) { return true; } +bool MySQLBackend::getUsers(std::vector &users) { + EXEC(m_getUsers, getUsers(users)); + if (!exec_ok) + return false; + + std::string jid; + while (m_getUsers->fetch() == 0) { + *m_getUsers >> jid; + users.push_back(jid); + } + + return true; +} + long MySQLBackend::addBuddy(long userId, const BuddyInfo &buddyInfo) { // "INSERT INTO " + m_prefix + "buddies (user_id, uin, subscription, groups, nickname, flags) VALUES (?, ?, ?, ?, ?, ?)" std::string groups = StorageBackend::serializeGroups(buddyInfo.groups); diff --git a/src/OAuth2.cpp b/src/OAuth2.cpp index d0ceadb62e24ae2924eb71dda472480cde5c4bde..bdbb56a8abedc69e8f62965ce48b604940a72517 100644 --- a/src/OAuth2.cpp +++ b/src/OAuth2.cpp @@ -71,12 +71,7 @@ std::string OAuth2::generateAuthURL() { return url; } -std::string OAuth2::handleOAuth2Code(const std::string &code, const std::string &state) { - if (m_state != state) { - std::string error = "Received state code '" + state + "' does not sent state code '" + m_state + "'"; - return error; - } - +std::string OAuth2::requestToken(const std::string &code, std::string &token) { std::string url = m_tokenURL + "?"; url += "client_id=" + Util::urlencode(m_clientId); url += "&client_secret=" + Util::urlencode(m_clientSecret); @@ -86,12 +81,18 @@ std::string OAuth2::handleOAuth2Code(const std::string &code, const std::string url += "&redirect_uri=" + Util::urlencode(m_redirectURL); } - std::string data; - HTTPRequest req; - req.GET(url, data); + rapidjson::Document resp; + HTTPRequest req(HTTPRequest::Get, url); + if (!req.execute(resp)) { + return req.getError(); + } - LOG4CXX_INFO(logger, "handleOAuth2Code received token: " << data); + rapidjson::Value& access_token = resp["access_token"]; + if (!access_token.IsString()) { + return "No 'access_token' object in the reply."; + } + token = access_token.GetString(); return ""; } diff --git a/src/PQXXBackend.cpp b/src/PQXXBackend.cpp index 803d1dac9b0bec30ecb1a695cc554dbe46d3e6ad..3f167b0fd11aac9821c5c7504c605c7d02688e41 100644 --- a/src/PQXXBackend.cpp +++ b/src/PQXXBackend.cpp @@ -245,6 +245,23 @@ bool PQXXBackend::getOnlineUsers(std::vector &users) { return true; } +bool PQXXBackend::getUsers(std::vector &users) { + try { + pqxx::nontransaction txn(*m_conn); + pqxx::result r = txn.exec("SELECT jid FROM " + m_prefix + "users"); + + for (pqxx::result::const_iterator it = r.begin(); it != r.end(); it++) { + users.push_back((*it)[0].as()); + } + } + catch (std::exception& e) { + LOG4CXX_ERROR(logger, e.what()); + return false; + } + + return true; +} + long PQXXBackend::addBuddy(long userId, const BuddyInfo &buddyInfo) { try { pqxx::nontransaction txn(*m_conn); diff --git a/src/SQLite3Backend.cpp b/src/SQLite3Backend.cpp index 00c8b28f79d81672048fa44d900a0da8c736c6b1..6a1b907efbd96a0be1f1060324e35342ecc912fe 100644 --- a/src/SQLite3Backend.cpp +++ b/src/SQLite3Backend.cpp @@ -103,6 +103,7 @@ SQLite3Backend::~SQLite3Backend(){ FINALIZE_STMT(m_getBuddySetting); FINALIZE_STMT(m_setUserOnline); FINALIZE_STMT(m_getOnlineUsers); + FINALIZE_STMT(m_getUsers); sqlite3_close(m_db); } } @@ -143,6 +144,7 @@ bool SQLite3Backend::connect() { PREP_STMT(m_setUserOnline, "UPDATE " + m_prefix + "users SET online=?, last_login=DATETIME('NOW') WHERE id=?"); PREP_STMT(m_getOnlineUsers, "SELECT jid FROM " + m_prefix + "users WHERE online=1"); + PREP_STMT(m_getUsers, "SELECT jid FROM " + m_prefix + "users"); return true; } @@ -283,6 +285,23 @@ bool SQLite3Backend::getOnlineUsers(std::vector &users) { return true; } +bool SQLite3Backend::getUsers(std::vector &users) { + sqlite3_reset(m_getUsers); + + int ret; + while((ret = sqlite3_step(m_getUsers)) == SQLITE_ROW) { + std::string jid = (const char *) sqlite3_column_text(m_getUsers, 0); + users.push_back(jid); + } + + if (ret != SQLITE_DONE) { + LOG4CXX_ERROR(logger, "getUsers query"<< (sqlite3_errmsg(m_db) == NULL ? "" : sqlite3_errmsg(m_db))); + return false; + } + + return true; +} + long SQLite3Backend::addBuddy(long userId, const BuddyInfo &buddyInfo) { // "INSERT INTO " + m_prefix + "buddies (user_id, uin, subscription, groups, nickname, flags) VALUES (?, ?, ?, ?, ?, ?)" std::string groups = StorageBackend::serializeGroups(buddyInfo.groups); diff --git a/src/ThreadPool.cpp b/src/ThreadPool.cpp index 69c0fc748613e477eb8487bb4c7273ab733b049c..6a601eb423938a80171b8226b844580fe0b00848 100644 --- a/src/ThreadPool.cpp +++ b/src/ThreadPool.cpp @@ -1,6 +1,8 @@ #include "transport/ThreadPool.h" #include "transport/Logging.h" +namespace Transport { + DEFINE_LOGGER(logger, "ThreadPool") boost::signals2::signal< void (Thread*, int) > onWorkCompleted; @@ -123,3 +125,5 @@ void ThreadPool::runAsThread(Thread *t) requestQueue.push(t); } } + +} diff --git a/src/UsersReconnecter.cpp b/src/UsersReconnecter.cpp index 67adac038721bf2934e8af377f152d51fef104e4..ca5623637f92ff928d35ba6e1729dc41488b65d5 100644 --- a/src/UsersReconnecter.cpp +++ b/src/UsersReconnecter.cpp @@ -23,6 +23,7 @@ #include "transport/Transport.h" #include "transport/Logging.h" #include "transport/Frontend.h" +#include "transport/Config.h" #include #include @@ -72,7 +73,12 @@ void UsersReconnecter::handleConnected() { LOG4CXX_INFO(logger, "Starting UserReconnecter."); m_started = true; - m_storageBackend->getOnlineUsers(m_users); + if (CONFIG_BOOL_DEFAULTED(m_component->getConfig(), "service.reconnect_all_users", false)) { + m_storageBackend->getUsers(m_users); + } + else { + m_storageBackend->getOnlineUsers(m_users); + } reconnectNextUser(); }