Merge pull request #193 from Salanto/universal-logger

Advanced Logging
This commit is contained in:
Rosemary Witchaven 2021-08-26 04:23:33 -05:00 committed by GitHub
commit 46c0414894
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 633 additions and 309 deletions

View File

@ -40,13 +40,15 @@ SOURCES += \
src/config_manager.cpp \
src/db_manager.cpp \
src/discord.cpp \
src/logger.cpp \
src/packets.cpp \
src/server.cpp \
src/testimony_recorder.cpp \
src/ws_client.cpp \
src/ws_proxy.cpp \
src/http_advertiser.cpp
src/http_advertiser.cpp \
src/logger/u_logger.cpp \
src/logger/writer_modcall.cpp \
src/logger/writer_full.cpp
HEADERS += include/advertiser.h \
include/aoclient.h \
@ -56,8 +58,10 @@ HEADERS += include/advertiser.h \
include/data_types.h \
include/db_manager.h \
include/discord.h \
include/logger.h \
include/server.h \
include/ws_client.h \
include/ws_proxy.h \
include/http_advertiser.h
include/http_advertiser.h \
include/logger/u_logger.h \
include/logger/writer_modcall.h \
include/logger/writer_full.h

View File

@ -75,6 +75,15 @@ class AOClient : public QObject {
*/
QString getIpid() const;
/**
* @brief Getter for the client's HWID.
*
* @return The HWID.
*
* @see #hwid
*/
QString getHwid() const;
/**
* @brief Calculates the client's IPID based on a hashed version of its IP.
*/
@ -2189,6 +2198,48 @@ class AOClient : public QObject {
* @param message The OOC message the client has sent.
*/
void loginAttempt(QString message);
signals:
/**
* @brief Signal connected to universal logger. Sends IC chat usage to the logger.
*/
void logIC(const QString& f_charName, const QString& f_oocName, const QString& f_ipid,
const QString& f_areaName, const QString &f_message);
/**
* @brief Signal connected to universal logger. Sends OOC chat usage to the logger.
*/
void logOOC(const QString& f_charName, const QString& f_oocName, const QString& f_ipid,
const QString& f_areaName, const QString& f_message);
/**
* @brief Signal connected to universal logger. Sends login attempt to the logger.
*/
void logLogin(const QString& f_charName, const QString& f_oocName, const QString& f_moderatorName,
const QString& f_ipid, const QString &f_areaName, const bool& f_success);
/**
* @brief Signal connected to universal logger. Sends command usage to the logger.
*/
void logCMD(const QString& f_charName, const QString &f_ipid, const QString& f_oocName, const QString f_command,
const QStringList f_args, const QString f_areaName);
/**
* @brief Signal connected to universal logger. Sends player kick information to the logger.
*/
void logKick(const QString& f_moderator, const QString& f_targetIPID, const QString& f_reason);
/**
* @brief Signal connected to universal logger. Sends ban information to the logger.
*/
void logBan(const QString& f_moderator, const QString& f_targetIPID, const QString &f_duration, const QString& f_reason);
/**
* @brief Signal connected to universal logger. Sends modcall information to the logger, triggering a write of the buffer
* when modcall logging is used.
*/
void logModcall(const QString& f_charName, const QString &f_ipid, const QString& f_oocName, const QString& f_areaName);
};
#endif // AOCLIENT_H

View File

@ -18,7 +18,6 @@
#ifndef AREA_DATA_H
#define AREA_DATA_H
#include "logger.h"
#include "aopacket.h"
#include "config_manager.h"

View File

@ -41,7 +41,7 @@ public:
*/
enum class LogType {
MODCALL,
FULL
FULL,
};
Q_ENUM(LogType)
};

View File

