Changeset - 00c5273fbb13
[Not reviewed]
0 6 0
Jan Kaluza - 9 years ago 2016-02-20 07:23:07
jkaluza@redhat.com
Slack: Handle channels starting with hash, do not reconnect to Slack RTM when URL expired
6 files changed with 43 insertions and 3 deletions:
0 comments (0 inline, 0 general)
backends/libpurple/main.cpp
Show inline comments
 
@@ -2077,27 +2077,30 @@ static void transportDataReceived(gpointer data, gint source, PurpleInputConditi
 
			LOG4CXX_INFO(logger, "Diconnecting from spectrum2 server");
 
			exit(errno);
 
		}
 
		std::string d = std::string(buffer, n);
 

	
 
		if (firstPing) {
 
			firstPing = false;
 
			NetworkPlugin::PluginConfig cfg;			
 
			cfg.setSupportMUC(true);
 
			if (CONFIG_STRING(config, "service.protocol") == "prpl-telegram") {
 
				cfg.setNeedPassword(false);
 
			}
 
			if (CONFIG_STRING(config, "service.protocol") != "prpl-irc") {
 
			if (CONFIG_STRING(config, "service.protocol") == "prpl-irc") {
 
				cfg.setNeedRegistration(false);
 
			}
 
			else {
 
				cfg.setNeedRegistration(true);
 
			}
 
			np->sendConfig(cfg);
 
		}
 

	
 
		np->handleDataRead(d);
 
	}
 
	else {
 
		if (writeInput != 0) {
 
			purple_input_remove_wrapped(writeInput);
 
			writeInput = 0;
 
		}
 
		np->readyForData();
 
	}
include/transport/WebSocketClient.h
Show inline comments
 
@@ -46,24 +46,25 @@
 

	
 
namespace Transport {
 

	
 
class Component;
 

	
 
class WebSocketClient {
 
	public:
 
		WebSocketClient(Component *component, const std::string &user);
 

	
 
		virtual ~WebSocketClient();
 

	
 
		void connectServer(const std::string &u);
 
		void disconnectServer();
 

	
 
		void write(const std::string &data);
 

	
 
		boost::signal<void (const std::string &payload)> onPayloadReceived;
 

	
 
		boost::signal<void ()> onWebSocketConnected;
 
		boost::signal<void (const boost::optional<Swift::Connection::Error> &error)> onWebSocketDisconnected;
 

	
 
	private:
 
		void handleDNSResult(const std::vector<Swift::HostAddress>&, boost::optional<Swift::DomainNameResolveError>);
 
		void handleDataRead(boost::shared_ptr<Swift::SafeByteArray> data);
 
