/*
EQ2Emulator: Everquest II Server Emulator
Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
This file is part of EQ2Emulator.
EQ2Emulator 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 3 of the License, or
(at your option) any later version.
EQ2Emulator 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 EQ2Emulator. If not, see .
*/
#include
#ifdef _WIN32
#include
#include
#include
#else
#include
#include
#include "../../common/unix.h"
#endif
#include "IRCReplyCodes.h"
#include "IRC.h"
#include "../Chat/Chat.h"
#include "../../common/Log.h"
#include "../Rules/Rules.h"
extern Chat chat;
extern RuleManager rule_manager;
IRC::IRC() {
running = false;
m_globalServer = 0;
m_servers.SetName("IRC::servers");
}
IRC::~IRC() {
map::iterator itr;
m_servers.writelock(__FUNCTION__, __LINE__);
for (itr = servers.begin(); itr != servers.end(); itr++)
safe_delete(itr->second);
m_servers.releasewritelock(__FUNCTION__, __LINE__);
safe_delete(m_globalServer);
}
ThreadReturnType ServerLoop(void *arg);
void IRC::Start() {
if (running)
return;
running = true;
#ifdef _WIN32
_beginthread(ServerLoop, 0, this);
#else
pthread_t thread;
pthread_create(&thread, NULL, ServerLoop, this);
pthread_detach(thread);
#endif
}
int32 IRC::GetNumServers() {
int32 count;
m_servers.readlock(__FUNCTION__, __LINE__);
count = servers.size();
m_servers.releasereadlock(__FUNCTION__, __LINE__);
return count;
}
const char * IRC::GetSafeChannelName(const char *channel_name) {
char *safe_channel_name;
size_t len;
assert(channel_name != NULL);
len = strlen(channel_name) + 2;
safe_channel_name = new char[len];
if (channel_name[0] != '#')
snprintf(safe_channel_name, len, "#%s", channel_name);
else
strncpy(safe_channel_name, channel_name, len);
return safe_channel_name;
}
void IRC::ConnectToServer(Client *client, const char *host, short port, const char *nick) {
int32 character_id;
IRCServer *server;
int ret;
assert(client != NULL);
assert(host != NULL);
character_id = client->GetCharacterID();
m_servers.writelock(__FUNCTION__, __LINE__);
if (servers.count(character_id) > 0)
client->Message(CHANNEL_COLOR_YELLOW, "You are already connected to IRC server %s.", servers[character_id]->GetHost());
else {
client->Message(CHANNEL_COLOR_YELLOW, "Connecting to IRC server %s:%i.", host, port);
server = new IRCServer(character_id, host, port, nick == NULL || strlen(nick) == 0 ? client->GetPlayer()->GetName() : nick);
ret = server->Connect();
switch (ret) {
case IRC_CONNECT_SUCCESS:
servers[character_id] = server;
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Successfully connected to IRC server!");
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "You can now join channels on this server using /irc join .");
break;
case IRC_CONNECT_ALREADY_CONNECTED:
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "You are already connected to this IRC server!");
break;
case IRC_CONNECT_NO_NICK:
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Could not connect to IRC server. A nick was never given.");
break;
case IRC_CONNECT_WINSOCK_INIT:
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Could not connect to IRC server. Failed to initailize Winsock.");
break;
case IRC_CONNECT_WINSOCK_VERSION:
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Could not connect to IRC server. Winsock version 2.2 was not found. Contact your EQ2Emu server administrator.");
break;
case IRC_CONNECT_SOCKET:
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Could not connect to IRC server. Failed to create a socket.");
break;
case IRC_CONNECT_NO_HOST:
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Could not connect to IRC server. That host does not exist.");
break;
case IRC_CONNECT_FAIL:
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Could not connect to IRC server. Failed to connect.");
break;
case IRC_CONNECT_SOCKET_OPT:
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "Could not connect to IRC server. Could not set socket options.");
break;
default:
client->Message(CHANNEL_COLOR_YELLOW, "Could not connect to IRC server. Unknown error (%i).", ret);
break;
}
}
m_servers.releasewritelock(__FUNCTION__, __LINE__);
}
void IRC::ConnectToGlobalServer(const char *host, short port, const char *nick) {
IRCServer* server = 0;
ChatChannel* channel = 0;
const char* channel_name = 0;
int ret;
assert(host != NULL);
assert(nick != NULL);
if (m_globalServer)
LogWrite(CHAT__ERROR, 0, "IRC", "You are already connected to the global IRC server %s.", m_globalServer->GetHost());
else {
LogWrite(CHAT__DEBUG, 0, "IRC", "Connecting to IRC server %s:%i.", host, port);
server = new IRCServer(host, port, nick);
ret = server->Connect();
switch (ret) {
case IRC_CONNECT_SUCCESS:
m_globalServer = server;
LogWrite(CHAT__DEBUG, 0, "IRC", "Successfully connected to the global IRC server!");
// Get the global irc channel
channel_name = rule_manager.GetGlobalRule(R_World, IRCChan)->GetString();
// Join the channel
m_globalServer->JoinChannel(channel_name);
// Remove the leading # if there was one
if (channel_name[0] == '#')
channel_name++;
// Check to see if a EQ2 chat channel exists, if not create it
if (!chat.ChannelExists(channel_name)) {
//chat.CreateChannel(channel_name);
ChatChannel* new_channel = new ChatChannel();
new_channel->SetName(channel_name);
new_channel->SetLevelRestriction(0);
new_channel->SetClassesAllowed(0);
new_channel->SetRacesAllowed(0);
new_channel->SetType(CHAT_CHANNEL_TYPE_WORLD);
chat.AddChannel(new_channel);
}
// Get the EQ2 Channel
channel = chat.GetChannel(channel_name);
// Make sure we got the channel
if (channel) {
// Set the channel as the global IRC channel
channel->SetGlobalIRCChannel(true);
}
else {
// "Should" never end up here as we make sure the channel exists before we get it, send an error if we do end up in here some how
LogWrite(CHAT__ERROR, 0, "IRC", "Unable to set the global IRC channel");
}
break;
case IRC_CONNECT_ALREADY_CONNECTED:
LogWrite(CHAT__ERROR, 0, "IRC", "You are already connected to the global IRC server!");
safe_delete(server);
break;
case IRC_CONNECT_NO_NICK:
LogWrite(CHAT__ERROR, 0, "IRC", "Could not connect to global IRC server. A nick was never given.");
safe_delete(server);
break;
case IRC_CONNECT_WINSOCK_INIT:
LogWrite(CHAT__ERROR, 0, "IRC", "Could not connect to global IRC server. Failed to initailize Winsock.");
safe_delete(server);
break;
case IRC_CONNECT_WINSOCK_VERSION:
LogWrite(CHAT__ERROR, 0, "IRC", "Could not connect to global IRC server. Winsock version 2.2 was not found. Contact your EQ2Emu server administrator.");
safe_delete(server);
break;
case IRC_CONNECT_SOCKET:
LogWrite(CHAT__ERROR, 0, "IRC", "Could not connect to global IRC server. Failed to create a socket.");
safe_delete(server);
break;
case IRC_CONNECT_NO_HOST:
LogWrite(CHAT__ERROR, 0, "IRC", "Could not connect to global IRC server. That host does not exist.");
safe_delete(server);
break;
case IRC_CONNECT_FAIL:
LogWrite(CHAT__ERROR, 0, "IRC", "Could not connect to global IRC server. Failed to connect.");
safe_delete(server);
break;
case IRC_CONNECT_SOCKET_OPT:
LogWrite(CHAT__ERROR, 0, "IRC", "Could not connect to global IRC server. Could not set socket options.");
safe_delete(server);
break;
default:
LogWrite(CHAT__ERROR, 0, "IRC", "Could not connect to global IRC server. Unknown error (%i).", ret);
safe_delete(server);
break;
}
}
}
void IRC::DisconnectFromServer(Client *client) {
IRCServer *server;
int32 character_id;
assert(client != NULL);
character_id = client->GetCharacterID();
m_servers.writelock(__FUNCTION__, __LINE__);
if (servers.count(character_id) == 0)
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "You are not connected to an IRC server!");
else {
server = servers[character_id];
client->Message(CHANNEL_COLOR_YELLOW, "You have been disconnected from IRC server %s.", server->GetHost());
server->Disconnect();
safe_delete(server);
servers.erase(character_id);
}
m_servers.releasewritelock(__FUNCTION__, __LINE__);
}
void IRC::DisconnectFromGlobalServer() {
if (!m_globalServer)
LogWrite(CHAT__ERROR, 0, "IRC", "You are not connected to a global IRC server!");
else {
LogWrite(CHAT__DEBUG, 0, "IRC", "You have been disconnected from the global IRC server (%s).", m_globalServer->GetHost());
m_globalServer->Disconnect();
safe_delete(m_globalServer);
}
}
void IRC::JoinChannel(Client *client, const char *channel_name) {
const char *safe_channel_name;
int32 character_id;
int ret;
assert(client != NULL);
assert(channel_name != NULL);
character_id = client->GetCharacterID();
//if the user didn't include a hash in the channel name, add it
safe_channel_name = GetSafeChannelName(channel_name);
m_servers.readlock(__FUNCTION__, __LINE__);
if (servers.count(character_id) == 0)
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "You are not connected to an IRC server!");
else {
ret = servers[character_id]->JoinChannel(safe_channel_name);
switch (ret) {
case IRC_JOIN_CHANNEL_SUCCESS:
client->Message(CHANNEL_COLOR_YELLOW, "Joining IRC channel %s.", safe_channel_name);
if (channel_name[0] == '#')
channel_name++;
if (!chat.ChannelExists(channel_name))
chat.CreateChannel(channel_name);
if (!chat.IsInChannel(client, channel_name))
chat.JoinChannel(client, channel_name);
break;
case IRC_JOIN_CHANNEL_ALREADY_IN:
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "You are already in that IRC channel!");
break;
default:
client->Message(CHANNEL_COLOR_YELLOW, "Error joining channel. Unknown error (%i).", ret);
break;
}
}
m_servers.releasereadlock(__FUNCTION__, __LINE__);
safe_delete_array(safe_channel_name);
}
void IRC::LeaveChannel(Client *client, const char *channel_name) {
const char *safe_channel_name;
int32 character_id;
int ret;
assert(client != NULL);
assert(channel_name != NULL);
character_id = client->GetCharacterID();
//if the user didn't include a hash in the channel name, add it
safe_channel_name = GetSafeChannelName(channel_name);
m_servers.readlock(__FUNCTION__, __LINE__);
if (servers.count(character_id) == 0)
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "You are not connected to an IRC server!");
else {
ret = servers[character_id]->LeaveChannel(safe_channel_name);
switch (ret) {
case IRC_LEAVE_CHANNEL_SUCCESS:
client->Message(CHANNEL_COLOR_YELLOW, "You have left IRC channel %s.", safe_channel_name);
break;
case IRC_LEAVE_CHANNEL_NOT_IN:
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "You are not in that IRC channel.");
break;
default:
client->Message(CHANNEL_COLOR_YELLOW, "Error leaving channel. Unknown error (%i).", ret);
break;
}
}
m_servers.releasereadlock(__FUNCTION__, __LINE__);
safe_delete_array(safe_channel_name);
}
void IRC::ListChannels(Client *client) {
vector *channels;
vector::iterator itr;
IRCServer *server;
int32 character_id, i;
assert(client != NULL);
character_id = client->GetCharacterID();
m_servers.readlock(__FUNCTION__, __LINE__);
if (servers.count(character_id) == 0)
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "You are not connected to an IRC server!");
else {
server = servers[character_id];
channels = server->GetChannels();
client->Message(CHANNEL_COLOR_YELLOW, "IRC Channels you are logged into on %s:", server->GetHost());
if (channels->size() == 0)
client->SimpleMessage(CHANNEL_COLOR_YELLOW, " None");
else {
i = 1;
for (itr = channels->begin(); itr != channels->end(); itr++)
client->Message(CHANNEL_COLOR_YELLOW, " %i) %s", i++, ((*itr)->GetName()));
}
}
m_servers.releasereadlock(__FUNCTION__, __LINE__);
}
void IRC::Say(Client *client, const char *channel_name, const char *message) {
const char *safe_channel_name;
int32 character_id;
int ret;
IRCServer* server = 0;
assert(channel_name != NULL);
assert(message != NULL);
if (client)
character_id = client->GetCharacterID();
//if the user didn't include a hash in the channel name, add it
safe_channel_name = GetSafeChannelName(channel_name);
m_servers.readlock(__FUNCTION__, __LINE__);
if (!m_globalServer && servers.count(character_id) == 0) {
if (client)
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "You are not connected to an IRC server!");
else
LogWrite(CHAT__DEBUG, 0, "IRC", "Not connected to a global IRC server");
}
else {
if (client)
server = servers[character_id];
else
server = m_globalServer;
ret = server->Say(safe_channel_name, message);
switch (ret) {
case IRC_SAY_SUCCESS:
//client->Message(CHANNEL_COLOR_YELLOW, "You say to %s, \"%s\"", safe_channel_name, message);
break;
case IRC_SAY_NOT_IN:
if (client)
client->Message(CHANNEL_COLOR_YELLOW, "You are not in channel %s. Use /irc list to see what channels you are in.", safe_channel_name);
else
LogWrite(CHAT__DEBUG, 0, "IRC", "Global IRC does not contain the channel %s.", safe_channel_name);
break;
default:
if (client)
client->Message(CHANNEL_COLOR_YELLOW, "Error sending message to %s. Unknown error (%i).", safe_channel_name, ret);
else
LogWrite(CHAT__DEBUG, 0, "IRC", "Error sending message to %s. Unknown error (%i).", safe_channel_name, ret);
break;
}
}
m_servers.releasereadlock(__FUNCTION__, __LINE__);
safe_delete_array(safe_channel_name);
}
void IRC::Say(Client *client, int32 channel_index, const char *message) {
IRCChannel *channel;
int32 character_id;
int ret;
assert(client != NULL);
assert(message != NULL);
character_id = client->GetCharacterID();
m_servers.readlock(__FUNCTION__, __LINE__);
if (servers.count(character_id) == 0)
client->SimpleMessage(CHANNEL_COLOR_YELLOW, "You are not connected to an IRC server!");
else {
ret = servers[character_id]->Say(channel_index, message);
channel = servers[character_id]->GetChannel(channel_index);
switch (ret) {
case IRC_SAY_SUCCESS:
client->Message(CHANNEL_COLOR_YELLOW, "You say to %s, \"%s\"", channel->GetName(), message);
break;
case IRC_SAY_NOT_IN:
client->Message(CHANNEL_COLOR_YELLOW, "You are not in a channel at index %u. Use /irc list to see what channels you are in.", channel_index);
break;
default:
client->Message(CHANNEL_COLOR_YELLOW, "Error sending message to channel at index %i. Unknown error (%i).", channel_index, ret);
break;
}
}
m_servers.releasereadlock(__FUNCTION__, __LINE__);
}
void IRC::Process() {
map::iterator itr;
vector removes;
vector::iterator itr_removes;
int32 character_id;
IRCServer *server;
m_servers.readlock(__FUNCTION__, __LINE__);
for (itr = servers.begin(); itr != servers.end(); itr++) {
server = itr->second;
if (server->IsConnected()) {
//if this connection fails to process, disconnect it
if (!server->Process())
removes.push_back(itr->first);
}
}
m_servers.releasereadlock(__FUNCTION__, __LINE__);
// Process the global irc server if there is one
if (m_globalServer && m_globalServer->IsConnected())
m_globalServer->Process();
//process any bad connections
if (removes.size() > 0) {
m_servers.writelock(__FUNCTION__, __LINE__);
for (itr_removes = removes.begin(); itr_removes != removes.end(); itr_removes++) {
character_id = *itr_removes;
if (servers.count(character_id) > 0) {
safe_delete(servers[character_id]);
servers.erase(character_id);
}
}
m_servers.releasewritelock(__FUNCTION__, __LINE__);
}
}
ThreadReturnType ServerLoop(void *arg) {
IRC *irc = (IRC *)arg;
while (irc->IsRunning()) {
irc->Process();
if (irc->GetNumServers() == 0)
Sleep(1000);
else
Sleep(100);
}
THREAD_RETURN(NULL);
}
IRCServer* IRC::GetServer(Client* client) {
IRCServer* ret = 0;
m_servers.readlock(__FUNCTION__, __LINE__);
if (servers.count(client->GetCharacterID()) != 0)
ret = servers[client->GetCharacterID()];
m_servers.releasereadlock(__FUNCTION__, __LINE__);
return ret;
}