@ -1,140 +0,0 @@
//////////////////////////////////////////////////////////////////////////////////////
// akashi - a server for Attorney Online 2 //
// Copyright (C) 2020 scatterflower //
// //
// This program is free software: you can redistribute it and/or modify //
// it under the terms of the GNU Affero General Public License as //
// published by the Free Software Foundation, either version 3 of the //
// License, or (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU Affero General Public License for more details. //
// //
// You should have received a copy of the GNU Affero General Public License //
// along with this program. If not, see <https://www.gnu.org/licenses/>. //
//////////////////////////////////////////////////////////////////////////////////////
#ifndef LOGGER_H
#define LOGGER_H
#include <QFile>
#include <QDebug>
#include <QString>
#include <QQueue>
#include <QDateTime>
#include "data_types.h"
/**
* @brief A class associated with an AreaData class to log various events happening inside the latter.
*/
class Logger
{
public:
/**
* @brief Constructs a Logger instance.
*
* @param f_max_length The maximum amount of entries the Logger can store at once.
*/
Logger(QString f_area_name, int f_max_length, const DataTypes::LogType& f_logType_r) :
m_maxLength(f_max_length), m_areaName(f_area_name), m_logType(f_logType_r) {};
/**
*@brief Returns a copy of the logger's buffer.
*/
QQueue<QString> buffer() const;
public slots:
/**
* @brief Logs an IC message.
*
* @param f_charName_r The character name of the client who sent the IC message.
* @param f_ipid_r The IPID of the aforementioned client.
* @param f_message_r The text of the IC message.
*/
void logIC(const QString& f_charName_r, const QString& f_ipid_r, const QString& f_message_r, const QString& f_showname);
/**
* @brief Logs an OOC message.
*
* @param f_areaName_r The name of the area where the event happened.
* @param f_charName_r The character name of the client who sent the OOC message.
* @param f_ipid_r The IPID of the aforementioned client.
* @param f_message_r The text of the OOC message.
*/
void logOOC(const QString& f_charName_r, const QString& f_ipid_r, const QString& f_message_r);
/**
* @brief Logs a mod call message.
*
* @param f_charName_r The character name of the client who sent the mod call.
* @param f_ipid_r The IPID of the aforementioned client.
* @param f_modcallReason_r The reason for the modcall.
*/
void logModcall(const QString& f_charName_r, const QString& f_ipid_r, const QString& f_modcallReason_r);
/**
* @brief Logs a command called in OOC.
*
* @details If the command is not one of any of the 'special' ones, it defaults to logOOC().
* The only thing that makes a command 'special' if it is handled differently in here.
*
* @param f_charName_r The character name of the client who sent the command.
* @param f_ipid_r The IPID of the aforementioned client.
* @param f_command_r The command being logged.
* @param f_cmdArgs_r The command arguments being logged.
*/
void logCmd(const QString& f_charName_r, const QString& f_ipid_r, const QString& f_command_r, const QStringList& f_cmdArgs_r);
/**
* @brief Logs a login attempt.
*
* @param f_charName_r The character name of the client that attempted to login.
* @param f_ipid_r The IPID of the aforementioned client.
* @param success True if the client successfully authenticated as a mod.
* @param f_modname_r If the client logged in with a modname, then this is it. Otherwise, it's `"moderator"`.
*/
void logLogin(const QString& f_charName_r, const QString& f_ipid_r, bool success, const QString& f_modname_r);
/**
* @brief Appends the contents of #buffer into `config/server.log`, emptying the former.
*/
void flush();
private:
/**
* @brief Contains entries that have not yet been flushed out into a log file.
*/
QQueue<QString> m_buffer;
/**
* @brief Convenience function to add an entry to #buffer.
*
* @details If the buffer's size is equal to #max_length, the first entry in the queue is removed,
* and the newest entry is added to the end.
*
* @param f_charName_r The character name of the client who 'caused' the source event for the entry to happen.
* @param f_ipid_r The IPID of the aforementioned client.
* @param f_type_r The type of entry that is being built, something that uniquely identifies entries of similar being.
* @param f_message_r Any additional information related to the entry.
*/
void addEntry(const QString& f_charName_r, const QString& f_ipid_r,
const QString& f_type_r, const QString& f_message_r);
/**
* @brief The max amount of entries that may be contained in #buffer.
*/
int m_maxLength;
QString m_areaName;
/**
* @brief Determines what kind of logging happens, `"full"` or `"modcall"`.
*
* @details This largely influences the resulting log file's name, and in case of a `"full"` setup,
* the in-memory buffer is auto-dumped to said file if full.
*/
DataTypes::LogType m_logType;
};
#endif // LOGGER_H

View File