		void handleConnected(bool error);
libtransport/WebSocketClient.cpp
Show inline comments
 
@@ -73,24 +73,31 @@ void WebSocketClient::connectServer() {
 
	m_dnsQuery->onResult.connect(boost::bind(&WebSocketClient::handleDNSResult, this, _1, _2));
 
	m_dnsQuery->run();
 
	m_reconnectTimer->stop();
 
}
 

	
 
void WebSocketClient::connectServer(const std::string &url) {
 
	std::string u = url.substr(6);
 
	m_host = u.substr(0, u.find("/"));
 
	m_path = u.substr(u.find("/"));
 
	connectServer();
 
}
 

	
 
void WebSocketClient::disconnectServer() {
 
	if (m_conn) {
 
		m_conn->onDataRead.disconnect(boost::bind(&WebSocketClient::handleDataRead, this, _1));
 
		m_conn->disconnect();
 
	}
 
}
 

	
 
void WebSocketClient::write(const std::string &data) {
 
	if (!m_conn) {
 
		return;
 
	}
 

	
 
	uint8_t opcode = 129; // UTF8
 
	if (data.empty()) {
 
		opcode = 138; // PONG
 
	}
 

	
 
	// Mask the payload
 
	char mask_bits[4] = {0x11, 0x22, 0x33, 0x44};
spectrum/src/frontends/slack/SlackRTM.cpp
Show inline comments
 
@@ -83,24 +83,32 @@ void SlackRTM::start() {
 
#define STORE_STRING_OPTIONAL(FROM, NAME) rapidjson::Value &NAME##_tmp = FROM[#NAME]; \
 
	std::string NAME; \
 
	if (NAME##_tmp.IsString()) {  \
 
		 NAME = NAME##_tmp.GetString(); \
 
	}
 

	
 
#define GET_OBJECT(FROM, NAME) rapidjson::Value &NAME = FROM[#NAME]; \
 
	if (!NAME.IsObject()) { \
 
		LOG4CXX_ERROR(logger, "No '" << #NAME << "' object in the reply."); \
 
		return; \
 
	}
 

	
 
#define STORE_INT(FROM, NAME) rapidjson::Value &NAME##_tmp = FROM[#NAME]; \
 
	if (!NAME##_tmp.IsInt()) {  \
 
		LOG4CXX_ERROR(logger, "No '" << #NAME << "' number in the reply."); \
 
		LOG4CXX_ERROR(logger, payload); \
 
		return; \
 
	} \
 
	int NAME = NAME##_tmp.GetInt();
 

	
 
void SlackRTM::handlePayloadReceived(const std::string &payload) {
 
	rapidjson::Document d;
 
	if (d.Parse<0>(payload.c_str()).HasParseError()) {
 
		LOG4CXX_ERROR(logger, "Error while parsing JSON");
 
		LOG4CXX_ERROR(logger, payload);
 
		return;
 
	}
 

	
 
	STORE_STRING(d, type);
 

	
 
	if (type == "message") {
 
		STORE_STRING(d, channel);
 
@@ -135,24 +143,35 @@ void SlackRTM::handlePayloadReceived(const std::string &payload) {
 
			
 
		}
 
		else {
 
			STORE_STRING(d, user);
 
			onMessageReceived(channel, user, text, ts);
 
		}
 
	}
 
	else if (type == "channel_joined"
 
		  || type == "channel_created") {
 
		std::map<std::string, SlackChannelInfo> &channels = m_idManager->getChannels();
 
		SlackAPI::getSlackChannelInfo(NULL, true, d, payload, channels);
 
	}
 
	else if (type == "error") {
 
		GET_OBJECT(d, error);
 
		STORE_INT(error, code);
 

	
 
		if (code == 1) {
 
			LOG4CXX_INFO(logger, "Reconnecting to Slack network");
 
			m_pingTimer->stop();
 
			m_client->disconnectServer();
 
			start();
 
		}
 
	}
 
}
 

	
 
void SlackRTM::sendMessage(const std::string &channel, const std::string &message) {
 
	m_counter++;
 

	
 
	std::string m = message;
 
	boost::replace_all(m, "\"", "\\\"");
 
	std::string msg = "{\"id\": " + boost::lexical_cast<std::string>(m_counter) + ", \"type\": \"message\", \"channel\":\"" + channel + "\", \"text\":\"" + m + "\"}";
 
	m_client->write(msg);
 
}
 

	
 
void SlackRTM::sendPing() {
spectrum/src/frontends/slack/SlackSession.cpp
Show inline comments
 
@@ -191,41 +191,48 @@ void SlackSession::handleJoinRoomCreated(const std::string &channelId, std::vect
 

	
 
	Swift::Presence::ref presence = Swift::Presence::create();
 
	presence->setFrom(Swift::JID(m_uinfo.jid + "/default"));
 
	presence->setTo(Swift::JID(to + "/" + name));
 
	presence->setType(Swift::Presence::Available);
 
	presence->addPayload(boost::shared_ptr<Swift::Payload>(new Swift::MUCPayload()));
 
	m_component->getFrontend()->onPresenceReceived(presence);
 

	
 
	m_onlineBuddiesTimer->start();
 
}
 

	
 
void SlackSession::handleJoinMessage(const std::string &message, std::vector<std::string> &args, bool quiet) {
 
	if (args[5][0] == '#') {
 
		args[5].erase(0, 1);
 
	}
 
	LOG4CXX_INFO(logger, m_uinfo.jid << ": Going to join the room " << args[3] << "@" << args[4] << ", transporting it to channel " << args[5]);
 
	m_api->createChannel(args[5], m_idManager->getSelfId(), boost::bind(&SlackSession::handleJoinRoomCreated, this, _1, args));
 
}
 

	
 
void SlackSession::handleSlackChannelCreated(const std::string &channelId) {
 
	m_slackChannel = channelId;
 

	
 
	LOG4CXX_INFO(logger, m_uinfo.jid << ": Main Slack Channel created, connecting the legacy network");
 
	Swift::Presence::ref presence = Swift::Presence::create();
 
	presence->setFrom(Swift::JID(m_uinfo.jid + "/default"));
 
	presence->setTo(m_component->getJID());
 
	presence->setType(Swift::Presence::Available);
 
	presence->addPayload(boost::shared_ptr<Swift::Payload>(new Swift::MUCPayload()));
 
	m_component->getFrontend()->onPresenceReceived(presence);
 
}
 

	
 
void SlackSession::leaveRoom(const std::string &channel) {
 
void SlackSession::leaveRoom(const std::string &channel_) {
 
	std::string channel = channel_;
 
	if (channel[0] == '#') {
 
		channel.erase(0, 1);
 
	}
 
	std::string channelId = m_idManager->getId(channel);
 
	std::string to = m_channel2jid[channelId];
 
	if (to.empty()) {
 
		LOG4CXX_ERROR(logger, "Spectrum 2 is not configured to transport this Slack channel.")
 
		return;
 
	}
 

	
 
	LOG4CXX_INFO(logger, m_uinfo.jid << ": Leaving the legacy network room " << to);
 

	
 
	Swift::Presence::ref presence = Swift::Presence::create();
 
	presence->setFrom(Swift::JID(m_uinfo.jid + "/default"));
 
	presence->setTo(Swift::JID(to + "/" + m_uinfo.uin));
spectrum/src/frontends/slack/SlackUserRegistration.cpp
Show inline comments
 
@@ -66,25 +66,25 @@ std::string SlackUserRegistration::createOAuth2URL(const std::vector<std::string
 
	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 bot");
 
	std::string url = oauth2->generateAuthURL();
 

	
 
	m_auths[oauth2->getState()] = oauth2;
 
	m_authsData[oauth2->getState()] = args;
 

	
 
	if (args.size() >= 3) {
 
		LOG4CXX_INFO(logger, "Generating OAUth2 URL with slack_channel=" << args[0] << ", 3rd_party_account=" << args[1]);
 
		LOG4CXX_INFO(logger, "Generating OAUth2 URL with slack_channel=" << args[1] << ", 3rd_party_account=" << args[2]);
 
	}
 
	else {
 
		LOG4CXX_WARN(logger, "Generating OAUth2 URL with too few arguments");
 
	}
 

	
 
	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;
 
@@ -171,24 +171,27 @@ std::string SlackUserRegistration::handleOAuth2Code(const std::string &code, con
 
	user.jid = domain;
 
	user.uin = uin;
 
	user.password = password;
 
	user.language = "en";
 
	user.encoding = "";
 
	user.vip = 0;
 

	
 
	registerUser(user, true);
 

	
 
	m_storageBackend->getUser(user.jid, user);
 

	
 
	if (!slackChannel.empty()) {
 
		if (slackChannel[0] == '#') {
 
			slackChannel.erase(0, 1);
 
		}
 
		m_storageBackend->getUserSetting(user.id, "slack_channel", type, slackChannel);
 
	}
 

	
 
	value = token;
 
	m_storageBackend->getUserSetting(user.id, "bot_token", type, value);
 

	
 
	value = access_token;
 
	m_storageBackend->getUserSetting(user.id, "access_token", type, value);
 

	
 
	LOG4CXX_INFO(logger, "Registered Slack user " << user.jid << ", slack_channel=" << slackChannel);
 

	
 
	if (oauth2) {
0 comments (0 inline, 0 general)