------

[ AD ] Port Monitor ( Try to use a Best WebSite Monitoring Tool )

------
#ifndef CLIENT_H
#define CLIENT_H

#ifdef _WIN32
	#define _WIN32_WINNT 0x0501
#endif

#include 
#include 
#include 
#include 
#include 

#include "auxiliar.h"

class User;
class PingPong;

class Client : private boost::noncopyable,
	public boost::enable_shared_from_this
{
	public:
		Client(boost::asio::io_service& service);
		~Client();

		static unsigned int clientCount;

		void start();

		void send(const std::string& msg);
		void close();

		void closeConnection(const std::string& reason);

		std::string getHostName();
		std::string getIP();

		boost::asio::ip::tcp::socket& socket() { return m_socket; }
		boost::asio::io_service::strand& _strand() { return strand; }

	protected:
		void receiveFromSocket(const boost::system::error_code& e, std::size_t bytes);
		void disconnectClient();

	private:
		User* m_user;
		bool sentMotd;

		void firstTime();
		void handleRequest(StringVec __msg);
		void sendLine(const char *fmt, ...);
		bool sentUser;
		bool sentCommon;
		bool closed;
		boost::asio::ip::tcp::socket m_socket;
		boost::asio::io_service::strand strand;
		boost::array m_buffers;
		uint32_t eventId, pongEvent;

		void handle_write(const boost::system::error_code& e, size_t bytes);
		void closeWithError(int error);
		void receive();

		PingPong* pingPong;
};
#endif

#include "client.h"

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#ifndef _WIN32
	#include 
	#include 
#endif

#include "user.h"
#include "channels.h"
#include "auxiliar.h"
#include "defines.h"
#include "configreader.h"
#include "dispatcher.h"
#include "mainframe.h"
#include "scripting.h"

extern Config* g_config;
extern Channels* g_channels;
extern MainFrame* g_mainFrame;
extern Script* g_script;
extern Dispatcher* g_dispatcher;

unsigned int Client::clientCount;

/*!
* @brief client class
* \todo shutdown socket on each error.
  */

/*!
* @brief Ping? Pong! class
  */

class PingPong
{
	public:
		PingPong(Client* c);
		~PingPong() {}

		void sendPing();
		int32_t getLastPingTime() { return lastPong; }

		void onReceivePong(const boost::system::error_code& e, std::size_t bytes_transferred);
		bool gotReply() { return reply; }

	private:
		Client* m_client;
		int32_t lastPong;
		std::string lastPing;
		bool reply;
		boost::array m_buffers;

	protected:
		void closeConnection();
};

PingPong::PingPong(Client* c)
	: m_client(c)
{
	reply = false;
	lastPing = "";
	lastPong = 0;
}

void PingPong::sendPing()
{
	reply = false;
	std::stringstream ss;
	ss << "PING :";
	for (int32_t i = 1; i < 10; ++i) {
		int32_t p = rand() % i + 1;
		ss << p;
		lastPing += static_cast(p);
	}
	ss << "\r\n";
	boost::asio::write(m_client->socket(), boost::asio::buffer(ss.str(), ss.str().length()));
	lastPong++;
	m_client->socket().async_receive(boost::asio::buffer(m_buffers),
		m_client->_strand().wrap(
			boost::bind(&PingPong::onReceivePong, this,
			boost::asio::placeholders::error,
			boost::asio::placeholders::bytes_transferred)));
}