@ -0,0 +1,126 @@
//////////////////////////////////////////////////////////////////////////////////////
// akashi - a server for Attorney Online 2 //
// Copyright (C) 2020 scatterflower //
// //
// This program is free software: you can redistribute it and/or modify //
// it under the terms of the GNU Affero General Public License as //
// published by the Free Software Foundation, either version 3 of the //
// License, or (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU Affero General Public License for more details. //
// //
// You should have received a copy of the GNU Affero General Public License //
// along with this program. If not, see <https://www.gnu.org/licenses/>. //
//////////////////////////////////////////////////////////////////////////////////////
#ifndef U_LOGGER_H
#define U_LOGGER_H
#include <QObject>
#include <QMap>
#include <QQueue>
#include <QDateTime>
#include "include/config_manager.h"
#include "include/logger/writer_full.h"
#include "include/logger/writer_modcall.h"
/**
* @brief The Universal Logger class to provide a common place to handle, store and write logs to file.
*/
class ULogger : public QObject
{
Q_OBJECT
public:
/**
* @brief Constructor for the universal logger. Determines which writer is initially created.
* @param Pointer to the Server.
*/
ULogger(QObject* parent = nullptr);
/**
* @brief Deconstructor of the universal logger. Deletes its writer before deconstruction.
*/
virtual ~ULogger();
/**
* @brief Returns the buffer of a respective area. Primarily used by the Discord Webhook.
* @param Name of the area which buffer is requested.
*/
QQueue<QString> buffer(const QString &f_areaName);
public slots:
/**
* @brief Adds an IC log entry to the area buffer and writes it to the respective log format.
*/
void logIC(const QString& f_charName, const QString& f_oocName, const QString& f_ipid,
const QString& f_areaName, const QString &f_message);
/**
* @brief Adds an OOC log entry to the area buffer and writes it to the respective log format.
*/
void logOOC(const QString& f_charName, const QString& f_oocName, const QString& f_ipid,
const QString& f_areaName, const QString& f_message);
/**
* @brief Adds an login attempt to the area buffer and writes it to the respective log format.
*/
void logLogin(const QString& f_charName, const QString& f_oocName, const QString& f_moderatorName,
const QString& f_ipid, const QString &f_areaName, const bool& f_success);
/**
* @brief Adds a command usage to the area buffer and writes it to the respective log format.
*/
void logCMD(const QString& f_charName, const QString &f_ipid, const QString& f_oocName, const QString f_command,
const QStringList f_args, const QString f_areaName);
/**
* @brief Adds a player kick to the area buffer and writes it to the respective log format.
*/
void logKick(const QString& f_moderator, const QString& f_targetIPID);
/**
* @brief Adds a player ban to the area buffer and writes it to the respective log format.
*/
void logBan(const QString& f_moderator, const QString& f_targetIPID, const QString &f_duration);
/**
* @brief Adds a modcall event to the area buffer, also triggers modcall writing.
*/
void logModcall(const QString& f_charName, const QString &f_ipid, const QString& f_oocName, const QString& f_areaName);
/**
* @brief Logs any connection attempt to the server, wether sucessful or not.
*/
void logConnectionAttempt(const QString& f_ip_address, const QString& f_ipid, const QString& f_hwid);
private:
/**
* @brief Updates the area buffer with a new entry, moving old ones if the buffer exceesed the maximum size.
* @param Name of the area which buffer is modified.
* @param Formatted QString to be added into the buffer.
*/
void updateAreaBuffer(const QString& f_areaName, const QString& f_logEntry);
/**
* @brief QMap of all available area buffers.
*
* @details This QMap uses the area name as the index key to access its respective buffer.
*/
QMap<QString, QQueue<QString>> m_bufferMap;
/**
* @brief Pointer to modcall writer. Handles QQueue delogging into area specific file.
*/
WriterModcall* writerModcall;
/**
* @brief Pointer to full writer. Handles single messages in one file.
*/
WriterFull* writerFull;
};
#endif //U_LOGGER_H

View File

@ -0,0 +1,65 @@
//////////////////////////////////////////////////////////////////////////////////////
// akashi - a server for Attorney Online 2 //
// Copyright (C) 2020 scatterflower //
// //
// This program is free software: you can redistribute it and/or modify //
// it under the terms of the GNU Affero General Public License as //
// published by the Free Software Foundation, either version 3 of the //
// License, or (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU Affero General Public License for more details. //
// //
// You should have received a copy of the GNU Affero General Public License //
// along with this program. If not, see <https://www.gnu.org/licenses/>. //
//////////////////////////////////////////////////////////////////////////////////////
#ifndef WRITER_FULL_H
#define WRITER_FULL_H
#include <QObject>
#include <QFile>
#include <QDir>
#include <QDateTime>
#include <QTextStream>
/**
* @brief A class to handle file interaction when writing in full log mode.
*/
class WriterFull : public QObject
{
Q_OBJECT
public:
/**
* @brief Constructor for full logwriter
*
* @param QObject pointer to the parent object.
*/
WriterFull(QObject* parent = nullptr);;
/**
* @brief Deconstructor for full logwriter.
*
* @details Doesn't really do anything, but its here for completeness sake.
*/
virtual ~WriterFull() {}
/**
* @brief Function to write log entry into a logfile.
* @param Preformatted QString which will be written into the logfile.
*/
void flush(const QString f_entry);
private:
/**
* @brief Filename of the logfile used. This will always be the time the server starts up.
*/
QFile l_logfile;
/**
* @brief Directory where logfiles will be stored.
*/
QDir l_dir;
};
#endif //WRITER_FULL_H

View File

@ -0,0 +1,68 @@
//////////////////////////////////////////////////////////////////////////////////////
// akashi - a server for Attorney Online 2 //
// Copyright (C) 2020 scatterflower //
// //
// This program is free software: you can redistribute it and/or modify //
// it under the terms of the GNU Affero General Public License as //
// published by the Free Software Foundation, either version 3 of the //
// License, or (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU Affero General Public License for more details. //
// //
// You should have received a copy of the GNU Affero General Public License //
// along with this program. If not, see <https://www.gnu.org/licenses/>. //
//////////////////////////////////////////////////////////////////////////////////////
#ifndef WRITER_MODCALL_H
#define WRITER_MODCALL_H
#include <QObject>
#include <QFile>
#include <QDir>
#include <QDateTime>
#include <QTextStream>
#include <QQueue>
/**
* @brief A class to handle file interaction when writing the modcall buffer.
*/
class WriterModcall : public QObject
{
Q_OBJECT
public:
/**
* @brief Constructor for modcall logwriter
*
* @param QObject pointer to the parent object.
*/
WriterModcall(QObject* parent = nullptr);;
/**
* @brief Deconstructor for modcall logwriter.
*
* @details Doesn't really do anything, but its here for completeness sake.
*/
virtual ~WriterModcall() {}
/**
* @brief Function to write area buffer into a logfile.
* @param QQueue of the area that will be written into the logfile.
* @param Name of the area for the filename.
*/
void flush(const QString f_areaName, QQueue<QString> f_buffer);
private:
/**
* @brief Filename of the logfile used.
*/
QFile l_logfile;
/**
* @brief Directory where logfiles will be stored.
*/
QDir l_dir;
};
#endif //WRITER_MODCALL_H

