Changeset - 5adb3d1f9733
[Not reviewed]
include/transport/Frontend.h
Show inline comments
 
@@ -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<std::string> &args) { return ""; }
 

	
 
		boost::signal<void (User *, const std::string &name, unsigned int id)> onVCardRequired;
 
		boost::signal<void (User *, boost::shared_ptr<Swift::VCard> vcard)> onVCardUpdated;
include/transport/HTTPRequest.h
Show inline comments
 
#ifndef HTTPREQ_H
 
#define HTTPREQ_H
 

	
 
#pragma once
 

	
 
#include "curl/curl.h"
 
#include "transport/Logging.h"
 
#include "transport/ThreadPool.h"
 
#include <iostream>
 
#include <sstream>
 
#include <string.h>
 
#include "rapidjson/document.h"
 

	
 
#include <boost/signal.hpp>
 

	
 
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<void ()> 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
include/transport/HTTPRequestQueue.h
Show inline comments
 
new file 100644
 

	
 
#pragma once
 

	
 
#include "curl/curl.h"
 
#include "transport/Logging.h"
 
#include "transport/ThreadPool.h"
 
#include <iostream>
 
#include <sstream>
 
#include <string.h>
 
#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<HTTPRequest *> m_queue;
 
		bool m_processing;
 
};
 

	
 
}
 

	
include/transport/MySQLBackend.h
Show inline comments
 
@@ -83,6 +83,8 @@ class MySQLBackend : public StorageBackend
 

	
 
		bool getOnlineUsers(std::vector<std::string> &users);
 

	
 
		bool getUsers(std::vector<std::string> &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;
 
};
 

	
 
}
include/transport/OAuth2.h
Show inline comments
 
@@ -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;
include/transport/PQXXBackend.h
Show inline comments
 
@@ -83,6 +83,8 @@ class PQXXBackend : public StorageBackend
 

	
 
		bool getOnlineUsers(std::vector<std::string> &users);
 

	
 
		bool getUsers(std::vector<std::string> &users);
 

	
 
		long addBuddy(long userId, const BuddyInfo &buddyInfo);
 

	
 
		void updateBuddy(long userId, const BuddyInfo &buddyInfo);
include/transport/SQLite3Backend.h
Show inline comments
 
@@ -72,6 +72,8 @@ class SQLite3Backend : public StorageBackend
 

	
 
		bool getOnlineUsers(std::vector<std::string> &users);
 

	
 
		bool getUsers(std::vector<std::string> &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;
 
};
 

	
 
}
include/transport/StorageBackend.h
Show inline comments
 
@@ -123,6 +123,8 @@ class StorageBackend
 
		/// getOnlineUsers
 
		virtual bool getOnlineUsers(std::vector<std::string> &users) = 0;
 

	
 
		virtual bool getUsers(std::vector<std::string> &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;
include/transport/ThreadPool.h
Show inline comments
 
#ifndef THREAD_POOL
 
#define THREAD_POOL
 
#pragma once 
 

	
 
#include <boost/thread.hpp>
 
#include <boost/thread/mutex.hpp>
 
@@ -8,6 +7,7 @@
 
#include <iostream>
 
#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
 
}
spectrum/src/frontends/slack/SlackAPI.cpp
Show inline comments
 
new file 100644
 
