diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 54c13d897..e6edcee36 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -3,6 +3,7 @@ CMAKE_MINIMUM_REQUIRED(VERSION 2.8) SET(common_sources base_packet.cpp classes.cpp + cli/eqemu_command_handler.cpp compression.cpp condition.cpp crash.cpp @@ -108,8 +109,7 @@ SET(common_sources tinyxml/tinyxmlerror.cpp tinyxml/tinyxmlparser.cpp util/directory.cpp - util/uuid.cpp -) + util/uuid.cpp) SET(common_headers any.h @@ -123,6 +123,8 @@ SET(common_headers crc16.h crc32.h cli/argh.h + cli/eqemu_command_handler.h + cli/terminal_color.hpp data_verification.h database.h dbcore.h @@ -270,8 +272,7 @@ SET(common_headers tinyxml/tinyxml.h util/memory_stream.h util/directory.h - util/uuid.h -) + util/uuid.h) SOURCE_GROUP(Event FILES event/event_loop.h diff --git a/common/cli/eqemu_command_handler.cpp b/common/cli/eqemu_command_handler.cpp new file mode 100644 index 000000000..a45948002 --- /dev/null +++ b/common/cli/eqemu_command_handler.cpp @@ -0,0 +1,192 @@ +/** + * EQEmulator: Everquest Server Emulator + * Copyright (C) 2001-2019 EQEmulator Development Team (https://github.com/EQEmu/Server) + * + * 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 +#include "eqemu_command_handler.h" +#include "../platform.h" + +namespace EQEmuCommand { + + std::map function_map; + + /** + * @param cmd + */ + void DisplayDebug(argh::parser &cmd) + { + if (cmd[{"-d", "--debug"}]) { + std::cout << "Positional args:\n"; + for (auto &pos_arg : cmd) + std::cout << '\t' << pos_arg << std::endl; + + std::cout << "Positional args:\n"; + for (auto &pos_arg : cmd.pos_args()) + std::cout << '\t' << pos_arg << std::endl; + + std::cout << "\nFlags:\n"; + for (auto &flag : cmd.flags()) + std::cout << '\t' << flag << std::endl; + + std::cout << "\nParameters:\n"; + for (auto ¶m : cmd.params()) + std::cout << '\t' << param.first << " : " << param.second << std::endl; + } + } + + /** + * @param arguments + * @param options + * @param cmd + * @param argc + * @param argv + */ + void ValidateCmdInput( + std::vector &arguments, + std::vector &options, + argh::parser &cmd, + int argc, + char **argv + ) + { + bool arguments_filled = true; + + for (auto &arg : arguments) { + if (cmd(arg).str().empty()) { + arguments_filled = false; + } + } + + if (!arguments_filled || argc == 2) { + std::string arguments_string; + for (auto &arg : arguments) { + arguments_string += " " + arg + "=*\n"; + } + + std::string options_string; + for (auto &opt : options) { + options_string += " " + opt + "\n"; + } + + std::cout << fmt::format( + "Command\n\n{0} \n\nArgs\n{1}\nOptions\n{2}", + argv[1], + arguments_string, + options_string + ) << std::endl; + + exit(1); + } + } + + /** + * @param in_function_map + * @param cmd + * @param argc + * @param argv + */ + void HandleMenu( + std::map &in_function_map, + argh::parser &cmd, + int argc, + char **argv + ) + { + std::string description; + bool ran_command = false; + for (auto &it: in_function_map) { + if (it.first == argv[1]) { + std::cout << std::endl; + std::cout << "> " << termcolor::cyan << "Executing CLI Command" << termcolor::reset << std::endl; + std::cout << std::endl; + + (it.second)(argc, argv, cmd, description); + ran_command = true; + } + } + + if (cmd[{"-h", "--help"}]) { + std::cout << std::endl; + std::cout << + "> " << + termcolor::yellow << + "EQEmulator [" + GetPlatformName() + "] CLI Menu" << + termcolor::reset + << std::endl + << std::endl; + + /** + * Get max command length for padding length + */ + int max_command_length = 0; + + for (auto &it: in_function_map) { + if (it.first.length() > max_command_length) { + std::stringstream command; + command << termcolor::colorize << termcolor::yellow << it.first << termcolor::reset; + max_command_length = command.str().length() + 5; + } + } + + /** + * Display command menu + */ + std::string command_section; + for (auto &it: in_function_map) { + description = ""; + + (it.second)(argc, argv, cmd, description); + + /** + * Print section header + */ + std::string command_prefix = it.first.substr(0, it.first.find(":")); + if (command_section != command_prefix) { + command_section = command_prefix; + std::cout << termcolor::reset << command_prefix << std::endl; + } + + /** + * Print commands + */ + std::stringstream command; + command << termcolor::colorize << termcolor::yellow << it.first << termcolor::reset; + printf(" %-*s %s\n", max_command_length, command.str().c_str(), description.c_str()); + } + + std::cout << std::endl; + } + else if (!ran_command) { + std::cerr << "Unknown command [" << argv[1] << "] ! Try --help" << std::endl; + } + + exit(1); + } + +} \ No newline at end of file diff --git a/common/cli/eqemu_command_handler.h b/common/cli/eqemu_command_handler.h new file mode 100644 index 000000000..12d23659d --- /dev/null +++ b/common/cli/eqemu_command_handler.h @@ -0,0 +1,76 @@ +/** + * EQEmulator: Everquest Server Emulator + * Copyright (C) 2001-2019 EQEmulator Development Team (https://github.com/EQEmu/Server) + * + * 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 + * + */ + +#ifndef EQEMU_EQEMU_COMMAND_HANDLER_H +#define EQEMU_EQEMU_COMMAND_HANDLER_H + +#include "argh.h" +#include "terminal_color.hpp" + +namespace EQEmuCommand { + + extern std::map function_map; + + /** + * @param arguments + * @param options + * @param cmd + * @param argc + * @param argv + */ + void ValidateCmdInput( + std::vector &arguments, + std::vector &options, + argh::parser &cmd, + int argc, + char **argv + ); + + /** + * @param cmd + */ + void DisplayDebug(argh::parser &cmd); + + /** + * @param in_function_map + * @param cmd + * @param argc + * @param argv + */ + void HandleMenu( + std::map &in_function_map, + argh::parser &cmd, + int argc, + char **argv + ); +}; + + +#endif //EQEMU_EQEMU_COMMAND_HANDLER_H diff --git a/common/cli/terminal_color.hpp b/common/cli/terminal_color.hpp new file mode 100644 index 000000000..a98f2483b --- /dev/null +++ b/common/cli/terminal_color.hpp @@ -0,0 +1,557 @@ +//! +//! termcolor +//! ~~~~~~~~~ +//! +//! termcolor is a header-only c++ library for printing colored messages +//! to the terminal. Written just for fun with a help of the Force. +//! +//! :copyright: (c) 2013 by Ihor Kalnytskyi +//! :license: BSD, see LICENSE for details +//! + +#ifndef TERMCOLOR_HPP_ +#define TERMCOLOR_HPP_ + +// the following snippet of code detects the current OS and +// defines the appropriate macro that is used to wrap some +// platform specific things +#if defined(_WIN32) || defined(_WIN64) +# define TERMCOLOR_OS_WINDOWS +#elif defined(__APPLE__) +# define TERMCOLOR_OS_MACOS +#elif defined(__unix__) || defined(__unix) +# define TERMCOLOR_OS_LINUX +#else +# error unsupported platform +#endif + + +// This headers provides the `isatty()`/`fileno()` functions, +// which are used for testing whether a standart stream refers +// to the terminal. As for Windows, we also need WinApi funcs +// for changing colors attributes of the terminal. +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) +# include +#elif defined(TERMCOLOR_OS_WINDOWS) +# include +# include +#endif + + +#include +#include + + + +namespace termcolor +{ + // Forward declaration of the `_internal` namespace. + // All comments are below. + namespace _internal + { + // An index to be used to access a private storage of I/O streams. See + // colorize / nocolorize I/O manipulators for details. + static int colorize_index = std::ios_base::xalloc(); + + inline FILE* get_standard_stream(const std::ostream& stream); + inline bool is_colorized(std::ostream& stream); + inline bool is_atty(const std::ostream& stream); + +#if defined(TERMCOLOR_OS_WINDOWS) + inline void win_change_attributes(std::ostream& stream, int foreground, int background=-1); +#endif + } + + inline + std::ostream& colorize(std::ostream& stream) + { + stream.iword(_internal::colorize_index) = 1L; + return stream; + } + + inline + std::ostream& nocolorize(std::ostream& stream) + { + stream.iword(_internal::colorize_index) = 0L; + return stream; + } + + inline + std::ostream& reset(std::ostream& stream) + { + if (_internal::is_colorized(stream)) + { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\e[1;00m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, -1, -1); +#endif + } + return stream; + } + + + inline + std::ostream& bold(std::ostream& stream) + { + if (_internal::is_colorized(stream)) + { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\e[1;1m"; +#elif defined(TERMCOLOR_OS_WINDOWS) +#endif + } + return stream; + } + + + inline + std::ostream& dark(std::ostream& stream) + { + if (_internal::is_colorized(stream)) + { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\e[1;2m"; +#elif defined(TERMCOLOR_OS_WINDOWS) +#endif + } + return stream; + } + + + inline + std::ostream& underline(std::ostream& stream) + { + if (_internal::is_colorized(stream)) + { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\e[1;4m"; +#elif defined(TERMCOLOR_OS_WINDOWS) +#endif + } + return stream; + } + + + inline + std::ostream& blink(std::ostream& stream) + { + if (_internal::is_colorized(stream)) + { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\e[1;5m"; +#elif defined(TERMCOLOR_OS_WINDOWS) +#endif + } + return stream; + } + + + inline + std::ostream& reverse(std::ostream& stream) + { + if (_internal::is_colorized(stream)) + { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\e[1;7m"; +#elif defined(TERMCOLOR_OS_WINDOWS) +#endif + } + return stream; + } + + + inline + std::ostream& concealed(std::ostream& stream) + { + if (_internal::is_colorized(stream)) + { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\e[1;8m"; +#elif defined(TERMCOLOR_OS_WINDOWS) +#endif + } + return stream; + } + + + inline + std::ostream& grey(std::ostream& stream) + { + if (_internal::is_colorized(stream)) + { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\e[1;30m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, + 0 // grey (black) + ); +#endif + } + return stream; + } + + inline + std::ostream& red(std::ostream& stream) + { + if (_internal::is_colorized(stream)) + { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\e[1;31m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, + FOREGROUND_RED + ); +#endif + } + return stream; + } + + inline + std::ostream& green(std::ostream& stream) + { + if (_internal::is_colorized(stream)) + { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\e[1;32m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, + FOREGROUND_GREEN + ); +#endif + } + return stream; + } + + inline + std::ostream& yellow(std::ostream& stream) + { + if (_internal::is_colorized(stream)) + { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\e[1;33m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, + FOREGROUND_GREEN | FOREGROUND_RED + ); +#endif + } + return stream; + } + + inline + std::ostream& blue(std::ostream& stream) + { + if (_internal::is_colorized(stream)) + { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\e[1;34m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, + FOREGROUND_BLUE + ); +#endif + } + return stream; + } + + inline + std::ostream& magenta(std::ostream& stream) + { + if (_internal::is_colorized(stream)) + { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\e[1;35m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, + FOREGROUND_BLUE | FOREGROUND_RED + ); +#endif + } + return stream; + } + + inline + std::ostream& cyan(std::ostream& stream) + { + if (_internal::is_colorized(stream)) + { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\e[1;36m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, + FOREGROUND_BLUE | FOREGROUND_GREEN + ); +#endif + } + return stream; + } + + inline + std::ostream& white(std::ostream& stream) + { + if (_internal::is_colorized(stream)) + { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\e[1;37m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, + FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED + ); +#endif + } + return stream; + } + + + + inline + std::ostream& on_grey(std::ostream& stream) + { + if (_internal::is_colorized(stream)) + { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\e[1;40m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, -1, + 0 // grey (black) + ); +#endif + } + return stream; + } + + inline + std::ostream& on_red(std::ostream& stream) + { + if (_internal::is_colorized(stream)) + { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\e[1;41m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, -1, + BACKGROUND_RED + ); +#endif + } + return stream; + } + + inline + std::ostream& on_green(std::ostream& stream) + { + if (_internal::is_colorized(stream)) + { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\e[1;42m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, -1, + BACKGROUND_GREEN + ); +#endif + } + return stream; + } + + inline + std::ostream& on_yellow(std::ostream& stream) + { + if (_internal::is_colorized(stream)) + { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\e[1;43m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, -1, + BACKGROUND_GREEN | BACKGROUND_RED + ); +#endif + } + return stream; + } + + inline + std::ostream& on_blue(std::ostream& stream) + { + if (_internal::is_colorized(stream)) + { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\e[1;44m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, -1, + BACKGROUND_BLUE + ); +#endif + } + return stream; + } + + inline + std::ostream& on_magenta(std::ostream& stream) + { + if (_internal::is_colorized(stream)) + { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\e[1;45m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, -1, + BACKGROUND_BLUE | BACKGROUND_RED + ); +#endif + } + return stream; + } + + inline + std::ostream& on_cyan(std::ostream& stream) + { + if (_internal::is_colorized(stream)) + { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\e[1;46m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, -1, + BACKGROUND_GREEN | BACKGROUND_BLUE + ); +#endif + } + return stream; + } + + inline + std::ostream& on_white(std::ostream& stream) + { + if (_internal::is_colorized(stream)) + { +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + stream << "\e[1;47m"; +#elif defined(TERMCOLOR_OS_WINDOWS) + _internal::win_change_attributes(stream, -1, + BACKGROUND_GREEN | BACKGROUND_BLUE | BACKGROUND_RED + ); +#endif + } + + return stream; + } + + + + //! Since C++ hasn't a way to hide something in the header from + //! the outer access, I have to introduce this namespace which + //! is used for internal purpose and should't be access from + //! the user code. + namespace _internal + { + //! Since C++ hasn't a true way to extract stream handler + //! from the a given `std::ostream` object, I have to write + //! this kind of hack. + inline + FILE* get_standard_stream(const std::ostream& stream) + { + if (&stream == &std::cout) + return stdout; + else if ((&stream == &std::cerr) || (&stream == &std::clog)) + return stderr; + + return 0; + } + + // Say whether a given stream should be colorized or not. It's always + // true for ATTY streams and may be true for streams marked with + // colorize flag. + inline + bool is_colorized(std::ostream& stream) + { + return is_atty(stream) || static_cast(stream.iword(colorize_index)); + } + + //! Test whether a given `std::ostream` object refers to + //! a terminal. + inline + bool is_atty(const std::ostream& stream) + { + FILE* std_stream = get_standard_stream(stream); + + // Unfortunately, fileno() ends with segmentation fault + // if invalid file descriptor is passed. So we need to + // handle this case gracefully and assume it's not a tty + // if standard stream is not detected, and 0 is returned. + if (!std_stream) + return false; + +#if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX) + return ::isatty(fileno(std_stream)); +#elif defined(TERMCOLOR_OS_WINDOWS) + return ::_isatty(_fileno(std_stream)); +#endif + } + +#if defined(TERMCOLOR_OS_WINDOWS) + //! Change Windows Terminal colors attribute. If some + //! parameter is `-1` then attribute won't changed. + inline void win_change_attributes(std::ostream& stream, int foreground, int background) + { + // yeah, i know.. it's ugly, it's windows. + static WORD defaultAttributes = 0; + + // Windows doesn't have ANSI escape sequences and so we use special + // API to change Terminal output color. That means we can't + // manipulate colors by means of "std::stringstream" and hence + // should do nothing in this case. + if (!_internal::is_atty(stream)) + return; + + // get terminal handle + HANDLE hTerminal = INVALID_HANDLE_VALUE; + if (&stream == &std::cout) + hTerminal = GetStdHandle(STD_OUTPUT_HANDLE); + else if (&stream == &std::cerr) + hTerminal = GetStdHandle(STD_ERROR_HANDLE); + + // save default terminal attributes if it unsaved + if (!defaultAttributes) + { + CONSOLE_SCREEN_BUFFER_INFO info; + if (!GetConsoleScreenBufferInfo(hTerminal, &info)) + return; + defaultAttributes = info.wAttributes; + } + + // restore all default settings + if (foreground == -1 && background == -1) + { + SetConsoleTextAttribute(hTerminal, defaultAttributes); + return; + } + + // get current settings + CONSOLE_SCREEN_BUFFER_INFO info; + if (!GetConsoleScreenBufferInfo(hTerminal, &info)) + return; + + if (foreground != -1) + { + info.wAttributes &= ~(info.wAttributes & 0x0F); + info.wAttributes |= static_cast(foreground); + } + + if (background != -1) + { + info.wAttributes &= ~(info.wAttributes & 0xF0); + info.wAttributes |= static_cast(background); + } + + SetConsoleTextAttribute(hTerminal, info.wAttributes); + } +#endif // TERMCOLOR_OS_WINDOWS + + } // namespace _internal + +} // namespace termcolor + + +#undef TERMCOLOR_OS_WINDOWS +#undef TERMCOLOR_OS_MACOS +#undef TERMCOLOR_OS_LINUX + +#endif // TERMCOLOR_HPP_ diff --git a/common/net/servertalk_client_connection.cpp b/common/net/servertalk_client_connection.cpp index 676d9d024..6a6fb079e 100644 --- a/common/net/servertalk_client_connection.cpp +++ b/common/net/servertalk_client_connection.cpp @@ -213,7 +213,7 @@ void EQ::Net::ServertalkClient::ProcessHello(EQ::Net::Packet &p) } } else { - LogF(Logs::General, Logs::Error, "Could not process hello, size != {0}", 1 + crypto_box_PUBLICKEYBYTES + crypto_box_NONCEBYTES); + LogError("Could not process hello, size != {0}", 1 + crypto_box_PUBLICKEYBYTES + crypto_box_NONCEBYTES); } } else { @@ -225,7 +225,7 @@ void EQ::Net::ServertalkClient::ProcessHello(EQ::Net::Packet &p) } } catch (std::exception &ex) { - LogF(Logs::General, Logs::Error, "Error parsing hello from server: {0}", ex.what()); + LogError("Error parsing hello from server: {0}", ex.what()); m_connection->Disconnect(); if (m_on_connect_cb) { @@ -252,7 +252,7 @@ void EQ::Net::ServertalkClient::ProcessHello(EQ::Net::Packet &p) } } catch (std::exception &ex) { - LogF(Logs::General, Logs::Error, "Error parsing hello from server: {0}", ex.what()); + LogError("Error parsing hello from server: {0}", ex.what()); m_connection->Disconnect(); if (m_on_connect_cb) { @@ -275,7 +275,7 @@ void EQ::Net::ServertalkClient::ProcessMessage(EQ::Net::Packet &p) std::unique_ptr decrypted_text(new unsigned char[message_len]); if (crypto_box_open_easy_afternm(&decrypted_text[0], (unsigned char*)&data[0], length, m_nonce_theirs, m_shared_key)) { - LogF(Logs::General, Logs::Error, "Error decrypting message from server"); + LogError("Error decrypting message from server"); (*(uint64_t*)&m_nonce_theirs[0])++; return; } @@ -323,7 +323,7 @@ void EQ::Net::ServertalkClient::ProcessMessage(EQ::Net::Packet &p) } } catch (std::exception &ex) { - LogF(Logs::General, Logs::Error, "Error parsing message from server: {0}", ex.what()); + LogError("Error parsing message from server: {0}", ex.what()); } } diff --git a/common/net/servertalk_server_connection.cpp b/common/net/servertalk_server_connection.cpp index c211545f0..c0e7ef72c 100644 --- a/common/net/servertalk_server_connection.cpp +++ b/common/net/servertalk_server_connection.cpp @@ -220,7 +220,7 @@ void EQ::Net::ServertalkServerConnection::ProcessHandshake(EQ::Net::Packet &p, b if (crypto_box_open_easy_afternm(&decrypted_text[0], (unsigned char*)p.Data() + crypto_box_PUBLICKEYBYTES + crypto_box_NONCEBYTES, cipher_len, m_nonce_theirs, m_shared_key)) { - LogF(Logs::General, Logs::Error, "Error decrypting handshake from client, dropping connection."); + LogError("Error decrypting handshake from client, dropping connection."); m_connection->Disconnect(); return; } @@ -229,7 +229,7 @@ void EQ::Net::ServertalkServerConnection::ProcessHandshake(EQ::Net::Packet &p, b std::string credentials = (const char*)&decrypted_text[0] + (m_identifier.length() + 1); if (!m_parent->CheckCredentials(credentials)) { - LogF(Logs::General, Logs::Error, "Got incoming connection with invalid credentials during handshake, dropping connection."); + LogError("Got incoming connection with invalid credentials during handshake, dropping connection."); m_connection->Disconnect(); return; } @@ -239,7 +239,7 @@ void EQ::Net::ServertalkServerConnection::ProcessHandshake(EQ::Net::Packet &p, b } } catch (std::exception &ex) { - LogF(Logs::General, Logs::Error, "Error parsing handshake from client: {0}", ex.what()); + LogError("Error parsing handshake from client: {0}", ex.what()); m_connection->Disconnect(); } } @@ -249,7 +249,7 @@ void EQ::Net::ServertalkServerConnection::ProcessHandshake(EQ::Net::Packet &p, b auto credentials = p.GetCString(m_identifier.length() + 1); if (!m_parent->CheckCredentials(credentials)) { - LogF(Logs::General, Logs::Error, "Got incoming connection with invalid credentials during handshake, dropping connection."); + LogError("Got incoming connection with invalid credentials during handshake, dropping connection."); m_connection->Disconnect(); return; } @@ -257,7 +257,7 @@ void EQ::Net::ServertalkServerConnection::ProcessHandshake(EQ::Net::Packet &p, b m_parent->ConnectionIdentified(this); } catch (std::exception &ex) { - LogF(Logs::General, Logs::Error, "Error parsing handshake from client: {0}", ex.what()); + LogError("Error parsing handshake from client: {0}", ex.what()); m_connection->Disconnect(); } } @@ -267,7 +267,7 @@ void EQ::Net::ServertalkServerConnection::ProcessHandshake(EQ::Net::Packet &p, b auto credentials = p.GetCString(m_identifier.length() + 1); if (!m_parent->CheckCredentials(credentials)) { - LogF(Logs::General, Logs::Error, "Got incoming connection with invalid credentials during handshake, dropping connection."); + LogError("Got incoming connection with invalid credentials during handshake, dropping connection."); m_connection->Disconnect(); return; } @@ -275,7 +275,7 @@ void EQ::Net::ServertalkServerConnection::ProcessHandshake(EQ::Net::Packet &p, b m_parent->ConnectionIdentified(this); } catch (std::exception &ex) { - LogF(Logs::General, Logs::Error, "Error parsing handshake from client: {0}", ex.what()); + LogError("Error parsing handshake from client: {0}", ex.what()); m_connection->Disconnect(); } #endif @@ -295,7 +295,7 @@ void EQ::Net::ServertalkServerConnection::ProcessMessage(EQ::Net::Packet &p) if (crypto_box_open_easy_afternm(&decrypted_text[0], (unsigned char*)&data[0], length, m_nonce_theirs, m_shared_key)) { - LogF(Logs::General, Logs::Error, "Error decrypting message from client"); + LogError("Error decrypting message from client"); (*(uint64_t*)&m_nonce_theirs[0])++; return; } @@ -343,6 +343,6 @@ void EQ::Net::ServertalkServerConnection::ProcessMessage(EQ::Net::Packet &p) } } catch (std::exception &ex) { - LogF(Logs::General, Logs::Error, "Error parsing message from client: {0}", ex.what()); + LogError("Error parsing message from client: {0}", ex.what()); } } diff --git a/loginserver/account_management.cpp b/loginserver/account_management.cpp index 2ac3c07b5..61c57c68e 100644 --- a/loginserver/account_management.cpp +++ b/loginserver/account_management.cpp @@ -26,11 +26,13 @@ extern LoginServer server; /** * @param username * @param password + * @param email * @return */ -bool AccountManagement::CreateLocalLoginServerAccount( +uint32 AccountManagement::CreateLocalLoginServerAccount( std::string username, - std::string password + std::string password, + std::string email ) { auto mode = server.options.GetEncryptionMode(); @@ -46,29 +48,32 @@ bool AccountManagement::CreateLocalLoginServerAccount( unsigned int db_id = 0; std::string db_loginserver = server.options.GetDefaultLoginServerName(); if (server.db->DoesLoginServerAccountExist(username, hash, db_loginserver, 1)) { - LogInfo( + LogWarning( "Attempting to create local login account for user [{0}] login [{1}] db_id [{2}] but already exists!", username, db_loginserver, db_id ); - return false; + return 0; } - if (server.db->CreateLoginData(username, hash, db_loginserver, db_id)) { + uint32 created_account_id = server.db->CreateLoginAccount(username, hash, db_loginserver, email); + if (created_account_id > 0) { LogInfo( - "Account creation success for user [{0}] encryption algorithm [{1}] ({2})", + "Account creation success for user [{0}] encryption algorithm [{1}] ({2}) id: [{3}]", username, GetEncryptionByModeId(mode), - mode + mode, + created_account_id ); - return true; + + return created_account_id; } LogError("Failed to create local login account for user [{0}]!", username); - return false; + return 0; } /** @@ -97,7 +102,7 @@ bool AccountManagement::CreateLoginserverWorldAdminAccount( ); if (server.db->DoesLoginserverWorldAdminAccountExist(username)) { - LogInfo( + LogWarning( "Attempting to create world admin account for user [{0}] but already exists!", username ); diff --git a/loginserver/account_management.h b/loginserver/account_management.h index 360ecb4b0..3bd14873a 100644 --- a/loginserver/account_management.h +++ b/loginserver/account_management.h @@ -21,6 +21,7 @@ #define EQEMU_ACCOUNT_MANAGEMENT_H #include "iostream" +#include "../common/types.h" class AccountManagement { public: @@ -28,9 +29,10 @@ public: /** * @param username * @param password + * @param email * @return */ - static bool CreateLocalLoginServerAccount(std::string username, std::string password); + static uint32 CreateLocalLoginServerAccount(std::string username, std::string password, std::string email = ""); /** * @param username diff --git a/loginserver/database.cpp b/loginserver/database.cpp index ce1de51ce..9f8e5a6c7 100644 --- a/loginserver/database.cpp +++ b/loginserver/database.cpp @@ -226,6 +226,41 @@ bool Database::CreateLoginData( return CreateLoginDataWithID(name, password, loginserver, free_id); } +/** + * @param name + * @param password + * @param loginserver + * @param email + * @return + */ +uint32 Database::CreateLoginAccount( + const std::string &name, + const std::string &password, + const std::string &loginserver, + const std::string &email +) +{ + uint32 free_id = GetFreeID(loginserver); + + if (free_id <= 0) { + return 0; + } + + auto query = fmt::format( + "INSERT INTO login_accounts (id, source_loginserver, account_name, account_password, account_email, last_login_date, last_ip_address, created_at) " + "VALUES ({0}, '{1}', '{2}', '{3}', '{4}', NOW(), '127.0.0.1', NOW())", + free_id, + EscapeString(loginserver), + EscapeString(name), + EscapeString(password), + EscapeString(email) + ); + + auto results = QueryDatabase(query); + + return (results.Success() ? free_id : 0); +} + /** * @param in_account_name * @param in_account_password diff --git a/loginserver/database.h b/loginserver/database.h index fec7ad797..aa3d2911b 100644 --- a/loginserver/database.h +++ b/loginserver/database.h @@ -248,6 +248,20 @@ public: Database::DbLoginServerAdmin GetLoginServerAdmin(const std::string &account_name); + /** + * @param name + * @param password + * @param loginserver + * @param email + * @return + */ + uint32 CreateLoginAccount( + const std::string &name, + const std::string &password, + const std::string &loginserver = "local", + const std::string &email = "local_creation" + ); + protected: std::string user, pass, host, port, name; MYSQL *database{}; diff --git a/loginserver/loginserver_command_handler.cpp b/loginserver/loginserver_command_handler.cpp index 3337a52e9..c8a60037d 100644 --- a/loginserver/loginserver_command_handler.cpp +++ b/loginserver/loginserver_command_handler.cpp @@ -30,30 +30,6 @@ extern LoginServer server; namespace LoginserverCommandHandler { - /** - * @param cmd - */ - void DisplayDebug(argh::parser &cmd) - { - if (cmd[{"-d", "--debug"}]) { - std::cout << "Positional args:\n"; - for (auto &pos_arg : cmd) - std::cout << '\t' << pos_arg << std::endl; - - std::cout << "Positional args:\n"; - for (auto &pos_arg : cmd.pos_args()) - std::cout << '\t' << pos_arg << std::endl; - - std::cout << "\nFlags:\n"; - for (auto &flag : cmd.flags()) - std::cout << '\t' << flag << std::endl; - - std::cout << "\nParameters:\n"; - for (auto ¶m : cmd.params()) - std::cout << '\t' << param.first << " : " << param.second << std::endl; - } - } - /** * @param argc * @param argv @@ -64,74 +40,46 @@ namespace LoginserverCommandHandler { argh::parser cmd; cmd.parse(argc, argv, argh::parser::PREFER_PARAM_FOR_UNREG_OPTION); - LoginserverCommandHandler::DisplayDebug(cmd); + EQEmuCommand::DisplayDebug(cmd); /** * Declare command mapping */ - std::map function_map; + auto function_map = EQEmuCommand::function_map; - function_map["create-loginserver-account"] = &LoginserverCommandHandler::CreateLocalLoginserverAccount; - function_map["create-loginserver-api-token"] = &LoginserverCommandHandler::CreateLoginserverApiToken; - function_map["create-loginserver-world-admin-account"] = &LoginserverCommandHandler::CreateLoginserverWorldAdminAccount; - function_map["list-loginserver-api-tokens"] = &LoginserverCommandHandler::ListLoginserverApiTokens; + /** + * Register commands + */ + function_map["login-user:create"] = &LoginserverCommandHandler::CreateLocalLoginserverAccount; + function_map["web-api-token:create"] = &LoginserverCommandHandler::CreateLoginserverApiToken; + function_map["web-api-token:list"] = &LoginserverCommandHandler::ListLoginserverApiTokens; + function_map["world-admin:create"] = &LoginserverCommandHandler::CreateLoginserverWorldAdminAccount; - std::map::const_iterator it = function_map.begin(); - - std::map::const_iterator end = function_map.end(); - - bool ran_command = false; - while (it != end) { - if (it->first == argv[1]) { - std::cout << std::endl; - std::cout << "###########################################################" << std::endl; - std::cout << "# Executing CLI Command" << std::endl; - std::cout << "###########################################################" << std::endl; - std::cout << std::endl; - - (it->second)(argc, argv, cmd); - ran_command = true; - } - ++it; - } - - if (cmd[{"-h", "--help"}] || !ran_command) { - std::cout << std::endl; - std::cout << "###########################################################" << std::endl; - std::cout << "# Loginserver CLI Menu" << std::endl; - std::cout << "###########################################################" << std::endl; - std::cout << std::endl; - std::cout << "# API" << std::endl; - std::cout << "> create-loginserver-api-token --write --read" << std::endl; - std::cout << "> list-loginserver-api-tokens" << std::endl; - std::cout << std::endl; - std::cout << "# User Accounts" << std::endl; - std::cout << "> create-loginserver-account --username=* --password=*" << std::endl; - std::cout << std::endl; - std::cout << "# World Accounts" << std::endl; - std::cout << "> create-loginserver-world-admin-account --username=* --password=* --email=*" << std::endl; - std::cout << std::endl; - std::cout << std::endl; - } - - exit(1); + EQEmuCommand::HandleMenu(function_map, cmd, argc, argv); } /** * @param argc * @param argv * @param cmd + * @param description */ - void CreateLoginserverApiToken(int argc, char **argv, argh::parser &cmd) + void CreateLoginserverApiToken(int argc, char **argv, argh::parser &cmd, std::string &description) { + description = "Creates Loginserver API Token"; + + if (cmd[{"-h", "--help"}]) { + return; + } + + std::vector arguments = {}; + std::vector options = { + "--read", + "--write" + }; + + EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv); + bool can_read = cmd[{"-r", "--read"}]; bool can_write = cmd[{"-w", "--write"}]; @@ -150,9 +98,16 @@ namespace LoginserverCommandHandler { * @param argc * @param argv * @param cmd + * @param description */ - void ListLoginserverApiTokens(int argc, char **argv, argh::parser &cmd) + void ListLoginserverApiTokens(int argc, char **argv, argh::parser &cmd, std::string &description) { + description = "Lists Loginserver API Tokens"; + + if (cmd[{"-h", "--help"}]) { + return; + } + for (auto &it : server.token_manager->loaded_api_tokens) { LogInfo( "token [{0}] can_write [{1}] can_read [{2}]", @@ -167,17 +122,30 @@ namespace LoginserverCommandHandler { * @param argc * @param argv * @param cmd + * @param description */ - void CreateLocalLoginserverAccount(int argc, char **argv, argh::parser &cmd) + void CreateLocalLoginserverAccount(int argc, char **argv, argh::parser &cmd, std::string &description) { - if (cmd("--username").str().empty() || cmd("--password").str().empty()) { - LogInfo("Command Example: create-loginserver-account --username=user --password=password"); - exit(1); + description = "Creates Local Loginserver Account"; + + std::vector arguments = { + "--username", + "--password" + }; + std::vector options = { + "--email=*" + }; + + if (cmd[{"-h", "--help"}]) { + return; } + EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv); + AccountManagement::CreateLocalLoginServerAccount( cmd("--username").str(), - cmd("--password").str() + cmd("--password").str(), + cmd("--email").str() ); } @@ -185,18 +153,25 @@ namespace LoginserverCommandHandler { * @param argc * @param argv * @param cmd + * @param description */ - void CreateLoginserverWorldAdminAccount(int argc, char **argv, argh::parser &cmd) + void CreateLoginserverWorldAdminAccount(int argc, char **argv, argh::parser &cmd, std::string &description) { - if ( - cmd("--username").str().empty() || - cmd("--password").str().empty() || - cmd("--email").str().empty()) { + description = "Creates Loginserver World Administrator Account"; - LogInfo("Command Example: create-loginserver-world-admin-account --username=* --password=* --email=*"); - exit(1); + std::vector arguments = { + "--username", + "--password" + "--email" + }; + std::vector options = {}; + + if (cmd[{"-h", "--help"}]) { + return; } + EQEmuCommand::ValidateCmdInput(arguments, options, cmd, argc, argv); + AccountManagement::CreateLoginserverWorldAdminAccount( cmd("--username").str(), cmd("--password").str(), diff --git a/loginserver/loginserver_command_handler.h b/loginserver/loginserver_command_handler.h index e37432807..44d46e83e 100644 --- a/loginserver/loginserver_command_handler.h +++ b/loginserver/loginserver_command_handler.h @@ -19,17 +19,17 @@ */ #include "iostream" -#include "../common/cli/argh.h" +#include "../common/cli/eqemu_command_handler.h" #ifndef EQEMU_LOGINSERVER_COMMAND_HANDLER_H #define EQEMU_LOGINSERVER_COMMAND_HANDLER_H namespace LoginserverCommandHandler { void CommandHandler(int argc, char **argv); - void CreateLoginserverApiToken(int argc, char **argv, argh::parser &cmd); - void ListLoginserverApiTokens(int argc, char **argv, argh::parser &cmd); - void CreateLocalLoginserverAccount(int argc, char **argv, argh::parser &cmd); - void CreateLoginserverWorldAdminAccount(int argc, char **argv, argh::parser &cmd); + void CreateLoginserverApiToken(int argc, char **argv, argh::parser &cmd, std::string &description); + void ListLoginserverApiTokens(int argc, char **argv, argh::parser &cmd, std::string &description); + void CreateLocalLoginserverAccount(int argc, char **argv, argh::parser &cmd, std::string &description); + void CreateLoginserverWorldAdminAccount(int argc, char **argv, argh::parser &cmd, std::string &description); }; diff --git a/loginserver/loginserver_webserver.cpp b/loginserver/loginserver_webserver.cpp index ca8d00404..45900c7d1 100644 --- a/loginserver/loginserver_webserver.cpp +++ b/loginserver/loginserver_webserver.cpp @@ -67,6 +67,7 @@ namespace LoginserverWebserver { Json::Value request_body = LoginserverWebserver::ParseRequestBody(request); std::string username = request_body.get("username", "").asString(); std::string password = request_body.get("password", "").asString(); + std::string email = request_body.get("email", "").asString(); Json::Value response; if (username.empty() || password.empty()) { diff --git a/loginserver/main.cpp b/loginserver/main.cpp index f94bdd0c7..9884e6c2a 100644 --- a/loginserver/main.cpp +++ b/loginserver/main.cpp @@ -48,10 +48,12 @@ int main(int argc, char** argv) RegisterExecutablePlatform(ExePlatformLogin); set_exception_handler(); - LogSys.LoadLogSettingsDefaults(); - LogInfo("Logging System Init"); + if (argc == 1) { + LogSys.LoadLogSettingsDefaults(); + } + server.config = EQ::JsonConfigFile::Load("login.json"); LogInfo("Config System Init"); @@ -123,7 +125,9 @@ int main(int argc, char** argv) server.config.GetVariableString("database", "db", "peq") ); - server.db->LoadLogSettings(LogSys.log_settings); + if (argc == 1) { + server.db->LoadLogSettings(LogSys.log_settings); + } /** * make sure our database got created okay, otherwise cleanup and exit @@ -186,7 +190,10 @@ int main(int argc, char** argv) LoginserverWebserver::RegisterRoutes(api); } - LoginserverCommandHandler::CommandHandler(argc, argv); + if (argc > 1) { + LogSys.LoadLogSettingsDefaults(); + LoginserverCommandHandler::CommandHandler(argc, argv); + } LogInfo("[Config] [Logging] IsTraceOn [{0}]", server.options.IsTraceOn()); LogInfo("[Config] [Logging] IsWorldTraceOn [{0}]", server.options.IsWorldTraceOn()); diff --git a/world/net.cpp b/world/net.cpp index 515f50b66..20248343f 100644 --- a/world/net.cpp +++ b/world/net.cpp @@ -427,7 +427,7 @@ int main(int argc, char** argv) { Log(Logs::General, Logs::World_Server, "Server (TCP) listener started."); server_connection->OnConnectionIdentified("Zone", [&console](std::shared_ptr connection) { - LogF(Logs::General, Logs::World_Server, "New Zone Server connection from {2} at {0}:{1}", + LogInfo("New Zone Server connection from {2} at {0}:{1}", connection->Handle()->RemoteIP(), connection->Handle()->RemotePort(), connection->GetUUID()); numzones++; @@ -435,7 +435,7 @@ int main(int argc, char** argv) { }); server_connection->OnConnectionRemoved("Zone", [](std::shared_ptr connection) { - LogF(Logs::General, Logs::World_Server, "Removed Zone Server connection from {0}", + LogInfo("Removed Zone Server connection from {0}", connection->GetUUID()); numzones--; @@ -443,35 +443,35 @@ int main(int argc, char** argv) { }); server_connection->OnConnectionIdentified("Launcher", [](std::shared_ptr connection) { - LogF(Logs::General, Logs::World_Server, "New Launcher connection from {2} at {0}:{1}", + LogInfo("New Launcher connection from {2} at {0}:{1}", connection->Handle()->RemoteIP(), connection->Handle()->RemotePort(), connection->GetUUID()); launcher_list.Add(connection); }); server_connection->OnConnectionRemoved("Launcher", [](std::shared_ptr connection) { - LogF(Logs::General, Logs::World_Server, "Removed Launcher connection from {0}", + LogInfo("Removed Launcher connection from {0}", connection->GetUUID()); launcher_list.Remove(connection); }); server_connection->OnConnectionIdentified("QueryServ", [](std::shared_ptr connection) { - LogF(Logs::General, Logs::World_Server, "New Query Server connection from {2} at {0}:{1}", + LogInfo("New Query Server connection from {2} at {0}:{1}", connection->Handle()->RemoteIP(), connection->Handle()->RemotePort(), connection->GetUUID()); QSLink.AddConnection(connection); }); server_connection->OnConnectionRemoved("QueryServ", [](std::shared_ptr connection) { - LogF(Logs::General, Logs::World_Server, "Removed Query Server connection from {0}", + LogInfo("Removed Query Server connection from {0}", connection->GetUUID()); QSLink.RemoveConnection(connection); }); server_connection->OnConnectionIdentified("UCS", [](std::shared_ptr connection) { - LogF(Logs::General, Logs::World_Server, "New UCS Server connection from {2} at {0}:{1}", + LogInfo("New UCS Server connection from {2} at {0}:{1}", connection->Handle()->RemoteIP(), connection->Handle()->RemotePort(), connection->GetUUID()); UCSLink.SetConnection(connection); @@ -480,7 +480,7 @@ int main(int argc, char** argv) { }); server_connection->OnConnectionRemoved("UCS", [](std::shared_ptr connection) { - LogF(Logs::General, Logs::World_Server, "Removed Query Server connection from {0}", + LogInfo("Removed Query Server connection from {0}", connection->GetUUID()); UCSLink.SetConnection(nullptr); @@ -489,14 +489,14 @@ int main(int argc, char** argv) { }); server_connection->OnConnectionIdentified("WebInterface", [](std::shared_ptr connection) { - LogF(Logs::General, Logs::World_Server, "New WebInterface Server connection from {2} at {0}:{1}", + LogInfo("New WebInterface Server connection from {2} at {0}:{1}", connection->Handle()->RemoteIP(), connection->Handle()->RemotePort(), connection->GetUUID()); web_interface.AddConnection(connection); }); server_connection->OnConnectionRemoved("WebInterface", [](std::shared_ptr connection) { - LogF(Logs::General, Logs::World_Server, "Removed WebInterface Server connection from {0}", + LogInfo("Removed WebInterface Server connection from {0}", connection->GetUUID()); web_interface.RemoveConnection(connection);