View File

@ -26,6 +26,7 @@
#include "include/discord.h"
#include "include/config_manager.h"
#include "include/http_advertiser.h"
#include "include/logger/u_logger.h"
#include <QCoreApplication>
#include <QDebug>
@ -148,6 +149,11 @@ class Server : public QObject {
*/
void updateHTTPAdvertiserConfig();
/**
* @brief Getter for an area specific buffer from the logger.
*/
QQueue<QString> getAreaBuffer(const QString& f_areaName);
/**
* @brief The collection of all currently connected clients.
*/
@ -277,7 +283,20 @@ class Server : public QObject {
*/
void banWebhookRequest(const QString& f_ipid, const QString& f_moderator, const QString& f_duration, const QString& f_reason, const int& f_banID);
/**
* @brief Signal connected to universal logger. Logs a client connection attempt.
* @param f_ip_address The IP Address of the incoming connection.
* @param f_ipid The IPID of the incoming connection.
* @param f_hdid The HDID of the incoming connection.
*/
void logConnectionAttempt(const QString& f_ip_address, const QString& f_ipid, const QString& f_hwid);
private:
/**
* @brief Connects new AOClient to the logger.
**/
void hookupLogger(AOClient* client);
/**
* @brief The proxy used for WebSocket connections.
*
@ -305,6 +324,11 @@ class Server : public QObject {
*/
QTimer* httpAdvertiserTimer;
/**
* @brief Handles the universal log framework.
*/
ULogger* logger;
/**
* @brief The port through which the server will accept TCP connections.
*/

View File

@ -342,6 +342,8 @@ bool AOClient::checkAuth(unsigned long long acl_mask)
QString AOClient::getIpid() const { return ipid; }
QString AOClient::getHwid() const { return hwid; }
Server* AOClient::getServer() { return server; }
void AOClient::onAfkTimeout()

View File

@ -50,10 +50,8 @@ AreaData::AreaData(QString p_name, int p_index) :
m_ignoreBgList = areas_ini.value("ignore_bglist", "false").toBool();
areas_ini.endGroup();
int log_size = ConfigManager::logBuffer();
DataTypes::LogType l_logType = ConfigManager::loggingType();
if (log_size == 0)
log_size = 500;
m_logger = new Logger(m_name, log_size, l_logType);
QTimer* timer1 = new QTimer();
m_timers.append(timer1);
QTimer* timer2 = new QTimer();
@ -277,44 +275,11 @@ void AreaData::toggleMusic()
m_toggleMusic = !m_toggleMusic;
}
void AreaData::log(const QString &f_clientName_r, const QString &f_clientIpid_r, const AOPacket &f_packet_r) const
{
auto l_header = f_packet_r.header;
if (l_header == "MS") {
m_logger->logIC(f_clientName_r, f_clientIpid_r, f_packet_r.contents.at(4), f_packet_r.contents.at(15));
} else if (l_header == "CT") {
m_logger->logOOC(f_clientName_r, f_clientIpid_r, f_packet_r.contents.at(1));
} else if (l_header == "ZZ") {
m_logger->logModcall(f_clientName_r, f_clientIpid_r, f_packet_r.contents.at(0));
}
}
void AreaData::logLogin(const QString &f_clientName_r, const QString &f_clientIpid_r, bool f_success, const QString& f_modname_r) const
{
m_logger->logLogin(f_clientName_r, f_clientIpid_r, f_success, f_modname_r);
}
void AreaData::logCmd(const QString &f_clientName_r, const QString &f_clientIpid_r, const QString &f_command_r, const QStringList &f_cmdArgs_r) const
{
m_logger->logCmd(f_clientName_r, f_clientIpid_r, f_command_r, f_cmdArgs_r);
}
void AreaData::flushLogs() const
{
m_logger->flush();
}
void AreaData::setEviMod(const EvidenceMod &f_eviMod_r)
{
m_eviMod = f_eviMod_r;
}
QQueue<QString> AreaData::buffer() const
{
return m_logger->buffer();
}
void AreaData::setTestimonyRecording(const TestimonyRecording &f_testimonyRecording_r)
{
m_testimonyRecording = f_testimonyRecording_r;

View File

@ -78,6 +78,7 @@ void AOClient::cmdBan(int argc, QStringList argv)
client->socket->close();
kick_counter++;
emit logBan(ban.moderator,ban.ipid,ban_duration,ban.reason);
if (ConfigManager::discordBanWebhookEnabled())
emit server->banWebhookRequest(ban.ipid, ban.moderator, ban_duration, ban.reason, ban_id);
}
@ -111,8 +112,15 @@ void AOClient::cmdKick(int argc, QStringList argv)
kick_counter++;
}
if (kick_counter > 0)
if (kick_counter > 0) {
if (ConfigManager::authType() == DataTypes::AuthType::ADVANCED){
emit logKick(moderator_name, target_ipid, reason);
}
else {
emit logKick("Moderator",target_ipid,reason);
}
sendServerMessage("Kicked " + QString::number(kick_counter) + " client(s) with ipid " + target_ipid + " for reason: " + reason);
}
else
sendServerMessage("User with ipid not found!");
}

