Files @ ba5c6ce41e34
Branch filter:

Location: libtransport.git/backends/libcommuni/ircnetworkplugin.cpp - annotation

Jan Kaluza
Cleanup libcommuni code a bit, fix the tests and PM receiving when it is initiated from IRC
addac0040c4a
addac0040c4a
addac0040c4a
addac0040c4a
addac0040c4a
addac0040c4a
addac0040c4a
addac0040c4a
addac0040c4a
addac0040c4a
addac0040c4a
addac0040c4a
addac0040c4a
addac0040c4a
addac0040c4a
addac0040c4a
addac0040c4a
addac0040c4a
addac0040c4a
addac0040c4a
bc3971f7d258
e312a8602ec7
e312a8602ec7
78e71f9345c7
4717bd007185
4717bd007185
bc3971f7d258
3014cc2e1c8c
3014cc2e1c8c
3014cc2e1c8c
bc3971f7d258
ba5c6ce41e34
0e56fb848472
8b6973539f23
ba5c6ce41e34
bc3971f7d258
3014cc2e1c8c
bc3971f7d258
05e06dd845ae
ba5c6ce41e34
0e56fb848472
0e56fb848472
0e56fb848472
0e56fb848472
0e56fb848472
ba5c6ce41e34
0e56fb848472
0e56fb848472
0e56fb848472
ba5c6ce41e34
ba5c6ce41e34
05e06dd845ae
05e06dd845ae
05e06dd845ae
05e06dd845ae
bc3971f7d258
bc3971f7d258
0e56fb848472
0e56fb848472
0e56fb848472
0e56fb848472
0e56fb848472
0e56fb848472
0e56fb848472
0e56fb848472
bc3971f7d258
bc3971f7d258
bc3971f7d258
bc3971f7d258
bc3971f7d258
8b6973539f23
8b6973539f23
ba5c6ce41e34
fd4946efe48c
ba5c6ce41e34
fd4946efe48c
fd4946efe48c
fd4946efe48c
8b6973539f23
8b6973539f23
bc3971f7d258
bc3971f7d258
bc3971f7d258
bc3971f7d258
bc3971f7d258
bc3971f7d258
bc3971f7d258
bc3971f7d258
05e06dd845ae
05e06dd845ae
05e06dd845ae
05e06dd845ae
05e06dd845ae
05e06dd845ae
05e06dd845ae
7e9dfbdeadda
05e06dd845ae
05e06dd845ae
05e06dd845ae
05e06dd845ae
05e06dd845ae
05e06dd845ae
05e06dd845ae
05e06dd845ae
05e06dd845ae
05e06dd845ae
05e06dd845ae
05e06dd845ae
05e06dd845ae
05e06dd845ae
05e06dd845ae
bc3971f7d258
0e56fb848472
58fbe0d388c6
05e06dd845ae
05e06dd845ae
05e06dd845ae
05e06dd845ae
05e06dd845ae
0e56fb848472
fdc1a4199a2a
fdc1a4199a2a
05e06dd845ae
ba5c6ce41e34
4717bd007185
fdc1a4199a2a
fdc1a4199a2a
bc3971f7d258
bc3971f7d258
bc3971f7d258
05e06dd845ae
05e06dd845ae
bc3971f7d258
05e06dd845ae
05e06dd845ae
e312a8602ec7
bc3971f7d258
8d72e074c0d5
bc3971f7d258
bc3971f7d258
05e06dd845ae
8d72e074c0d5
ba5c6ce41e34
8d72e074c0d5
8c529dbabbbc
8c529dbabbbc
8c529dbabbbc
8d72e074c0d5
05e06dd845ae
05e06dd845ae
fdc1a4199a2a
05e06dd845ae
fdc1a4199a2a
ba5c6ce41e34
ba5c6ce41e34
ba5c6ce41e34
ba5c6ce41e34
ba5c6ce41e34
ba5c6ce41e34
05e06dd845ae
05e06dd845ae
05e06dd845ae
f7dc91ccb482
05e06dd845ae
05e06dd845ae
05e06dd845ae
05e06dd845ae
fdc1a4199a2a
74cd11e265a3
05e06dd845ae
58fbe0d388c6
58fbe0d388c6
58fbe0d388c6
58fbe0d388c6
a32e962a1a97
a32e962a1a97
a32e962a1a97
05e06dd845ae
05e06dd845ae
84a6e647e6fb
84a6e647e6fb
da1044e0dcab
84a6e647e6fb
84a6e647e6fb
84a6e647e6fb
84a6e647e6fb
05e06dd845ae
05e06dd845ae
05e06dd845ae
74cd11e265a3
bc3971f7d258
bc3971f7d258
eb1df41b3ea2
eb1df41b3ea2
eb1df41b3ea2
eb1df41b3ea2
eb1df41b3ea2
eb1df41b3ea2
eb1df41b3ea2
eb1df41b3ea2
eb1df41b3ea2
eb1df41b3ea2
eb1df41b3ea2
bc3971f7d258
05e06dd845ae
05e06dd845ae
05e06dd845ae
05e06dd845ae
05e06dd845ae
0e56fb848472
05e06dd845ae
05e06dd845ae
05e06dd845ae
05e06dd845ae
05e06dd845ae
05e06dd845ae
05e06dd845ae
05e06dd845ae
05e06dd845ae
fdc1a4199a2a
fdc1a4199a2a
05e06dd845ae
fdc1a4199a2a
fdc1a4199a2a
fdc1a4199a2a
4717bd007185
05e06dd845ae
05e06dd845ae
05e06dd845ae
ba5c6ce41e34
bc3971f7d258
05e06dd845ae
bc3971f7d258
bc3971f7d258
bc3971f7d258
05e06dd845ae
05e06dd845ae
fdc1a4199a2a
05e06dd845ae
05e06dd845ae
8d72e074c0d5
8d72e074c0d5
05e06dd845ae
05e06dd845ae
05e06dd845ae
8d72e074c0d5
0e56fb848472
05e06dd845ae
05e06dd845ae
05e06dd845ae
05e06dd845ae
8d72e074c0d5
bc3971f7d258
/**
 * XMPP - libpurple transport
 *
 * Copyright (C) 2013, 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
 */