/**
 
 * XMPP - libpurple transport
 
 *
 
 * Copyright (C) 2009, Jan Kaluza <hanzz@soc.pidgin.im>
 
 *
 
 * 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 <boost/foreach.hpp>
 
#include <boost/make_shared.hpp>
 
#include <map>
 
#include <iterator>
 

	
 
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);
 
}
 

	
 
}
spectrum/src/frontends/slack/SlackAPI.h
Show inline comments
 
new file 100644
 
/**
 
 * Spectrum 2 Slack Frontend
 
 *
 
 * Copyright (C) 2015, Jan Kaluza <hanzz.k@gmail.com>
 
 *
 
 * 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 <string>
 
#include <algorithm>
 
#include <map>
 

	
 
#include <boost/signal.hpp>
 

	
 
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;
 
};
 

	
 
}
spectrum/src/frontends/slack/SlackFrontend.cpp
Show inline comments
 
@@ -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 <boost/bind.hpp>
 
#include <boost/smart_ptr/make_shared.hpp>
 
@@ -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<Swift::DiscoInfo> SlackFrontend::sendCapabilitiesRequest(Swift
 
}
 

	
 
void SlackFrontend::reconnectUser(const std::string &user) {
 

	
 
	return static_cast<SlackUserManager *>(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<SlackUserManager *>(m_userManager)->handleOAuth2Code(code, state);
 
}
 

	
 
std::string SlackFrontend::getOAuth2URL(const std::vector<std::string> &args) {
 
	return static_cast<SlackUserManager *>(m_userManager)->getOAuth2URL(args);
 
}
 

	
 
void SlackFrontend::disconnectFromServer() {
spectrum/src/frontends/slack/SlackFrontend.h
Show inline comments
 
@@ -25,13 +25,15 @@
 
#include <vector>
 
#include <boost/bind.hpp>
 

	
 
#define THREAD_POOL(X) static_cast<SlackFrontend *>(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<std::string> &args);
 
		
 
			void handleMessage(boost::shared_ptr<Swift::Message> 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;
 
	};
 
}
spectrum/src/frontends/slack/SlackInstallation.cpp
Show inline comments
 
new file 100644
 
/**
 
 * XMPP - libpurple transport
 
 *
 
 * Copyright (C) 2009, Jan Kaluza <hanzz@soc.pidgin.im>
 
 *
 
 * 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 <boost/foreach.hpp>
 
#include <boost/make_shared.hpp>
 
#include <map>
 
#include <iterator>
 

	
 
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 <username> <password>\". 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));
 
}
 

	
 

	
 
}
spectrum/src/frontends/slack/SlackInstallation.h
Show inline comments
 
new file 100644
 
/**
 
 * Spectrum 2 Slack Frontend
 
 *
 
 * Copyright (C) 2015, Jan Kaluza <hanzz.k@gmail.com>
 
 *
 
 * 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 <string>
 
#include <algorithm>
 
#include <map>
 

	
 
#include <boost/signal.hpp>
 

	
 
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<void (const std::string &user)> 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;
 
};
 

	
 
}
spectrum/src/frontends/slack/SlackRTM.cpp
Show inline comments
 
new file 100644
 
/**
 
 * XMPP - libpurple transport
 
 *
 
 * Copyright (C) 2009, Jan Kaluza <hanzz@soc.pidgin.im>
 
 *
 
 * 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 <boost/foreach.hpp>
 
#include <boost/make_shared.hpp>
 
#include <map>
 
#include <iterator>
 

	
 
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<Swift::SafeByteArray> 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<Swift::HostAddress> &addrs, boost::optional<Swift::DomainNameResolveError>) {
 
	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));
 
}
 

	
 
}
spectrum/src/frontends/slack/SlackRTM.h
Show inline comments
 
new file 100644
 
/**
 
 * Spectrum 2 Slack Frontend
 
 *
 
 * Copyright (C) 2015, Jan Kaluza <hanzz.k@gmail.com>
 
 *
 
 * 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 <Swiften/Network/TLSConnectionFactory.h>
 
#include <Swiften/Network/HostAddressPort.h>
 
#include <Swiften/TLS/PlatformTLSFactories.h>
 
#include <Swiften/TLS/TLSOptions.h>
 
#include <Swiften/Network/DomainNameResolveError.h>
 
#include <Swiften/Network/DomainNameAddressQuery.h>
 
#include <Swiften/Network/DomainNameResolver.h>
 
#include <Swiften/Network/HostAddress.h>
 
#include <Swiften/Base/SafeByteArray.h>
 

	
 
#include <string>
 
#include <algorithm>
 
#include <map>
 

	
 
#include <boost/signal.hpp>
 

	
 
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<Swift::HostAddress>&, boost::optional<Swift::DomainNameResolveError>);
 
		void handleDataRead(boost::shared_ptr<Swift::SafeByteArray> 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<Swift::DomainNameAddressQuery> m_dnsQuery;
 
		boost::shared_ptr<Swift::Connection> m_conn;
 
		Swift::TLSConnectionFactory *m_tlsConnectionFactory;
 
		std::string m_host;
 
		std::string m_path;
 
};
 

	
 
}
spectrum/src/frontends/slack/SlackUserManager.cpp
Show inline comments
 
@@ -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<SlackUserRegistration *>(m_userRegistration)->handleOAuth2Code(code, state);
 
}
 

	
 
std::string SlackUserManager::getOAuth2URL(const std::vector<std::string> &args) {
 
	return static_cast<SlackUserRegistration *>(m_userRegistration)->createOAuth2URL(args);
 
}
 

	
 

	
 
}
spectrum/src/frontends/slack/SlackUserManager.h
Show inline comments
 
@@ -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<std::string> &args);
 

	
 
	private:
 
		Component *m_component;
 
		UserRegistration *m_userRegistration;
 
		StorageBackend *m_storageBackend;
 
		std::map<std::string, SlackInstallation *> m_installations;
 
};
 

	
 
}
spectrum/src/frontends/slack/SlackUserRegistration.cpp
Show inline comments
 
@@ -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 <boost/shared_ptr.hpp>
 
#include <boost/thread.hpp>
 
@@ -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<std::string> &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<std::string> 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) {
spectrum/src/frontends/slack/SlackUserRegistration.h
Show inline comments
 
@@ -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<std::string> &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<std::string, OAuth2 *> m_auths;
 
		std::map<std::string, std::vector<std::string> > m_authsData;
 

	
 
};
 

	
src/AdminInterface.cpp
Show inline comments
 
@@ -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<std::string> 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";
src/Config.cpp
Show inline comments
 
@@ -103,6 +103,7 @@ bool Config::load(std::istream &ifs, boost::program_options::options_description
 
		("service.jid_escaping", value<bool>()->default_value(true), "")
 
		("service.vip_only", value<bool>()->default_value(false), "")
 
		("service.vip_message", value<std::string>()->default_value(""), "")
 
		("service.reconnect_all_users", value<bool>()->default_value(false), "")
 
		("vhosts.vhost", value<std::vector<std::string> >()->multitoken(), "")
 
		("identity.name", value<std::string>()->default_value("Spectrum 2 Transport"), "Name showed in service discovery.")
 
		("identity.category", value<std::string>()->default_value("gateway"), "Disco#info identity category. 'gateway' by default.")
src/HTTPRequest.cpp
Show inline comments
 
@@ -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;
 
}
 

	
 
}
src/HTTPRequestQueue.cpp
Show inline comments
 
new file 100644
 
#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();
 
	}
 
}
 

	
 

	
 
}
src/MySQLBackend.cpp
Show inline comments
 
@@ -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<std::string> &users) {
 
	return true;
 
}
 

	
 
bool MySQLBackend::getUsers(std::vector<std::string> &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);
src/OAuth2.cpp
Show inline comments
 
@@ -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 "";
 
}
 

	
src/PQXXBackend.cpp
Show inline comments
 
@@ -245,6 +245,23 @@ bool PQXXBackend::getOnlineUsers(std::vector<std::string> &users) {
 
	return true;
 
}
 

	
 
bool PQXXBackend::getUsers(std::vector<std::string> &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<std::string>());
 
		}
 
	}
 
	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);
src/SQLite3Backend.cpp
Show inline comments
 
@@ -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<std::string> &users) {
 
	return true;
 
}
 

	
 
bool SQLite3Backend::getUsers(std::vector<std::string> &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);
src/ThreadPool.cpp
Show inline comments
 
#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);
 
	}
 
}
 

	
 
}
src/UsersReconnecter.cpp
Show inline comments
 
@@ -23,6 +23,7 @@
 
#include "transport/Transport.h"
 
#include "transport/Logging.h"
 
#include "transport/Frontend.h"
 
#include "transport/Config.h"
 

	
 
#include <iostream>
 
#include <boost/bind.hpp>
 
@@ -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();
 
}
0 comments (0 inline, 0 general)