View File

@ -1,118 +0,0 @@
//////////////////////////////////////////////////////////////////////////////////////
// akashi - a server for Attorney Online 2 //
// Copyright (C) 2020 scatterflower //
// //
// This program is free software: you can redistribute it and/or modify //
// it under the terms of the GNU Affero General Public License as //
// published by the Free Software Foundation, either version 3 of the //
// License, or (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU Affero General Public License for more details. //
// //
// You should have received a copy of the GNU Affero General Public License //
// along with this program. If not, see <https://www.gnu.org/licenses/>. //
//////////////////////////////////////////////////////////////////////////////////////
#include <QDir>
#include "include/logger.h"
void Logger::logIC(const QString& f_charName_r, const QString& f_ipid_r, const QString& f_message_r, const QString& f_showname_r)
{
addEntry((f_charName_r + " (" + f_showname_r + ") "), f_ipid_r, "IC", f_message_r);
}
void Logger::logOOC(const QString& f_charName_r, const QString& f_ipid_r, const QString& f_message_r)
{
addEntry(f_charName_r, f_ipid_r, "OOC", f_message_r);
}
void Logger::logModcall(const QString& f_charName_r, const QString& f_ipid_r, const QString& f_modcallReason_r)
{
addEntry(f_charName_r, f_ipid_r, "MODCALL", f_modcallReason_r);
}
void Logger::logCmd(const QString& f_charName_r, const QString& f_ipid_r, const QString& f_command_r, const QStringList& f_cmdArgs_r)
{
// Some commands contain sensitive data, like passwords
// These must be filtered out
if (f_command_r == "login") {
addEntry(f_charName_r, f_ipid_r, "LOGIN", "Attempted login");
}
else if (f_command_r == "rootpass") {
addEntry(f_charName_r, f_ipid_r, "USERS", "Root password created");
}
else if (f_command_r == "adduser" && !f_cmdArgs_r.isEmpty()) {
addEntry(f_charName_r, f_ipid_r, "USERS", "Added user " + f_cmdArgs_r.at(0));
}
else {
QString message = "/" + f_command_r + f_cmdArgs_r.join(" ");
logOOC(f_charName_r, f_ipid_r, message);
}
}
void Logger::logLogin(const QString& f_charName_r, const QString& f_ipid_r, bool success, const QString& f_modname_r)
{
QString l_message = success ? "Logged in as " + f_modname_r : "Failed to log in as " + f_modname_r;
addEntry(f_charName_r, f_ipid_r, "LOGIN", l_message);
}
void Logger::addEntry(
const QString& f_charName_r,
const QString& f_ipid_r,
const QString& f_type_r,
const QString& f_message_r)
{
QString l_time = QDateTime::currentDateTime().toString("ddd MMMM d yyyy | hh:mm:ss");
QString l_logEntry = QStringLiteral("[%1][%2][%6] %3(%4): %5\n")
.arg(l_time, m_areaName, f_charName_r, f_ipid_r, f_message_r, f_type_r);
if (m_buffer.length() < m_maxLength) {
m_buffer.enqueue(l_logEntry);
if (m_logType == DataTypes::LogType::FULL) {
flush();
}
}
else {
m_buffer.dequeue();
m_buffer.enqueue(l_logEntry);
}
}
void Logger::flush()
{
QDir l_dir("logs/");
if (!l_dir.exists()) {
l_dir.mkpath(".");
}
QFile l_logfile;
switch (m_logType) {
case DataTypes::LogType::MODCALL:
l_logfile.setFileName(QString("logs/report_%1_%2.log").arg(m_areaName, (QDateTime::currentDateTime().toString("yyyy-MM-dd_hhmmss"))));
break;
case DataTypes::LogType::FULL:
l_logfile.setFileName(QString("logs/%1.log").arg(QDate::currentDate().toString("yyyy-MM-dd")));
break;
}
if (l_logfile.open(QIODevice::WriteOnly | QIODevice::Append)) {
QTextStream file_stream(&l_logfile);
while (!m_buffer.isEmpty())
file_stream << m_buffer.dequeue();
}
l_logfile.close();
}
QQueue<QString> Logger::buffer() const
{
return m_buffer;
}

View File