void PingPong::onReceivePong(const boost::system::error_code& e, std::size_t bytes_transferred)
{
	if (e) {
		m_client->socket().async_receive(boost::asio::buffer(m_buffers),
			m_client->_strand().wrap(
				boost::bind(&PingPong::onReceivePong, this,
					boost::asio::placeholders::error,
					boost::asio::placeholders::bytes_transferred)));
		return;
	}
	std::string str(m_buffers.data());
	size_t __wat = str.rfind("\r\n");
	if (__wat == std::string::npos) {
		__wat = str.rfind("\n");
		if (__wat == std::string::npos) {
			m_client->socket().async_read_some(boost::asio::buffer(m_buffers),
				m_client->_strand().wrap(
					boost::bind(&PingPong::onReceivePong, this,
						boost::asio::placeholders::error,
						boost::asio::placeholders::bytes_transferred)));
			return;
		}
	}
	str.erase(__wat);
	if (str.substr(0, 4) == "PONG") {
		std::string pong = str.substr(5, str.length()-5);
		if (pong == lastPing && getLastPingTime() != 120) {
			lastPong = 0xF * 8;
			reply = true;
			g_dispatcher->addEvent(new Task(lastPong, boost::bind(
				&PingPong::sendPing, this)));
		}
	} else {
		g_dispatcher->addEvent(new Task(40, boost::bind(
			&PingPong::closeConnection, this)));
	}
}

void PingPong::closeConnection()
{
	std::stringstream p;
	p << "ERROR: Closing link (Ping timeout)";
	boost::asio::write(m_client->socket(), boost::asio::buffer(p.str(), p.str().length()));
	boost::system::error_code ignored_ec;
	m_client->socket().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec);
	m_client->socket().close();
}

Client::Client(boost::asio::io_service& service)
	: m_socket(service), strand(service)
{
	pingPong = new PingPong(this);
	eventId = pongEvent = 0;
	clientCount++;
	sentUser = sentCommon = sentMotd = false;
	m_user = NULL;
	closed = false;
}

Client::~Client()
{
	if (clientCount > 0)
		clientCount--;
}

void Client::sendLine(const char *fmt, ...)
{
	va_list arg;
	va_start(arg, fmt);
	char buffer[512];
	vsprintf(buffer, fmt, arg);
	va_end(arg);
	send(std::string(buffer));
}

void Client::firstTime()
{
	std::stringstream ss;
	ss << MOTD_COMMON << "Looking up your hostname\r\n";
	send(ss.str());
	ss.str("");
	ss << MOTD_COMMON << "Checking Ident\r\n";
	send(ss.str());
	ss.str("");
	if (!getHostName().empty())
		ss << MOTD_COMMON << "Found your hostname: " << getHostName() << ".";
	else
		ss << MOTD_COMMON << "Couldn't look up your hostname... Using your IP address instead";

	ss << "\r\n";
	send(ss.str());
	ss.str("");
	switch (ident(getIP())) {
		case SOCKET_NOT_INITED:
		{
			std::clog << "[Info - Client::useIdent] - Unable to initalize socket." << std::endl;
			break;
		}
		case SOCKET_NOT_CONNECTED:
		{
			std::clog << "[Info - Client::useIdent] - Unable to connect socket." << std::endl;
			break;
		}
		case SOCKET_UNABLE_TO_WRITE:
		{
			std::clog << "[Info - Client::useIdent] - Unable to write data on the socket." << std::endl;
			break;
		}
		case SOCKET_UNABLE_TO_READ:
		{
			std::clog << "[Info - Client::useIdent] - Unable to read data on the socket." << std::endl;
			break;
		}
		case CONNECTION_SUCCESS:
		default:
		{
			ss << MOTD_COMMON << "Got ident response";
			ss << "\r\n";
			send(ss.str());
			return;
		}
	}
	ss.str("");
	ss << MOTD_COMMON << "No ident response";
	ss << "\r\n";
	send(ss.str());
}

void Client::start()
{
	g_script->addClient(this);
	std::clog << "New Client." << std::endl;
	firstTime();
	g_mainFrame->addClient(this);
	receive();
}

void 
Client::receive()
{
	m_socket.async_receive(boost::asio::buffer(m_buffers),
		strand.wrap(
			boost::bind(&Client::receiveFromSocket, shared_from_this(),
				boost::asio::placeholders::error,
					boost::asio::placeholders::bytes_transferred)));
}

std::string Client::getIP()
{
	return m_socket.remote_endpoint().address().to_string();
}

