Turn things in common into separate libs.

Took 2 hours 27 minutes
This commit is contained in:
Lukas Brübach 2025-10-04 15:10:20 +02:00
parent 53d80efab8
commit 01378b8314
389 changed files with 336 additions and 233 deletions

View file

@ -0,0 +1,107 @@
#include "../include/utility/expression.h"
#include "../../../common/lib/peglib.h"
#include <QByteArray>
#include <QString>
#include <QtMath>
#include <functional>
peg::parser math(R"(
EXPRESSION <- P0
P0 <- P1 (P1_OPERATOR P1)*
P1 <- P2 (P2_OPERATOR P2)*
P2 <- P3 (P3_OPERATOR P3)*
P3 <- NUMBER / FUNCTION / VARIABLE / '(' P0 ')'
P1_OPERATOR <- < [-+] >
P2_OPERATOR <- < [/*] >
P3_OPERATOR <- < '^' >
NUMBER <- < '-'? [0-9]+ >
NAME <- < [a-z][a-z0-9]* >
VARIABLE <- < [x] >
FUNCTION <- NAME '(' EXPRESSION ( [,\n] EXPRESSION )* ')'
%whitespace <- [ \t\r]*
)");
QMap<QString, std::function<double(double)>> *default_functions = nullptr;
Expression::Expression(double initial) : value(initial)
{
if (default_functions == nullptr) {
default_functions = new QMap<QString, std::function<double(double)>>();
default_functions->insert("abs", [](double a) { return qFabs(a); });
default_functions->insert("ceil", [](double a) { return qCeil(a); });
default_functions->insert("cos", [](double a) { return qCos(a); });
default_functions->insert("floor", [](double a) { return qFloor(a); });
default_functions->insert("log", [](double a) { return qLn(a); });
default_functions->insert("log10", [](double a) { return qLn(a); });
default_functions->insert("round", [](double a) { return qRound(a); });
default_functions->insert("sin", [](double a) { return qSin(a); });
default_functions->insert("sqrt", [](double a) { return qSqrt(a); });
default_functions->insert("tan", [](double a) { return qTan(a); });
default_functions->insert("trunc", [](double a) { return std::trunc(a); });
}
fns = QMap<QString, std::function<double(double)>>(*default_functions);
}
double Expression::eval(const peg::Ast &ast)
{
const auto &nodes = ast.nodes;
if (ast.name == "NUMBER") {
return stod(std::string(ast.token));
} else if (ast.name == "FUNCTION") {
QString name = QString::fromStdString(std::string(nodes[0]->token));
if (!fns.contains(name))
return 0;
return fns[name](eval(*nodes[1]));
} else if (ast.name == "VARIABLE") {
return value;
} else if (ast.name[0] == 'P') {
double result = eval(*nodes[0]);
for (unsigned int i = 1; i < nodes.size(); i += 2) {
double arg = eval(*nodes[i + 1]);
char operation = nodes[i]->token[0];
switch (operation) {
case '+':
result += arg;
break;
case '-':
result -= arg;
break;
case '*':
result *= arg;
break;
case '/':
result /= arg;
break;
case '^':
result = qPow(result, arg);
break;
default:
result = 0;
break;
}
}
return result;
} else {
return -1;
}
}
double Expression::parse(const QString &expr)
{
QByteArray ba = expr.toUtf8();
math.enable_ast();
std::shared_ptr<peg::Ast> ast;
if (math.parse(ba.data(), ast)) {
ast = peg::AstOptimizer(true).optimize(ast);
return eval(*ast);
}
return 0;
}

View file

@ -0,0 +1,77 @@
#include "../include/utility/featureset.h"
#include <QDebug>
#include <QMap>
FeatureSet::FeatureSet()
{
}
QMap<QString, bool> FeatureSet::getDefaultFeatureList()
{
initalizeFeatureList(featureList);
return featureList;
}
void FeatureSet::initalizeFeatureList(QMap<QString, bool> &_featureList)
{
// default features [name], [is required to connect]
_featureList.insert("client_id", false);
_featureList.insert("client_ver", false);
_featureList.insert("feature_set", false);
_featureList.insert("user_ban_history", false);
_featureList.insert("room_chat_history", false);
_featureList.insert("client_warnings", false);
_featureList.insert("mod_log_lookup", false);
_featureList.insert("idle_client", false);
_featureList.insert("forgot_password", false);
_featureList.insert("websocket", false);
// featureList.insert("hashed_password_login", false);
// These are temp to force users onto a newer client
_featureList.insert("2.7.0_min_version", false);
_featureList.insert("2.8.0_min_version", false);
}
void FeatureSet::enableRequiredFeature(QMap<QString, bool> &_featureList, const QString &featureName)
{
if (_featureList.contains(featureName))
_featureList.insert(featureName, true);
}
void FeatureSet::disableRequiredFeature(QMap<QString, bool> &_featureList, const QString &featureName)
{
if (_featureList.contains(featureName))
_featureList.insert(featureName, false);
}
QMap<QString, bool>
FeatureSet::addFeature(QMap<QString, bool> &_featureList, const QString &featureName, bool isFeatureRequired)
{
_featureList.insert(featureName, isFeatureRequired);
return _featureList;
}
QMap<QString, bool> FeatureSet::identifyMissingFeatures(const QMap<QString, bool> &suppliedFeatures,
QMap<QString, bool> requiredFeatures)
{
QMap<QString, bool> missingList;
QMap<QString, bool>::iterator i;
for (i = requiredFeatures.begin(); i != requiredFeatures.end(); ++i) {
if (!suppliedFeatures.contains(i.key())) {
missingList.insert(i.key(), i.value());
}
}
return missingList;
}
bool FeatureSet::isRequiredFeaturesMissing(const QMap<QString, bool> &suppliedFeatures,
QMap<QString, bool> requiredFeatures)
{
QMap<QString, bool>::iterator i;
for (i = requiredFeatures.begin(); i != requiredFeatures.end(); ++i) {
if (i.value() && suppliedFeatures.contains(i.key())) {
return true;
}
}
return false;
}

View file

@ -0,0 +1,74 @@
#include "../include/utility/key_signals.h"
#include <QKeyEvent>
bool KeySignals::eventFilter(QObject * /*object*/, QEvent *event)
{
QKeyEvent *kevent;
if (event->type() != QEvent::KeyPress)
return false;
kevent = static_cast<QKeyEvent *>(event);
switch (kevent->key()) {
case Qt::Key_Return:
case Qt::Key_Enter:
if (kevent->modifiers().testFlag(Qt::AltModifier) && kevent->modifiers().testFlag(Qt::ControlModifier))
emit onCtrlAltEnter();
else if (kevent->modifiers() & Qt::ControlModifier)
emit onCtrlEnter();
else
emit onEnter();
break;
case Qt::Key_Right:
if (kevent->modifiers() & Qt::ShiftModifier)
emit onShiftRight();
break;
case Qt::Key_Left:
if (kevent->modifiers() & Qt::ShiftModifier)
emit onShiftLeft();
break;
case Qt::Key_Delete:
case Qt::Key_Backspace:
emit onDelete();
break;
case Qt::Key_Minus:
if (kevent->modifiers().testFlag(Qt::AltModifier) && kevent->modifiers().testFlag(Qt::ControlModifier))
emit onCtrlAltMinus();
break;
case Qt::Key_Equal:
if (kevent->modifiers().testFlag(Qt::AltModifier) && kevent->modifiers().testFlag(Qt::ControlModifier))
emit onCtrlAltEqual();
break;
case Qt::Key_BracketLeft:
if (kevent->modifiers().testFlag(Qt::AltModifier) && kevent->modifiers().testFlag(Qt::ControlModifier))
emit onCtrlAltLBracket();
break;
case Qt::Key_BracketRight:
if (kevent->modifiers().testFlag(Qt::AltModifier) && kevent->modifiers().testFlag(Qt::ControlModifier))
emit onCtrlAltRBracket();
break;
case Qt::Key_S:
if (kevent->modifiers() & Qt::ShiftModifier)
emit onShiftS();
break;
case Qt::Key_C:
if (kevent->modifiers() & Qt::ControlModifier)
emit onCtrlC();
break;
default:
return false;
}
return false;
}

View file

@ -0,0 +1,25 @@
#include "../include/utility/levenshtein.h"
#include <algorithm>
#include <vector>
int levenshteinDistance(const QString &s1, const QString &s2)
{
int len1 = s1.size();
int len2 = s2.size();
std::vector<std::vector<int>> dp(len1 + 1, std::vector<int>(len2 + 1));
for (int i = 0; i <= len1; i++)
dp[i][0] = i;
for (int j = 0; j <= len2; j++)
dp[0][j] = j;
for (int i = 1; i <= len1; i++) {
for (int j = 1; j <= len2; j++) {
int cost = (s1[i - 1] == s2[j - 1]) ? 0 : 1;
dp[i][j] = std::min({dp[i - 1][j] + 1, dp[i][j - 1] + 1, dp[i - 1][j - 1] + cost});
}
}
return dp[len1][len2];
}

142
libs/utility/src/logger.cpp Normal file
View file

@ -0,0 +1,142 @@
#include "../include/utility/logger.h"
#include "version_string.h"
#include <QApplication>
#include <QDateTime>
#include <QLocale>
#include <QSysInfo>
#include <iostream>
#define LOGGER_MAX_ENTRIES 128
#define LOGGER_FILENAME "qdebug.txt"
Logger::Logger() : logToFileEnabled(false)
{
logBuffer.append(getClientVersion());
logBuffer.append(getSystemArchitecture());
logBuffer.append(getSystemLocale());
logBuffer.append(getClientInstallInfo());
logBuffer.append(QString("-").repeated(75));
std::cerr << getClientVersion().toStdString() << std::endl;
std::cerr << getSystemArchitecture().toStdString() << std::endl;
std::cerr << getSystemLocale().toStdString() << std::endl;
std::cerr << getClientInstallInfo().toStdString() << std::endl;
}
Logger::~Logger()
{
closeLogfileSession();
logBuffer.clear();
}
void Logger::logToFile(bool enabled)
{
if (enabled) {
openLogfileSession();
} else {
closeLogfileSession();
}
}
QString Logger::getClientVersion()
{
return "Client Version: " + QString::fromStdString(VERSION_STRING);
}
void Logger::openLogfileSession()
{
if (logToFileEnabled) {
return;
}
fileHandle.setFileName(LOGGER_FILENAME);
fileHandle.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text);
fileStream.setDevice(&fileHandle);
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
fileStream << "Log session started at " << QDateTime::currentDateTime().toString() << Qt::endl;
fileStream << getClientVersion() << Qt::endl;
fileStream << getSystemArchitecture() << Qt::endl;
fileStream << getClientInstallInfo() << Qt::endl;
#else
fileStream << "Log session started at " << QDateTime::currentDateTime().toString() << endl;
fileStream << getClientVersion() << endl;
fileStream << getSystemArchitecture() << endl;
fileStream << getClientInstallInfo() << endl;
#endif
logToFileEnabled = true;
}
void Logger::closeLogfileSession()
{
if (!logToFileEnabled)
return;
logToFileEnabled = false;
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
fileStream << "Log session closed at " << QDateTime::currentDateTime().toString() << Qt::endl;
#else
fileStream << "Log session closed at " << QDateTime::currentDateTime().toString() << endl;
#endif
fileHandle.close();
}
void Logger::log(QtMsgType /* type */, const QMessageLogContext & /* ctx */, const QString &message)
{
QMetaObject::invokeMethod(this, "internalLog", Qt::QueuedConnection, Q_ARG(const QString &, message));
}
void Logger::internalLog(const QString &message)
{
QMutexLocker locker(&mutex);
logBuffer.append(message);
if (logBuffer.size() > LOGGER_MAX_ENTRIES) {
logBuffer.removeAt(1);
}
emit logEntryAdded(message);
std::cerr << message.toStdString() << std::endl; // Print to stdout
if (logToFileEnabled) {
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
fileStream << message << Qt::endl; // Print to fileStream
#else
fileStream << message << endl; // Print to fileStream
#endif
}
}
QString Logger::getSystemArchitecture()
{
QString result;
if (!getClientOperatingSystem().isEmpty()) {
// We don't want translatable strings in the 'Debug Log' for easier troubleshooting
result.append(QString("Client Operating System: ") + getClientOperatingSystem() + "\n");
}
result.append(QString("Build Architecture: ") + QString::fromStdString(BUILD_ARCHITECTURE) + "\n");
result.append(QString("Qt Version: ") + QT_VERSION_STR);
return result;
}
QString Logger::getClientOperatingSystem()
{
return QSysInfo::prettyProductName();
}
QString Logger::getSystemLocale()
{
QString result(QString("System Locale: ") + QLocale().name());
return result;
}
QString Logger::getClientInstallInfo()
{
// don't rely on settingsCache->getIsPortableBuild() since the logger is initialized earlier
bool isPortable = QFile::exists(qApp->applicationDirPath() + "/portable.dat");
QString result(QString("Install Mode: ") + (isPortable ? "Portable" : "Standard"));
return result;
}

View file

@ -0,0 +1,39 @@
#include "../include/utility/passwordhasher.h"
#include "../../rng/include/rng/rng_sfmt.h"
#include <QCryptographicHash>
QString PasswordHasher::computeHash(const QString &password, const QString &salt)
{
QCryptographicHash::Algorithm algo = QCryptographicHash::Sha512;
const int rounds = 1000;
QByteArray hash = (salt + password).toUtf8();
for (int i = 0; i < rounds; ++i) {
hash = QCryptographicHash::hash(hash, algo);
}
QString hashedPass = salt + QString(hash.toBase64());
return hashedPass;
}
QString PasswordHasher::generateRandomSalt(const int len)
{
static const char alphanum[] = "0123456789"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz";
QString ret;
int size = sizeof(alphanum) - 1;
for (int i = 0; i < len; ++i) {
ret.append(alphanum[rng->rand(0, size)]);
}
return ret;
}
QString PasswordHasher::generateActivationToken()
{
return QCryptographicHash::hash(generateRandomSalt().toUtf8(), QCryptographicHash::Md5).toBase64().left(16);
}