@ -0,0 +1,156 @@
//////////////////////////////////////////////////////////////////////////////////////
// akashi - a server for Attorney Online 2 //
// Copyright (C) 2020 scatterflower //
// //
// This program is free software: you can redistribute it and/or modify //
// it under the terms of the GNU Affero General Public License as //
// published by the Free Software Foundation, either version 3 of the //
// License, or (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU Affero General Public License for more details. //
// //
// You should have received a copy of the GNU Affero General Public License //
// along with this program. If not, see <https://www.gnu.org/licenses/>. //
//////////////////////////////////////////////////////////////////////////////////////
#include "include/logger/u_logger.h"
ULogger::ULogger(QObject* parent) :
QObject(parent)
{
switch (ConfigManager::loggingType()) {
case DataTypes::LogType::MODCALL :
writerModcall = new WriterModcall;
break;
case DataTypes::LogType::FULL :
writerFull = new WriterFull;
break;
}
}
ULogger::~ULogger()
{
switch (ConfigManager::loggingType()) {
case DataTypes::LogType::MODCALL :
writerModcall->deleteLater();
break;
case DataTypes::LogType::FULL :
writerFull->deleteLater();
break;
}
}
void ULogger::logIC(const QString& f_charName, const QString& f_oocName, const QString& f_ipid,
const QString& f_areaName, const QString& f_message)
{
QString l_time = QDateTime::currentDateTime().toString("ddd MMMM d yyyy | hh:mm:ss");
QString l_logEntry = QStringLiteral("[%1][%5][IC][%2(%3)][%4]%6\n")
.arg(l_time, f_charName, f_oocName, f_ipid, f_areaName, f_message);
updateAreaBuffer(f_areaName,l_logEntry);
}
void ULogger::logOOC(const QString& f_charName, const QString& f_oocName, const QString& f_ipid,
const QString& f_areaName, const QString& f_message)
{
QString l_time = QDateTime::currentDateTime().toString("ddd MMMM d yyyy | hh:mm:ss");
QString l_logEntry = QStringLiteral("[%1][%5][OOC][%2(%3)][%4]%6\n")
.arg(l_time, f_charName, f_oocName, f_ipid, f_areaName, f_message);
updateAreaBuffer(f_areaName,l_logEntry);
}
void ULogger::logLogin(const QString& f_charName, const QString& f_oocName, const QString& f_moderatorName,
const QString& f_ipid, const QString& f_areaName, const bool &f_success)
{
QString l_time = QDateTime::currentDateTime().toString("ddd MMMM d yyyy | hh:mm:ss");
QString l_success = f_success ? "SUCCESS][" + f_moderatorName : "FAILED][" + f_moderatorName;
QString l_logEntry = QStringLiteral("[%1][LOGIN][%2][%3][%4(%5)]\n")
.arg(l_time, l_success, f_ipid, f_charName, f_oocName);
updateAreaBuffer(f_areaName, l_logEntry);
}
void ULogger::logCMD(const QString& f_charName,const QString& f_ipid, const QString& f_oocName, const QString f_command,
const QStringList f_args, const QString f_areaName)
{
QString l_time = QDateTime::currentDateTime().toString("ddd MMMM d yyyy | hh:mm:ss");
QString l_logEntry;
// Some commands contain sensitive data, like passwords
// These must be filtered out
if (f_command == "login") {
l_logEntry = QStringLiteral("[%1][%2][LOGIN][%5][%3(%4)]\n")
.arg(l_time, f_areaName, f_charName, f_oocName, f_ipid);
}
else if (f_command == "rootpass") {
l_logEntry = QStringLiteral("[%1][%2][ROOTPASS][%5][%3(%4)]\n")
.arg(l_time, f_areaName, f_charName, f_oocName, f_ipid);
}
else if (f_command == "adduser" && !f_args.isEmpty()) {
l_logEntry = QStringLiteral("[%1][%2][USERADD][%6][%3(%4)]%5\n")
.arg(l_time, f_areaName, f_charName, f_oocName, f_args.at(0), f_ipid);
}
else {
l_logEntry = QStringLiteral("[%1][%2][CMD][%7][%3(%4)]/%5 %6\n")
.arg(l_time, f_areaName, f_charName, f_oocName, f_command, f_args.join(" "), f_ipid);
}
updateAreaBuffer(f_areaName,l_logEntry);
}
void ULogger::logKick(const QString& f_moderator, const QString& f_targetIPID)
{
QString l_time = QDateTime::currentDateTime().toString("ddd MMMM d yyyy | hh:mm:ss");
QString l_logEntry = QStringLiteral("[%1][%2][KICK][%3]\n")
.arg(l_time, f_moderator, f_targetIPID);
updateAreaBuffer("SERVER",l_logEntry);
}
void ULogger::logBan(const QString &f_moderator, const QString &f_targetIPID, const QString &f_duration)
{
QString l_time = QDateTime::currentDateTime().toString("ddd MMMM d yyyy | hh:mm:ss");
QString l_logEntry = QStringLiteral("[%1][%2][BAN][%3][%4]\n")
.arg(l_time, f_moderator, f_targetIPID, f_duration);
updateAreaBuffer("SERVER",l_logEntry);
}
void ULogger::logModcall(const QString &f_charName, const QString &f_ipid, const QString &f_oocName, const QString &f_areaName)
{
QString l_time = QDateTime::currentDateTime().toString("ddd MMMM d yyyy | hh:mm:ss");
QString l_logEvent = QStringLiteral("[%1][%2][MODCALL][%5][%3(%4)]\n")
.arg(l_time, f_areaName, f_charName, f_oocName, f_ipid);
updateAreaBuffer(f_areaName, l_logEvent);
if (ConfigManager::loggingType() == DataTypes::LogType::MODCALL) {
writerModcall->flush(f_areaName, buffer(f_areaName));
}
}
void ULogger::logConnectionAttempt(const QString& f_ip_address, const QString& f_ipid, const QString& f_hwid)
{
QString l_time = QDateTime::currentDateTime().toString("ddd MMMM d yyyy | hh:mm:ss");
QString l_logEntry = QStringLiteral("[%1][CONNECT][%2][%3][%4]\n")
.arg(l_time, f_ip_address, f_ipid, f_hwid);
updateAreaBuffer("SERVER",l_logEntry);
}
void ULogger::updateAreaBuffer(const QString& f_areaName, const QString& f_logEntry)
{
QQueue<QString>l_buffer = m_bufferMap.value(f_areaName);
if (l_buffer.length() <= ConfigManager::logBuffer()) {
l_buffer.enqueue(f_logEntry);
}
else {
l_buffer.dequeue();
l_buffer.enqueue(f_logEntry);
}
m_bufferMap.insert(f_areaName, l_buffer);
if (ConfigManager::loggingType() == DataTypes::LogType::FULL){
writerFull->flush(f_logEntry);
}
}
QQueue<QString> ULogger::buffer(const QString& f_areaName)
{
return m_bufferMap.value(f_areaName);
}