std::string Client::getHostName()
{
	hostent* remoteHost = gethostbyaddr(getIP().c_str(), 4, AF_INET);
	if (remoteHost)
		return std::string(remoteHost->h_name);

	return std::string();
}

void Client::send(const std::string& msg)
{
	boost::asio::async_write(m_socket, boost::asio::buffer(msg),
		boost::bind(&Client::handle_write, shared_from_this(),
			boost::asio::placeholders::error,
			boost::asio::placeholders::bytes_transferred));
}

void
Client::handle_write(const boost::system::error_code& e, size_t bytes)
{
	if (e)
		closeWithError(0xFF);
}

void Client::closeWithError(int error)
{
	switch (error) {
		case 10054:
		{
			closeConnection("Read error: Connection reset by peer");
			break;
		}
		case 10053:
		{
			closeConnection("Software caused connection abort");
			break;
		}
		default:
		{
			closeConnection("Input/Output error");
			break;
		}
	}
}

void Client::receiveFromSocket(const boost::system::error_code& e, std::size_t bytes)
{
	if (e) {
		closeWithError(10054);
		return;
	}
	std::string str(m_buffers.data());
	if (str.empty())
		return;

	size_t __wat = str.rfind("\r\n");
	if (__wat == std::string::npos) {
		__wat = str.rfind("\n");
		if (__wat == std::string::npos) {
		 	receive();
			return;
		}
	}
	str.erase(__wat);
	/*size_t start = 0, end;
	std::string sep = (str.rfind("\r\n") != std::string::npos ? "\r\n" : "\n");
	while ((end = str.find(sep, start)) != std::string::npos) {
		str.erase(end);
		start = end + sep.size();
	}*/
	std::clog << "Received message from client: " << getIP() << " :" << str << std::endl;
	StringVec splited = splitString(str, " ");
	std::clog << "splited[0] = " << splited[0] << "." << std::endl;
	if (splited[0] == "USER") {
		if (splited.size() < 4) {
			receive();
			return;
		}

		std::string localUser = splited[4];
		size_t sep = localUser.find(":");
		if (sep == std::string::npos) {
			receive();
			return;
		}

		localUser.erase(sep);
		if (!m_user) {
			if (localUser.length() > 15)
				m_user = new User(localUser.substr(0, 15), this);
			else
				m_user = new User(localUser, this);
		} else {
			if (localUser.length() > 15)
				m_user->setName(localUser.substr(0, 15));
			else
				m_user->setName(localUser);
		}
		if (!sentUser)
			sentUser = true;

	} else if (splited[0] == "NICK") {
		if (splited.size() < 2) {
			receive();
			return;
		}

		bool canUse = true;
		for (uint32_t i = 0; i < sizeof(forbiddenNames) / sizeof(const char*); ++i) {
			if (toLower(splited[1]) == toLower(std::string(&forbiddenNames[i]))) {
				canUse = false;
				sendLine(":%s 433 * %s :Nickname is already in use.\r\n", g_config->getString(Config::C_HOST).c_str(),
					splited[1].c_str());
				break;
			}
		}
		if (!canUse) {
			receive();
			return;
		}

		if (!m_user) {
			if (splited.size() > 15)
				m_user = new User(splited[1].substr(0, 15), this);
			else
				m_user = new User(splited[1], this);
		}
		if (splited.size() > 15)
			m_user->setNick(splited[1].substr(0, 15));
		else
			m_user->setNick(splited[1]);

		if (!sentCommon) {
			m_user->sendMotd(":%s 001 %s :Welcome to %s IRC network, %s\r\n", m_user->getServer().c_str(),
				m_user->getName().c_str(), g_config->getString(0x07).c_str(), m_user->getNick().c_str());
			m_user->sendMotd(":%s 002 %s :Your host is %s running version %s\r\n", m_user->getServer().c_str(),
				m_user->getName().c_str(), m_user->getHost().c_str(), __DISTURBTION__);
			m_user->sendMotd(":%s 003 %s :This server was created %s %s\r\n", m_user->getServer().c_str(),
				m_user->getName().c_str(), __TIME__, __DATE__);
			m_user->sendMotd(":%s 252 %s %d :operator(s) online\r\n", m_user->getServer().c_str(),
				m_user->getName().c_str(), m_user->operCount);
			m_user->sendMotd(":%s NOTICE %s :Highest connection count: %d (%d clients)\r\n", m_user->getServer().c_str(),
				m_user->getName().c_str(), m_user->userCount, Client::clientCount);
			m_user->sendMotd(":%s 221 %s +i\r\n", m_user->getServer().c_str(),
				m_user->getNick().c_str());
			m_user->setMode('i');
			m_user->sendMotd(":%s!%s@%s MODE %s +i\r\n", m_user->getServer().c_str(),
				m_user->getIdent().c_str(), m_user->getHost().c_str(),
				m_user->getNick().c_str());
			sentCommon = true;
		}
		if (!m_user->getName().empty() && !m_user->getNick().empty())
			g_mainFrame->addUser(m_user);

	} else {
		if (!sentUser || !m_user && !closed) {
			eventId = g_dispatcher->addEvent(new Task(120, boost::bind(&Client::closeConnection, this, "Registration timeout")));
			receive();
			closed = true;
		} else {
			if (pongEvent == 0)
				pongEvent = g_dispatcher->addEvent(new Task(0, boost::bind(&PingPong::sendPing, pingPong)));

			if (pingPong->gotReply()) {
				g_dispatcher->stopEvent(eventId);
				handleRequest(splited);
			}
		}
	}
}

