Changeset - 0e56fb848472
[Not reviewed]
0 5 0
Jan Kaluza - 13 years ago 2012-10-04 08:48:14
hanzz.k@gmail.com
Support more service.irc_server values and try another one when server disconnects us
5 files changed with 46 insertions and 14 deletions:
0 comments (0 inline, 0 general)
backends/libcommuni/ircnetworkplugin.cpp
Show inline comments
 
#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_socket = new QTcpSocket();
 
	m_socket->connectToHost(FROM_UTF8(host), port);
 
	connect(m_socket, SIGNAL(readyRead()), this, SLOT(readData()));
 

	
 
	m_server = CONFIG_STRING_DEFAULTED(config, "service.irc_server", "");
 
	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;
 

	
 
	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());
 
}
 
@@ -51,55 +71,55 @@ MyIrcSession *IRCNetworkPlugin::createSession(const std::string &user, const std
 
		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_server.empty()) {
 
	if (!m_servers.empty()) {
 
		// legacy name is users nickname
 
		if (m_sessions[user] != NULL) {
 
			LOG4CXX_WARN(logger, user << ": Already logged in.");
 
			return;
 
		}
 

	
 
		m_sessions[user] = createSession(user, m_server, legacyName, password, "");
 
		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_server.empty()) {
 
	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) {
 
@@ -137,25 +157,25 @@ void IRCNetworkPlugin::handleRoomSubjectChangedRequest(const std::string &user,
 
	}
 

	
 
	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_server.empty()) {
 
		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");
 
@@ -173,19 +193,19 @@ void IRCNetworkPlugin::handleJoinRoomRequest(const std::string &user, const std:
 
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_server.empty()) {
 
	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);
 
	}
 
}
backends/libcommuni/ircnetworkplugin.h
Show inline comments
 
@@ -19,28 +19,31 @@ class IRCNetworkPlugin : public QObject, public NetworkPlugin {
 
		void handleLoginRequest(const std::string &user, const std::string &legacyName, const std::string &password);
 

	
 
		void handleLogoutRequest(const std::string &user, const std::string &legacyName);
 

	
 
		void handleMessageSendRequest(const std::string &user, const std::string &legacyName, const std::string &message, const std::string &/*xhtml*/);
 

	
 
		void handleJoinRoomRequest(const std::string &user, const std::string &room, const std::string &nickname, const std::string &password);
 

	
 
		void handleLeaveRoomRequest(const std::string &user, const std::string &room);
 

	
 
		void handleRoomSubjectChangedRequest(const std::string &user, const std::string &room, const std::string &message);
 

	
 
		void tryNextServer();
 

	
 
	public slots:
 
		void readData();
 
		void sendData(const std::string &string);
 

	
 
	private:
 
		MyIrcSession *createSession(const std::string &user, const std::string &hostname, const std::string &nickname, const std::string &password, const std::string &suffix = "");
 
		std::string getSessionName(const std::string &user, const std::string &legacyName);
 
		std::string getTargetName(const std::string &legacyName);
 

	
 
	private:
 
		Config *config;
 
		QTcpSocket *m_socket;
 
		std::map<std::string, MyIrcSession *> m_sessions;
 
		std::string m_server;
 
		std::vector<std::string> m_servers;
 
		int m_currentServer;
 
		std::string m_identify;
 
};
 
\ No newline at end of file
backends/libcommuni/session.cpp
Show inline comments
 
@@ -5,63 +5,72 @@
 
 * restriction applied to their modification, redistribution, using and so on.
 
 * You can study them, modify them, use them in your own program - either
 
 * completely or partially. By using it you may give me some credits in your
 
 * program, but you don't have to.
 
 */
 

	
 
#include "session.h"
 
#include <QtCore>
 
#include <iostream>
 
#include <IrcCommand>
 
#include <IrcMessage>
 

	
 
#include "ircnetworkplugin.h"
 

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

	
 
#include "transport/logging.h"
 

	
 
DEFINE_LOGGER(logger, "IRCSession");
 

	
 
MyIrcSession::MyIrcSession(const std::string &user, NetworkPlugin *np, const std::string &suffix, QObject* parent) : IrcSession(parent)
 
MyIrcSession::MyIrcSession(const std::string &user, IRCNetworkPlugin *np, const std::string &suffix, QObject* parent) : IrcSession(parent)
 