View File

@ -0,0 +1,39 @@
//////////////////////////////////////////////////////////////////////////////////////
// akashi - a server for Attorney Online 2 //
// Copyright (C) 2020 scatterflower //
// //
// This program is free software: you can redistribute it and/or modify //
// it under the terms of the GNU Affero General Public License as //
// published by the Free Software Foundation, either version 3 of the //
// License, or (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU Affero General Public License for more details. //
// //
// You should have received a copy of the GNU Affero General Public License //
// along with this program. If not, see <https://www.gnu.org/licenses/>. //
//////////////////////////////////////////////////////////////////////////////////////
#include "include/logger/writer_full.h"
WriterFull::WriterFull(QObject* parent) :
QObject(parent)
{
l_dir.setPath("logs/");
if (!l_dir.exists()) {
l_dir.mkpath(".");
}
}
void WriterFull::flush(const QString f_entry)
{
l_logfile.setFileName(QString("logs/%1.log").arg(QDate::currentDate().toString("yyyy-MM-dd")));
if (l_logfile.open(QIODevice::WriteOnly | QIODevice::Append)) {
QTextStream file_stream(&l_logfile);
file_stream << f_entry;
}
l_logfile.close();
};

View File

@ -0,0 +1,47 @@
//////////////////////////////////////////////////////////////////////////////////////
// akashi - a server for Attorney Online 2 //
// Copyright (C) 2020 scatterflower //
// //
// This program is free software: you can redistribute it and/or modify //
// it under the terms of the GNU Affero General Public License as //
// published by the Free Software Foundation, either version 3 of the //
// License, or (at your option) any later version. //
// //
// This program is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU Affero General Public License for more details. //
// //
// You should have received a copy of the GNU Affero General Public License //
// along with this program. If not, see <https://www.gnu.org/licenses/>. //
//////////////////////////////////////////////////////////////////////////////////////
#include "include/logger/writer_modcall.h"
WriterModcall::WriterModcall(QObject* parent) :
QObject(parent)
{
l_dir.setPath("logs/");
if (!l_dir.exists()) {
l_dir.mkpath(".");
}
l_dir.setPath("logs/modcall");
if (!l_dir.exists()) {
l_dir.mkpath(".");
}
}
void WriterModcall::flush(const QString f_areaName, QQueue<QString> f_buffer)
{
l_logfile.setFileName(QString("logs/modcall/report_%1_%2.log").arg(f_areaName, (QDateTime::currentDateTime().toString("yyyy-MM-dd_hhmmss"))));
if (l_logfile.open(QIODevice::WriteOnly | QIODevice::Append)) {
QTextStream file_stream(&l_logfile);
while (!f_buffer.isEmpty())
file_stream << f_buffer.dequeue();
}
l_logfile.close();
};

View File