void Client::disconnectClient()
{
	g_script->removeClient(this);
	std::clog << "Client disconnected: " << getIP() << std::endl;
	clientCount--;
	if (m_user)
		m_user->clearChannels();

	if (m_user) {
		delete m_user;
		m_user = NULL;
	}
}

void Client::closeConnection(const std::string& reason)
{
	std::clog << "Closing connection: " << getIP() << " reason: " << reason << "...";
	g_script->removeClient(this);
	if (m_user) {
		m_user->quit(reason);
		delete m_user;
		m_user = NULL;
	}
	boost::system::error_code ignored_ec;
	m_socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec);
	m_socket.close();
	std::clog << "[OK]" << std::endl;
}

void Client::handleRequest(StringVec __msg)
{
	StringVec splited = __msg;
	/* change mode */
	if (splited[0] == "MODE") {
		if (splited.size() < 2) //requires 2 params
			return;

		if (splited[1] != m_user->getNick()) { //channel mode
			if (splited[1].substr(0, 1) == "#") {
				Channel* chan = m_user->getChannelByName(splited[1]);
				if (!chan) {
					m_user->sendLine(":%s 442 %s %s :You're not on that channel\r\n", m_user->getServer().c_str(),
						m_user->getName().c_str(), splited[1].c_str());
					return;
				} else {
					if (chan->getUserMode(m_user, UM_OP)) {
						for (_UsersMap::iterator it = chan->getUsers()->begin(); it != chan->getUsers()->end(); ++it)
							it->second->sendLine(":%s!%s@%s MODE %s %s\r\n", m_user->getNick().c_str(),
								m_user->getIdent().c_str(), m_user->getHost().c_str(), splited[1].c_str(),
									splited[2].c_str());
					} else {
						m_user->sendLine(":%s 482 %s %s :You're not channel operator\r\n", m_user->getServer().c_str(),
							m_user->getName().c_str(), splited[1].c_str());
						return;
					}
				}
			}
		} else { //user mode
			if (m_user->hasMode(*const_cast(splited[2].c_str()) - 65))
				return;

			m_user->setMode(*const_cast(splited[2].c_str()));
			m_user->sendLine(":%s!%s@%s MODE %s %s\r\n", m_user->getNick().c_str(),
				m_user->getIdent().c_str(), m_user->getHost().c_str(), m_user->getNick().c_str(),
					splited[2].c_str());
		}
	/* join a channel */
	} else if (splited[0] == "JOIN") {
		if (splited.size() < 1)
			return;

		Channel* p = NULL;
		if ((p = m_user->getChannelByName(splited[1])))
			return;

		if (!(p = g_channels->getChannel(splited[1]))) { //create new
			Channel* chan = new Channel(splited[1]);
			m_user->sendLine(":%s!%s@%s JOIN %s\r\n", m_user->getName().c_str(),
				m_user->getIdent().c_str(), m_user->getHost().c_str(), splited[1].c_str());
			m_user->sendLine(":%s 353 %s = %s @%s\r\n", m_user->getServer().c_str(),
				m_user->getNick().c_str(), chan->getName().c_str(), m_user->getNick().c_str());
			m_user->sendLine(":%s 366 %s %s :End of /NAMES list.\r\n", m_user->getServer().c_str(),
				m_user->getNick().c_str(), chan->getName().c_str());
			g_channels->addChannel(chan);
			m_user->joinChannel(splited[1]);
		} else { //already exists
			Ban *b = p->getBan(m_user);
			if (!B) { //b& or not
				if (!p->addUser(m_user))
					return;

				std::string users = "";
				_UsersMap *__u = p->getUsers();
				for (_UsersMap::const_iterator it = __u->begin(); it != __u->end(); ++it) {
					if (!it->second)
						continue;

					if (p->getUserMode(it->second, UM_OP))
						users += "@" + it->second->getNick() + " ";
					else if (p->getUserMode(it->second, UM_VOICE))
						users += "+" + it->second->getNick() + " ";
					else
						users += it->second->getNick() + " ";
				}
				m_user->sendLine(":%s!%s@%s JOIN %s\r\n", m_user->getName().c_str(),
					m_user->getIdent().c_str(), m_user->getHost().c_str(), splited[1].c_str());
				m_user->sendLine(":%s 353 %s = %s %s\r\n", m_user->getServer().c_str(),
					m_user->getNick().c_str(), p->getName().c_str(), users.c_str());
				m_user->sendLine(":%s 366 %s %s :End of /NAMES list.\r\n", m_user->getServer().c_str(),
					m_user->getNick().c_str(), p->getName().c_str());
				m_user->joinChannel(splited[1]);
			} else {
				m_user->sendLine(":%s 474 %s %s :Cannot join channel, you are banned (+B)\r\n",
					m_user->getServer().c_str(), m_user->getNick().c_str(), splited[1].c_str());
				return;
			}
		}
	/* part a channel */
	} else if (splited[0] == "PART") {
		//TODO
	/* become an operator */
	} else if (splited[0] == "OPER") {
		if (splited.size() > 1) {
			if (!m_user->checkOperPassword(splited[1], splited[2])) {
				m_user->sendLine(":%s 491 %s :No Operator block for your host\r\n", m_user->getServer().c_str(),
					m_user->getNick().c_str());
				return;
			} else {
				m_user->sendLine(":%s 381 %s :You are now IRC Operator\r\n", m_user->getServer().c_str(),
					m_user->getNick().c_str());
			}
		}
	/* who is someone */
	} else if (splited[0] == "WHOIS") {
		//TODO
	/* private message channel/someone */
	} else if (splited[0] == "PRIVMSG") {
		if (splited[1].substr(0, 1) == "#") {
			//private message a channel
			//lets do some checks on b&
			//or +m
			Channel* p = g_channels->getChannel(splited[1]);
			if (!p) {
				m_user->sendLine(":%s 403 %s %s :No such channel.\r\n", m_user->getServer().c_str(),
					m_user->getNick().c_str(), splited[1].c_str());
				return;
			} else if ((p = m_user->getChannelByName(splited[1]))) {
				if (p->getMode(CM_MODERATED)) {
					if (p->getUserMode(m_user, UM_NONE)) {
						m_user->sendLine(":%s 404 %s %s :Cannot send to channel\r\n", m_user->getServer().c_str(),
							m_user->getNick().c_str(), splited[1].c_str());
						return;
					} else {
						std::string msg = "";
						for (int32_t i = 2; i < static_cast(splited.size()); ++i)
							msg += splited[i] + " ";

						_UsersMap *__u = p->getUsers();
						for (_UsersMap::const_iterator it = __u->begin(); it != __u->end(); ++it) {
							if (!it->second)
								continue;

							it->second->sendPrivateMessage(m_user->getNick(), m_user->getIdent(),
								m_user->getHost(), splited[1], msg);
						}
					}
				} else {
					std::string msg = "";
					for (int32_t i = 2; i < static_cast(splited.size()); ++i)
						msg += splited[i] + " ";

					_UsersMap *__u = p->getUsers();
					for (_UsersMap::const_iterator it = __u->begin(); it != __u->end(); ++it) {
						if (!it->second)
							continue;

						it->second->sendPrivateMessage(m_user->getNick(), m_user->getIdent(),
							m_user->getHost(), splited[1], msg);
					}
				}
			}
		} else {
			// private message someone
			// maybe hes using modes +R
			User* user = g_mainFrame->getUserByName(splited[1]);
			if (!user) {
				m_user->sendLine(":%s 401 %s %s :No such nick\r\n", m_user->getServer().c_str(),
					m_user->getNick().c_str(), splited[1].c_str());
				return;
			} else {
				if (splited.size() < 2) {
					m_user->sendLine(":%s 412 %s :No text to send\r\n", m_user->getServer().c_str(),
						m_user->getNick().c_str());
					return;
				}
				if (!user->hasMode('R')) {
					std::string msg = "";
					for (int i = 2; i < static_cast(splited.size()); ++i)
						msg += splited[i] + " ";

					user->sendPrivateMessage(m_user->getNick(), m_user->getIdent(),
						m_user->getHost(), user->getNick(), msg);
				}
			}
		}
	/* restart server */
	} else if (splited[0] == "RESTART") {
		if (m_user->getType() != UT_IRC_OP) {
			m_user->sendLine(":%s 481 %s :Premission Denied: Insufficient privileges\r\n", m_user->getServer().c_str(),
				m_user->getNick().c_str());
			return;
		}
		std::string exec_name = g_config->getString(Config::C_EXECUTABLE_NAME);
		std::string cmd;
	#ifdef _WIN32
		cmd = exec_name + ".exe";
	#else
		cmd = "./" + exec_name;
	#endif
		std::system(cmd.c_str());
		std::exit(1);
	/* quit */
	} else if (splited[0] == "QUIT") {
		if (splited.size() > 0)
			closeConnection(splited[1]);
		else
			closeConnection("");

	/* user host */
	} else if (splited[0] == "USERHOST") {
		if (splited.size() < 1)
			return;

		User* target = g_mainFrame->getUserByName(splited[1]);
		if (!target)
			return;

		m_user->sendLine(":%s 302 %s :%s=+%s@%s\r\n", m_user->getServer().c_str(),
			m_user->getNick().c_str(), target->getName().c_str(),
			target->getIdent().c_str(), target->getHost().c_str());
	/* reload config */
	} else if (splited[0] == "RELOAD") {
		if (m_user->getType() != UT_IRC_OP)
			return;

		g_config->reload();
		g_script->reload();
		/*if (g_config->reload() && g_script->reload())
			m_user->sendPrivateMessage(Me[0].nick, Me[0].ident, Me[0].host, Me[0].nick, 
				"Reloaded everything.");
		else
			m_user->sendPrivateMessage(Me[0].nick, Me[0].ident, Me[0].host, Me[0].nick,
				"Unable to reload anything.");*/
	}
}

void Client::close()
{
	g_script->removeClient(this);
	clientCount--;
	delete m_user;
	m_user = NULL;
	boost::system::error_code ignored_ec;
	m_socket.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec);
	m_socket.close();
}

+ Recent posts