#include "ircnetworkplugin.h"
#include <IrcCommand>
#include <IrcMessage>
#include "transport/Logging.h"

DEFINE_LOGGER(logger, "IRCNetworkPlugin");

#define FROM_UTF8(WHAT) QString::fromUtf8((WHAT).c_str(), (WHAT).size())
#define TO_UTF8(WHAT) std::string((WHAT).toUtf8().data(), (WHAT).toUtf8().size())

IRCNetworkPlugin::IRCNetworkPlugin(Config *config, Swift::QtEventLoop *loop, const std::string &host, int port) {
	m_config = config;
	m_currentServer = 0;
	m_firstPing = true;

	m_socket = new QTcpSocket();
	m_socket->connectToHost(FROM_UTF8(host), port);
	connect(m_socket, SIGNAL(readyRead()), this, SLOT(readData()));

	std::string server = CONFIG_STRING_DEFAULTED(m_config, "service.irc_server", "");
	if (!server.empty()) {
		m_servers.push_back(server);
	}
	else {
		std::list<std::string> list;
		list = CONFIG_LIST_DEFAULTED(m_config, "service.irc_server", list);
		m_servers.insert(m_servers.begin(), list.begin(), list.end());
	}

	if (CONFIG_HAS_KEY(m_config, "service.irc_identify")) {
		m_identify = CONFIG_STRING(m_config, "service.irc_identify");
	}
	else {
		m_identify = "NickServ identify $name $password";
	}
}