{
 
	this->np = np;
 
	this->user = user;
 
	this->suffix = suffix;
 
	m_connected = false;
 
	rooms = 0;
 
	connect(this, SIGNAL(disconnected()), SLOT(on_disconnected()));
 
	connect(this, SIGNAL(socketError(QAbstractSocket::SocketError)), SLOT(on_socketError(QAbstractSocket::SocketError)));
 
	connect(this, SIGNAL(connected()), SLOT(on_connected()));
 
	connect(this, SIGNAL(messageReceived(IrcMessage*)), this, SLOT(onMessageReceived(IrcMessage*)));
 
}
 

	
 
void MyIrcSession::on_connected() {
 
	m_connected = true;
 
	if (suffix.empty()) {
 
		np->handleConnected(user);
 
	}
 

	
 
	for(AutoJoinMap::iterator it = m_autoJoin.begin(); it != m_autoJoin.end(); it++) {
 
		sendCommand(IrcCommand::createJoin(FROM_UTF8(it->second->getChannel()), FROM_UTF8(it->second->getPassword())));
 
	}
 

	
 
	if (getIdentify().find(" ") != std::string::npos) {
 
		std::string to = getIdentify().substr(0, getIdentify().find(" "));
 
		std::string what = getIdentify().substr(getIdentify().find(" ") + 1);
 
		sendCommand(IrcCommand::createMessage(FROM_UTF8(to), FROM_UTF8(what)));
 
	}
 
}
 

	
 
void MyIrcSession::on_socketError(QAbstractSocket::SocketError error) {
 
	on_disconnected();
 
}
 

	
 
void MyIrcSession::on_disconnected() {
 
	if (suffix.empty())
 
	if (suffix.empty()) {
 
		np->handleDisconnected(user, 0, "");
 
		np->tryNextServer();
 
	}
 
	m_connected = false;
 
}
 

	
 
bool MyIrcSession::correctNickname(std::string &nickname) {
 
	bool flags = 0;
 
	switch(nickname.at(0)) {
 
		case '@': nickname = nickname.substr(1); flags = 1; break;
 
		case '+': nickname = nickname.substr(1); break;
 
		default: break;
 
	}
 
	return flags;
 
}
 
@@ -183,27 +192,25 @@ void MyIrcSession::on_numericMessageReceived(IrcMessage *message) {
 
			members = m->parameters().value(3).split(" ");
 

	
 
			LOG4CXX_INFO(logger, user << ": Received members for " << TO_UTF8(channel) << suffix);
 
			for (int i = 0; i < members.size(); i++) {
 
				bool flags = 0;
 
				std::string nickname = TO_UTF8(members.at(i));
 
				flags = correctNickname(nickname);
 
				m_modes[TO_UTF8(channel) + nickname] = flags;
 
				np->handleParticipantChanged(user, nickname, TO_UTF8(channel) + suffix,(int) flags, pbnetwork::STATUS_ONLINE);
 
			}
 
			break;
 
		case 432:
 
			if (m_connected) {
 
			np->handleDisconnected(user, pbnetwork::CONNECTION_ERROR_INVALID_USERNAME, "Erroneous Nickname");
 
			}
 
			break;
 
		default:
 
			break;
 
	}
 

	
 
	//qDebug() << "numeric message received:" << receiver() << origin << code << params;
 
}
 

	
 
