/* 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; }