void IRCNetworkPlugin::tryNextServer() {
	if (!m_servers.empty()) {
		int nextServer = (m_currentServer + 1) % m_servers.size();
		LOG4CXX_INFO(logger, "Server " << m_servers[m_currentServer] << " disconnected user. Next server to try will be " << m_servers[nextServer]);
		m_currentServer = nextServer;
	}
}

void IRCNetworkPlugin::readData() {
	size_t availableBytes = m_socket->bytesAvailable();
	if (availableBytes == 0)
		return;

	if (m_firstPing) {
		m_firstPing = false;

		NetworkPlugin::PluginConfig cfg;
		cfg.setNeedRegistration(false);
		cfg.setSupportMUC(true);
		cfg.disableJIDEscaping();
		sendConfig(cfg);
	}

	std::string d = std::string(m_socket->readAll().data(), availableBytes);
	handleDataRead(d);
}

void IRCNetworkPlugin::sendData(const std::string &string) {
	m_socket->write(string.c_str(), string.size());
}

MyIrcSession *IRCNetworkPlugin::createSession(const std::string &user, const std::string &hostname, const std::string &nickname, const std::string &password, const std::string &suffix) {
	MyIrcSession *session = new MyIrcSession(user, this, suffix);
	session->setUserName(FROM_UTF8(nickname));
	session->setNickName(FROM_UTF8(nickname));
	session->setRealName(FROM_UTF8(nickname));
	session->setHost(FROM_UTF8(hostname));
	session->setPort(6667);
// 	session->setEncoding("UTF8");

	if (!password.empty()) {
		std::string identify = m_identify;
		boost::replace_all(identify, "$password", password);
		boost::replace_all(identify, "$name", nickname);
		session->setIdentify(identify);
	}

	LOG4CXX_INFO(logger, user << ": Connecting " << hostname << " as " << nickname << ", suffix=" << suffix);

	session->open();

	return session;
}

void IRCNetworkPlugin::handleLoginRequest(const std::string &user, const std::string &legacyName, const std::string &password) {
	if (!m_servers.empty()) {
		// legacy name is user's nickname
		if (m_sessions[user] != NULL) {
			LOG4CXX_WARN(logger, user << ": Already logged in.");
			return;
		}

		m_sessions[user] = createSession(user, m_servers[m_currentServer], legacyName, password, "");
	}
	else {
		// We are waiting for first room join to connect user to IRC network, because we don't know which
		// network he chooses...
		LOG4CXX_INFO(logger, user << ": Ready for connections");
		handleConnected(user);
	}
}

void IRCNetworkPlugin::handleLogoutRequest(const std::string &user, const std::string &legacyName) {
	if (m_sessions[user] == NULL) {
		LOG4CXX_WARN(logger, user << ": Already disconnected.");
		return;
	}
	LOG4CXX_INFO(logger, user << ": Disconnecting.");
	m_sessions[user]->close();
	m_sessions[user]->deleteLater();
	m_sessions.erase(user);
}

std::string IRCNetworkPlugin::getSessionName(const std::string &user, const std::string &legacyName) {
	std::string u = user;
	if (!CONFIG_BOOL(m_config, "service.server_mode") && m_servers.empty()) {
		u = user + legacyName.substr(legacyName.find("@") + 1);
		if (u.find("/") != std::string::npos) {
			u = u.substr(0, u.find("/"));
		}
	}
	return u;
}

std::string IRCNetworkPlugin::getTargetName(const std::string &legacyName) {
	std::string r = legacyName;
	if (legacyName.find("/") == std::string::npos) {
		r = legacyName.substr(0, r.find("@"));
	}
	else {
		r = legacyName.substr(legacyName.find("/") + 1);
	}
	return r;
}