void MyIrcSession::onMessageReceived(IrcMessage *message) {
 
	LOG4CXX_INFO(logger, user << ": " << TO_UTF8(message->toString()));
 
	switch (message->type()) {
 
		case IrcMessage::Join:
backends/libcommuni/session.h
Show inline comments
 
@@ -9,44 +9,46 @@
 
 */
 

	
 
#ifndef SESSION_H
 
#define SESSION_H
 

	
 
#include <IrcSession>
 
#include <transport/networkplugin.h>
 
#include "Swiften/Swiften.h"
 
#include <boost/smart_ptr/make_shared.hpp>
 

	
 
using namespace Transport;
 

	
 
class IRCNetworkPlugin;
 

	
 
class MyIrcSession : public IrcSession
 
{
 
    Q_OBJECT
 

	
 
public:
 
	class AutoJoinChannel {
 
		public:
 
			AutoJoinChannel(const std::string &channel = "", const std::string &password = "") : m_channel(channel), m_password(password) {}
 
			virtual ~AutoJoinChannel() {}
 

	
 
			const std::string &getChannel() { return m_channel; }
 
			const std::string &getPassword() { return m_password; }
 
		private:
 
			std::string m_channel;
 
			std::string m_password;
 
	};
 

	
 
	typedef std::map<std::string, boost::shared_ptr<AutoJoinChannel> > AutoJoinMap;
 

	
 
	MyIrcSession(const std::string &user, NetworkPlugin *np, const std::string &suffix = "", QObject* parent = 0);
 
	MyIrcSession(const std::string &user, IRCNetworkPlugin *np, const std::string &suffix = "", QObject* parent = 0);
 
	std::map<std::string, bool> m_modes;
 
	std::string suffix;
 
	int rooms;
 

	
 
	void addAutoJoinChannel(const std::string &channel, const std::string &password) {
 
		m_autoJoin[channel] = boost::make_shared<AutoJoinChannel>(channel, password);
 
	}
 

	
 
	void removeAutoJoinChannel(const std::string &channel) {
 
		m_autoJoin.erase(channel);
 
	}
 

	
 
@@ -63,29 +65,30 @@ public:
 
	void on_joined(IrcMessage *message);
 
	void on_parted(IrcMessage *message);
 
	void on_quit(IrcMessage *message);
 
	void on_nickChanged(IrcMessage *message);
 
	void on_modeChanged(IrcMessage *message);
 
	void on_topicChanged(IrcMessage *message);
 
	void on_messageReceived(IrcMessage *message);
 
	void on_numericMessageReceived(IrcMessage *message);
 

	
 
protected Q_SLOTS:
 
	void on_connected();
 
	void on_disconnected();
 
	void on_socketError(QAbstractSocket::SocketError error);
 

	
 
	void onMessageReceived(IrcMessage* message);
 

	
 
protected:
 
	NetworkPlugin *np;
 
	IRCNetworkPlugin *np;
 
	std::string user;
 
	std::string m_identify;
 
	AutoJoinMap m_autoJoin;
 
	std::string m_topicData;
 
	bool m_connected;
 
};
 

	
 
//class MyIrcBuffer : public Irc::Buffer
 
//{
 
//    Q_OBJECT
 

	
 
//public:
include/transport/config.h
Show inline comments
 
@@ -44,25 +44,24 @@ const myType &safeAs(const boost::program_options::variable_value &var, const my
 

	
 
#define CONFIG_HAS_KEY(PTR, KEY) (*PTR).hasKey(KEY)
 
#define CONFIG_STRING(PTR, KEY) (*PTR)[KEY].as<std::string>()
 
#define CONFIG_INT(PTR, KEY) (*PTR)[KEY].as<int>()
 
#define CONFIG_BOOL(PTR, KEY) (*PTR)[KEY].as<bool>()
 
#define CONFIG_LIST(PTR, KEY) (*PTR)[KEY].as<std::list<std::string> >()
 
#define CONFIG_VECTOR(PTR, KEY) ((*PTR).hasKey(KEY) ? (*PTR)[KEY].as<std::vector<std::string> >() : std::vector<std::string>())
 

	
 
#define CONFIG_STRING_DEFAULTED(PTR, KEY, DEF) ((*PTR).hasKey(KEY) ? Transport::safeAs<std::string>((*PTR)[KEY], DEF) : DEF)
 
#define CONFIG_BOOL_DEFAULTED(PTR, KEY, DEF) ((*PTR).hasKey(KEY) ? Transport::safeAs<bool>((*PTR)[KEY], DEF) : DEF)
 
#define CONFIG_LIST_DEFAULTED(PTR, KEY, DEF) ((*PTR).hasKey(KEY) ? Transport::safeAs<std::list<std::string> >((*PTR)[KEY], DEF) : DEF)
 

	
 

	
 
namespace Transport {
 

	
 
/// Represents variable:value pairs.
 
typedef boost::program_options::variables_map Variables;
 

	
 
/// Represents config file.
 

	
 
/// It's used to load config file and allows others parts of libtransport to be configured
 
/// properly. Config files are text files which use "ini" format. Variables are divided into multiple
 
/// sections. Every class is configurable with some variables which change its behavior. Check particular
 
/// class documentation to get a list of all relevant variables for that class.
 
class Config {
0 comments (0 inline, 0 general)