Files @ 58fbe0d388c6
Branch filter:

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

HanzZ
Communi: Handle and forward socket errors. Fixes for example situation when user tries to join the room on server which does not exist.
bc3971f7d258
e312a8602ec7
e312a8602ec7
4717bd007185
4717bd007185
4717bd007185
bc3971f7d258
3014cc2e1c8c
3014cc2e1c8c
3014cc2e1c8c
bc3971f7d258
bc3971f7d258
0e56fb848472
8b6973539f23
bc3971f7d258
3014cc2e1c8c
bc3971f7d258
05e06dd845ae
0e56fb848472
0e56fb848472
0e56fb848472
0e56fb848472
0e56fb848472
0e56fb848472
0e56fb848472
0e56fb848472
0e56fb848472
0e56fb848472
0e56fb848472
0e56fb848472
05e06dd845ae
05e06dd845ae
05e06dd845ae
05e06dd845ae
05e06dd845ae
05e06dd845ae
bc3971f7d258
bc3971f7d258
0e56fb848472
0e56fb848472
0e56fb848472
0e56fb848472
0e56fb848472
0e56fb848472
0e56fb848472
0e56fb848472
bc3971f7d258
bc3971f7d258
bc3971f7d258
bc3971f7d258
bc3971f7d258
8b6973539f23
8b6973539f23
8b6973539f23
8b6973539f23
fd4946efe48c
8b6973539f23
8b6973539f23
8b6973539f23
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
05e06dd845ae
4717bd007185
fdc1a4199a2a
fdc1a4199a2a
bc3971f7d258
bc3971f7d258
bc3971f7d258
05e06dd845ae
05e06dd845ae
bc3971f7d258
05e06dd845ae
05e06dd845ae
e312a8602ec7
bc3971f7d258
8d72e074c0d5
bc3971f7d258
bc3971f7d258
05e06dd845ae
8d72e074c0d5
0e56fb848472
8d72e074c0d5
8c529dbabbbc
8c529dbabbbc
8c529dbabbbc
8d72e074c0d5
05e06dd845ae
05e06dd845ae
fdc1a4199a2a
05e06dd845ae
fdc1a4199a2a
05e06dd845ae
8c529dbabbbc
8c529dbabbbc
8c529dbabbbc
8c529dbabbbc
8c529dbabbbc
8c529dbabbbc
05e06dd845ae
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
bc3971f7d258
05e06dd845ae
bc3971f7d258
bc3971f7d258
bc3971f7d258
05e06dd845ae
05e06dd845ae
fdc1a4199a2a
05e06dd845ae
05e06dd845ae
8d72e074c0d5
8d72e074c0d5
05e06dd845ae
05e06dd845ae
05e06dd845ae
8d72e074c0d5
0e56fb848472
05e06dd845ae
05e06dd845ae
05e06dd845ae
05e06dd845ae
8d72e074c0d5
bc3971f7d258
#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) {
	this->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(config, "service.irc_server", "");
	if (!server.empty()) {
		m_servers.push_back(server);
	}
	else {
		
		std::list<std::string> list;
		list = CONFIG_LIST_DEFAULTED(config, "service.irc_server", list);
		
		m_servers.insert(m_servers.begin(), list.begin(), list.end());
	}

	if (CONFIG_HAS_KEY(config, "service.irc_identify")) {
		m_identify = CONFIG_STRING(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;
		// Users can join the network without registering if we allow
		// one user to connect multiple IRC networks.
		NetworkPlugin::PluginConfig cfg;
		if (m_servers.empty()) {
			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 choose...
		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(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 (!CONFIG_BOOL(config, "service.server_mode")) {
		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);
	}
}