From 18d28ae8d3e2b44955cf1504e8420f580ad368f9 Mon Sep 17 00:00:00 2001 From: KimLS Date: Sun, 9 Apr 2017 20:17:48 -0700 Subject: [PATCH] Console initial implementation --- common/CMakeLists.txt | 8 + common/net/console_server.cpp | 78 ++++ common/net/console_server.h | 49 ++ common/net/console_server_connection.cpp | 221 +++++++++ common/net/console_server_connection.h | 63 +++ common/net/tcp_server.cpp | 14 +- common/net/tcp_server.h | 1 + common/string_util.cpp | 530 +++++++++++---------- common/string_util.h | 28 +- world/CMakeLists.txt | 3 + world/clientlist.cpp | 47 +- world/clientlist.h | 1 + world/console.cpp | 562 +++++++++++++++++++---- world/console.h | 44 +- world/net.cpp | 6 +- world/world_console_connection.cpp | 19 + world/world_console_connection.h | 36 ++ zone/net.cpp | 5 +- 18 files changed, 1302 insertions(+), 413 deletions(-) create mode 100644 common/net/console_server.cpp create mode 100644 common/net/console_server.h create mode 100644 common/net/console_server_connection.cpp create mode 100644 common/net/console_server_connection.h create mode 100644 world/world_console_connection.cpp create mode 100644 world/world_console_connection.h diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 4b82667bb..06486d2db 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -73,6 +73,8 @@ SET(common_sources platform.cpp event/event_loop.cpp json/jsoncpp.cpp + net/console_server.cpp + net/console_server_connection.cpp net/crc32.cpp net/daybreak_connection.cpp net/eqstream.cpp @@ -207,6 +209,8 @@ SET(common_headers event/timer.h json/json.h json/json-forwards.h + net/console_server.h + net/console_server_connection.h net/crc32.h net/daybreak_connection.h net/daybreak_structs.h @@ -270,6 +274,10 @@ SOURCE_GROUP(Json FILES ) SOURCE_GROUP(Net FILES + net/console_server.cpp + net/console_server.h + net/console_server_connection.cpp + net/console_server_connection.h net/crc32.cpp net/crc32.h net/daybreak_connection.cpp diff --git a/common/net/console_server.cpp b/common/net/console_server.cpp new file mode 100644 index 000000000..d8f08b05d --- /dev/null +++ b/common/net/console_server.cpp @@ -0,0 +1,78 @@ +#include "console_server.h" +#include "../string_util.h" +#include + +EQ::Net::ConsoleServer::ConsoleServer(const std::string &addr, int port) +{ + m_server.reset(new EQ::Net::TCPServer()); + m_server->Listen(addr, port, false, [this](std::shared_ptr connection) { + ConsoleServerConnection *c = new ConsoleServerConnection(this, connection); + m_connections.insert(std::make_pair(c->GetUUID(), std::unique_ptr(c))); + }); +} + +EQ::Net::ConsoleServer::~ConsoleServer() +{ +} + +void EQ::Net::ConsoleServer::RegisterCall(const std::string &command, int status_required, const std::string& help_definition, ConsoleServerCallback fn) +{ + m_commands[command] = { fn, status_required, help_definition }; +} + +void EQ::Net::ConsoleServer::RegisterLogin(ConsoleServerLoginCallback fn) +{ + m_login = fn; +} + +void EQ::Net::ConsoleServer::ConnectionDisconnected(ConsoleServerConnection *c) +{ + auto iter = m_connections.find(c->GetUUID()); + if (iter != m_connections.end()) { + m_connections.erase(iter); + } +} + +void EQ::Net::ConsoleServer::ProcessCommand(ConsoleServerConnection *c, const std::string &cmd) +{ + auto split = SplitString(cmd, ' '); + + if (split.size() > 0) { + auto command = split[0]; + ToLowerString(command); + + if (command == "help" || command == "?") { + c->SendLine("Commands:"); + for (auto &cmd_def : m_commands) { + if (cmd_def.second.status_required <= c->Admin()) { + auto display = fmt::format(" {0}", cmd_def.second.help_definition); + c->SendLine(display); + } + } + + c->SendPrompt(); + return; + } + + split.erase(split.begin(), split.begin() + 1); + + auto cmd_def = m_commands.find(command); + if (cmd_def != m_commands.end()) { + if (c->Admin() >= cmd_def->second.status_required) { + cmd_def->second.fn(c, command, split); + c->SendPrompt(); + } + else { + c->SendLine(fmt::format("Access denied for command: {0}", command)); + c->SendPrompt(); + } + } + else { + c->SendLine(fmt::format("Command not found: {0}", command)); + c->SendPrompt(); + } + } + else { + c->SendPrompt(); + } +} diff --git a/common/net/console_server.h b/common/net/console_server.h new file mode 100644 index 000000000..5a0c48f8e --- /dev/null +++ b/common/net/console_server.h @@ -0,0 +1,49 @@ +#pragma once + +#include "console_server_connection.h" +#include +#include + +namespace EQ +{ + namespace Net + { + struct ConsoleLoginStatus + { + int status; + int account_id; + std::string account_name; + }; + + class ConsoleServer + { + public: + typedef std::function&)> ConsoleServerCallback; + typedef std::function ConsoleServerLoginCallback; + ConsoleServer(const std::string &addr, int port); + ~ConsoleServer(); + + void RegisterCall(const std::string& command, int status_required, const std::string& help_definition, ConsoleServerCallback fn); + void RegisterLogin(ConsoleServerLoginCallback fn); + + private: + void ConnectionDisconnected(ConsoleServerConnection *c); + void ProcessCommand(ConsoleServerConnection *c, const std::string& cmd); + + std::unique_ptr m_server; + + std::map> m_connections; + + struct ConsoleServerCommand + { + ConsoleServerCallback fn; + int status_required; + std::string help_definition; + }; + + std::map m_commands; + ConsoleServerLoginCallback m_login; + friend class ConsoleServerConnection; + }; + } +} diff --git a/common/net/console_server_connection.cpp b/common/net/console_server_connection.cpp new file mode 100644 index 000000000..8764f09b6 --- /dev/null +++ b/common/net/console_server_connection.cpp @@ -0,0 +1,221 @@ +#include "console_server.h" +#include "../common/util/uuid.h" +#include "../common/net/packet.h" +#include "../common/eqemu_logsys.h" + +EQ::Net::ConsoleServerConnection::ConsoleServerConnection(ConsoleServer *parent, std::shared_ptr connection) +{ + m_parent = parent; + m_connection = connection; + m_uuid = EQ::Util::UUID::Generate().ToString(); + m_cursor = 0; + memset(m_line, 0, MaxConsoleLineLength); + m_accept_messages = false; + m_user_id = 0; + m_admin = 0; + + m_connection->OnRead(std::bind(&ConsoleServerConnection::OnRead, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); + m_connection->OnDisconnect(std::bind(&ConsoleServerConnection::OnDisconnect, this, std::placeholders::_1)); + m_connection->Start(); + ClearBuffer(); + + auto addr = m_connection->RemoteIP(); + + SendLine(fmt::format("Establishing connection from: {0}:{1}", addr, m_connection->RemotePort())); + + if (addr.find("127.0.0.1") != std::string::npos || addr.find("::0") != std::string::npos) { + SendLine("Connection established from localhost, assuming admin"); + m_status = ConsoleStatusLoggedIn; + m_admin = 255; + SendPrompt(); + } + else { + m_status = ConsoleStatusWaitingForLogin; + Send("Username: "); + } +} + +EQ::Net::ConsoleServerConnection::~ConsoleServerConnection() +{ +} + +void EQ::Net::ConsoleServerConnection::SendClear() +{ + EQ::Net::DynamicPacket clear; + clear.PutUInt8(0, 0); + m_connection->Write((const char*)clear.Data(), clear.Length()); +} + +void EQ::Net::ConsoleServerConnection::Send(const std::string &msg) +{ + m_connection->Write(msg.c_str(), msg.length()); +} + +void EQ::Net::ConsoleServerConnection::SendLine(const std::string &line) +{ + Send(line); + SendNewLine(); +} + +void EQ::Net::ConsoleServerConnection::SendNewLine() +{ + EQ::Net::DynamicPacket newline; + newline.PutUInt8(0, 10); + newline.PutUInt8(1, 13); + m_connection->Write((const char*)newline.Data(), newline.Length()); +} + +void EQ::Net::ConsoleServerConnection::QueueMessage(const std::string &msg) +{ + if (!m_accept_messages) { + return; + } + + if (m_cursor == 0) { + size_t len = m_user.length() + 2; + for (size_t i = 0; i < len; ++i) { + Send("\x08"); + } + + SendLine(msg); + SendPrompt(); + } + else { + std::string cmd(m_line, m_line + m_cursor); + + size_t len = m_user.length() + 2 + cmd.length(); + for (size_t i = 0; i < len; ++i) { + Send("\x08"); + } + + if (msg.length() < cmd.length()) { + Send(msg); + size_t blank_spaces = 2 + cmd.length() - msg.length(); + + for (size_t i = 0; i < blank_spaces; ++i) { + Send(" "); + } + + SendNewLine(); + } + else { + SendLine(msg); + } + + SendPrompt(); + Send(cmd); + } +} + +void EQ::Net::ConsoleServerConnection::OnRead(TCPConnection *c, const unsigned char *data, size_t sz) +{ + for (size_t i = 0; i < sz; ++i) { + unsigned char c = data[i]; + + switch (c) { + case 0: + m_cursor = 0; + break; + case 10: // \n + { + if (m_cursor > 0) { + std::string cmd(m_line, m_line + m_cursor); + ProcessCommand(cmd); + m_cursor = 0; + } + else { + ProcessCommand(""); + } + } + break; + case 13: // \r + break; + case 8: + if (m_cursor > 0) { + m_cursor--; + } + break; + case 255: + //255 is always followed by a character + i++; + if (i < sz) { + unsigned char c = data[i]; + + if (c == 255) { + //Escaped 255 + if (m_cursor < MaxConsoleLineLength && isprint(c)) { + m_line[m_cursor] = c; + m_cursor++; + } + + break; + } + + if (c == 251 || c == 252 || c == 253 || c == 254) { + //Option code is always followed by an extra character + + //We don't really care about negotiation tho. + i++; + } + } + + + break; + default: + if (m_cursor < MaxConsoleLineLength && isprint(c)) { + m_line[m_cursor] = c; + m_cursor++; + } + + break; + } + } +} + +void EQ::Net::ConsoleServerConnection::OnDisconnect(TCPConnection *c) +{ + m_parent->ConnectionDisconnected(this); +} + +void EQ::Net::ConsoleServerConnection::ProcessCommand(const std::string &cmd) +{ + if (m_status == ConsoleStatusWaitingForLogin) { + m_user = cmd; + m_status = ConsoleStatusWaitingForPassword; + Send("Password: "); + return; + } + + if (m_status == ConsoleStatusWaitingForPassword) { + auto status = m_parent->m_login(m_user, cmd); + if (status.account_id == 0) { + m_status = ConsoleStatusFailedLogin; + SendLine("Access denied"); + m_connection->Disconnect(); + return; + } + + if (status.status < ConsoleLoginStatus) { + m_status = ConsoleStatusFailedLogin; + SendLine("Access denied"); + m_connection->Disconnect(); + return; + } + + m_user = status.account_name; + m_user_id = status.account_id; + m_admin = status.status; + m_status = ConsoleStatusLoggedIn; + SendPrompt(); + return; + } + + if (m_status == ConsoleStatusLoggedIn) { + m_parent->ProcessCommand(this, cmd); + } +} + +void EQ::Net::ConsoleServerConnection::SendPrompt() +{ + Send(fmt::format("{0}> ", m_user)); +} diff --git a/common/net/console_server_connection.h b/common/net/console_server_connection.h new file mode 100644 index 000000000..d826ba07b --- /dev/null +++ b/common/net/console_server_connection.h @@ -0,0 +1,63 @@ +#pragma once + +#include "tcp_server.h" +#include +#include + +namespace EQ +{ + namespace Net + { + enum ConsoleConnectionStatus + { + ConsoleStatusWaitingForLogin, + ConsoleStatusWaitingForPassword, + ConsoleStatusLoggedIn, + ConsoleStatusFailedLogin + }; + + const int MaxConsoleLineLength = 512; + const int ConsoleLoginStatus = 50; + class ConsoleServer; + class ConsoleServerConnection + { + public: + ConsoleServerConnection(ConsoleServer *parent, std::shared_ptr connection); + ~ConsoleServerConnection(); + + std::string GetUUID() const { return m_uuid; } + void ClearBuffer() { m_cursor = 0; } + void Close() { m_connection->Disconnect(); } + + void SendClear(); + void Send(const std::string &msg); + void SendLine(const std::string &line); + void SendNewLine(); + void SendPrompt(); + ConsoleConnectionStatus Status() const { return m_status; } + std::string UserName() const { return m_user; } + int UserId() const { return m_user_id; } + int Admin() const { return m_admin; } + + bool AcceptMessages() const { return m_accept_messages; } + void SetAcceptMessages(bool v) { m_accept_messages = v; } + void QueueMessage(const std::string &msg); + private: + void OnRead(TCPConnection* c, const unsigned char* data, size_t sz); + void OnDisconnect(TCPConnection* c); + void ProcessCommand(const std::string &cmd); + + ConsoleServer *m_parent; + std::shared_ptr m_connection; + std::string m_uuid; + ConsoleConnectionStatus m_status; + std::string m_user; + int m_user_id; + int m_admin; + bool m_accept_messages; + + size_t m_cursor; + char m_line[MaxConsoleLineLength]; + }; + } +} diff --git a/common/net/tcp_server.cpp b/common/net/tcp_server.cpp index addd19365..ed68d54dc 100644 --- a/common/net/tcp_server.cpp +++ b/common/net/tcp_server.cpp @@ -15,6 +15,16 @@ EQ::Net::TCPServer::~TCPServer() { } void EQ::Net::TCPServer::Listen(int port, bool ipv6, std::function)> cb) +{ + if (ipv6) { + Listen("::", port, ipv6, cb); + } + else { + Listen("0.0.0.0", port, ipv6, cb); + } +} + +void EQ::Net::TCPServer::Listen(const std::string &addr, int port, bool ipv6, std::function)> cb) { if (m_socket) { return; @@ -29,10 +39,10 @@ void EQ::Net::TCPServer::Listen(int port, bool ipv6, std::function)> cb); + void Listen(const std::string &addr, int port, bool ipv6, std::function)> cb); void Close(); void AddClient(uv_tcp_t *c); diff --git a/common/string_util.cpp b/common/string_util.cpp index 902d13335..3e9986b48 100644 --- a/common/string_util.cpp +++ b/common/string_util.cpp @@ -15,6 +15,7 @@ */ #include "string_util.h" +#include #ifdef _WINDOWS #include @@ -71,246 +72,6 @@ const std::string StringFormat(const char* format, ...) return output; } -// normal strncpy doesnt put a null term on copied strings, this one does -// ref: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wcecrt/htm/_wcecrt_strncpy_wcsncpy.asp -char* strn0cpy(char* dest, const char* source, uint32 size) { - if (!dest) - return 0; - if (size == 0 || source == 0) { - dest[0] = 0; - return dest; - } - strncpy(dest, source, size); - dest[size - 1] = 0; - return dest; -} - -// String N w/null Copy Truncated? -// return value =true if entire string(source) fit, false if it was truncated -bool strn0cpyt(char* dest, const char* source, uint32 size) { - if (!dest) - return 0; - if (size == 0 || source == 0) { - dest[0] = 0; - return false; - } - strncpy(dest, source, size); - dest[size - 1] = 0; - return (bool) (source[strlen(dest)] == 0); -} - -const char *MakeLowerString(const char *source) { - static char str[128]; - if (!source) - return nullptr; - MakeLowerString(source, str); - return str; -} - -void MakeLowerString(const char *source, char *target) { - if (!source || !target) { - *target=0; - return; - } - while (*source) - { - *target = tolower(*source); - target++;source++; - } - *target = 0; -} - -int MakeAnyLenString(char** ret, const char* format, ...) { - int buf_len = 128; - int chars = -1; - va_list argptr, tmpargptr; - va_start(argptr, format); - while (chars == -1 || chars >= buf_len) { - safe_delete_array(*ret); - if (chars == -1) - buf_len *= 2; - else - buf_len = chars + 1; - *ret = new char[buf_len]; - va_copy(tmpargptr, argptr); - chars = vsnprintf(*ret, buf_len, format, tmpargptr); - } - va_end(argptr); - return chars; -} - -uint32 AppendAnyLenString(char** ret, uint32* bufsize, uint32* strlen, const char* format, ...) { - if (*bufsize == 0) - *bufsize = 256; - if (*ret == 0) - *strlen = 0; - int chars = -1; - char* oldret = 0; - va_list argptr, tmpargptr; - va_start(argptr, format); - while (chars == -1 || chars >= (int32)(*bufsize-*strlen)) { - if (chars == -1) - *bufsize += 256; - else - *bufsize += chars + 25; - oldret = *ret; - *ret = new char[*bufsize]; - if (oldret) { - if (*strlen) - memcpy(*ret, oldret, *strlen); - safe_delete_array(oldret); - } - va_copy(tmpargptr, argptr); - chars = vsnprintf(&(*ret)[*strlen], (*bufsize-*strlen), format, tmpargptr); - } - va_end(argptr); - *strlen += chars; - return *strlen; -} - -uint32 hextoi(const char* num) { - if (num == nullptr) - return 0; - - int len = strlen(num); - if (len < 3) - return 0; - - if (num[0] != '0' || (num[1] != 'x' && num[1] != 'X')) - return 0; - - uint32 ret = 0; - int mul = 1; - for (int i=len-1; i>=2; i--) { - if (num[i] >= 'A' && num[i] <= 'F') - ret += ((num[i] - 'A') + 10) * mul; - else if (num[i] >= 'a' && num[i] <= 'f') - ret += ((num[i] - 'a') + 10) * mul; - else if (num[i] >= '0' && num[i] <= '9') - ret += (num[i] - '0') * mul; - else - return 0; - mul *= 16; - } - return ret; -} - -uint64 hextoi64(const char* num) { - if (num == nullptr) - return 0; - - int len = strlen(num); - if (len < 3) - return 0; - - if (num[0] != '0' || (num[1] != 'x' && num[1] != 'X')) - return 0; - - uint64 ret = 0; - int mul = 1; - for (int i=len-1; i>=2; i--) { - if (num[i] >= 'A' && num[i] <= 'F') - ret += ((num[i] - 'A') + 10) * mul; - else if (num[i] >= 'a' && num[i] <= 'f') - ret += ((num[i] - 'a') + 10) * mul; - else if (num[i] >= '0' && num[i] <= '9') - ret += (num[i] - '0') * mul; - else - return 0; - mul *= 16; - } - return ret; -} - -bool atobool(const char* iBool) { - - if (iBool == nullptr) - return false; - if (!strcasecmp(iBool, "true")) - return true; - if (!strcasecmp(iBool, "false")) - return false; - if (!strcasecmp(iBool, "yes")) - return true; - if (!strcasecmp(iBool, "no")) - return false; - if (!strcasecmp(iBool, "on")) - return true; - if (!strcasecmp(iBool, "off")) - return false; - if (!strcasecmp(iBool, "enable")) - return true; - if (!strcasecmp(iBool, "disable")) - return false; - if (!strcasecmp(iBool, "enabled")) - return true; - if (!strcasecmp(iBool, "disabled")) - return false; - if (!strcasecmp(iBool, "y")) - return true; - if (!strcasecmp(iBool, "n")) - return false; - if (atoi(iBool)) - return true; - return false; -} - -// removes the crap and turns the underscores into spaces. -char *CleanMobName(const char *in, char *out) -{ - unsigned i, j; - - for(i = j = 0; i < strlen(in); i++) - { - // convert _ to space.. any other conversions like this? I *think* this - // is the only non alpha char that's not stripped but converted. - if(in[i] == '_') - { - out[j++] = ' '; - } - else - { - if(isalpha(in[i]) || (in[i] == '`')) // numbers, #, or any other crap just gets skipped - out[j++] = in[i]; - } - } - out[j] = 0; // terimnate the string before returning it - return out; -} - - -void RemoveApostrophes(std::string &s) -{ - for(unsigned int i = 0; i < s.length(); ++i) - if(s[i] == '\'') - s[i] = '_'; -} - -char *RemoveApostrophes(const char *s) -{ - auto NewString = new char[strlen(s) + 1]; - - strcpy(NewString, s); - - for(unsigned int i = 0 ; i < strlen(NewString); ++i) - if(NewString[i] == '\'') - NewString[i] = '_'; - - return NewString; -} - -const char *ConvertArray(int input, char *returnchar) -{ - sprintf(returnchar, "%i" ,input); - return returnchar; -} - -const char *ConvertArrayF(float input, char *returnchar) -{ - sprintf(returnchar, "%0.2f", input); - return returnchar; -} - std::vector SplitString(const std::string &str, char delim) { std::vector ret; std::stringstream ss(str); @@ -396,16 +157,35 @@ std::string EscapeString(const char *src, size_t sz) { return ret; } -bool isAlphaNumeric(const char *text) -{ - for (unsigned int charIndex=0; charIndex 'z') && - (text[charIndex] < 'A' || text[charIndex] > 'Z') && - (text[charIndex] < '0' || text[charIndex] > '9')) - return false; +bool StringIsNumber(const std::string &s) { + try { + auto r = stod(s); + return true; + } + catch (std::exception) { + return false; + } +} + +void ToLowerString(std::string &s) { + std::transform(s.begin(), s.end(), s.begin(), ::tolower); +} + +void ToUpperString(std::string &s) { + std::transform(s.begin(), s.end(), s.begin(), ::toupper); +} + +std::string JoinString(const std::vector& ar, const std::string &delim) { + std::string ret; + for (size_t i = 0; i < ar.size(); ++i) { + if (i != 0) { + ret += delim; + } + + ret += ar[i]; } - return true; + return ret; } void find_replace(std::string& string_subject, const std::string& search_string, const std::string& replace_string) { @@ -415,3 +195,257 @@ void find_replace(std::string& string_subject, const std::string& search_string, index = string_subject.find_first_of(search_string); } } + +//Const char based + +// normal strncpy doesnt put a null term on copied strings, this one does +// ref: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wcecrt/htm/_wcecrt_strncpy_wcsncpy.asp +char* strn0cpy(char* dest, const char* source, uint32 size) { + if (!dest) + return 0; + if (size == 0 || source == 0) { + dest[0] = 0; + return dest; + } + strncpy(dest, source, size); + dest[size - 1] = 0; + return dest; +} + +// String N w/null Copy Truncated? +// return value =true if entire string(source) fit, false if it was truncated +bool strn0cpyt(char* dest, const char* source, uint32 size) { + if (!dest) + return 0; + if (size == 0 || source == 0) { + dest[0] = 0; + return false; + } + strncpy(dest, source, size); + dest[size - 1] = 0; + return (bool)(source[strlen(dest)] == 0); +} + +const char *MakeLowerString(const char *source) { + static char str[128]; + if (!source) + return nullptr; + MakeLowerString(source, str); + return str; +} + +void MakeLowerString(const char *source, char *target) { + if (!source || !target) { + *target = 0; + return; + } + while (*source) + { + *target = tolower(*source); + target++; source++; + } + *target = 0; +} + +int MakeAnyLenString(char** ret, const char* format, ...) { + int buf_len = 128; + int chars = -1; + va_list argptr, tmpargptr; + va_start(argptr, format); + while (chars == -1 || chars >= buf_len) { + safe_delete_array(*ret); + if (chars == -1) + buf_len *= 2; + else + buf_len = chars + 1; + *ret = new char[buf_len]; + va_copy(tmpargptr, argptr); + chars = vsnprintf(*ret, buf_len, format, tmpargptr); + } + va_end(argptr); + return chars; +} + +uint32 AppendAnyLenString(char** ret, uint32* bufsize, uint32* strlen, const char* format, ...) { + if (*bufsize == 0) + *bufsize = 256; + if (*ret == 0) + *strlen = 0; + int chars = -1; + char* oldret = 0; + va_list argptr, tmpargptr; + va_start(argptr, format); + while (chars == -1 || chars >= (int32)(*bufsize - *strlen)) { + if (chars == -1) + *bufsize += 256; + else + *bufsize += chars + 25; + oldret = *ret; + *ret = new char[*bufsize]; + if (oldret) { + if (*strlen) + memcpy(*ret, oldret, *strlen); + safe_delete_array(oldret); + } + va_copy(tmpargptr, argptr); + chars = vsnprintf(&(*ret)[*strlen], (*bufsize - *strlen), format, tmpargptr); + } + va_end(argptr); + *strlen += chars; + return *strlen; +} + +uint32 hextoi(const char* num) { + if (num == nullptr) + return 0; + + int len = strlen(num); + if (len < 3) + return 0; + + if (num[0] != '0' || (num[1] != 'x' && num[1] != 'X')) + return 0; + + uint32 ret = 0; + int mul = 1; + for (int i = len - 1; i >= 2; i--) { + if (num[i] >= 'A' && num[i] <= 'F') + ret += ((num[i] - 'A') + 10) * mul; + else if (num[i] >= 'a' && num[i] <= 'f') + ret += ((num[i] - 'a') + 10) * mul; + else if (num[i] >= '0' && num[i] <= '9') + ret += (num[i] - '0') * mul; + else + return 0; + mul *= 16; + } + return ret; +} + +uint64 hextoi64(const char* num) { + if (num == nullptr) + return 0; + + int len = strlen(num); + if (len < 3) + return 0; + + if (num[0] != '0' || (num[1] != 'x' && num[1] != 'X')) + return 0; + + uint64 ret = 0; + int mul = 1; + for (int i = len - 1; i >= 2; i--) { + if (num[i] >= 'A' && num[i] <= 'F') + ret += ((num[i] - 'A') + 10) * mul; + else if (num[i] >= 'a' && num[i] <= 'f') + ret += ((num[i] - 'a') + 10) * mul; + else if (num[i] >= '0' && num[i] <= '9') + ret += (num[i] - '0') * mul; + else + return 0; + mul *= 16; + } + return ret; +} + +bool atobool(const char* iBool) { + + if (iBool == nullptr) + return false; + if (!strcasecmp(iBool, "true")) + return true; + if (!strcasecmp(iBool, "false")) + return false; + if (!strcasecmp(iBool, "yes")) + return true; + if (!strcasecmp(iBool, "no")) + return false; + if (!strcasecmp(iBool, "on")) + return true; + if (!strcasecmp(iBool, "off")) + return false; + if (!strcasecmp(iBool, "enable")) + return true; + if (!strcasecmp(iBool, "disable")) + return false; + if (!strcasecmp(iBool, "enabled")) + return true; + if (!strcasecmp(iBool, "disabled")) + return false; + if (!strcasecmp(iBool, "y")) + return true; + if (!strcasecmp(iBool, "n")) + return false; + if (atoi(iBool)) + return true; + return false; +} + +// removes the crap and turns the underscores into spaces. +char *CleanMobName(const char *in, char *out) +{ + unsigned i, j; + + for (i = j = 0; i < strlen(in); i++) + { + // convert _ to space.. any other conversions like this? I *think* this + // is the only non alpha char that's not stripped but converted. + if (in[i] == '_') + { + out[j++] = ' '; + } + else + { + if (isalpha(in[i]) || (in[i] == '`')) // numbers, #, or any other crap just gets skipped + out[j++] = in[i]; + } + } + out[j] = 0; // terimnate the string before returning it + return out; +} + + +void RemoveApostrophes(std::string &s) +{ + for (unsigned int i = 0; i < s.length(); ++i) + if (s[i] == '\'') + s[i] = '_'; +} + +char *RemoveApostrophes(const char *s) +{ + auto NewString = new char[strlen(s) + 1]; + + strcpy(NewString, s); + + for (unsigned int i = 0; i < strlen(NewString); ++i) + if (NewString[i] == '\'') + NewString[i] = '_'; + + return NewString; +} + +const char *ConvertArray(int input, char *returnchar) +{ + sprintf(returnchar, "%i", input); + return returnchar; +} + +const char *ConvertArrayF(float input, char *returnchar) +{ + sprintf(returnchar, "%0.2f", input); + return returnchar; +} + +bool isAlphaNumeric(const char *text) +{ + for (unsigned int charIndex = 0; charIndex 'z') && + (text[charIndex] < 'A' || text[charIndex] > 'Z') && + (text[charIndex] < '0' || text[charIndex] > '9')) + return false; + } + + return true; +} \ No newline at end of file diff --git a/common/string_util.h b/common/string_util.h index c69f01456..0db8ff2c9 100644 --- a/common/string_util.h +++ b/common/string_util.h @@ -23,34 +23,34 @@ #include "types.h" +//std::string based +const std::string StringFormat(const char* format, ...); +const std::string vStringFormat(const char* format, va_list args); +std::vector SplitString(const std::string &s, char delim); +std::string EscapeString(const char *src, size_t sz); +std::string EscapeString(const std::string &s); +bool StringIsNumber(const std::string &s); +void ToLowerString(std::string &s); +void ToUpperString(std::string &s); +std::string JoinString(const std::vector& ar, const std::string &delim); +void find_replace(std::string& string_subject, const std::string& search_string, const std::string& replace_string); + +//const char based + bool atobool(const char* iBool); bool isAlphaNumeric(const char *text); bool strn0cpyt(char* dest, const char* source, uint32 size); - char *CleanMobName(const char *in, char *out); char *RemoveApostrophes(const char *s); char* strn0cpy(char* dest, const char* source, uint32 size); - const char *ConvertArray(int input, char *returnchar); const char *ConvertArrayF(float input, char *returnchar); const char *MakeLowerString(const char *source); -const std::string StringFormat(const char* format, ...); -const std::string vStringFormat(const char* format, va_list args); - int MakeAnyLenString(char** ret, const char* format, ...); - -std::string EscapeString(const char *src, size_t sz); -std::string EscapeString(const std::string &s); - -std::vector SplitString(const std::string &s, char delim); - uint32 AppendAnyLenString(char** ret, uint32* bufsize, uint32* strlen, const char* format, ...); uint32 hextoi(const char* num); - uint64 hextoi64(const char* num); - void MakeLowerString(const char *source, char *target); void RemoveApostrophes(std::string &s); -void find_replace(std::string& string_subject, const std::string& search_string, const std::string& replace_string); #endif diff --git a/world/CMakeLists.txt b/world/CMakeLists.txt index b98307ca3..233c2e1c9 100644 --- a/world/CMakeLists.txt +++ b/world/CMakeLists.txt @@ -20,6 +20,7 @@ SET(world_sources web_interface_eqw.cpp wguild_mgr.cpp world_config.cpp + world_console_connection.cpp worlddb.cpp zonelist.cpp zoneserver.cpp @@ -47,6 +48,8 @@ SET(world_headers web_interface_eqw.h wguild_mgr.h world_config.h + world_console_connection.h + world_tcp_connection.h worlddb.h zonelist.h zoneserver.h diff --git a/world/clientlist.cpp b/world/clientlist.cpp index e7a3918f0..3f1c3f50c 100644 --- a/world/clientlist.cpp +++ b/world/clientlist.cpp @@ -1010,27 +1010,27 @@ void ClientList::ConsoleSendWhoAll(const char* to, int16 admin, Who_All_Struct* else AppendAnyLenString(&output, &outsize, &outlen, "\n"); iterator.Reset(); - while(iterator.MoreElements()) { + while (iterator.MoreElements()) { cle = iterator.GetData(); const char* tmpZone = database.GetZoneName(cle->zone()); if ( - (cle->Online() >= CLE_Status_Zoning) - && (whom == 0 || ( - ((cle->Admin() >= 80 && cle->GetGM()) || whom->gmlookup == 0xFFFF) && - (whom->lvllow == 0xFFFF || (cle->level() >= whom->lvllow && cle->level() <= whom->lvlhigh)) && - (whom->wclass == 0xFFFF || cle->class_() == whom->wclass) && - (whom->wrace == 0xFFFF || cle->race() == whom->wrace) && - (whomlen == 0 || ( - (tmpZone != 0 && strncasecmp(tmpZone, whom->whom, whomlen) == 0) || - strncasecmp(cle->name(),whom->whom, whomlen) == 0 || - (strncasecmp(guild_mgr.GetGuildName(cle->GuildID()), whom->whom, whomlen) == 0) || - (admin >= 100 && strncasecmp(cle->AccountName(), whom->whom, whomlen) == 0) - )) - )) -) { + (cle->Online() >= CLE_Status_Zoning) + && (whom == 0 || ( + ((cle->Admin() >= 80 && cle->GetGM()) || whom->gmlookup == 0xFFFF) && + (whom->lvllow == 0xFFFF || (cle->level() >= whom->lvllow && cle->level() <= whom->lvlhigh)) && + (whom->wclass == 0xFFFF || cle->class_() == whom->wclass) && + (whom->wrace == 0xFFFF || cle->race() == whom->wrace) && + (whomlen == 0 || ( + (tmpZone != 0 && strncasecmp(tmpZone, whom->whom, whomlen) == 0) || + strncasecmp(cle->name(), whom->whom, whomlen) == 0 || + (strncasecmp(guild_mgr.GetGuildName(cle->GuildID()), whom->whom, whomlen) == 0) || + (admin >= 100 && strncasecmp(cle->AccountName(), whom->whom, whomlen) == 0) + )) + )) + ) { line[0] = 0; -// MYRA - use new (5.x) Status labels in who for telnet connection - if (cle->Admin() >=250) + // MYRA - use new (5.x) Status labels in who for telnet connection + if (cle->Admin() >= 250) strcpy(tmpgm, "* GM-Impossible * "); else if (cle->Admin() >= 200) strcpy(tmpgm, "* GM-Mgmt * "); @@ -1062,11 +1062,12 @@ void ClientList::ConsoleSendWhoAll(const char* to, int16 admin, Who_All_Struct* strcpy(tmpgm, "* Steward * "); else tmpgm[0] = 0; -// end Myra + // end Myra if (guild_mgr.GuildExists(cle->GuildID())) { snprintf(tmpguild, 36, " <%s>", guild_mgr.GetGuildName(cle->GuildID())); - } else + } + else tmpguild[0] = 0; if (cle->LFG()) @@ -1082,7 +1083,7 @@ void ClientList::ConsoleSendWhoAll(const char* to, int16 admin, Who_All_Struct* if (cle->Anon() == 2) { // Roleplay if (admin >= 100 && admin >= cle->Admin()) - sprintf(line, " %s[RolePlay %i %s] %s (%s)%s zone: %s%s%s", tmpgm, cle->level(), GetClassIDName(cle->class_(),cle->level()), cle->name(), GetRaceIDName(cle->race()), tmpguild, tmpZone, LFG, accinfo); + sprintf(line, " %s[RolePlay %i %s] %s (%s)%s zone: %s%s%s", tmpgm, cle->level(), GetClassIDName(cle->class_(), cle->level()), cle->name(), GetRaceIDName(cle->race()), tmpguild, tmpZone, LFG, accinfo); else if (cle->Admin() >= 80 && admin < 80 && cle->GetGM()) { iterator.Advance(); continue; @@ -1092,7 +1093,7 @@ void ClientList::ConsoleSendWhoAll(const char* to, int16 admin, Who_All_Struct* } else if (cle->Anon() == 1) { // Anon if (admin >= 100 && admin >= cle->Admin()) - sprintf(line, " %s[ANON %i %s] %s (%s)%s zone: %s%s%s", tmpgm, cle->level(), GetClassIDName(cle->class_(),cle->level()), cle->name(), GetRaceIDName(cle->race()), tmpguild, tmpZone, LFG, accinfo); + sprintf(line, " %s[ANON %i %s] %s (%s)%s zone: %s%s%s", tmpgm, cle->level(), GetClassIDName(cle->class_(), cle->level()), cle->name(), GetRaceIDName(cle->race()), tmpguild, tmpZone, LFG, accinfo); else if (cle->Admin() >= 80 && cle->GetGM()) { iterator.Advance(); continue; @@ -1101,7 +1102,7 @@ void ClientList::ConsoleSendWhoAll(const char* to, int16 admin, Who_All_Struct* sprintf(line, " %s[ANONYMOUS] %s%s%s", tmpgm, cle->name(), LFG, accinfo); } else - sprintf(line, " %s[%i %s] %s (%s)%s zone: %s%s%s", tmpgm, cle->level(), GetClassIDName(cle->class_(),cle->level()), cle->name(), GetRaceIDName(cle->race()), tmpguild, tmpZone, LFG, accinfo); + sprintf(line, " %s[%i %s] %s (%s)%s zone: %s%s%s", tmpgm, cle->level(), GetClassIDName(cle->class_(), cle->level()), cle->name(), GetRaceIDName(cle->race()), tmpguild, tmpZone, LFG, accinfo); AppendAnyLenString(&output, &outsize, &outlen, line); if (outlen >= 3584) { @@ -1132,6 +1133,8 @@ void ClientList::ConsoleSendWhoAll(const char* to, int16 admin, Who_All_Struct* AppendAnyLenString(&output, &outsize, &outlen, "\r\n"); else AppendAnyLenString(&output, &outsize, &outlen, "\n"); + + //console_list.SendConsoleWho(connection, to, admin, &output, &outsize, &outlen); } if (output) connection->SendEmoteMessageRaw(to, 0, 0, 10, output); diff --git a/world/clientlist.h b/world/clientlist.h index 383654bc0..4cee08a7f 100644 --- a/world/clientlist.h +++ b/world/clientlist.h @@ -7,6 +7,7 @@ #include "../common/rulesys.h" #include "../common/servertalk.h" #include "../common/event/timer.h" +#include "../common/net/console_server_connection.h" #include #include diff --git a/world/console.cpp b/world/console.cpp index 578523a52..10b669505 100644 --- a/world/console.cpp +++ b/world/console.cpp @@ -1,103 +1,505 @@ #include "console.h" -#include "../common/eqemu_config.h" -#include "../common/util/uuid.h" -#include "../common/eqemu_logsys.h" -#include "../common//net/packet.h" +#include "clientlist.h" +#include "login_server.h" +#include "login_server_list.h" +#include "world_config.h" +#include "world_console_connection.h" +#include "worlddb.h" +#include "zonelist.h" +#include "zoneserver.h" +#include "../common/string_util.h" +#include "../common/md5.h" -ConsoleConnection::ConsoleConnection(ConsoleServer *parent, std::shared_ptr connection) -{ - m_parent = parent; - m_connection = connection; - m_uuid = EQ::Util::UUID::Generate().ToString(); +extern ClientList client_list; +extern ZSList zoneserver_list; +extern LoginServerList loginserverlist; - 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(); +struct EQ::Net::ConsoleLoginStatus CheckLogin(const std::string& username, const std::string& password) { + struct EQ::Net::ConsoleLoginStatus ret; + ret.account_id = database.CheckLogin(username.c_str(), password.c_str()); + if (ret.account_id == 0) { + return ret; + } + + char account_name[64]; + database.GetAccountName(ret.account_id, account_name); + + ret.account_name = account_name; + ret.status = database.CheckStatus(ret.account_id); + return ret; } -ConsoleConnection::~ConsoleConnection() -{ +void ConsoleNull(EQ::Net::ConsoleServerConnection* connection, const std::string& command, const std::vector& args) { } -void ConsoleConnection::Clear() -{ - EQ::Net::DynamicPacket clear; - clear.PutUInt8(0, 0); - m_connection->Write((const char*)clear.Data(), clear.Length()); +void ConsoleWhoami(EQ::Net::ConsoleServerConnection* connection, const std::string& command, const std::vector& args) { + connection->SendLine(fmt::format("You are logged in as '{0}'", connection->UserName())); + connection->SendLine(fmt::format("You are known as '*{0}'", connection->UserName())); + connection->SendLine(fmt::format("AccessLevel: '{0}'", connection->Admin())); } -void ConsoleConnection::SendLine(const std::string &line) -{ - m_connection->Write(line.c_str(), line.length()); - SendNewLine(); +void ConsoleZoneStatus(EQ::Net::ConsoleServerConnection* connection, const std::string& command, const std::vector& args) { + WorldConsoleTCPConnection console_connection(connection); + zoneserver_list.SendZoneStatus(0, connection->Admin(), &console_connection); } -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 ConsoleWho(EQ::Net::ConsoleServerConnection* connection, const std::string& command, const std::vector& args) { + Who_All_Struct whom; + memset(&whom, 0, sizeof(whom)); + whom.lvllow = 0xFFFF; + whom.lvlhigh = 0xFFFF; + whom.wclass = 0xFFFF; + whom.wrace = 0xFFFF; + whom.gmlookup = 0xFFFF; + + for (auto &arg : args) { + if (strcasecmp(arg.c_str(), "gm") == 0) { + whom.gmlookup = 1; + } + else if (StringIsNumber(arg)) { + if (whom.lvllow == 0xFFFF) { + whom.lvllow = atoi(arg.c_str()); + whom.lvlhigh = whom.lvllow; + } + else if (atoi(arg.c_str()) > int(whom.lvllow)) + whom.lvlhigh = atoi(arg.c_str()); + else + whom.lvllow = atoi(arg.c_str()); + } + else { + strn0cpy(whom.whom, arg.c_str(), sizeof(whom.whom)); + } + } + + WorldConsoleTCPConnection console_connection(connection); + client_list.ConsoleSendWhoAll(0, connection->Admin(), &whom, &console_connection); } -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 ConsoleUptime(EQ::Net::ConsoleServerConnection* connection, const std::string& command, const std::vector& args) { + if (args.size() < 1) { + return; + } + + if (StringIsNumber(args[0]) && atoi(args[0].c_str()) > 0) { + auto pack = new ServerPacket(ServerOP_Uptime, sizeof(ServerUptime_Struct)); + ServerUptime_Struct* sus = (ServerUptime_Struct*)pack->pBuffer; + snprintf(sus->adminname, sizeof(sus->adminname), "*%s", connection->UserName()); + sus->zoneserverid = atoi(args[0].c_str()); + ZoneServer* zs = zoneserver_list.FindByID(sus->zoneserverid); + if (zs) + zs->SendPacket(pack); + else + connection->SendLine("Zoneserver not found."); + delete pack; + } + else { + WorldConsoleTCPConnection console_connection(connection); + ZSList::ShowUpTime(&console_connection); + } } -void ConsoleConnection::ProcessReadBuffer() -{ - size_t buffer_start = 0; - for (size_t i = 0; i < m_buffer.size(); ++i) { - char c = m_buffer[i]; +void ConsoleMd5(EQ::Net::ConsoleServerConnection* connection, const std::string& command, const std::vector& args) { + if (args.size() < 1) { + return; + } + + uint8 md5[16]; + MD5::Generate((const uchar*)args[0].c_str(), strlen(args[0].c_str()), md5); + connection->SendLine(StringFormat("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])); +} - switch (c) { - case 0: - //Clear buffer - break; - case 10: - case 13: - //New Line - break; - case 8: - //Backspace - break; - default: - break; +void ConsoleEmote(EQ::Net::ConsoleServerConnection* connection, const std::string& command, const std::vector& args) { + if (args.size() < 3) { + return; + } + + auto join_args = args; + join_args.erase(join_args.begin(), join_args.begin() + 2); + + if (strcasecmp(args[0].c_str(), "world") == 0) + zoneserver_list.SendEmoteMessageRaw(0, 0, 0, atoi(args[1].c_str()), JoinString(join_args, " ").c_str()); + else { + ZoneServer* zs = zoneserver_list.FindByName(args[0].c_str()); + if (zs != 0) + zs->SendEmoteMessageRaw(0, 0, 0, atoi(args[1].c_str()), JoinString(join_args, " ").c_str()); + else + zoneserver_list.SendEmoteMessageRaw(args[0].c_str(), 0, 0, atoi(args[1].c_str()), JoinString(join_args, " ").c_str()); + } +} + +void ConsoleAcceptMessages(EQ::Net::ConsoleServerConnection* connection, const std::string& command, const std::vector& args) { + if (args.size() < 1) { + connection->SendLine("Usage: acceptmessages [on/off]"); + return; + } + + if (strcasecmp(args[0].c_str(), "on") == 0) + connection->SetAcceptMessages(true); + else if (strcasecmp(args[0].c_str(), "off") == 0) + connection->SetAcceptMessages(false); + else + connection->SendLine("Usage: acceptmessages [on/off]"); +} + +void ConsoleTell(EQ::Net::ConsoleServerConnection* connection, const std::string& command, const std::vector& args) { + if (args.size() < 2) { + return; + } + + char tmpname[64]; + tmpname[0] = '*'; + strcpy(&tmpname[1], connection->UserName().c_str()); + std::string to = args[0]; + + auto join_args = args; + join_args.erase(join_args.begin(), join_args.begin() + 1); + + zoneserver_list.SendChannelMessage(tmpname, to.c_str(), 7, 0, JoinString(join_args, " ").c_str()); +} + +void ConsoleBroadcast(EQ::Net::ConsoleServerConnection* connection, const std::string& command, const std::vector& args) { + if (args.size() < 1) { + return; + } + + char tmpname[64]; + tmpname[0] = '*'; + strcpy(&tmpname[1], connection->UserName().c_str()); + zoneserver_list.SendChannelMessage(tmpname, 0, 6, 0, JoinString(args, " ").c_str()); +} + +void ConsoleGMSay(EQ::Net::ConsoleServerConnection* connection, const std::string& command, const std::vector& args) { + if (args.size() < 1) { + return; + } + + char tmpname[64]; + tmpname[0] = '*'; + strcpy(&tmpname[1], connection->UserName().c_str()); + zoneserver_list.SendChannelMessage(tmpname, 0, 11, 0, JoinString(args, " ").c_str()); +} + +void ConsoleOOC(EQ::Net::ConsoleServerConnection* connection, const std::string& command, const std::vector& args) { + if (args.size() < 1) { + return; + } + + char tmpname[64]; + tmpname[0] = '*'; + strcpy(&tmpname[1], connection->UserName().c_str()); + zoneserver_list.SendChannelMessage(tmpname, 0, 5, 0, JoinString(args, " ").c_str()); +} + +void ConsoleAuction(EQ::Net::ConsoleServerConnection* connection, const std::string& command, const std::vector& args) { + if (args.size() < 1) { + return; + } + + char tmpname[64]; + tmpname[0] = '*'; + strcpy(&tmpname[1], connection->UserName().c_str()); + zoneserver_list.SendChannelMessage(tmpname, 0, 4, 0, JoinString(args, " ").c_str()); +} + +void ConsoleKick(EQ::Net::ConsoleServerConnection* connection, const std::string& command, const std::vector& args) { + if (args.size() < 1) { + return; + } + + char tmpname[64]; + tmpname[0] = '*'; + strcpy(&tmpname[1], connection->UserName().c_str()); + 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, args[0].c_str()); + skp->adminrank = connection->Admin(); + zoneserver_list.SendPacket(pack); + delete pack; +} + +void ConsoleLock(EQ::Net::ConsoleServerConnection* connection, const std::string& command, const std::vector& args) { + WorldConfig::LockWorld(); + if (loginserverlist.Connected()) { + loginserverlist.SendStatus(); + connection->SendLine("World locked."); + } + else { + connection->SendLine("World locked, but login server not connected."); + } +} + +void ConsoleUnlock(EQ::Net::ConsoleServerConnection* connection, const std::string& command, const std::vector& args) { + WorldConfig::UnlockWorld(); + if (loginserverlist.Connected()) { + loginserverlist.SendStatus(); + connection->SendLine("World unlocked."); + } + else { + connection->SendLine("World unlocked, but login server not connected."); + } +} + +void ConsoleZoneShutdown(EQ::Net::ConsoleServerConnection* connection, const std::string& command, const std::vector& args) { + if (args.size() < 1) { + connection->SendLine("Usage: zoneshutdown zoneshortname"); + return; + } + + if (args[0].length() == 0) { + connection->SendLine("Usage: zoneshutdown zoneshortname"); + } + else { + char tmpname[64]; + tmpname[0] = '*'; + strcpy(&tmpname[1], connection->UserName().c_str()); + + 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 (StringIsNumber(args[0])) + s->ZoneServerID = atoi(args[0].c_str()); + else + s->zoneid = database.GetZoneID(args[0].c_str()); + + 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 + connection->SendLine("Error: ZoneShutdown: neither ID nor name specified"); + + if (zs == 0) + connection->SendLine("Error: ZoneShutdown: zoneserver not found"); + else + zs->SendPacket(pack); + + delete pack; + } +} + +void ConsoleZoneBootup(EQ::Net::ConsoleServerConnection* connection, const std::string& command, const std::vector& args) { + if (args.size() < 2) { + return; + } + + if (args[1].length() == 0 || !StringIsNumber(args[0])) { + connection->SendLine("Usage: zonebootup ZoneServerID# zoneshortname"); + } + else { + char tmpname[64]; + tmpname[0] = '*'; + strcpy(&tmpname[1], connection->UserName().c_str()); + + Log(Logs::Detail, Logs::World_Server, "Console ZoneBootup: %s, %s, %s", tmpname, args[1].c_str(), args[0].c_str()); + + if (args.size() > 2) { + zoneserver_list.SOPZoneBootup(tmpname, atoi(args[0].c_str()), args[1].c_str(), (bool)(strcasecmp(args[1].c_str(), "static") == 0)); + } + else { + zoneserver_list.SOPZoneBootup(tmpname, atoi(args[0].c_str()), args[1].c_str(), false); } } } -void ConsoleConnection::ProcessCommand(const std::string &cmd) -{ -} +void ConsoleZoneLock(EQ::Net::ConsoleServerConnection* connection, const std::string& command, const std::vector& args) { + if (args.size() < 1) { + return; + } + + if (strcasecmp(args[0].c_str(), "list") == 0) { + WorldConsoleTCPConnection console_connection(connection); + zoneserver_list.ListLockedZones(0, &console_connection); + } + else if (strcasecmp(args[0].c_str(), "lock") == 0 && connection->Admin() >= 101) { + if (args.size() < 2) { + return; + } -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); + uint16 tmp = database.GetZoneID(args[1].c_str()); + if (tmp) { + if (zoneserver_list.SetLockedZone(tmp, true)) + zoneserver_list.SendEmoteMessage(0, 0, 80, 15, "Zone locked: %s", database.GetZoneName(tmp)); + else + connection->SendLine("Failed to change lock"); + } + else + connection->SendLine("Usage: #zonelock lock [zonename]"); + } + else if (strcasecmp(args[0].c_str(), "unlock") == 0 && connection->Admin() >= 101) { + if (args.size() < 2) { + return; + } + + uint16 tmp = database.GetZoneID(args[1].c_str()); + if (tmp) { + if (zoneserver_list.SetLockedZone(tmp, false)) + zoneserver_list.SendEmoteMessage(0, 0, 80, 15, "Zone unlocked: %s", database.GetZoneName(tmp)); + else + connection->SendLine("Failed to change lock"); + } + else + connection->SendLine("Usage: #zonelock unlock [zonename]"); + } + else { + connection->SendLine("#zonelock sub-commands"); + connection->SendLine(" list"); + if (connection->Admin() >= 101) { + connection->SendLine(" lock [zonename]"); + connection->SendLine(" unlock [zonename]"); + } } } + +void ConsoleFlag(EQ::Net::ConsoleServerConnection* connection, const std::string& command, const std::vector& args) { + if (args.size() < 2) { + return; + } + + if (args[1].length() == 0 || !StringIsNumber(args[0])) + connection->SendLine("Usage: flag [status] [accountname]"); + else + { + if (atoi(args[0].c_str()) > connection->Admin()) + connection->SendLine("You cannot set people's status to higher than your own"); + else if (!database.SetAccountStatus(args[1].c_str(), atoi(args[0].c_str()))) + connection->SendLine("Unable to flag account!"); + else + connection->SendLine("Account Flaged"); + } +} + +void ConsoleSetPass(EQ::Net::ConsoleServerConnection* connection, const std::string& command, const std::vector& args) { + if (args.size() != 2) { + connection->SendLine("Format: setpass accountname password"); + } + else { + int16 tmpstatus = 0; + uint32 tmpid = database.GetAccountIDByName(args[0].c_str(), &tmpstatus); + if (!tmpid) + connection->SendLine("Error: Account not found"); + else if (tmpstatus > connection->Admin()) + connection->SendLine("Cannot change password: Account's status is higher than yours"); + else if (database.SetLocalPassword(tmpid, args[1].c_str())) + connection->SendLine("Password changed."); + else + connection->SendLine("Error changing password."); + } +} + +void ConsoleVersion(EQ::Net::ConsoleServerConnection* connection, const std::string& command, const std::vector& args) { + connection->SendLine(StringFormat("Current version information.")); + connection->SendLine(StringFormat(" %s", CURRENT_VERSION)); + connection->SendLine(StringFormat(" Compiled on: %s at %s", COMPILE_DATE, COMPILE_TIME)); + connection->SendLine(StringFormat(" Last modified on: %s", LAST_MODIFIED)); +} + +void ConsoleWorldShutdown(EQ::Net::ConsoleServerConnection* connection, const std::string& command, const std::vector& args) { + if (args.size() == 2) { + int32 time, interval; + if (StringIsNumber(args[0]) && StringIsNumber(args[1]) && ((time = atoi(args[0].c_str()))>0) && ((interval = atoi(args[1].c_str()))>0)) { + zoneserver_list.WorldShutDown(time, interval); + } + else { + connection->SendLine("Usage: worldshutdown [now] [disable] ([time] [interval])"); + } + } + else if(args.size() == 1) { + if (strcasecmp(args[0].c_str(), "now") == 0) { + zoneserver_list.WorldShutDown(0, 0); + } + else if (strcasecmp(args[0].c_str(), "disable") == 0) { + connection->SendLine(":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 { + connection->SendLine("Usage: worldshutdown [now] [disable] ([time] [interval])"); + } + } + else { + connection->SendLine("Usage: worldshutdown [now] [disable] ([time] [interval])"); + } +} + +void ConsoleIpLookup(EQ::Net::ConsoleServerConnection* connection, const std::string& command, const std::vector& args) { + if (args.size() > 0) { + WorldConsoleTCPConnection console_connection(connection); + client_list.SendCLEList(connection->Admin(), 0, &console_connection, args[0].c_str()); + } +} + +void ConsoleSignalCharByName(EQ::Net::ConsoleServerConnection* connection, const std::string& command, const std::vector& args) { + if (args.size() < 2) { + return; + } + + connection->SendLine(StringFormat("Signal Sent to %s with ID %i", (char*) args[0].c_str(), atoi(args[1].c_str()))); + uint32 message_len = strlen((char*) args[0].c_str()) + 1; + auto pack = new ServerPacket(ServerOP_CZSignalClientByName, + sizeof(CZClientSignalByName_Struct) + message_len); + CZClientSignalByName_Struct* CZSC = (CZClientSignalByName_Struct*) pack->pBuffer; + strn0cpy(CZSC->Name, (char*) args[0].c_str(), 64); + CZSC->data = atoi(args[1].c_str()); + zoneserver_list.SendPacket(pack); + safe_delete(pack); +} + +void ConsoleReloadWorld(EQ::Net::ConsoleServerConnection* connection, const std::string& command, const std::vector& args) { + connection->SendLine("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); +} + +void ConsoleQuit(EQ::Net::ConsoleServerConnection* connection, const std::string& command, const std::vector& args) { + connection->SendLine("Exiting..."); + connection->Close(); +} + +void RegisterConsoleFunctions(std::unique_ptr& console) +{ + console->RegisterLogin(std::bind(CheckLogin, std::placeholders::_1, std::placeholders::_2)); + console->RegisterCall("whoami", 50, "whoami", std::bind(ConsoleWhoami, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); + console->RegisterCall("who", 50, "who", std::bind(ConsoleWho, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); + console->RegisterCall("zonestatus", 50, "zonestatus", std::bind(ConsoleZoneStatus, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); + console->RegisterCall("uptime", 50, "uptime [zoneID#]", std::bind(ConsoleUptime, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); + console->RegisterCall("md5", 50, "md5", std::bind(ConsoleMd5, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); + console->RegisterCall("emote", 50, "emote [zonename or charname or world] [type] [message]", std::bind(ConsoleEmote, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); + console->RegisterCall("echo", 50, "echo [on/off]", std::bind(ConsoleNull, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); + console->RegisterCall("acceptmessages", 50, "acceptmessages [on/off]", std::bind(ConsoleAcceptMessages, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); + console->RegisterCall("tell", 50, "tell [name] [message]", std::bind(ConsoleTell, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); + console->RegisterCall("broadcast", 50, "broadcast [message]", std::bind(ConsoleBroadcast, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); + console->RegisterCall("gmsay", 50, "gmsay [message]", std::bind(ConsoleGMSay, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); + console->RegisterCall("ooc", 50, "ooc [message]", std::bind(ConsoleOOC, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); + console->RegisterCall("auction", 50, "auction [message]", std::bind(ConsoleAuction, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); + console->RegisterCall("kick", 150, "kick [charname]", std::bind(ConsoleKick, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); + console->RegisterCall("lock", 150, "lock", std::bind(ConsoleLock, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); + console->RegisterCall("unlock", 150, "unlock", std::bind(ConsoleUnlock, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); + console->RegisterCall("zoneshutdown", 150, "zoneshutdown [zonename or ZoneServerID]", std::bind(ConsoleZoneShutdown, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); + console->RegisterCall("zonebootup", 150, "zonebootup [ZoneServerID] [zonename]", std::bind(ConsoleZoneBootup, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); + console->RegisterCall("zonelock", 150, "zonelock [list|lock|unlock] [zonename]", std::bind(ConsoleZoneLock, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); + console->RegisterCall("flag", 200, "flag [status] [accountname]", std::bind(ConsoleFlag, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); + console->RegisterCall("setpass", 200, "setpass [accountname] [newpass]", std::bind(ConsoleSetPass, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); + console->RegisterCall("version", 50, "version", std::bind(ConsoleVersion, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); + console->RegisterCall("worldshutdown", 200, "worldshutdown", std::bind(ConsoleWorldShutdown, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); + console->RegisterCall("iplookup", 50, "IPLookup [name]", std::bind(ConsoleIpLookup, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); + console->RegisterCall("lsreconnect", 50, "LSReconnect", std::bind(ConsoleNull, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); + console->RegisterCall("signalcharbyname", 50, "signalcharbyname charname ID", std::bind(ConsoleSignalCharByName, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); + console->RegisterCall("reloadworld", 200, "reloadworld", std::bind(ConsoleReloadWorld, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); + console->RegisterCall("ping", 50, "ping", std::bind(ConsoleNull, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); + console->RegisterCall("quit", 50, "quit", std::bind(ConsoleQuit, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); + console->RegisterCall("exit", 50, "exit", std::bind(ConsoleQuit, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); +} diff --git a/world/console.h b/world/console.h index 57fc3671b..174349595 100644 --- a/world/console.h +++ b/world/console.h @@ -1,45 +1,5 @@ #pragma once -#include "../common/net/tcp_server.h" -#include "../common/event/timer.h" -#include -#include +#include "../common/net/console_server.h" -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 +void RegisterConsoleFunctions(std::unique_ptr &console); diff --git a/world/net.cpp b/world/net.cpp index 20c7afec3..df3749829 100644 --- a/world/net.cpp +++ b/world/net.cpp @@ -83,7 +83,6 @@ union semun { #include "web_interface.h" #include "console.h" -#include "../common/net/tcp_server.h" #include "../common/net/servertalk_server.h" ClientList client_list; @@ -376,10 +375,11 @@ int main(int argc, char** argv) { database.LoadCharacterCreateAllocations(); database.LoadCharacterCreateCombos(); - std::unique_ptr console; + std::unique_ptr console; if (Config->TelnetEnabled) { Log(Logs::General, Logs::World_Server, "Console (TCP) listener started."); - console.reset(new ConsoleServer()); + console.reset(new EQ::Net::ConsoleServer(Config->TelnetIP, Config->TelnetTCPPort)); + RegisterConsoleFunctions(console); } std::unique_ptr server_connection; diff --git a/world/world_console_connection.cpp b/world/world_console_connection.cpp new file mode 100644 index 000000000..930f6da95 --- /dev/null +++ b/world/world_console_connection.cpp @@ -0,0 +1,19 @@ +#include "world_console_connection.h" +#include +#include + +void WorldConsoleTCPConnection::SendEmoteMessage(const char *to, uint32 to_guilddbid, int16 to_minstatus, 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(to, to_guilddbid, to_minstatus, type, buffer); +} + +void WorldConsoleTCPConnection::SendEmoteMessageRaw(const char *to, uint32 to_guilddbid, int16 to_minstatus, uint32 type, const char *message) +{ + m_connection->SendLine(message); +} diff --git a/world/world_console_connection.h b/world/world_console_connection.h new file mode 100644 index 000000000..5629f1def --- /dev/null +++ b/world/world_console_connection.h @@ -0,0 +1,36 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2017 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 +*/ +#pragma once + +#include "world_tcp_connection.h" +#include "../common/net/console_server_connection.h" + +class WorldConsoleTCPConnection : public WorldTCPConnection +{ +public: + WorldConsoleTCPConnection(EQ::Net::ConsoleServerConnection *c) { m_connection = c; } + virtual ~WorldConsoleTCPConnection() { } + virtual void SendEmoteMessage(const char* to, uint32 to_guilddbid, int16 to_minstatus, uint32 type, const char* message, ...); + virtual void SendEmoteMessageRaw(const char* to, uint32 to_guilddbid, int16 to_minstatus, uint32 type, const char* message); + + virtual inline bool IsConsole() { return true; } + virtual inline bool IsZoneServer() { return false; } +private: + EQ::Net::ConsoleServerConnection *m_connection; +}; + diff --git a/zone/net.cpp b/zone/net.cpp index d4958b9a0..79fa0a2b6 100644 --- a/zone/net.cpp +++ b/zone/net.cpp @@ -481,9 +481,10 @@ int main(int argc, char** argv) { worldwasconnected = true; } else { - if (worldwasconnected && is_zone_loaded) + if (worldwasconnected && is_zone_loaded) { entity_list.ChannelMessageFromWorld(0, 0, 6, 0, 0, "WARNING: World server connection lost"); - worldwasconnected = false; + worldwasconnected = false; + } } if (is_zone_loaded) {