void IRCNetworkPlugin::handleMessageSendRequest(const std::string &user, const std::string &legacyName, const std::string &message, const std::string &/*xhtml*/, const std::string &/*id*/) {
	std::string session = getSessionName(user, legacyName);
	if (m_sessions[session] == NULL) {
		LOG4CXX_WARN(logger, user << ": Session name: " << session << ", No session for user");
		return;
	}

	std::string target = getTargetName(legacyName);
	// We are sending PM message. On XMPP side, user is sending PM using the particular channel,
	// for example #room@irc.freenode.org/hanzz. On IRC side, we are forwarding this message
	// just to "hanzz". Therefore we have to somewhere store, that message from "hanzz" should
	// be mapped to #room@irc.freenode.org/hanzz.
	if (legacyName.find("/") != std::string::npos) {
		m_sessions[session]->addPM(target, legacyName.substr(0, legacyName.find("@")));
	}

	LOG4CXX_INFO(logger, user << ": Session name: " << session << ", message to " << target);

	if (message.find("/me") == 0) {
		m_sessions[session]->sendCommand(IrcCommand::createCtcpAction(FROM_UTF8(target), FROM_UTF8(message.substr(4))));
	}
	else {
		m_sessions[session]->sendCommand(IrcCommand::createMessage(FROM_UTF8(target), FROM_UTF8(message)));
	}

	if (target.find("#") == 0) {
		handleMessage(user, legacyName, message, TO_UTF8(m_sessions[session]->nickName()));
	}
}

void IRCNetworkPlugin::handleRoomSubjectChangedRequest(const std::string &user, const std::string &room, const std::string &message) {
	std::string session = getSessionName(user, room);
	if (m_sessions[session] == NULL) {
		LOG4CXX_WARN(logger, user << ": Session name: " << session << ", No session for user");
		return;
	}

	std::string target = getTargetName(room);
	m_sessions[session]->sendCommand(IrcCommand::createTopic(FROM_UTF8(target), FROM_UTF8(message)));
}

void IRCNetworkPlugin::handleJoinRoomRequest(const std::string &user, const std::string &room, const std::string &nickname, const std::string &password) {
	std::string session = getSessionName(user, room);
	std::string target = getTargetName(room);

	LOG4CXX_INFO(logger, user << ": Session name: " << session << ", Joining room " << target);
	if (m_sessions[session] == NULL) {
		if (m_servers.empty()) {
			// in gateway mode we want to login this user to network according to legacyName
			if (room.find("@") != std::string::npos) {
				// suffix is %irc.freenode.net to let MyIrcSession return #room%irc.freenode.net
				m_sessions[session] = createSession(user, room.substr(room.find("@") + 1), nickname, "", room.substr(room.find("@")));
			}
			else {
				LOG4CXX_WARN(logger, user << ": There's no proper server defined in room to which this user wants to join: " << room);
				return;
			}
		}
		else {
			LOG4CXX_WARN(logger, user << ": Join room requested for unconnected user");
			return;
		}
	}

	m_sessions[session]->addAutoJoinChannel(target, password);
	m_sessions[session]->sendCommand(IrcCommand::createJoin(FROM_UTF8(target), FROM_UTF8(password)));
	m_sessions[session]->rooms += 1;

	// update nickname, because we have nickname per session, no nickname per room.
	handleRoomNicknameChanged(user, target, TO_UTF8(m_sessions[session]->nickName()));
}

void IRCNetworkPlugin::handleLeaveRoomRequest(const std::string &user, const std::string &room) {
	std::string session = getSessionName(user, room);
	std::string target = getTargetName(room);

	LOG4CXX_INFO(logger, user << ": Session name: " << session << ", Leaving room " << target);
	if (m_sessions[session] == NULL)
		return;

	m_sessions[session]->sendCommand(IrcCommand::createPart(FROM_UTF8(target)));
	m_sessions[session]->removeAutoJoinChannel(target);
	m_sessions[session]->rooms -= 1;

	if (m_sessions[session]->rooms <= 0 && m_servers.empty()) {
		LOG4CXX_INFO(logger, user << ": Session name: " << session << ", User is not in any room, disconnecting from network");
		m_sessions[session]->close();
		m_sessions[session]->deleteLater();
		m_sessions.erase(session);
	}
}