@ -36,6 +36,7 @@ void AOClient::pktHardwareId(AreaData* area, int argc, QStringList argv, AOPacke
Q_UNUSED(packet);
hwid = argv[0];
emit server->logConnectionAttempt(remote_ip.toString(), ipid, hwid);
auto ban = server->db_manager->isHDIDBanned(hwid);
if (ban.first) {
sendPacket("BD", {ban.second + "\nBan ID: " + QString::number(server->db_manager->getBanID(hwid))});
@ -215,8 +216,8 @@ void AOClient::pktIcChat(AreaData* area, int argc, QStringList argv, AOPacket pa
if (pos != "")
validated_packet.contents[5] = pos;
area->log(current_char, ipid, validated_packet);
server->broadcast(validated_packet, current_area);
emit logIC((current_char + " " + showname), ooc_name,ipid,server->areas[current_area]->name(),last_message);
area->updateLastICMessage(validated_packet.contents);
server->can_send_ic_messages = false;
@ -263,13 +264,13 @@ void AOClient::pktOocChat(AreaData* area, int argc, QStringList argv, AOPacket p
int cmd_argc = cmd_argv.length();
handleCommand(command, cmd_argc, cmd_argv);
area->logCmd(current_char, ipid, command, cmd_argv);
emit logCMD((current_char + " " + showname),ipid, ooc_name,command,cmd_argv,server->areas[current_area]->name());
return;
}
else {
server->broadcast(final_packet, current_area);
}
area->log(current_char, ipid, final_packet);
emit logOOC((current_char + " " + showname), ooc_name, ipid,area->name(),message);
}
void AOClient::pktPing(AreaData* area, int argc, QStringList argv, AOPacket packet)
@ -418,17 +419,16 @@ void AOClient::pktModCall(AreaData* area, int argc, QStringList argv, AOPacket p
if (client->authenticated)
client->sendPacket(packet);
}
area->log(current_char, ipid, packet);
emit logModcall((current_char + " " + showname),ipid, ooc_name, server->areas[current_area]->name());
if (ConfigManager::discordModcallWebhookEnabled()) {
QString name = ooc_name;
if (ooc_name.isEmpty())
name = current_char;
emit server->modcallWebhookRequest(name, server->areas[current_area]->name(), packet.contents[0], area->buffer());
QString l_areaName = area->name();
emit server->modcallWebhookRequest(name, l_areaName, packet.contents[0],server->getAreaBuffer(l_areaName));
}
area->flushLogs();
}
void AOClient::pktAddEvidence(AreaData* area, int argc, QStringList argv, AOPacket packet)
@ -990,7 +990,7 @@ void AOClient::loginAttempt(QString message)
sendPacket("AUTH", {"0"}); // Client: "Login unsuccessful."
sendServerMessage("Incorrect password.");
}
server->areas.value(current_area)->logLogin(current_char, ipid, authenticated, "moderator");
emit logLogin((current_char + " " + showname),ooc_name,"Moderator", ipid, server->areas.value(current_area)->name(),authenticated);
break;
case DataTypes::AuthType::ADVANCED:
QStringList login = message.split(" ");
@ -1014,7 +1014,7 @@ void AOClient::loginAttempt(QString message)
sendPacket("AUTH", {"0"}); // Client: "Login unsuccessful."
sendServerMessage("Incorrect password.");
}
server->areas.value(current_area)->logLogin(current_char, ipid, authenticated, username);
emit logLogin((current_char + " " + showname),ooc_name, username, ipid, server->areas.value(current_area)->name(),authenticated);
break;
}
sendServerMessage("Exiting login prompt.");

View File

@ -69,6 +69,10 @@ void Server::start()
httpAdvertiserTimer->start(300000);
}
logger = new ULogger(this);
connect(this, &Server::logConnectionAttempt,
logger, &ULogger::logConnectionAttempt);
proxy = new WSProxy(port, ws_port, this);
if(ws_port != -1)
proxy->start();
@ -169,6 +173,7 @@ void Server::clientConnected()
// tsuserver4. It should disable fantacrypt
// completely in any client 2.4.3 or newer
client->sendPacket(decryptor);
hookupLogger(client);
#ifdef NET_DEBUG
qDebug() << client->remote_ip.toString() << "connected";
#endif
@ -279,6 +284,11 @@ void Server::updateHTTPAdvertiserConfig()
}
QQueue<QString> Server::getAreaBuffer(const QString &f_areaName)
{
return logger->buffer(f_areaName);
}
void Server::allowMessage()
{
can_send_ic_messages = true;
@ -306,6 +316,24 @@ void Server::handleDiscordIntegration()
return;
}
void Server::hookupLogger(AOClient* client)
{
connect(client, &AOClient::logIC,
logger, &ULogger::logIC);
connect(client, &AOClient::logOOC,
logger, &ULogger::logOOC);
connect(client, &AOClient::logLogin,
logger, &ULogger::logLogin);
connect(client, &AOClient::logCMD,
logger, &ULogger::logCMD);
connect(client, &AOClient::logBan,
logger, &ULogger::logBan);
connect(client, &AOClient::logKick,
logger, &ULogger::logKick);
connect(client, &AOClient::logModcall,
logger, &ULogger::logModcall);
}
Server::~Server()
{
for (AOClient* client : qAsConst(clients)) {