Console initial implementation

This commit is contained in:
KimLS
2017-04-09 20:17:48 -07:00
parent 3acd845a79
commit 18d28ae8d3
18 changed files with 1302 additions and 413 deletions
+78
View File
@@ -0,0 +1,78 @@
#include "console_server.h"
#include "../string_util.h"
#include <fmt/format.h>
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<EQ::Net::TCPConnection> connection) {
ConsoleServerConnection *c = new ConsoleServerConnection(this, connection);
m_connections.insert(std::make_pair(c->GetUUID(), std::unique_ptr<ConsoleServerConnection>(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();
}
}
+49
View File
@@ -0,0 +1,49 @@
#pragma once
#include "console_server_connection.h"
#include <functional>
#include <vector>
namespace EQ
{
namespace Net
{
struct ConsoleLoginStatus
{
int status;
int account_id;
std::string account_name;
};
class ConsoleServer
{
public:
typedef std::function<void(ConsoleServerConnection*, const std::string&, const std::vector<std::string>&)> ConsoleServerCallback;
typedef std::function<struct ConsoleLoginStatus(const std::string&, const std::string&)> 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<TCPServer> m_server;
std::map<std::string, std::unique_ptr<ConsoleServerConnection>> m_connections;
struct ConsoleServerCommand
{
ConsoleServerCallback fn;
int status_required;
std::string help_definition;
};
std::map<std::string, ConsoleServerCommand> m_commands;
ConsoleServerLoginCallback m_login;
friend class ConsoleServerConnection;
};
}
}
+221
View File
@@ -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<TCPConnection> 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));
}
+63
View File
@@ -0,0 +1,63 @@
#pragma once
#include "tcp_server.h"
#include <memory>
#include <map>
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<TCPConnection> 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<TCPConnection> 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];
};
}
}
+12 -2
View File
@@ -15,6 +15,16 @@ EQ::Net::TCPServer::~TCPServer() {
}
void EQ::Net::TCPServer::Listen(int port, bool ipv6, std::function<void(std::shared_ptr<TCPConnection>)> 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<void(std::shared_ptr<TCPConnection>)> cb)
{
if (m_socket) {
return;
@@ -29,10 +39,10 @@ void EQ::Net::TCPServer::Listen(int port, bool ipv6, std::function<void(std::sha
sockaddr_storage iaddr;
if (ipv6) {
uv_ip6_addr("::", port, (sockaddr_in6*)&iaddr);
uv_ip6_addr(addr.c_str(), port, (sockaddr_in6*)&iaddr);
}
else {
uv_ip4_addr("0.0.0.0", port, (sockaddr_in*)&iaddr);
uv_ip4_addr(addr.c_str(), port, (sockaddr_in*)&iaddr);
}
uv_tcp_bind(m_socket, (sockaddr*)&iaddr, 0);
+1
View File
@@ -13,6 +13,7 @@ namespace EQ
~TCPServer();
void Listen(int port, bool ipv6, std::function<void(std::shared_ptr<TCPConnection>)> cb);
void Listen(const std::string &addr, int port, bool ipv6, std::function<void(std::shared_ptr<TCPConnection>)> cb);
void Close();
void AddClient(uv_tcp_t *c);