diff --git a/common/eqemu_config.cpp b/common/eqemu_config.cpp index 1501c0559..96980f834 100644 --- a/common/eqemu_config.cpp +++ b/common/eqemu_config.cpp @@ -125,11 +125,24 @@ void EQEmuConfig::do_world(TiXmlElement *ele) if (text) { WorldTCPPort = atoi(text); } - text = sub_ele->Attribute("telnet"); - if (text && !strcasecmp(text, "enabled")) { + } + + sub_ele = ele->FirstChildElement("telnet"); + if (sub_ele != nullptr) { + text = sub_ele->Attribute("ip"); + if (text) { + TelnetIP = text; + } + text = sub_ele->Attribute("port"); + if (text) { + TelnetTCPPort = atoi(text); + } + text = sub_ele->Attribute("enabled"); + if (text && !strcasecmp(text, "true")) { TelnetEnabled = true; } } + // Get the element sub_ele = ele->FirstChildElement("http"); if (sub_ele != nullptr) { @@ -390,6 +403,12 @@ std::string EQEmuConfig::GetByName(const std::string &var_name) const if (var_name == "WorldIP") { return (WorldIP); } + if (var_name == "TelnetTCPPort") { + return (itoa(TelnetTCPPort)); + } + if (var_name == "TelnetIP") { + return (TelnetIP); + } if (var_name == "TelnetEnabled") { return (TelnetEnabled ? "true" : "false"); } @@ -510,6 +529,8 @@ void EQEmuConfig::Dump() const std::cout << "Locked = " << Locked << std::endl; std::cout << "WorldTCPPort = " << WorldTCPPort << std::endl; std::cout << "WorldIP = " << WorldIP << std::endl; + std::cout << "TelnetTCPPort = " << TelnetTCPPort << std::endl; + std::cout << "TelnetIP = " << TelnetIP << std::endl; std::cout << "TelnetEnabled = " << TelnetEnabled << std::endl; std::cout << "WorldHTTPPort = " << WorldHTTPPort << std::endl; std::cout << "WorldHTTPMimeFile = " << WorldHTTPMimeFile << std::endl; diff --git a/common/eqemu_config.h b/common/eqemu_config.h index 7146e29a8..0f51f550f 100644 --- a/common/eqemu_config.h +++ b/common/eqemu_config.h @@ -49,6 +49,8 @@ class EQEmuConfig : public XMLParser bool Locked; uint16 WorldTCPPort; std::string WorldIP; + uint16 TelnetTCPPort; + std::string TelnetIP; bool TelnetEnabled; int32 MaxClients; bool WorldHTTPEnabled; @@ -135,6 +137,7 @@ class EQEmuConfig : public XMLParser // World Locked = false; WorldTCPPort = 9000; + TelnetTCPPort = 9001; TelnetEnabled = false; WorldHTTPEnabled = false; WorldHTTPPort = 9080; @@ -189,6 +192,7 @@ class EQEmuConfig : public XMLParser DefaultStatus = 0; // For where zones need to connect to. WorldIP = "127.0.0.1"; + TelnetIP = "127.0.0.1"; // Dynamics to start //DynamicCount=5; MaxClients = -1; diff --git a/common/net/daybreak_connection.cpp b/common/net/daybreak_connection.cpp index 28e9951e2..329187ed7 100644 --- a/common/net/daybreak_connection.cpp +++ b/common/net/daybreak_connection.cpp @@ -119,6 +119,7 @@ void EQ::Net::DaybreakConnectionManager::Process() auto time_since_last_recv = std::chrono::duration_cast(now - connection->m_last_recv); if ((size_t)time_since_last_recv.count() > m_options.connect_stale_ms) { iter = m_connections.erase(iter); + Log(Logs::Detail, Logs::Netcode, "Disconnect reason: Connect Mode Timeout {0} > {1}", (size_t)time_since_last_recv.count(), m_options.connect_stale_ms); connection->ChangeStatus(StatusDisconnecting); continue; } @@ -127,6 +128,7 @@ void EQ::Net::DaybreakConnectionManager::Process() auto time_since_last_recv = std::chrono::duration_cast(now - connection->m_last_recv); if ((size_t)time_since_last_recv.count() > m_options.stale_connection_ms) { iter = m_connections.erase(iter); + Log(Logs::Detail, Logs::Netcode, "Disconnect reason: Time since last recv {0} > {1}", (size_t)time_since_last_recv.count(), m_options.stale_connection_ms); connection->ChangeStatus(StatusDisconnecting); continue; } @@ -276,7 +278,7 @@ EQ::Net::DaybreakConnection::DaybreakConnection(DaybreakConnectionManager *owner m_encode_passes[1] = owner->m_options.encode_passes[1]; m_hold_time = Clock::now(); m_buffered_packets_length = 0; - m_resend_delay = 500; + m_resend_delay = m_owner->m_options.resend_delay_ms + 25; m_rolling_ping = 100; m_combined.reset(new char[512]); m_combined[0] = 0; @@ -299,7 +301,7 @@ EQ::Net::DaybreakConnection::DaybreakConnection(DaybreakConnectionManager *owner m_crc_bytes = 0; m_hold_time = Clock::now(); m_buffered_packets_length = 0; - m_resend_delay = 500; + m_resend_delay = m_resend_delay = m_owner->m_options.resend_delay_ms + 25; m_rolling_ping = 100; m_combined.reset(new char[512]); m_combined[0] = 0; @@ -318,6 +320,7 @@ void EQ::Net::DaybreakConnection::Close() SendDisconnect(); m_close_time = Clock::now(); + Log(Logs::Detail, Logs::Netcode, "Disconnect reason: Server Request"); ChangeStatus(StatusDisconnecting); } else { @@ -355,6 +358,11 @@ void EQ::Net::DaybreakConnection::ResetStats() void EQ::Net::DaybreakConnection::Process() { try { + m_resend_delay = (size_t)(m_stats.last_ping * m_owner->m_options.resend_delay_factor) + m_owner->m_options.resend_delay_ms; + if (m_resend_delay > 500) { + m_resend_delay = 500; + } + auto now = Clock::now(); auto time_since_hold = (size_t)std::chrono::duration_cast(now - m_hold_time).count(); if (time_since_hold >= m_owner->m_options.hold_length_ms) { @@ -719,6 +727,7 @@ void EQ::Net::DaybreakConnection::ProcessDecodedPacket(const Packet &p) SendDisconnect(); } + Log(Logs::Detail, Logs::Netcode, "Disconnect reason: OP_SessionRequest from client."); ChangeStatus(StatusDisconnecting); break; } @@ -747,10 +756,6 @@ void EQ::Net::DaybreakConnection::ProcessDecodedPacket(const Packet &p) DynamicPacket out; out.PutSerialize(0, response); InternalSend(out); - - m_resend_delay = (size_t)(request.avg_ping * m_owner->m_options.resend_delay_factor) + m_owner->m_options.resend_delay_ms; - m_resend_delay = std::min(m_resend_delay, m_owner->m_options.resend_delay_max); - m_resend_delay = std::max(m_resend_delay, m_owner->m_options.resend_delay_min); break; } case OP_SessionStatResponse: @@ -1051,7 +1056,7 @@ void EQ::Net::DaybreakConnection::Ack(int stream, uint16_t seq) auto iter = s->sent_packets.begin(); while (iter != s->sent_packets.end()) { if (iter->first <= seq) { - uint64_t round_time = (uint64_t)std::chrono::duration_cast(now - iter->second.first_sent).count(); + uint64_t round_time = (uint64_t)std::chrono::duration_cast(now - iter->second.last_sent).count(); m_stats.total_ping += round_time; m_stats.total_acks++; m_stats.max_ping = std::max(m_stats.max_ping, round_time); @@ -1072,7 +1077,7 @@ void EQ::Net::DaybreakConnection::OutOfOrderAck(int stream, uint16_t seq) auto s = &m_streams[stream]; auto iter = s->sent_packets.find(seq); if (iter != s->sent_packets.end()) { - uint64_t round_time = (uint64_t)std::chrono::duration_cast(now - iter->second.first_sent).count(); + uint64_t round_time = (uint64_t)std::chrono::duration_cast(now - iter->second.last_sent).count(); m_stats.total_ping += round_time; m_stats.total_acks++; m_stats.max_ping = std::max(m_stats.max_ping, round_time); diff --git a/common/net/daybreak_connection.h b/common/net/daybreak_connection.h index fb81da818..a89e5b3e6 100644 --- a/common/net/daybreak_connection.h +++ b/common/net/daybreak_connection.h @@ -209,10 +209,10 @@ namespace EQ DaybreakConnectionManagerOptions() { max_connection_count = 0; keepalive_delay_ms = 9000; - resend_delay_ms = 300; + resend_delay_ms = 50; resend_delay_factor = 1.5; resend_delay_min = 50; - resend_delay_max = 1000; + resend_delay_max = 500; connect_delay_ms = 500; stale_connection_ms = 90000; connect_stale_ms = 5000; diff --git a/world/CMakeLists.txt b/world/CMakeLists.txt index e2a39225e..b98307ca3 100644 --- a/world/CMakeLists.txt +++ b/world/CMakeLists.txt @@ -6,6 +6,7 @@ SET(world_sources client.cpp cliententry.cpp clientlist.cpp + console.cpp eql_config.cpp launcher_link.cpp launcher_list.cpp @@ -31,6 +32,7 @@ SET(world_headers client.h cliententry.h clientlist.h + console.h eql_config.h launcher_link.h launcher_list.h diff --git a/world/console.cpp b/world/console.cpp index fa5ca0ffe..578523a52 100644 --- a/world/console.cpp +++ b/world/console.cpp @@ -1,890 +1,103 @@ -/* EQEMu: Everquest Server Emulator - Copyright (C) 2001-2002 EQEMu Development Team (http://eqemu.org) - - This program 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; version 2 of the License. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY except by those people which sell it, which - are required to give you total support for your newly bought product; - 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 this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ -#include "../common/global_define.h" -#include -#include -#include -#include -#include - - -#include "../common/version.h" #include "console.h" -#include "zoneserver.h" -#include "worlddb.h" -#include "../common/packet_dump.h" -#include "../common/seperator.h" -#include "../common/eq_packet_structs.h" -#include "../common/eq_packet.h" -#include "login_server.h" -#include "login_server_list.h" -#include "../common/serverinfo.h" -#include "../common/md5.h" -#include "../common/opcodemgr.h" -#include "../common/rulesys.h" -#include "../common/ruletypes.h" -#include "../common/string_util.h" -#include "world_config.h" -#include "zoneserver.h" -#include "zonelist.h" -#include "clientlist.h" -#include "launcher_list.h" -#include "ucs.h" -#include "queryserv.h" +#include "../common/eqemu_config.h" +#include "../common/util/uuid.h" +#include "../common/eqemu_logsys.h" +#include "../common//net/packet.h" -#ifdef _WINDOWS - #define snprintf _snprintf - #define strncasecmp _strnicmp - #define strcasecmp _stricmp -#endif - -extern ZSList zoneserver_list; -extern uint32 numzones; -extern LoginServerList loginserverlist; -extern ClientList client_list; -extern LauncherList launcher_list; -extern UCSConnection UCSLink; -extern QueryServConnection QSLink; -extern volatile bool RunLoops; - -ConsoleList console_list; -void CatchSignal(int sig_num); - -Console::Console(EmuTCPConnection* itcpc) -: WorldTCPConnection(), - timeout_timer(RuleI(Console, SessionTimeOut)), - prompt_timer(1) +ConsoleConnection::ConsoleConnection(ConsoleServer *parent, std::shared_ptr connection) { - tcpc = itcpc; - tcpc->SetEcho(true); - state = 0; - paccountid = 0; - memset(paccountname, 0, sizeof(paccountname)); - admin = 0; - pAcceptMessages = false; + m_parent = parent; + m_connection = connection; + m_uuid = EQ::Util::UUID::Generate().ToString(); + + m_connection->OnRead(std::bind(&ConsoleConnection::OnRead, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); + m_connection->OnDisconnect(std::bind(&ConsoleConnection::OnDisconnect, this, std::placeholders::_1)); + m_connection->Start(); + Clear(); } -Console::~Console() { - if (tcpc) - tcpc->Free(); +ConsoleConnection::~ConsoleConnection() +{ } -void Console::Die() { - state = CONSOLE_STATE_CLOSED; - struct in_addr in; - in.s_addr = GetIP(); - Log(Logs::Detail, Logs::World_Server,"Removing console from %s:%d",inet_ntoa(in),GetPort()); - tcpc->Disconnect(); +void ConsoleConnection::Clear() +{ + EQ::Net::DynamicPacket clear; + clear.PutUInt8(0, 0); + m_connection->Write((const char*)clear.Data(), clear.Length()); } -bool Console::SendChannelMessage(const ServerChannelMessage_Struct* scm) { - if (!pAcceptMessages) - return false; - switch (scm->chan_num) { - if(RuleB(Chat, ServerWideAuction)){ - case 4: { - SendMessage(1, "%s auctions, '%s'", scm->from, scm->message); - break; - } - } - if(RuleB(Chat, ServerWideOOC)){ - case 5: { - SendMessage(1, "%s says ooc, '%s'", scm->from, scm->message); - break; - } - } - case 6: { - SendMessage(1, "%s BROADCASTS, '%s'", scm->from, scm->message); +void ConsoleConnection::SendLine(const std::string &line) +{ + m_connection->Write(line.c_str(), line.length()); + SendNewLine(); +} + +void ConsoleConnection::SendNewLine() +{ + EQ::Net::DynamicPacket newline; + newline.PutUInt8(0, 10); + newline.PutUInt8(1, 13); + m_connection->Write((const char*)newline.Data(), newline.Length()); +} + +void ConsoleConnection::OnRead(EQ::Net::TCPConnection *c, const unsigned char *data, size_t sz) +{ + m_buffer.insert(m_buffer.end(), (const char*)data, (const char*)data + sz); + ProcessReadBuffer(); +} + +void ConsoleConnection::ProcessReadBuffer() +{ + size_t buffer_start = 0; + for (size_t i = 0; i < m_buffer.size(); ++i) { + char c = m_buffer[i]; + + switch (c) { + case 0: + //Clear buffer break; - } - case 7: { - SendMessage(1, "[%s] tells you, '%s'", scm->from, scm->message); - auto pack = new ServerPacket(ServerOP_ChannelMessage, - sizeof(ServerChannelMessage_Struct) + strlen(scm->message) + 1); - memcpy(pack->pBuffer, scm, pack->size); - ServerChannelMessage_Struct* scm2 = (ServerChannelMessage_Struct*) pack->pBuffer; - strcpy(scm2->deliverto, scm2->from); - scm2->noreply = true; - client_list.SendPacket(scm->from, pack); - safe_delete(pack); + case 10: + case 13: + //New Line break; - } - case 11: { - SendMessage(1, "%s GMSAYS, '%s'", scm->from, scm->message); + case 8: + //Backspace break; - } - default: { - return false; - } - } - return true; -} - -bool Console::SendEmoteMessage(uint32 type, const char* message, ...) { - if (!message) - return false; - if (!pAcceptMessages) - return false; - va_list argptr; - char buffer[1024]; - - va_start(argptr, message); - vsnprintf(buffer, sizeof(buffer), message, argptr); - va_end(argptr); - - SendMessage(1, message); - return true; -} - -bool Console::SendEmoteMessageRaw(uint32 type, const char* message) { - if (!message) - return false; - if (!pAcceptMessages) - return false; - SendMessage(1, message); - return true; -} - -void Console::SendEmoteMessage(const char* to, uint32 to_guilddbid, int16 to_minstatus, uint32 type, const char* message, ...) { - if (!message) - return; - if (to_guilddbid != 0 || to_minstatus > Admin()) - return; - va_list argptr; - char buffer[1024]; - - va_start(argptr, message); - vsnprintf(buffer, sizeof(buffer), message, argptr); - va_end(argptr); - - SendEmoteMessageRaw(to, to_guilddbid, to_minstatus, type, buffer); -} - -void Console::SendEmoteMessageRaw(const char* to, uint32 to_guilddbid, int16 to_minstatus, uint32 type, const char* message) { - if (!message) - return; - if (to_guilddbid != 0 || to_minstatus > Admin()) - return; - SendMessage(1, message); -} - -void Console::SendMessage(uint8 newline, const char* message, ...) { - if (!message) - return; - char* buffer = 0; - uint32 bufsize = 1500; - if (message) - bufsize += strlen(message); - buffer = new char[bufsize]; - memset(buffer, 0, bufsize); - if (message != 0) { - va_list argptr; - - va_start(argptr, message); - vsnprintf(buffer, bufsize - 512, message, argptr); - va_end(argptr); - } - - if (newline) { - char outbuf[3]; - outbuf[0] = 13; - outbuf[1] = 10; - outbuf[2] = 0; - for (int i=0; i < newline; i++) - strcat(buffer, outbuf); - } - tcpc->Send((uchar*) buffer, strlen(buffer)); - safe_delete_array(buffer); -} - -bool Console::Process() { - if (state == CONSOLE_STATE_CLOSED) - return false; - - if (!tcpc->Connected()) { - struct in_addr in; - in.s_addr = GetIP(); - Log(Logs::Detail, Logs::World_Server,"Removing console (!tcpc->Connected) from %s:%d",inet_ntoa(in),GetPort()); - return false; - } - //if we have not gotten the special markers after this timer, send login prompt - if(prompt_timer.Check()) { - struct in_addr in; - in.s_addr = GetIP(); - - std::string connecting_ip = inet_ntoa(in); - - SendMessage(2, StringFormat("Establishing connection from IP: %s Port: %d", inet_ntoa(in), GetPort()).c_str()); - - if (connecting_ip.find("127.0.0.1") != std::string::npos) { - SendMessage(2, StringFormat("Connecting established from local host, auto assuming admin").c_str()); - state = CONSOLE_STATE_CONNECTED; - tcpc->SetEcho(false); - admin = 255; - SendPrompt(); - } - else { - if (tcpc->GetMode() == EmuTCPConnection::modeConsole) - tcpc->Send((const uchar*) "Username: ", strlen("Username: ")); - } - - prompt_timer.Disable(); - - } - - if (timeout_timer.Check()) { - SendMessage(1, 0); - SendMessage(1, "Timeout, disconnecting..."); - struct in_addr in; - in.s_addr = GetIP(); - Log(Logs::Detail, Logs::World_Server,"TCP connection timeout from %s:%d",inet_ntoa(in),GetPort()); - return false; - } - - if (tcpc->GetMode() == EmuTCPConnection::modePacket) { - struct in_addr in; - in.s_addr = GetIP(); - if(tcpc->GetPacketMode() == EmuTCPConnection::packetModeZone) { - auto zs = new ZoneServer(tcpc); - Log(Logs::Detail, Logs::World_Server,"New zoneserver #%d from %s:%d", zs->GetID(), inet_ntoa(in), GetPort()); - zoneserver_list.Add(zs); - numzones++; - tcpc = 0; - } else if(tcpc->GetPacketMode() == EmuTCPConnection::packetModeLauncher) { - Log(Logs::Detail, Logs::World_Server,"New launcher from %s:%d", inet_ntoa(in), GetPort()); - launcher_list.Add(tcpc); - tcpc = 0; - } - else if(tcpc->GetPacketMode() == EmuTCPConnection::packetModeUCS) - { - Log(Logs::Detail, Logs::World_Server,"New UCS Connection from %s:%d", inet_ntoa(in), GetPort()); - UCSLink.SetConnection(tcpc); - tcpc = 0; - } - else if(tcpc->GetPacketMode() == EmuTCPConnection::packetModeQueryServ) - { - Log(Logs::Detail, Logs::World_Server,"New QS Connection from %s:%d", inet_ntoa(in), GetPort()); - QSLink.SetConnection(tcpc); - tcpc = 0; - } - else { - Log(Logs::Detail, Logs::World_Server,"Unsupported packet mode from %s:%d", inet_ntoa(in), GetPort()); - } - return false; - } - char* command = 0; - while ((command = tcpc->PopLine())) { - timeout_timer.Start(); - ProcessCommand(command); - delete command; - } - return true; -} - -void ConsoleList::Add(Console* con) { - list.Insert(con); -} - -void ConsoleList::Process() { - LinkedListIterator iterator(list); - iterator.Reset(); - - while(iterator.MoreElements()) { - if (!iterator.GetData()->Process()) - iterator.RemoveCurrent(); - else - iterator.Advance(); - } -} - -void ConsoleList::KillAll() { - LinkedListIterator iterator(list); - iterator.Reset(); - - while(iterator.MoreElements()) { - iterator.GetData()->Die(); - iterator.RemoveCurrent(); - } -} - -void ConsoleList::SendConsoleWho(WorldTCPConnection* connection, const char* to, int16 admin, char** output, uint32* outsize, uint32* outlen) { - LinkedListIterator iterator(list); - iterator.Reset(); - struct in_addr in; - int x = 0; - - while(iterator.MoreElements()) { - in.s_addr = iterator.GetData()->GetIP(); - if (admin >= iterator.GetData()->Admin()) - AppendAnyLenString(output, outsize, outlen, " Console: %s:%i AccID: %i AccName: %s", inet_ntoa(in), iterator.GetData()->GetPort(), iterator.GetData()->AccountID(), iterator.GetData()->AccountName()); - else - AppendAnyLenString(output, outsize, outlen, " Console: AccID: %i AccName: %s", iterator.GetData()->AccountID(), iterator.GetData()->AccountName()); - if (*outlen >= 3584) { - connection->SendEmoteMessageRaw(to, 0, 0, 10, *output); - safe_delete(*output); - *outsize = 0; - *outlen = 0; - } - else { - if (connection->IsConsole()) - AppendAnyLenString(output, outsize, outlen, "\r\n"); - else - AppendAnyLenString(output, outsize, outlen, "\n"); - } - x++; - iterator.Advance(); - } - AppendAnyLenString(output, outsize, outlen, "%i consoles connected", x); -} - -void ConsoleList::SendChannelMessage(const ServerChannelMessage_Struct* scm) { - LinkedListIterator iterator(list); - iterator.Reset(); - - while(iterator.MoreElements()) { - iterator.GetData()->SendChannelMessage(scm); - iterator.Advance(); - } -} - -void ConsoleList::SendEmoteMessage(uint32 type, const char* message, ...) { - va_list argptr; - char buffer[1024]; - - va_start(argptr, message); - vsnprintf(buffer, sizeof(buffer), message, argptr); - va_end(argptr); - - SendEmoteMessageRaw(type, buffer); -} - -void ConsoleList::SendEmoteMessageRaw(uint32 type, const char* message) { - LinkedListIterator iterator(list); - iterator.Reset(); - - while(iterator.MoreElements()) { - iterator.GetData()->SendEmoteMessageRaw(type, message); - iterator.Advance(); - } -} - -Console* ConsoleList::FindByAccountName(const char* accname) { - LinkedListIterator iterator(list); - iterator.Reset(); - - while(iterator.MoreElements()) { - if (strcasecmp(iterator.GetData()->AccountName(), accname) == 0) - return iterator.GetData(); - - iterator.Advance(); - } - return 0; -} - -void Console::ProcessCommand(const char* command) { - switch(state) - { - case CONSOLE_STATE_USERNAME: - { - if (strlen(command) >= 16) { - SendMessage(1, 0); - SendMessage(2, "Username buffer overflow."); - SendMessage(1, "Bye Bye."); - state = CONSOLE_STATE_CLOSED; - return; - } - strcpy(paccountname, command); - state = CONSOLE_STATE_PASSWORD; - SendMessage(0, "Password: "); - tcpc->SetEcho(false); - break; - } - case CONSOLE_STATE_PASSWORD: - { - if (strlen(command) >= 16) { - SendMessage(1, 0); - SendMessage(2, "Password buffer overflow."); - SendMessage(1, "Bye Bye."); - state = CONSOLE_STATE_CLOSED; - return; - } - paccountid = database.CheckLogin(paccountname ,command); - if (paccountid == 0) { - SendMessage(1, 0); - SendMessage(2, "Login failed."); - SendMessage(1, "Bye Bye."); - state = CONSOLE_STATE_CLOSED; - return; - } - database.GetAccountName(paccountid, paccountname); // fixes case and stuff - admin = database.CheckStatus(paccountid); - if (!(admin >= consoleLoginStatus)) { - SendMessage(1, 0); - SendMessage(2, "Access denied."); - SendMessage(1, "Bye Bye."); - state = CONSOLE_STATE_CLOSED; - return; - } - Log(Logs::Detail, Logs::World_Server,"TCP console authenticated: Username=%s, Admin=%d",paccountname,admin); - SendMessage(1, 0); - SendMessage(2, "Login accepted."); - state = CONSOLE_STATE_CONNECTED; - tcpc->SetEcho(true); - SendPrompt(); - break; - } - case CONSOLE_STATE_CONNECTED: { - Log(Logs::Detail, Logs::World_Server,"TCP command: %s: \"%s\"",paccountname ,command); - Seperator sep(command); - if (strcasecmp(sep.arg[0], "help") == 0 || strcmp(sep.arg[0], "?") == 0) { - SendMessage(1, " whoami"); - SendMessage(1, " who"); - SendMessage(1, " zonestatus"); - SendMessage(1, " uptime [zoneID#]"); - SendMessage(1, " emote [zonename or charname or world] [type] [message]"); - SendMessage(1, " echo [on/off]"); - SendMessage(1, " acceptmessages [on/off]"); - SendMessage(1, " tell [name] [message]"); - SendMessage(1, " broadcast [message]"); - SendMessage(1, " gmsay [message]"); - SendMessage(1, " ooc [message]"); - SendMessage(1, " auction [message]"); - if (admin >= consoleKickStatus) - SendMessage(1, " kick [charname]"); - if (admin >= consoleLockStatus) - SendMessage(1, " lock/unlock"); - if (admin >= consoleZoneStatus) { - SendMessage(1, " zoneshutdown [zonename or ZoneServerID]"); - SendMessage(1, " zonebootup [ZoneServerID] [zonename]"); - SendMessage(1, " zonelock [list|lock|unlock] [zonename]"); - } - if (admin >= consoleFlagStatus) - SendMessage(1, " flag [status] [accountname]"); - if (admin >= consolePassStatus) - SendMessage(1, " setpass [accountname] [newpass]"); - if (admin >= consoleWorldStatus) { - SendMessage(1, " version"); - SendMessage(1, " worldshutdown"); - } - if (admin >= 201) { - SendMessage(1, " IPLookup [name]"); - } - if (admin >= 100) { - SendMessage(1, " LSReconnect"); - SendMessage(1, " signalcharbyname charname ID"); - SendMessage(1, " reloadworld"); - } - } - else if (strcasecmp(sep.arg[0], "ping") == 0) { - // do nothing - } - else if (strcasecmp(sep.arg[0], "signalcharbyname") == 0) { - SendMessage(1, "Signal Sent to %s with ID %i", (char*) sep.arg[1], atoi(sep.arg[2])); - uint32 message_len = strlen((char*) sep.arg[1]) + 1; - auto pack = new ServerPacket(ServerOP_CZSignalClientByName, - sizeof(CZClientSignalByName_Struct) + message_len); - CZClientSignalByName_Struct* CZSC = (CZClientSignalByName_Struct*) pack->pBuffer; - strn0cpy(CZSC->Name, (char*) sep.arg[1], 64); - CZSC->data = atoi(sep.arg[2]); - zoneserver_list.SendPacket(pack); - safe_delete(pack); - } - else if (strcasecmp(sep.arg[0], "setpass") == 0 && admin >= consolePassStatus) { - if (sep.argnum != 2) - SendMessage(1, "Format: setpass accountname password"); - else { - - int16 tmpstatus = 0; - uint32 tmpid = database.GetAccountIDByName(sep.arg[1], &tmpstatus); - if (!tmpid) - SendMessage(1, "Error: Account not found"); - else if (tmpstatus > admin) - SendMessage(1, "Cannot change password: Account's status is higher than yours"); - else if (database.SetLocalPassword(tmpid, sep.arg[2])) - SendMessage(1, "Password changed."); - else - SendMessage(1, "Error changing password."); - } - } - else if (strcasecmp(sep.arg[0], "uptime") == 0) { - if (sep.IsNumber(1) && atoi(sep.arg[1]) > 0) { - auto pack = new ServerPacket(ServerOP_Uptime, sizeof(ServerUptime_Struct)); - ServerUptime_Struct* sus = (ServerUptime_Struct*) pack->pBuffer; - snprintf(sus->adminname, sizeof(sus->adminname), "*%s", this->GetName()); - sus->zoneserverid = atoi(sep.arg[1]); - ZoneServer* zs = zoneserver_list.FindByID(sus->zoneserverid); - if (zs) - zs->SendPacket(pack); - else - SendMessage(1, "Zoneserver not found."); - delete pack; - } - else { - ZSList::ShowUpTime(this); - } - } - else if (strcasecmp(sep.arg[0], "md5") == 0) { - uint8 md5[16]; - MD5::Generate((const uchar*) sep.argplus[1], strlen(sep.argplus[1]), md5); - SendMessage(1, "MD5: %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", md5[0], md5[1], md5[2], md5[3], md5[4], md5[5], md5[6], md5[7], md5[8], md5[9], md5[10], md5[11], md5[12], md5[13], md5[14], md5[15]); - } - else if (strcasecmp(sep.arg[0], "whoami") == 0) { - SendMessage(1, "You are logged in as '%s'", this->AccountName()); - SendMessage(1, "You are known as '*%s'", this->AccountName()); - SendMessage(1, "AccessLevel: %d", this->Admin()); - } - else if (strcasecmp(sep.arg[0], "echo") == 0) { - if (strcasecmp(sep.arg[1], "on") == 0) - tcpc->SetEcho(true); - else if (strcasecmp(sep.arg[1], "off") == 0) { - if (pAcceptMessages) - SendMessage(1, "Echo can not be turned off while acceptmessages is on"); - else - tcpc->SetEcho(false); - } - else - SendMessage(1, "Usage: echo [on/off]"); - } - else if (strcasecmp(sep.arg[0], "acceptmessages") == 0) { - if (strcasecmp(sep.arg[1], "on") == 0) - if (tcpc->GetEcho()) - SendMessage(1, "AcceptMessages can not be turned on while echo is on"); - else - pAcceptMessages = true; - else if (strcasecmp(sep.arg[1], "off") == 0) - pAcceptMessages = false; - else - SendMessage(1, "Usage: acceptmessages [on/off]"); - } - else if (strcasecmp(sep.arg[0], "tell") == 0) { - char tmpname[64]; - tmpname[0] = '*'; - strcpy(&tmpname[1], paccountname); - zoneserver_list.SendChannelMessage(tmpname, sep.arg[1], 7, 0, sep.argplus[2]); - } - else if (strcasecmp(sep.arg[0], "broadcast") == 0) { - char tmpname[64]; - tmpname[0] = '*'; - strcpy(&tmpname[1], paccountname); - zoneserver_list.SendChannelMessage(tmpname, 0, 6, 0, sep.argplus[1]); - } - else if (strcasecmp(sep.arg[0], "ooc") == 0) { - char tmpname[64]; - tmpname[0] = '*'; - strcpy(&tmpname[1], paccountname); - zoneserver_list.SendChannelMessage(tmpname, 0, 5, 0, sep.argplus[1]); - } - else if (strcasecmp(sep.arg[0], "auction") == 0) { - char tmpname[64]; - tmpname[0] = '*'; - strcpy(&tmpname[1], paccountname); - zoneserver_list.SendChannelMessage(tmpname, 0, 4, 0, sep.argplus[1]); - } - else if (strcasecmp(sep.arg[0], "gmsay") == 0 || strcasecmp(sep.arg[0], "pr") == 0) { - char tmpname[64]; - tmpname[0] = '*'; - strcpy(&tmpname[1], paccountname); - zoneserver_list.SendChannelMessage(tmpname, 0, 11, 0, sep.argplus[1]); - } - else if (strcasecmp(sep.arg[0], "emote") == 0) { - if (strcasecmp(sep.arg[1], "world") == 0) - zoneserver_list.SendEmoteMessageRaw(0, 0, 0, atoi(sep.arg[2]), sep.argplus[3]); - else { - ZoneServer* zs = zoneserver_list.FindByName(sep.arg[1]); - if (zs != 0) - zs->SendEmoteMessageRaw(0, 0, 0, atoi(sep.arg[2]), sep.argplus[3]); - else - zoneserver_list.SendEmoteMessageRaw(sep.arg[1], 0, 0, atoi(sep.arg[2]), sep.argplus[3]); - } - } - else if (strcasecmp(sep.arg[0], "movechar") == 0) { - if(sep.arg[1][0]==0 || sep.arg[2][0] == 0) - SendMessage(1, "Usage: movechar [charactername] [zonename]"); - else { - if (!database.GetZoneID(sep.arg[2])) - SendMessage(1, "Error: Zone '%s' not found", sep.arg[2]); - else if (!database.CheckUsedName((char*) sep.arg[1])) { - if (!database.MoveCharacterToZone((char*) sep.arg[1], (char*) sep.arg[2])) - SendMessage(1, "Character Move Failed!"); - else - SendMessage(1, "Character has been moved."); - } - else - SendMessage(1, "Character Does Not Exist"); - } - } - else if (strcasecmp(sep.arg[0], "flag") == 0 && this->Admin() >= consoleFlagStatus) { -// SCORPIOUS2K - reversed parameter order for flag - if(sep.arg[2][0]==0 || !sep.IsNumber(1)) - SendMessage(1, "Usage: flag [status] [accountname]"); - else - { - if (atoi(sep.arg[1]) > this->Admin()) - SendMessage(1, "You cannot set people's status to higher than your own"); - else if (atoi(sep.arg[1]) < 0 && this->Admin() < consoleFlagStatus) - SendMessage(1, "You have too low of status to change flags"); - else if (!database.SetAccountStatus(sep.arg[2], atoi(sep.arg[1]))) - SendMessage(1, "Unable to flag account!"); - else - SendMessage(1, "Account Flaged"); - } - } - else if (strcasecmp(sep.arg[0], "kick") == 0 && admin >= consoleKickStatus) { - char tmpname[64]; - tmpname[0] = '*'; - strcpy(&tmpname[1], paccountname); - auto pack = new ServerPacket; - pack->opcode = ServerOP_KickPlayer; - pack->size = sizeof(ServerKickPlayer_Struct); - pack->pBuffer = new uchar[pack->size]; - ServerKickPlayer_Struct* skp = (ServerKickPlayer_Struct*) pack->pBuffer; - strcpy(skp->adminname, tmpname); - strcpy(skp->name, sep.arg[1]); - skp->adminrank = this->Admin(); - zoneserver_list.SendPacket(pack); - delete pack; - } - else if (strcasecmp(sep.arg[0], "who") == 0) { - auto whom = new Who_All_Struct; - memset(whom, 0, sizeof(Who_All_Struct)); - whom->lvllow = 0xFFFF; - whom->lvlhigh = 0xFFFF; - whom->wclass = 0xFFFF; - whom->wrace = 0xFFFF; - whom->gmlookup = 0xFFFF; - for (int i=1; i<=sep.argnum; i++) { - if (strcasecmp(sep.arg[i], "gm") == 0) - whom->gmlookup = 1; - else if (sep.IsNumber(i)) { - if (whom->lvllow == 0xFFFF) { - whom->lvllow = atoi(sep.arg[i]); - whom->lvlhigh = whom->lvllow; - } - else if (atoi(sep.arg[i]) > int(whom->lvllow)) - whom->lvlhigh = atoi(sep.arg[i]); - else - whom->lvllow = atoi(sep.arg[i]); - } - else - strn0cpy(whom->whom, sep.arg[i], sizeof(whom->whom)); - } - client_list.ConsoleSendWhoAll(0, admin, whom, this); - delete whom; - } - else if (strcasecmp(sep.arg[0], "zonestatus") == 0) { - zoneserver_list.SendZoneStatus(0, admin, this); - } - else if (strcasecmp(sep.arg[0], "exit") == 0 || strcasecmp(sep.arg[0], "quit") == 0) { - SendMessage(1, "Bye Bye."); - state = CONSOLE_STATE_CLOSED; - } - else if (strcasecmp(sep.arg[0], "zoneshutdown") == 0 && admin >= consoleZoneStatus) { - if (sep.arg[1][0] == 0) { - SendMessage(1, "Usage: zoneshutdown zoneshortname"); - } else { - char tmpname[64]; - tmpname[0] = '*'; - strcpy(&tmpname[1], paccountname); - - auto pack = new ServerPacket; - pack->size = sizeof(ServerZoneStateChange_struct); - pack->pBuffer = new uchar[pack->size]; - memset(pack->pBuffer, 0, sizeof(ServerZoneStateChange_struct)); - ServerZoneStateChange_struct* s = (ServerZoneStateChange_struct *) pack->pBuffer; - pack->opcode = ServerOP_ZoneShutdown; - strcpy(s->adminname, tmpname); - if (sep.arg[1][0] >= '0' && sep.arg[1][0] <= '9') - s->ZoneServerID = atoi(sep.arg[1]); - else - s->zoneid = database.GetZoneID(sep.arg[1]); - - ZoneServer* zs = 0; - if (s->ZoneServerID != 0) - zs = zoneserver_list.FindByID(s->ZoneServerID); - else if (s->zoneid != 0) - zs = zoneserver_list.FindByName(database.GetZoneName(s->zoneid)); - else - SendMessage(1, "Error: ZoneShutdown: neither ID nor name specified"); - - if (zs == 0) - SendMessage(1, "Error: ZoneShutdown: zoneserver not found"); - else - zs->SendPacket(pack); - - delete pack; - } - } - else if (strcasecmp(sep.arg[0], "zonebootup") == 0 && admin >= consoleZoneStatus) { - if (sep.arg[2][0] == 0 || !sep.IsNumber(1)) { - SendMessage(1, "Usage: zonebootup ZoneServerID# zoneshortname"); - } else { - char tmpname[64]; - tmpname[0] = '*'; - strcpy(&tmpname[1], paccountname); - - Log(Logs::Detail, Logs::World_Server,"Console ZoneBootup: %s, %s, %s",tmpname,sep.arg[2],sep.arg[1]); - zoneserver_list.SOPZoneBootup(tmpname, atoi(sep.arg[1]), sep.arg[2], (bool) (strcasecmp(sep.arg[3], "static") == 0)); - } - } - else if (strcasecmp(sep.arg[0], "worldshutdown") == 0 && admin >= consoleWorldStatus) { - int32 time, interval; - if(sep.IsNumber(1) && sep.IsNumber(2) && ((time=atoi(sep.arg[1]))>0) && ((interval=atoi(sep.arg[2]))>0)) { - zoneserver_list.WorldShutDown(time, interval); - } - else if(strcasecmp(sep.arg[1], "now") == 0) { - zoneserver_list.WorldShutDown(0, 0); - } - else if(strcasecmp(sep.arg[1], "disable") == 0) { - SendEmoteMessage(0,0,0,15,":SYSTEM MSG:World shutdown aborted."); - zoneserver_list.SendEmoteMessage(0,0,0,15,":SYSTEM MSG:World shutdown aborted."); - zoneserver_list.shutdowntimer->Disable(); - zoneserver_list.reminder->Disable(); - } - else { - SendMessage(1, "Usage: worldshutdown [now] [disable] ([time] [interval])"); - //Go ahead and shut down since that's what this used to do when invoked this way. - zoneserver_list.WorldShutDown(0, 0); - } - } - else if (strcasecmp(sep.arg[0], "lock") == 0 && admin >= consoleLockStatus) { - WorldConfig::LockWorld(); - if (loginserverlist.Connected()) { - loginserverlist.SendStatus(); - SendMessage(1, "World locked."); - } - else { - SendMessage(1, "World locked, but login server not connected."); - } - } - else if (strcasecmp(sep.arg[0], "unlock") == 0 && admin >= consoleLockStatus) { - WorldConfig::UnlockWorld(); - if (loginserverlist.Connected()) { - loginserverlist.SendStatus(); - SendMessage(1, "World unlocked."); - } - else { - SendMessage(1, "World unlocked, but login server not connected."); - } - } - else if (strcasecmp(sep.arg[0], "version") == 0 && admin >= consoleWorldStatus) { - SendMessage(1, "Current version information."); - SendMessage(1, " %s", CURRENT_VERSION); - SendMessage(1, " Compiled on: %s at %s", COMPILE_DATE, COMPILE_TIME); - SendMessage(1, " Last modified on: %s", LAST_MODIFIED); - } - else if (strcasecmp(sep.arg[0], "serverinfo") == 0 && admin >= 200) { - if (strcasecmp(sep.arg[1], "os") == 0) { - #ifdef _WINDOWS - GetOS(); - char intbuffer [sizeof(unsigned long)]; - SendMessage(1, "Operating system information."); - SendMessage(1, " %s", Ver_name); - SendMessage(1, " Build number: %s", ultoa(Ver_build, intbuffer, 10)); - SendMessage(1, " Minor version: %s", ultoa(Ver_min, intbuffer, 10)); - SendMessage(1, " Major version: %s", ultoa(Ver_maj, intbuffer, 10)); - SendMessage(1, " Platform Id: %s", ultoa(Ver_pid, intbuffer, 10)); - #else - char os_string[100]; - SendMessage(1, "Operating system information."); - SendMessage(1, " %s", GetOS(os_string)); - #endif - } - else { - SendMessage(1, "Usage: Serverinfo [type]"); - SendMessage(1, " OS - Operating system version information."); - } - } - else if (strcasecmp(sep.arg[0], "IPLookup") == 0 && admin >= 201) { - client_list.SendCLEList(admin, 0, this, sep.argplus[1]); - } - else if (strcasecmp(sep.arg[0], "LSReconnect") == 0 && admin >= 100) { - #ifdef _WINDOWS - _beginthread(AutoInitLoginServer, 0, nullptr); - #else - pthread_t thread; - pthread_create(&thread, nullptr, &AutoInitLoginServer, nullptr); - #endif - RunLoops = true; - SendMessage(1, " Login Server Reconnect manually restarted by Console"); - Log(Logs::Detail, Logs::World_Server,"Login Server Reconnect manually restarted by Console"); - } - else if (strcasecmp(sep.arg[0], "zonelock") == 0 && admin >= consoleZoneStatus) { - if (strcasecmp(sep.arg[1], "list") == 0) { - zoneserver_list.ListLockedZones(0, this); - } - else if (strcasecmp(sep.arg[1], "lock") == 0 && admin >= 101) { - uint16 tmp = database.GetZoneID(sep.arg[2]); - if (tmp) { - if (zoneserver_list.SetLockedZone(tmp, true)) - zoneserver_list.SendEmoteMessage(0, 0, 80, 15, "Zone locked: %s", database.GetZoneName(tmp)); - else - SendMessage(1, "Failed to change lock"); - } - else - SendMessage(1, "Usage: #zonelock lock [zonename]"); - } - else if (strcasecmp(sep.arg[1], "unlock") == 0 && admin >= 101) { - uint16 tmp = database.GetZoneID(sep.arg[2]); - if (tmp) { - if (zoneserver_list.SetLockedZone(tmp, false)) - zoneserver_list.SendEmoteMessage(0, 0, 80, 15, "Zone unlocked: %s", database.GetZoneName(tmp)); - else - SendMessage(1, "Failed to change lock"); - } - else - SendMessage(1, "Usage: #zonelock unlock [zonename]"); - } - else { - SendMessage(1, "#zonelock sub-commands"); - SendMessage(1, " list"); - if (admin >= 101) { - SendMessage(1, " lock [zonename]"); - SendMessage(1, " unlock [zonename]"); - } - } - } - else if (strcasecmp(sep.arg[0], "reloadworld") == 0 && admin > 101) - { - SendEmoteMessage(0,0,0,15,"Reloading World..."); - auto pack = new ServerPacket(ServerOP_ReloadWorld, sizeof(ReloadWorld_Struct)); - ReloadWorld_Struct* RW = (ReloadWorld_Struct*) pack->pBuffer; - RW->Option = 1; - zoneserver_list.SendPacket(pack); - safe_delete(pack); - } - else if (strcasecmp(sep.arg[0], "") == 0){ - /* Hit Enter with no command */ - } - else { - SendMessage(1, "Command unknown."); - } - if (state == CONSOLE_STATE_CONNECTED) - SendPrompt(); - break; - } - default: { + default: break; } } } -void Console::SendPrompt() { - if (tcpc->GetEcho()) - SendMessage(0, "%s> ", paccountname); +void ConsoleConnection::ProcessCommand(const std::string &cmd) +{ } +void ConsoleConnection::OnDisconnect(EQ::Net::TCPConnection *c) +{ + m_parent->ConnectionDisconnected(this); +} + +ConsoleServer::ConsoleServer() +{ + auto config = EQEmuConfig::get(); + + m_server.reset(new EQ::Net::TCPServer()); + m_server->Listen(config->TelnetTCPPort, false, [this](std::shared_ptr connection) { + ConsoleConnection *c = new ConsoleConnection(this, connection); + m_connections.insert(std::make_pair(c->GetUUID(), std::unique_ptr(c))); + }); +} + +ConsoleServer::~ConsoleServer() +{ +} + +void ConsoleServer::ConnectionDisconnected(ConsoleConnection *c) +{ + auto iter = m_connections.find(c->GetUUID()); + if (iter != m_connections.end()) { + m_connections.erase(iter); + } +} diff --git a/world/console.h b/world/console.h new file mode 100644 index 000000000..57fc3671b --- /dev/null +++ b/world/console.h @@ -0,0 +1,45 @@ +#pragma once + +#include "../common/net/tcp_server.h" +#include "../common/event/timer.h" +#include +#include + +class ConsoleServer; +class ConsoleConnection +{ +public: + ConsoleConnection(ConsoleServer *parent, std::shared_ptr connection); + ~ConsoleConnection(); + + std::string GetUUID() const { return m_uuid; } + + void Clear(); + void SendLine(const std::string &line); + void SendNewLine(); +private: + void OnRead(EQ::Net::TCPConnection* c, const unsigned char* data, size_t sz); + void ProcessReadBuffer(); + void ProcessCommand(const std::string& cmd); + void OnDisconnect(EQ::Net::TCPConnection* c); + + ConsoleServer *m_parent; + std::shared_ptr m_connection; + std::string m_uuid; + + std::vector m_buffer; +}; + +class ConsoleServer +{ +public: + ConsoleServer(); + ~ConsoleServer(); + + void ConnectionDisconnected(ConsoleConnection *c); +private: + + std::unique_ptr m_server; + std::map> m_connections; + friend class ConsoleConnection; +}; \ No newline at end of file diff --git a/world/console.old.cpp b/world/console.old.cpp new file mode 100644 index 000000000..fa5ca0ffe --- /dev/null +++ b/world/console.old.cpp @@ -0,0 +1,890 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2002 EQEMu Development Team (http://eqemu.org) + + This program 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; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY except by those people which sell it, which + are required to give you total support for your newly bought product; + 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 this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#include "../common/global_define.h" +#include +#include +#include +#include +#include + + +#include "../common/version.h" +#include "console.h" +#include "zoneserver.h" +#include "worlddb.h" +#include "../common/packet_dump.h" +#include "../common/seperator.h" +#include "../common/eq_packet_structs.h" +#include "../common/eq_packet.h" +#include "login_server.h" +#include "login_server_list.h" +#include "../common/serverinfo.h" +#include "../common/md5.h" +#include "../common/opcodemgr.h" +#include "../common/rulesys.h" +#include "../common/ruletypes.h" +#include "../common/string_util.h" +#include "world_config.h" +#include "zoneserver.h" +#include "zonelist.h" +#include "clientlist.h" +#include "launcher_list.h" +#include "ucs.h" +#include "queryserv.h" + +#ifdef _WINDOWS + #define snprintf _snprintf + #define strncasecmp _strnicmp + #define strcasecmp _stricmp +#endif + +extern ZSList zoneserver_list; +extern uint32 numzones; +extern LoginServerList loginserverlist; +extern ClientList client_list; +extern LauncherList launcher_list; +extern UCSConnection UCSLink; +extern QueryServConnection QSLink; +extern volatile bool RunLoops; + +ConsoleList console_list; +void CatchSignal(int sig_num); + +Console::Console(EmuTCPConnection* itcpc) +: WorldTCPConnection(), + timeout_timer(RuleI(Console, SessionTimeOut)), + prompt_timer(1) +{ + tcpc = itcpc; + tcpc->SetEcho(true); + state = 0; + paccountid = 0; + memset(paccountname, 0, sizeof(paccountname)); + admin = 0; + pAcceptMessages = false; +} + +Console::~Console() { + if (tcpc) + tcpc->Free(); +} + +void Console::Die() { + state = CONSOLE_STATE_CLOSED; + struct in_addr in; + in.s_addr = GetIP(); + Log(Logs::Detail, Logs::World_Server,"Removing console from %s:%d",inet_ntoa(in),GetPort()); + tcpc->Disconnect(); +} + +bool Console::SendChannelMessage(const ServerChannelMessage_Struct* scm) { + if (!pAcceptMessages) + return false; + switch (scm->chan_num) { + if(RuleB(Chat, ServerWideAuction)){ + case 4: { + SendMessage(1, "%s auctions, '%s'", scm->from, scm->message); + break; + } + } + if(RuleB(Chat, ServerWideOOC)){ + case 5: { + SendMessage(1, "%s says ooc, '%s'", scm->from, scm->message); + break; + } + } + case 6: { + SendMessage(1, "%s BROADCASTS, '%s'", scm->from, scm->message); + break; + } + case 7: { + SendMessage(1, "[%s] tells you, '%s'", scm->from, scm->message); + auto pack = new ServerPacket(ServerOP_ChannelMessage, + sizeof(ServerChannelMessage_Struct) + strlen(scm->message) + 1); + memcpy(pack->pBuffer, scm, pack->size); + ServerChannelMessage_Struct* scm2 = (ServerChannelMessage_Struct*) pack->pBuffer; + strcpy(scm2->deliverto, scm2->from); + scm2->noreply = true; + client_list.SendPacket(scm->from, pack); + safe_delete(pack); + break; + } + case 11: { + SendMessage(1, "%s GMSAYS, '%s'", scm->from, scm->message); + break; + } + default: { + return false; + } + } + return true; +} + +bool Console::SendEmoteMessage(uint32 type, const char* message, ...) { + if (!message) + return false; + if (!pAcceptMessages) + return false; + va_list argptr; + char buffer[1024]; + + va_start(argptr, message); + vsnprintf(buffer, sizeof(buffer), message, argptr); + va_end(argptr); + + SendMessage(1, message); + return true; +} + +bool Console::SendEmoteMessageRaw(uint32 type, const char* message) { + if (!message) + return false; + if (!pAcceptMessages) + return false; + SendMessage(1, message); + return true; +} + +void Console::SendEmoteMessage(const char* to, uint32 to_guilddbid, int16 to_minstatus, uint32 type, const char* message, ...) { + if (!message) + return; + if (to_guilddbid != 0 || to_minstatus > Admin()) + return; + va_list argptr; + char buffer[1024]; + + va_start(argptr, message); + vsnprintf(buffer, sizeof(buffer), message, argptr); + va_end(argptr); + + SendEmoteMessageRaw(to, to_guilddbid, to_minstatus, type, buffer); +} + +void Console::SendEmoteMessageRaw(const char* to, uint32 to_guilddbid, int16 to_minstatus, uint32 type, const char* message) { + if (!message) + return; + if (to_guilddbid != 0 || to_minstatus > Admin()) + return; + SendMessage(1, message); +} + +void Console::SendMessage(uint8 newline, const char* message, ...) { + if (!message) + return; + char* buffer = 0; + uint32 bufsize = 1500; + if (message) + bufsize += strlen(message); + buffer = new char[bufsize]; + memset(buffer, 0, bufsize); + if (message != 0) { + va_list argptr; + + va_start(argptr, message); + vsnprintf(buffer, bufsize - 512, message, argptr); + va_end(argptr); + } + + if (newline) { + char outbuf[3]; + outbuf[0] = 13; + outbuf[1] = 10; + outbuf[2] = 0; + for (int i=0; i < newline; i++) + strcat(buffer, outbuf); + } + tcpc->Send((uchar*) buffer, strlen(buffer)); + safe_delete_array(buffer); +} + +bool Console::Process() { + if (state == CONSOLE_STATE_CLOSED) + return false; + + if (!tcpc->Connected()) { + struct in_addr in; + in.s_addr = GetIP(); + Log(Logs::Detail, Logs::World_Server,"Removing console (!tcpc->Connected) from %s:%d",inet_ntoa(in),GetPort()); + return false; + } + //if we have not gotten the special markers after this timer, send login prompt + if(prompt_timer.Check()) { + struct in_addr in; + in.s_addr = GetIP(); + + std::string connecting_ip = inet_ntoa(in); + + SendMessage(2, StringFormat("Establishing connection from IP: %s Port: %d", inet_ntoa(in), GetPort()).c_str()); + + if (connecting_ip.find("127.0.0.1") != std::string::npos) { + SendMessage(2, StringFormat("Connecting established from local host, auto assuming admin").c_str()); + state = CONSOLE_STATE_CONNECTED; + tcpc->SetEcho(false); + admin = 255; + SendPrompt(); + } + else { + if (tcpc->GetMode() == EmuTCPConnection::modeConsole) + tcpc->Send((const uchar*) "Username: ", strlen("Username: ")); + } + + prompt_timer.Disable(); + + } + + if (timeout_timer.Check()) { + SendMessage(1, 0); + SendMessage(1, "Timeout, disconnecting..."); + struct in_addr in; + in.s_addr = GetIP(); + Log(Logs::Detail, Logs::World_Server,"TCP connection timeout from %s:%d",inet_ntoa(in),GetPort()); + return false; + } + + if (tcpc->GetMode() == EmuTCPConnection::modePacket) { + struct in_addr in; + in.s_addr = GetIP(); + if(tcpc->GetPacketMode() == EmuTCPConnection::packetModeZone) { + auto zs = new ZoneServer(tcpc); + Log(Logs::Detail, Logs::World_Server,"New zoneserver #%d from %s:%d", zs->GetID(), inet_ntoa(in), GetPort()); + zoneserver_list.Add(zs); + numzones++; + tcpc = 0; + } else if(tcpc->GetPacketMode() == EmuTCPConnection::packetModeLauncher) { + Log(Logs::Detail, Logs::World_Server,"New launcher from %s:%d", inet_ntoa(in), GetPort()); + launcher_list.Add(tcpc); + tcpc = 0; + } + else if(tcpc->GetPacketMode() == EmuTCPConnection::packetModeUCS) + { + Log(Logs::Detail, Logs::World_Server,"New UCS Connection from %s:%d", inet_ntoa(in), GetPort()); + UCSLink.SetConnection(tcpc); + tcpc = 0; + } + else if(tcpc->GetPacketMode() == EmuTCPConnection::packetModeQueryServ) + { + Log(Logs::Detail, Logs::World_Server,"New QS Connection from %s:%d", inet_ntoa(in), GetPort()); + QSLink.SetConnection(tcpc); + tcpc = 0; + } + else { + Log(Logs::Detail, Logs::World_Server,"Unsupported packet mode from %s:%d", inet_ntoa(in), GetPort()); + } + return false; + } + char* command = 0; + while ((command = tcpc->PopLine())) { + timeout_timer.Start(); + ProcessCommand(command); + delete command; + } + return true; +} + +void ConsoleList::Add(Console* con) { + list.Insert(con); +} + +void ConsoleList::Process() { + LinkedListIterator iterator(list); + iterator.Reset(); + + while(iterator.MoreElements()) { + if (!iterator.GetData()->Process()) + iterator.RemoveCurrent(); + else + iterator.Advance(); + } +} + +void ConsoleList::KillAll() { + LinkedListIterator iterator(list); + iterator.Reset(); + + while(iterator.MoreElements()) { + iterator.GetData()->Die(); + iterator.RemoveCurrent(); + } +} + +void ConsoleList::SendConsoleWho(WorldTCPConnection* connection, const char* to, int16 admin, char** output, uint32* outsize, uint32* outlen) { + LinkedListIterator iterator(list); + iterator.Reset(); + struct in_addr in; + int x = 0; + + while(iterator.MoreElements()) { + in.s_addr = iterator.GetData()->GetIP(); + if (admin >= iterator.GetData()->Admin()) + AppendAnyLenString(output, outsize, outlen, " Console: %s:%i AccID: %i AccName: %s", inet_ntoa(in), iterator.GetData()->GetPort(), iterator.GetData()->AccountID(), iterator.GetData()->AccountName()); + else + AppendAnyLenString(output, outsize, outlen, " Console: AccID: %i AccName: %s", iterator.GetData()->AccountID(), iterator.GetData()->AccountName()); + if (*outlen >= 3584) { + connection->SendEmoteMessageRaw(to, 0, 0, 10, *output); + safe_delete(*output); + *outsize = 0; + *outlen = 0; + } + else { + if (connection->IsConsole()) + AppendAnyLenString(output, outsize, outlen, "\r\n"); + else + AppendAnyLenString(output, outsize, outlen, "\n"); + } + x++; + iterator.Advance(); + } + AppendAnyLenString(output, outsize, outlen, "%i consoles connected", x); +} + +void ConsoleList::SendChannelMessage(const ServerChannelMessage_Struct* scm) { + LinkedListIterator iterator(list); + iterator.Reset(); + + while(iterator.MoreElements()) { + iterator.GetData()->SendChannelMessage(scm); + iterator.Advance(); + } +} + +void ConsoleList::SendEmoteMessage(uint32 type, const char* message, ...) { + va_list argptr; + char buffer[1024]; + + va_start(argptr, message); + vsnprintf(buffer, sizeof(buffer), message, argptr); + va_end(argptr); + + SendEmoteMessageRaw(type, buffer); +} + +void ConsoleList::SendEmoteMessageRaw(uint32 type, const char* message) { + LinkedListIterator iterator(list); + iterator.Reset(); + + while(iterator.MoreElements()) { + iterator.GetData()->SendEmoteMessageRaw(type, message); + iterator.Advance(); + } +} + +Console* ConsoleList::FindByAccountName(const char* accname) { + LinkedListIterator iterator(list); + iterator.Reset(); + + while(iterator.MoreElements()) { + if (strcasecmp(iterator.GetData()->AccountName(), accname) == 0) + return iterator.GetData(); + + iterator.Advance(); + } + return 0; +} + +void Console::ProcessCommand(const char* command) { + switch(state) + { + case CONSOLE_STATE_USERNAME: + { + if (strlen(command) >= 16) { + SendMessage(1, 0); + SendMessage(2, "Username buffer overflow."); + SendMessage(1, "Bye Bye."); + state = CONSOLE_STATE_CLOSED; + return; + } + strcpy(paccountname, command); + state = CONSOLE_STATE_PASSWORD; + SendMessage(0, "Password: "); + tcpc->SetEcho(false); + break; + } + case CONSOLE_STATE_PASSWORD: + { + if (strlen(command) >= 16) { + SendMessage(1, 0); + SendMessage(2, "Password buffer overflow."); + SendMessage(1, "Bye Bye."); + state = CONSOLE_STATE_CLOSED; + return; + } + paccountid = database.CheckLogin(paccountname ,command); + if (paccountid == 0) { + SendMessage(1, 0); + SendMessage(2, "Login failed."); + SendMessage(1, "Bye Bye."); + state = CONSOLE_STATE_CLOSED; + return; + } + database.GetAccountName(paccountid, paccountname); // fixes case and stuff + admin = database.CheckStatus(paccountid); + if (!(admin >= consoleLoginStatus)) { + SendMessage(1, 0); + SendMessage(2, "Access denied."); + SendMessage(1, "Bye Bye."); + state = CONSOLE_STATE_CLOSED; + return; + } + Log(Logs::Detail, Logs::World_Server,"TCP console authenticated: Username=%s, Admin=%d",paccountname,admin); + SendMessage(1, 0); + SendMessage(2, "Login accepted."); + state = CONSOLE_STATE_CONNECTED; + tcpc->SetEcho(true); + SendPrompt(); + break; + } + case CONSOLE_STATE_CONNECTED: { + Log(Logs::Detail, Logs::World_Server,"TCP command: %s: \"%s\"",paccountname ,command); + Seperator sep(command); + if (strcasecmp(sep.arg[0], "help") == 0 || strcmp(sep.arg[0], "?") == 0) { + SendMessage(1, " whoami"); + SendMessage(1, " who"); + SendMessage(1, " zonestatus"); + SendMessage(1, " uptime [zoneID#]"); + SendMessage(1, " emote [zonename or charname or world] [type] [message]"); + SendMessage(1, " echo [on/off]"); + SendMessage(1, " acceptmessages [on/off]"); + SendMessage(1, " tell [name] [message]"); + SendMessage(1, " broadcast [message]"); + SendMessage(1, " gmsay [message]"); + SendMessage(1, " ooc [message]"); + SendMessage(1, " auction [message]"); + if (admin >= consoleKickStatus) + SendMessage(1, " kick [charname]"); + if (admin >= consoleLockStatus) + SendMessage(1, " lock/unlock"); + if (admin >= consoleZoneStatus) { + SendMessage(1, " zoneshutdown [zonename or ZoneServerID]"); + SendMessage(1, " zonebootup [ZoneServerID] [zonename]"); + SendMessage(1, " zonelock [list|lock|unlock] [zonename]"); + } + if (admin >= consoleFlagStatus) + SendMessage(1, " flag [status] [accountname]"); + if (admin >= consolePassStatus) + SendMessage(1, " setpass [accountname] [newpass]"); + if (admin >= consoleWorldStatus) { + SendMessage(1, " version"); + SendMessage(1, " worldshutdown"); + } + if (admin >= 201) { + SendMessage(1, " IPLookup [name]"); + } + if (admin >= 100) { + SendMessage(1, " LSReconnect"); + SendMessage(1, " signalcharbyname charname ID"); + SendMessage(1, " reloadworld"); + } + } + else if (strcasecmp(sep.arg[0], "ping") == 0) { + // do nothing + } + else if (strcasecmp(sep.arg[0], "signalcharbyname") == 0) { + SendMessage(1, "Signal Sent to %s with ID %i", (char*) sep.arg[1], atoi(sep.arg[2])); + uint32 message_len = strlen((char*) sep.arg[1]) + 1; + auto pack = new ServerPacket(ServerOP_CZSignalClientByName, + sizeof(CZClientSignalByName_Struct) + message_len); + CZClientSignalByName_Struct* CZSC = (CZClientSignalByName_Struct*) pack->pBuffer; + strn0cpy(CZSC->Name, (char*) sep.arg[1], 64); + CZSC->data = atoi(sep.arg[2]); + zoneserver_list.SendPacket(pack); + safe_delete(pack); + } + else if (strcasecmp(sep.arg[0], "setpass") == 0 && admin >= consolePassStatus) { + if (sep.argnum != 2) + SendMessage(1, "Format: setpass accountname password"); + else { + + int16 tmpstatus = 0; + uint32 tmpid = database.GetAccountIDByName(sep.arg[1], &tmpstatus); + if (!tmpid) + SendMessage(1, "Error: Account not found"); + else if (tmpstatus > admin) + SendMessage(1, "Cannot change password: Account's status is higher than yours"); + else if (database.SetLocalPassword(tmpid, sep.arg[2])) + SendMessage(1, "Password changed."); + else + SendMessage(1, "Error changing password."); + } + } + else if (strcasecmp(sep.arg[0], "uptime") == 0) { + if (sep.IsNumber(1) && atoi(sep.arg[1]) > 0) { + auto pack = new ServerPacket(ServerOP_Uptime, sizeof(ServerUptime_Struct)); + ServerUptime_Struct* sus = (ServerUptime_Struct*) pack->pBuffer; + snprintf(sus->adminname, sizeof(sus->adminname), "*%s", this->GetName()); + sus->zoneserverid = atoi(sep.arg[1]); + ZoneServer* zs = zoneserver_list.FindByID(sus->zoneserverid); + if (zs) + zs->SendPacket(pack); + else + SendMessage(1, "Zoneserver not found."); + delete pack; + } + else { + ZSList::ShowUpTime(this); + } + } + else if (strcasecmp(sep.arg[0], "md5") == 0) { + uint8 md5[16]; + MD5::Generate((const uchar*) sep.argplus[1], strlen(sep.argplus[1]), md5); + SendMessage(1, "MD5: %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", md5[0], md5[1], md5[2], md5[3], md5[4], md5[5], md5[6], md5[7], md5[8], md5[9], md5[10], md5[11], md5[12], md5[13], md5[14], md5[15]); + } + else if (strcasecmp(sep.arg[0], "whoami") == 0) { + SendMessage(1, "You are logged in as '%s'", this->AccountName()); + SendMessage(1, "You are known as '*%s'", this->AccountName()); + SendMessage(1, "AccessLevel: %d", this->Admin()); + } + else if (strcasecmp(sep.arg[0], "echo") == 0) { + if (strcasecmp(sep.arg[1], "on") == 0) + tcpc->SetEcho(true); + else if (strcasecmp(sep.arg[1], "off") == 0) { + if (pAcceptMessages) + SendMessage(1, "Echo can not be turned off while acceptmessages is on"); + else + tcpc->SetEcho(false); + } + else + SendMessage(1, "Usage: echo [on/off]"); + } + else if (strcasecmp(sep.arg[0], "acceptmessages") == 0) { + if (strcasecmp(sep.arg[1], "on") == 0) + if (tcpc->GetEcho()) + SendMessage(1, "AcceptMessages can not be turned on while echo is on"); + else + pAcceptMessages = true; + else if (strcasecmp(sep.arg[1], "off") == 0) + pAcceptMessages = false; + else + SendMessage(1, "Usage: acceptmessages [on/off]"); + } + else if (strcasecmp(sep.arg[0], "tell") == 0) { + char tmpname[64]; + tmpname[0] = '*'; + strcpy(&tmpname[1], paccountname); + zoneserver_list.SendChannelMessage(tmpname, sep.arg[1], 7, 0, sep.argplus[2]); + } + else if (strcasecmp(sep.arg[0], "broadcast") == 0) { + char tmpname[64]; + tmpname[0] = '*'; + strcpy(&tmpname[1], paccountname); + zoneserver_list.SendChannelMessage(tmpname, 0, 6, 0, sep.argplus[1]); + } + else if (strcasecmp(sep.arg[0], "ooc") == 0) { + char tmpname[64]; + tmpname[0] = '*'; + strcpy(&tmpname[1], paccountname); + zoneserver_list.SendChannelMessage(tmpname, 0, 5, 0, sep.argplus[1]); + } + else if (strcasecmp(sep.arg[0], "auction") == 0) { + char tmpname[64]; + tmpname[0] = '*'; + strcpy(&tmpname[1], paccountname); + zoneserver_list.SendChannelMessage(tmpname, 0, 4, 0, sep.argplus[1]); + } + else if (strcasecmp(sep.arg[0], "gmsay") == 0 || strcasecmp(sep.arg[0], "pr") == 0) { + char tmpname[64]; + tmpname[0] = '*'; + strcpy(&tmpname[1], paccountname); + zoneserver_list.SendChannelMessage(tmpname, 0, 11, 0, sep.argplus[1]); + } + else if (strcasecmp(sep.arg[0], "emote") == 0) { + if (strcasecmp(sep.arg[1], "world") == 0) + zoneserver_list.SendEmoteMessageRaw(0, 0, 0, atoi(sep.arg[2]), sep.argplus[3]); + else { + ZoneServer* zs = zoneserver_list.FindByName(sep.arg[1]); + if (zs != 0) + zs->SendEmoteMessageRaw(0, 0, 0, atoi(sep.arg[2]), sep.argplus[3]); + else + zoneserver_list.SendEmoteMessageRaw(sep.arg[1], 0, 0, atoi(sep.arg[2]), sep.argplus[3]); + } + } + else if (strcasecmp(sep.arg[0], "movechar") == 0) { + if(sep.arg[1][0]==0 || sep.arg[2][0] == 0) + SendMessage(1, "Usage: movechar [charactername] [zonename]"); + else { + if (!database.GetZoneID(sep.arg[2])) + SendMessage(1, "Error: Zone '%s' not found", sep.arg[2]); + else if (!database.CheckUsedName((char*) sep.arg[1])) { + if (!database.MoveCharacterToZone((char*) sep.arg[1], (char*) sep.arg[2])) + SendMessage(1, "Character Move Failed!"); + else + SendMessage(1, "Character has been moved."); + } + else + SendMessage(1, "Character Does Not Exist"); + } + } + else if (strcasecmp(sep.arg[0], "flag") == 0 && this->Admin() >= consoleFlagStatus) { +// SCORPIOUS2K - reversed parameter order for flag + if(sep.arg[2][0]==0 || !sep.IsNumber(1)) + SendMessage(1, "Usage: flag [status] [accountname]"); + else + { + if (atoi(sep.arg[1]) > this->Admin()) + SendMessage(1, "You cannot set people's status to higher than your own"); + else if (atoi(sep.arg[1]) < 0 && this->Admin() < consoleFlagStatus) + SendMessage(1, "You have too low of status to change flags"); + else if (!database.SetAccountStatus(sep.arg[2], atoi(sep.arg[1]))) + SendMessage(1, "Unable to flag account!"); + else + SendMessage(1, "Account Flaged"); + } + } + else if (strcasecmp(sep.arg[0], "kick") == 0 && admin >= consoleKickStatus) { + char tmpname[64]; + tmpname[0] = '*'; + strcpy(&tmpname[1], paccountname); + auto pack = new ServerPacket; + pack->opcode = ServerOP_KickPlayer; + pack->size = sizeof(ServerKickPlayer_Struct); + pack->pBuffer = new uchar[pack->size]; + ServerKickPlayer_Struct* skp = (ServerKickPlayer_Struct*) pack->pBuffer; + strcpy(skp->adminname, tmpname); + strcpy(skp->name, sep.arg[1]); + skp->adminrank = this->Admin(); + zoneserver_list.SendPacket(pack); + delete pack; + } + else if (strcasecmp(sep.arg[0], "who") == 0) { + auto whom = new Who_All_Struct; + memset(whom, 0, sizeof(Who_All_Struct)); + whom->lvllow = 0xFFFF; + whom->lvlhigh = 0xFFFF; + whom->wclass = 0xFFFF; + whom->wrace = 0xFFFF; + whom->gmlookup = 0xFFFF; + for (int i=1; i<=sep.argnum; i++) { + if (strcasecmp(sep.arg[i], "gm") == 0) + whom->gmlookup = 1; + else if (sep.IsNumber(i)) { + if (whom->lvllow == 0xFFFF) { + whom->lvllow = atoi(sep.arg[i]); + whom->lvlhigh = whom->lvllow; + } + else if (atoi(sep.arg[i]) > int(whom->lvllow)) + whom->lvlhigh = atoi(sep.arg[i]); + else + whom->lvllow = atoi(sep.arg[i]); + } + else + strn0cpy(whom->whom, sep.arg[i], sizeof(whom->whom)); + } + client_list.ConsoleSendWhoAll(0, admin, whom, this); + delete whom; + } + else if (strcasecmp(sep.arg[0], "zonestatus") == 0) { + zoneserver_list.SendZoneStatus(0, admin, this); + } + else if (strcasecmp(sep.arg[0], "exit") == 0 || strcasecmp(sep.arg[0], "quit") == 0) { + SendMessage(1, "Bye Bye."); + state = CONSOLE_STATE_CLOSED; + } + else if (strcasecmp(sep.arg[0], "zoneshutdown") == 0 && admin >= consoleZoneStatus) { + if (sep.arg[1][0] == 0) { + SendMessage(1, "Usage: zoneshutdown zoneshortname"); + } else { + char tmpname[64]; + tmpname[0] = '*'; + strcpy(&tmpname[1], paccountname); + + auto pack = new ServerPacket; + pack->size = sizeof(ServerZoneStateChange_struct); + pack->pBuffer = new uchar[pack->size]; + memset(pack->pBuffer, 0, sizeof(ServerZoneStateChange_struct)); + ServerZoneStateChange_struct* s = (ServerZoneStateChange_struct *) pack->pBuffer; + pack->opcode = ServerOP_ZoneShutdown; + strcpy(s->adminname, tmpname); + if (sep.arg[1][0] >= '0' && sep.arg[1][0] <= '9') + s->ZoneServerID = atoi(sep.arg[1]); + else + s->zoneid = database.GetZoneID(sep.arg[1]); + + ZoneServer* zs = 0; + if (s->ZoneServerID != 0) + zs = zoneserver_list.FindByID(s->ZoneServerID); + else if (s->zoneid != 0) + zs = zoneserver_list.FindByName(database.GetZoneName(s->zoneid)); + else + SendMessage(1, "Error: ZoneShutdown: neither ID nor name specified"); + + if (zs == 0) + SendMessage(1, "Error: ZoneShutdown: zoneserver not found"); + else + zs->SendPacket(pack); + + delete pack; + } + } + else if (strcasecmp(sep.arg[0], "zonebootup") == 0 && admin >= consoleZoneStatus) { + if (sep.arg[2][0] == 0 || !sep.IsNumber(1)) { + SendMessage(1, "Usage: zonebootup ZoneServerID# zoneshortname"); + } else { + char tmpname[64]; + tmpname[0] = '*'; + strcpy(&tmpname[1], paccountname); + + Log(Logs::Detail, Logs::World_Server,"Console ZoneBootup: %s, %s, %s",tmpname,sep.arg[2],sep.arg[1]); + zoneserver_list.SOPZoneBootup(tmpname, atoi(sep.arg[1]), sep.arg[2], (bool) (strcasecmp(sep.arg[3], "static") == 0)); + } + } + else if (strcasecmp(sep.arg[0], "worldshutdown") == 0 && admin >= consoleWorldStatus) { + int32 time, interval; + if(sep.IsNumber(1) && sep.IsNumber(2) && ((time=atoi(sep.arg[1]))>0) && ((interval=atoi(sep.arg[2]))>0)) { + zoneserver_list.WorldShutDown(time, interval); + } + else if(strcasecmp(sep.arg[1], "now") == 0) { + zoneserver_list.WorldShutDown(0, 0); + } + else if(strcasecmp(sep.arg[1], "disable") == 0) { + SendEmoteMessage(0,0,0,15,":SYSTEM MSG:World shutdown aborted."); + zoneserver_list.SendEmoteMessage(0,0,0,15,":SYSTEM MSG:World shutdown aborted."); + zoneserver_list.shutdowntimer->Disable(); + zoneserver_list.reminder->Disable(); + } + else { + SendMessage(1, "Usage: worldshutdown [now] [disable] ([time] [interval])"); + //Go ahead and shut down since that's what this used to do when invoked this way. + zoneserver_list.WorldShutDown(0, 0); + } + } + else if (strcasecmp(sep.arg[0], "lock") == 0 && admin >= consoleLockStatus) { + WorldConfig::LockWorld(); + if (loginserverlist.Connected()) { + loginserverlist.SendStatus(); + SendMessage(1, "World locked."); + } + else { + SendMessage(1, "World locked, but login server not connected."); + } + } + else if (strcasecmp(sep.arg[0], "unlock") == 0 && admin >= consoleLockStatus) { + WorldConfig::UnlockWorld(); + if (loginserverlist.Connected()) { + loginserverlist.SendStatus(); + SendMessage(1, "World unlocked."); + } + else { + SendMessage(1, "World unlocked, but login server not connected."); + } + } + else if (strcasecmp(sep.arg[0], "version") == 0 && admin >= consoleWorldStatus) { + SendMessage(1, "Current version information."); + SendMessage(1, " %s", CURRENT_VERSION); + SendMessage(1, " Compiled on: %s at %s", COMPILE_DATE, COMPILE_TIME); + SendMessage(1, " Last modified on: %s", LAST_MODIFIED); + } + else if (strcasecmp(sep.arg[0], "serverinfo") == 0 && admin >= 200) { + if (strcasecmp(sep.arg[1], "os") == 0) { + #ifdef _WINDOWS + GetOS(); + char intbuffer [sizeof(unsigned long)]; + SendMessage(1, "Operating system information."); + SendMessage(1, " %s", Ver_name); + SendMessage(1, " Build number: %s", ultoa(Ver_build, intbuffer, 10)); + SendMessage(1, " Minor version: %s", ultoa(Ver_min, intbuffer, 10)); + SendMessage(1, " Major version: %s", ultoa(Ver_maj, intbuffer, 10)); + SendMessage(1, " Platform Id: %s", ultoa(Ver_pid, intbuffer, 10)); + #else + char os_string[100]; + SendMessage(1, "Operating system information."); + SendMessage(1, " %s", GetOS(os_string)); + #endif + } + else { + SendMessage(1, "Usage: Serverinfo [type]"); + SendMessage(1, " OS - Operating system version information."); + } + } + else if (strcasecmp(sep.arg[0], "IPLookup") == 0 && admin >= 201) { + client_list.SendCLEList(admin, 0, this, sep.argplus[1]); + } + else if (strcasecmp(sep.arg[0], "LSReconnect") == 0 && admin >= 100) { + #ifdef _WINDOWS + _beginthread(AutoInitLoginServer, 0, nullptr); + #else + pthread_t thread; + pthread_create(&thread, nullptr, &AutoInitLoginServer, nullptr); + #endif + RunLoops = true; + SendMessage(1, " Login Server Reconnect manually restarted by Console"); + Log(Logs::Detail, Logs::World_Server,"Login Server Reconnect manually restarted by Console"); + } + else if (strcasecmp(sep.arg[0], "zonelock") == 0 && admin >= consoleZoneStatus) { + if (strcasecmp(sep.arg[1], "list") == 0) { + zoneserver_list.ListLockedZones(0, this); + } + else if (strcasecmp(sep.arg[1], "lock") == 0 && admin >= 101) { + uint16 tmp = database.GetZoneID(sep.arg[2]); + if (tmp) { + if (zoneserver_list.SetLockedZone(tmp, true)) + zoneserver_list.SendEmoteMessage(0, 0, 80, 15, "Zone locked: %s", database.GetZoneName(tmp)); + else + SendMessage(1, "Failed to change lock"); + } + else + SendMessage(1, "Usage: #zonelock lock [zonename]"); + } + else if (strcasecmp(sep.arg[1], "unlock") == 0 && admin >= 101) { + uint16 tmp = database.GetZoneID(sep.arg[2]); + if (tmp) { + if (zoneserver_list.SetLockedZone(tmp, false)) + zoneserver_list.SendEmoteMessage(0, 0, 80, 15, "Zone unlocked: %s", database.GetZoneName(tmp)); + else + SendMessage(1, "Failed to change lock"); + } + else + SendMessage(1, "Usage: #zonelock unlock [zonename]"); + } + else { + SendMessage(1, "#zonelock sub-commands"); + SendMessage(1, " list"); + if (admin >= 101) { + SendMessage(1, " lock [zonename]"); + SendMessage(1, " unlock [zonename]"); + } + } + } + else if (strcasecmp(sep.arg[0], "reloadworld") == 0 && admin > 101) + { + SendEmoteMessage(0,0,0,15,"Reloading World..."); + auto pack = new ServerPacket(ServerOP_ReloadWorld, sizeof(ReloadWorld_Struct)); + ReloadWorld_Struct* RW = (ReloadWorld_Struct*) pack->pBuffer; + RW->Option = 1; + zoneserver_list.SendPacket(pack); + safe_delete(pack); + } + else if (strcasecmp(sep.arg[0], "") == 0){ + /* Hit Enter with no command */ + } + else { + SendMessage(1, "Command unknown."); + } + if (state == CONSOLE_STATE_CONNECTED) + SendPrompt(); + break; + } + default: { + break; + } + } +} + +void Console::SendPrompt() { + if (tcpc->GetEcho()) + SendMessage(0, "%s> ", paccountname); +} + diff --git a/world/net.cpp b/world/net.cpp index 788ffcaad..20c7afec3 100644 --- a/world/net.cpp +++ b/world/net.cpp @@ -81,6 +81,7 @@ union semun { #include "ucs.h" #include "queryserv.h" #include "web_interface.h" +#include "console.h" #include "../common/net/tcp_server.h" #include "../common/net/servertalk_server.h" @@ -375,6 +376,12 @@ int main(int argc, char** argv) { database.LoadCharacterCreateAllocations(); database.LoadCharacterCreateCombos(); + std::unique_ptr console; + if (Config->TelnetEnabled) { + Log(Logs::General, Logs::World_Server, "Console (TCP) listener started."); + console.reset(new ConsoleServer()); + } + std::unique_ptr server_connection; server_connection.reset(new EQ::Net::ServertalkServer());