mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-04-27 07:48:01 -07:00
Some checks failed
Build Desktop / Configure (push) Has been cancelled
Build Docker Image / amd64 & arm64 (push) Has been cancelled
Build Desktop / Debian 11 (push) Has been cancelled
Build Desktop / Debian 13 (push) Has been cancelled
Build Desktop / Debian 12 (push) Has been cancelled
Build Desktop / Fedora 43 (push) Has been cancelled
Build Desktop / Fedora 42 (push) Has been cancelled
Build Desktop / Servatrice_Debian 11 (push) Has been cancelled
Build Desktop / Ubuntu 24.04 (push) Has been cancelled
Build Desktop / Ubuntu 26.04 (push) Has been cancelled
Build Desktop / Ubuntu 22.04 (push) Has been cancelled
Build Desktop / Arch (push) Has been cancelled
Build Desktop / macOS 14 (push) Has been cancelled
Build Desktop / macOS 15 (push) Has been cancelled
Build Desktop / macOS 13 Intel (push) Has been cancelled
Build Desktop / macOS 15 Debug (push) Has been cancelled
Build Desktop / Windows 10 (push) Has been cancelled
* fix compiling on arch * redo all the logging in affected files
1462 lines
54 KiB
C++
1462 lines
54 KiB
C++
#include "servatrice_database_interface.h"
|
|
|
|
#include "servatrice.h"
|
|
#include "serversocketinterface.h"
|
|
#include "settingscache.h"
|
|
|
|
#include <QChar>
|
|
#include <QDateTime>
|
|
#include <QDebug>
|
|
#include <QLoggingCategory>
|
|
#include <QSqlError>
|
|
#include <QSqlQuery>
|
|
#include <libcockatrice/deck_list/deck_list.h>
|
|
#include <libcockatrice/protocol/pb/game_replay.pb.h>
|
|
#include <libcockatrice/utility/passwordhasher.h>
|
|
|
|
inline Q_LOGGING_CATEGORY(DatabaseInterfaceLog, "database_interface");
|
|
|
|
Servatrice_DatabaseInterface::Servatrice_DatabaseInterface(int _instanceId, Servatrice *_server)
|
|
: instanceId(_instanceId), sqlDatabase(QSqlDatabase()), server(_server)
|
|
{
|
|
}
|
|
|
|
Servatrice_DatabaseInterface::~Servatrice_DatabaseInterface()
|
|
{
|
|
// reset all prepared statements
|
|
qDeleteAll(preparedStatements);
|
|
preparedStatements.clear();
|
|
|
|
sqlDatabase.close();
|
|
}
|
|
|
|
void Servatrice_DatabaseInterface::initDatabase(const QSqlDatabase &_sqlDatabase)
|
|
{
|
|
if (_sqlDatabase.isValid()) {
|
|
sqlDatabase = QSqlDatabase::cloneDatabase(_sqlDatabase, "pool_" + QString::number(instanceId));
|
|
openDatabase();
|
|
}
|
|
}
|
|
|
|
bool Servatrice_DatabaseInterface::initDatabase(const QString &type,
|
|
const QString &hostName,
|
|
const QString &databaseName,
|
|
const QString &userName,
|
|
const QString &password)
|
|
{
|
|
sqlDatabase = QSqlDatabase::addDatabase(type, "main");
|
|
sqlDatabase.setHostName(hostName);
|
|
sqlDatabase.setDatabaseName(databaseName);
|
|
sqlDatabase.setUserName(userName);
|
|
sqlDatabase.setPassword(password);
|
|
|
|
return openDatabase();
|
|
}
|
|
|
|
bool Servatrice_DatabaseInterface::openDatabase()
|
|
{
|
|
if (sqlDatabase.isOpen())
|
|
sqlDatabase.close();
|
|
|
|
const QString poolStr = instanceId == -1 ? QString("main") : QString("pool %1").arg(instanceId);
|
|
qCDebug(DatabaseInterfaceLog).noquote() << poolStr << "Opening database...";
|
|
if (!sqlDatabase.open()) {
|
|
qCCritical(DatabaseInterfaceLog) << poolStr << "Error opening database:" << sqlDatabase.lastError().text();
|
|
return false;
|
|
}
|
|
|
|
QSqlQuery *versionQuery = prepareQuery("select version from {prefix}_schema_version limit 1");
|
|
if (!execSqlQuery(versionQuery)) {
|
|
qCCritical(DatabaseInterfaceLog) << poolStr << "Error opening database: unable to load database schema version"
|
|
<< "(hint: ensure the cockatrice_schema_version exists)";
|
|
return false;
|
|
}
|
|
|
|
if (versionQuery->next()) {
|
|
const int dbversion = versionQuery->value(0).toInt();
|
|
const int expectedversion = DATABASE_SCHEMA_VERSION;
|
|
if (dbversion < expectedversion) {
|
|
qCCritical(DatabaseInterfaceLog) << poolStr
|
|
<< "Error opening database: the database schema version is too old, you "
|
|
"need to run the migrations to update it from version"
|
|
<< dbversion << "to version" << expectedversion;
|
|
return false;
|
|
} else if (dbversion > expectedversion) {
|
|
qCCritical(DatabaseInterfaceLog) << poolStr << "Error opening database: the database schema version"
|
|
<< dbversion << "is too new, you need to update servatrice"
|
|
<< "(this servatrice actually uses version" << expectedversion << ")";
|
|
return false;
|
|
}
|
|
} else {
|
|
qCCritical(DatabaseInterfaceLog) << poolStr
|
|
<< "Error opening database: unable to load database schema version (hint: "
|
|
"ensure the cockatrice_schema_version contains a single record)";
|
|
return false;
|
|
}
|
|
|
|
// reset all prepared statements
|
|
qDeleteAll(preparedStatements);
|
|
preparedStatements.clear();
|
|
return true;
|
|
}
|
|
|
|
bool Servatrice_DatabaseInterface::checkSql()
|
|
{
|
|
if (!sqlDatabase.isValid()) {
|
|
return false;
|
|
}
|
|
|
|
auto query = QSqlQuery(sqlDatabase);
|
|
if (query.exec("select 1") && !query.isActive()) {
|
|
return openDatabase();
|
|
}
|
|
|
|
if (query.lastError().isValid()) {
|
|
const auto &poolStr = instanceId == -1 ? QString("main") : QString("pool %1").arg(instanceId);
|
|
qCCritical(DatabaseInterfaceLog) << poolStr << "Error executing query:" << query.lastError().text();
|
|
|
|
sqlDatabase.close();
|
|
return openDatabase();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
QSqlQuery *Servatrice_DatabaseInterface::prepareQuery(const QString &queryText)
|
|
{
|
|
if (preparedStatements.contains(queryText)) {
|
|
return preparedStatements.value(queryText);
|
|
}
|
|
|
|
QString prefixedQueryText = queryText;
|
|
prefixedQueryText.replace("{prefix}", server->getDbPrefix());
|
|
auto *query = new QSqlQuery(sqlDatabase);
|
|
query->prepare(prefixedQueryText);
|
|
|
|
preparedStatements.insert(queryText, query);
|
|
return query;
|
|
}
|
|
|
|
bool Servatrice_DatabaseInterface::execSqlQuery(QSqlQuery *query)
|
|
{
|
|
if (query->exec())
|
|
return true;
|
|
const QString poolStr = instanceId == -1 ? QString("main") : QString("pool %1").arg(instanceId);
|
|
qCCritical(DatabaseInterfaceLog) << poolStr << "Error executing query:" << query->lastError().text();
|
|
sqlDatabase.close();
|
|
openDatabase();
|
|
return false;
|
|
}
|
|
|
|
bool Servatrice_DatabaseInterface::usernameIsValid(const QString &user, QString &error)
|
|
{
|
|
int minNameLength = settingsCache->value("users/minnamelength", 6).toInt();
|
|
if (minNameLength < 1)
|
|
minNameLength = 1;
|
|
int maxNameLength = settingsCache->value("users/maxnamelength", 12).toInt();
|
|
bool allowLowercase = settingsCache->value("users/allowlowercase", true).toBool();
|
|
bool allowUppercase = settingsCache->value("users/allowuppercase", true).toBool();
|
|
bool allowNumerics = settingsCache->value("users/allownumerics", true).toBool();
|
|
bool allowPunctuationPrefix = settingsCache->value("users/allowpunctuationprefix", false).toBool();
|
|
QString allowedPunctuation = settingsCache->value("users/allowedpunctuation", "_").toString();
|
|
QString disallowedWordsStr = settingsCache->value("users/disallowedwords", "").toString();
|
|
QStringList disallowedWords = disallowedWordsStr.split(",", Qt::SkipEmptyParts);
|
|
disallowedWords.removeDuplicates();
|
|
QVariant displayDisallowedWords = settingsCache->value("users/displaydisallowedwords");
|
|
QString disallowedRegExpStr;
|
|
if (displayDisallowedWords.isValid()) {
|
|
disallowedWordsStr = displayDisallowedWords.toString().trimmed();
|
|
if (!disallowedWordsStr.isEmpty()) {
|
|
disallowedWordsStr.prepend("\n");
|
|
}
|
|
} else {
|
|
disallowedRegExpStr = settingsCache->value("users/disallowedregexp", "").toString();
|
|
}
|
|
|
|
error = QString("%1|%2|%3|%4|%5|%6|%7|%8|%9")
|
|
.arg(minNameLength)
|
|
.arg(maxNameLength)
|
|
.arg(allowLowercase)
|
|
.arg(allowUppercase)
|
|
.arg(allowNumerics)
|
|
.arg(allowPunctuationPrefix)
|
|
.arg(allowedPunctuation)
|
|
.arg(disallowedWordsStr)
|
|
.arg(disallowedRegExpStr);
|
|
|
|
if (user.length() < minNameLength || user.length() > maxNameLength)
|
|
return false;
|
|
|
|
if (!allowPunctuationPrefix && allowedPunctuation.contains(user.at(0)))
|
|
return false;
|
|
|
|
for (const QString &word : disallowedWords) {
|
|
if (user.contains(word, Qt::CaseInsensitive))
|
|
return false;
|
|
}
|
|
|
|
for (const QRegularExpression ®Exp : settingsCache->disallowedRegExp) {
|
|
if (regExp.match(user).hasMatch())
|
|
return false;
|
|
}
|
|
|
|
QString regEx("\\A[");
|
|
if (allowLowercase)
|
|
regEx.append("a-z");
|
|
if (allowUppercase)
|
|
regEx.append("A-Z");
|
|
if (allowNumerics)
|
|
regEx.append("0-9");
|
|
regEx.append(QRegularExpression::escape(allowedPunctuation));
|
|
regEx.append("]+\\z");
|
|
|
|
QRegularExpression re = QRegularExpression(regEx);
|
|
return re.match(user).hasMatch();
|
|
}
|
|
|
|
bool Servatrice_DatabaseInterface::registerUser(const QString &userName,
|
|
const QString &realName,
|
|
const QString &password,
|
|
bool passwordNeedsHash,
|
|
const QString &emailAddress,
|
|
const QString &country,
|
|
bool active)
|
|
{
|
|
if (!checkSql())
|
|
return false;
|
|
|
|
QString passwordSha512;
|
|
if (passwordNeedsHash) {
|
|
passwordSha512 = PasswordHasher::computeHash(password, PasswordHasher::generateRandomSalt());
|
|
} else {
|
|
passwordSha512 = password;
|
|
}
|
|
QString token = active ? QString() : PasswordHasher::generateActivationToken();
|
|
|
|
QSqlQuery *query =
|
|
prepareQuery("insert into {prefix}_users "
|
|
"(name, realname, password_sha512, email, country, registrationDate, active, token, "
|
|
"admin, avatar_bmp, clientid, privlevel, privlevelStartDate, privlevelEndDate) "
|
|
"values "
|
|
"(:userName, :realName, :password_sha512, :email, :country, UTC_TIMESTAMP(), :active, "
|
|
":token, 0, '', '', 'NONE', UTC_TIMESTAMP(), UTC_TIMESTAMP())");
|
|
query->bindValue(":userName", userName);
|
|
query->bindValue(":realName", realName);
|
|
query->bindValue(":password_sha512", passwordSha512);
|
|
query->bindValue(":email", emailAddress);
|
|
query->bindValue(":country", country);
|
|
query->bindValue(":active", active ? 1 : 0);
|
|
query->bindValue(":token", token);
|
|
|
|
if (!execSqlQuery(query)) {
|
|
qCWarning(DatabaseInterfaceLog) << "Failed to insert user: " << query->lastError()
|
|
<< " sql: " << query->lastQuery();
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool Servatrice_DatabaseInterface::activateUser(const QString &userName, const QString &token)
|
|
{
|
|
if (!checkSql())
|
|
return false;
|
|
|
|
QSqlQuery *activateQuery =
|
|
prepareQuery("select name from {prefix}_users where active=0 and name=:username and token=:token");
|
|
|
|
activateQuery->bindValue(":username", userName);
|
|
activateQuery->bindValue(":token", token);
|
|
if (!execSqlQuery(activateQuery)) {
|
|
qCWarning(DatabaseInterfaceLog) << "Account activation failed: SQL error." << activateQuery->lastError()
|
|
<< " sql: " << activateQuery->lastQuery();
|
|
return false;
|
|
}
|
|
|
|
if (activateQuery->next()) {
|
|
const QString name = activateQuery->value(0).toString();
|
|
// redundant check
|
|
if (name == userName) {
|
|
|
|
QSqlQuery *query = prepareQuery("update {prefix}_users set active=1 where name = :userName");
|
|
query->bindValue(":userName", userName);
|
|
|
|
if (!execSqlQuery(query)) {
|
|
qCWarning(DatabaseInterfaceLog)
|
|
<< "Failed to activate user: " << query->lastError() << " sql: " << query->lastQuery();
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
AuthenticationResult Servatrice_DatabaseInterface::checkUserPassword(Server_ProtocolHandler *handler,
|
|
const QString &user,
|
|
const QString &password,
|
|
const QString &clientId,
|
|
QString &reasonStr,
|
|
int &banSecondsLeft,
|
|
bool passwordNeedsHash)
|
|
{
|
|
switch (server->getAuthenticationMethod()) {
|
|
case Servatrice::AuthenticationNone:
|
|
return UnknownUser;
|
|
case Servatrice::AuthenticationPassword: {
|
|
QString configPassword = settingsCache->value("authentication/password").toString();
|
|
if (configPassword == password)
|
|
return PasswordRight;
|
|
|
|
return NotLoggedIn;
|
|
}
|
|
case Servatrice::AuthenticationSql: {
|
|
if (!checkSql())
|
|
return UnknownUser;
|
|
|
|
if (!usernameIsValid(user, reasonStr))
|
|
return UsernameInvalid;
|
|
|
|
if (checkUserIsBanned(handler->getAddress(), user, clientId, reasonStr, banSecondsLeft))
|
|
return UserIsBanned;
|
|
|
|
QSqlQuery *passwordQuery =
|
|
prepareQuery("select password_sha512, active from {prefix}_users where name = :name");
|
|
passwordQuery->bindValue(":name", user);
|
|
if (!execSqlQuery(passwordQuery)) {
|
|
qCWarning(DatabaseInterfaceLog) << "Login denied: SQL error";
|
|
return NotLoggedIn;
|
|
}
|
|
|
|
if (passwordQuery->next()) {
|
|
const QString correctPasswordSha512 = passwordQuery->value(0).toString();
|
|
const bool userIsActive = passwordQuery->value(1).toBool();
|
|
if (!userIsActive) {
|
|
qCWarning(DatabaseInterfaceLog) << "Login denied: user not active";
|
|
return UserIsInactive;
|
|
}
|
|
QString hashedPassword;
|
|
if (passwordNeedsHash) {
|
|
hashedPassword = PasswordHasher::computeHash(password, correctPasswordSha512.left(16));
|
|
} else {
|
|
hashedPassword = password;
|
|
}
|
|
if (correctPasswordSha512 == hashedPassword) {
|
|
qCDebug(DatabaseInterfaceLog) << "Login accepted: password right";
|
|
return PasswordRight;
|
|
} else {
|
|
qCDebug(DatabaseInterfaceLog) << "Login denied: password wrong";
|
|
return NotLoggedIn;
|
|
}
|
|
} else {
|
|
qCDebug(DatabaseInterfaceLog) << "Login accepted: unknown user";
|
|
return UnknownUser;
|
|
}
|
|
}
|
|
}
|
|
return UnknownUser;
|
|
}
|
|
|
|
bool Servatrice_DatabaseInterface::checkUserIsBanned(const QString &ipAddress,
|
|
const QString &userName,
|
|
const QString &clientId,
|
|
QString &banReason,
|
|
int &banSecondsRemaining)
|
|
{
|
|
if (server->getAuthenticationMethod() != Servatrice::AuthenticationSql)
|
|
return false;
|
|
|
|
if (!checkSql()) {
|
|
qCWarning(DatabaseInterfaceLog) << "Failed to check if user is banned. Database invalid.";
|
|
return false;
|
|
}
|
|
|
|
return checkUserIsIpBanned(ipAddress, banReason, banSecondsRemaining) ||
|
|
checkUserIsNameBanned(userName, banReason, banSecondsRemaining) ||
|
|
checkUserIsIdBanned(clientId, banReason, banSecondsRemaining);
|
|
}
|
|
|
|
bool Servatrice_DatabaseInterface::checkUserIsIdBanned(const QString &clientId,
|
|
QString &banReason,
|
|
int &banSecondsRemaining)
|
|
{
|
|
if (clientId.isEmpty())
|
|
return false;
|
|
|
|
QSqlQuery *idBanQuery =
|
|
prepareQuery("select"
|
|
" timestampdiff(second, now(), date_add(b.time_from, interval b.minutes minute)),"
|
|
" b.minutes <=> 0,"
|
|
" b.visible_reason"
|
|
" from {prefix}_bans b"
|
|
" where"
|
|
" b.time_from = (select max(c.time_from)"
|
|
" from {prefix}_bans c"
|
|
" where c.clientid = :id)"
|
|
" and b.clientid = :id2");
|
|
|
|
idBanQuery->bindValue(":id", clientId);
|
|
idBanQuery->bindValue(":id2", clientId);
|
|
if (!execSqlQuery(idBanQuery)) {
|
|
qCWarning(DatabaseInterfaceLog) << "Id ban check failed: SQL error." << idBanQuery->lastError();
|
|
return false;
|
|
}
|
|
|
|
if (idBanQuery->next()) {
|
|
const int secondsLeft = idBanQuery->value(0).toInt();
|
|
const bool permanentBan = idBanQuery->value(1).toInt();
|
|
if ((secondsLeft > 0) || permanentBan) {
|
|
banReason = idBanQuery->value(2).toString();
|
|
banSecondsRemaining = permanentBan ? 0 : secondsLeft;
|
|
qCDebug(DatabaseInterfaceLog) << "User is banned by client id" << clientId;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Servatrice_DatabaseInterface::checkUserIsNameBanned(const QString &userName,
|
|
QString &banReason,
|
|
int &banSecondsRemaining)
|
|
{
|
|
QSqlQuery *nameBanQuery =
|
|
prepareQuery("select timestampdiff(second, now(), date_add(b.time_from, interval b.minutes minute)), b.minutes "
|
|
"<=> 0, b.visible_reason from {prefix}_bans b where b.time_from = (select max(c.time_from) from "
|
|
"{prefix}_bans c where c.user_name = :name2) and b.user_name = :name1");
|
|
nameBanQuery->bindValue(":name1", userName);
|
|
nameBanQuery->bindValue(":name2", userName);
|
|
if (!execSqlQuery(nameBanQuery)) {
|
|
qCWarning(DatabaseInterfaceLog) << "Name ban check failed: SQL error" << nameBanQuery->lastError();
|
|
return false;
|
|
}
|
|
|
|
if (nameBanQuery->next()) {
|
|
const int secondsLeft = nameBanQuery->value(0).toInt();
|
|
const bool permanentBan = nameBanQuery->value(1).toInt();
|
|
if ((secondsLeft > 0) || permanentBan) {
|
|
banReason = nameBanQuery->value(2).toString();
|
|
banSecondsRemaining = permanentBan ? 0 : secondsLeft;
|
|
qCDebug(DatabaseInterfaceLog) << "Username" << userName << "is banned by name";
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Servatrice_DatabaseInterface::checkUserIsIpBanned(const QString &ipAddress,
|
|
QString &banReason,
|
|
int &banSecondsRemaining)
|
|
{
|
|
QSqlQuery *ipBanQuery =
|
|
prepareQuery("select"
|
|
" timestampdiff(second, now(), date_add(b.time_from, interval b.minutes minute)),"
|
|
" b.minutes <=> 0,"
|
|
" b.visible_reason"
|
|
" from {prefix}_bans b"
|
|
" where"
|
|
" b.time_from = (select max(c.time_from)"
|
|
" from {prefix}_bans c"
|
|
" where c.ip_address = :address)"
|
|
" and b.ip_address = :address2");
|
|
|
|
ipBanQuery->bindValue(":address", ipAddress);
|
|
ipBanQuery->bindValue(":address2", ipAddress);
|
|
if (!execSqlQuery(ipBanQuery)) {
|
|
qCWarning(DatabaseInterfaceLog) << "IP ban check failed: SQL error." << ipBanQuery->lastError();
|
|
return false;
|
|
}
|
|
|
|
if (ipBanQuery->next()) {
|
|
const int secondsLeft = ipBanQuery->value(0).toInt();
|
|
const bool permanentBan = ipBanQuery->value(1).toInt();
|
|
if ((secondsLeft > 0) || permanentBan) {
|
|
banReason = ipBanQuery->value(2).toString();
|
|
banSecondsRemaining = permanentBan ? 0 : secondsLeft;
|
|
qCDebug(DatabaseInterfaceLog) << "User is banned by address" << ipAddress;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Servatrice_DatabaseInterface::activeUserExists(const QString &user)
|
|
{
|
|
if (server->getAuthenticationMethod() == Servatrice::AuthenticationSql) {
|
|
checkSql();
|
|
|
|
QSqlQuery *query = prepareQuery("select 1 from {prefix}_users where name = :name and active = 1");
|
|
query->bindValue(":name", user);
|
|
if (!execSqlQuery(query))
|
|
return false;
|
|
return query->next();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Servatrice_DatabaseInterface::userExists(const QString &user)
|
|
{
|
|
if (server->getAuthenticationMethod() == Servatrice::AuthenticationSql) {
|
|
checkSql();
|
|
|
|
QSqlQuery *query = prepareQuery("select 1 from {prefix}_users where name = :name");
|
|
query->bindValue(":name", user);
|
|
if (!execSqlQuery(query))
|
|
return false;
|
|
return query->next();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
QString Servatrice_DatabaseInterface::getUserSalt(const QString &user)
|
|
{
|
|
if (server->getAuthenticationMethod() == Servatrice::AuthenticationSql) {
|
|
checkSql();
|
|
|
|
QSqlQuery *query =
|
|
prepareQuery("SELECT SUBSTRING(password_sha512, 1, 16) FROM {prefix}_users WHERE name = :name");
|
|
|
|
query->bindValue(":name", user);
|
|
if (!execSqlQuery(query)) {
|
|
return {};
|
|
}
|
|
|
|
if (!query->next()) {
|
|
return {};
|
|
}
|
|
|
|
return query->value(0).toString();
|
|
}
|
|
return {};
|
|
}
|
|
|
|
int Servatrice_DatabaseInterface::getUserIdInDB(const QString &name)
|
|
{
|
|
if (server->getAuthenticationMethod() == Servatrice::AuthenticationSql) {
|
|
QSqlQuery *query = prepareQuery("select id from {prefix}_users where name = :name and active = 1");
|
|
query->bindValue(":name", name);
|
|
if (!execSqlQuery(query))
|
|
return -1;
|
|
if (!query->next())
|
|
return -1;
|
|
return query->value(0).toInt();
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
bool Servatrice_DatabaseInterface::isInBuddyList(const QString &whoseList, const QString &who)
|
|
{
|
|
if (server->getAuthenticationMethod() == Servatrice::AuthenticationNone)
|
|
return false;
|
|
|
|
if (!checkSql())
|
|
return false;
|
|
|
|
int id1 = getUserIdInDB(whoseList);
|
|
int id2 = getUserIdInDB(who);
|
|
|
|
QSqlQuery *query =
|
|
prepareQuery("select 1 from {prefix}_buddylist where id_user1 = :id_user1 and id_user2 = :id_user2");
|
|
query->bindValue(":id_user1", id1);
|
|
query->bindValue(":id_user2", id2);
|
|
if (!execSqlQuery(query))
|
|
return false;
|
|
return query->next();
|
|
}
|
|
|
|
bool Servatrice_DatabaseInterface::isInIgnoreList(const QString &whoseList, const QString &who)
|
|
{
|
|
if (server->getAuthenticationMethod() == Servatrice::AuthenticationNone)
|
|
return false;
|
|
|
|
if (!checkSql())
|
|
return false;
|
|
|
|
int id1 = getUserIdInDB(whoseList);
|
|
int id2 = getUserIdInDB(who);
|
|
|
|
QSqlQuery *query =
|
|
prepareQuery("select 1 from {prefix}_ignorelist where id_user1 = :id_user1 and id_user2 = :id_user2");
|
|
query->bindValue(":id_user1", id1);
|
|
query->bindValue(":id_user2", id2);
|
|
if (!execSqlQuery(query))
|
|
return false;
|
|
return query->next();
|
|
}
|
|
|
|
ServerInfo_User Servatrice_DatabaseInterface::evalUserQueryResult(const QSqlQuery *query, bool complete, bool withId)
|
|
{
|
|
ServerInfo_User result;
|
|
|
|
if (withId)
|
|
result.set_id(query->value(0).toInt());
|
|
result.set_name(query->value(1).toString().toStdString());
|
|
|
|
const int is_admin = query->value(2).toInt();
|
|
int userLevel = ServerInfo_User::IsUser | ServerInfo_User::IsRegistered;
|
|
if (is_admin & 1)
|
|
userLevel |= ServerInfo_User::IsAdmin | ServerInfo_User::IsModerator;
|
|
else if (is_admin & 2)
|
|
userLevel |= ServerInfo_User::IsModerator;
|
|
|
|
if (is_admin & 4)
|
|
userLevel |= ServerInfo_User::IsJudge;
|
|
|
|
result.set_user_level(userLevel);
|
|
|
|
const QString country = query->value(3).toString();
|
|
if (!country.isEmpty())
|
|
result.set_country(country.toStdString());
|
|
|
|
const QString privlevel = query->value(4).toString();
|
|
if (!privlevel.isEmpty())
|
|
result.set_privlevel(privlevel.toStdString());
|
|
|
|
const auto &pawn_left_override = query->value(5).toString();
|
|
const auto &pawn_right_override = query->value(6).toString();
|
|
if (!pawn_left_override.isEmpty()) {
|
|
result.mutable_pawn_colors()->set_left_side(pawn_left_override.toStdString());
|
|
}
|
|
if (!pawn_right_override.isEmpty()) {
|
|
result.mutable_pawn_colors()->set_right_side(pawn_right_override.toStdString());
|
|
}
|
|
|
|
if (complete) {
|
|
const QString realName = query->value(7).toString();
|
|
if (!realName.isEmpty())
|
|
result.set_real_name(realName.toStdString());
|
|
|
|
const QByteArray avatarBmp = query->value(8).toByteArray();
|
|
if (avatarBmp.size())
|
|
result.set_avatar_bmp(avatarBmp.data(), avatarBmp.size());
|
|
|
|
const QDateTime regDate = query->value(9).toDateTime();
|
|
if (!regDate.toString(Qt::ISODate).isEmpty()) {
|
|
// the registration date is in utc
|
|
qint64 accountAgeInSeconds = regDate.secsTo(QDateTime::currentDateTimeUtc());
|
|
result.set_accountage_secs(accountAgeInSeconds);
|
|
}
|
|
|
|
const QString email = query->value(10).toString();
|
|
if (!email.isEmpty())
|
|
result.set_email(email.toStdString());
|
|
|
|
const QString clientid = query->value(11).toString();
|
|
if (!clientid.isEmpty())
|
|
result.set_clientid(clientid.toStdString());
|
|
}
|
|
return result;
|
|
}
|
|
|
|
ServerInfo_User Servatrice_DatabaseInterface::getUserData(const QString &name, bool withId)
|
|
{
|
|
ServerInfo_User result;
|
|
result.set_name(name.toStdString());
|
|
result.set_user_level(ServerInfo_User::IsUser);
|
|
|
|
if (server->getAuthenticationMethod() == Servatrice::AuthenticationSql) {
|
|
if (!checkSql())
|
|
return result;
|
|
|
|
QSqlQuery *query = prepareQuery("select id, name, admin, country, privlevel, leftPawnColorOverride, "
|
|
"rightPawnColorOverride, realname, avatar_bmp, registrationDate, "
|
|
"email, clientid from {prefix}_users where "
|
|
"name = :name and active = 1");
|
|
query->bindValue(":name", name);
|
|
if (!execSqlQuery(query))
|
|
return result;
|
|
|
|
if (query->next())
|
|
return evalUserQueryResult(query, true, withId);
|
|
else
|
|
return result;
|
|
} else
|
|
return result;
|
|
}
|
|
|
|
void Servatrice_DatabaseInterface::clearSessionTables()
|
|
{
|
|
lockSessionTables();
|
|
QSqlQuery *query =
|
|
prepareQuery("update {prefix}_sessions set end_time=now() where end_time is null and id_server = :id_server");
|
|
query->bindValue(":id_server", server->getServerID());
|
|
execSqlQuery(query);
|
|
unlockSessionTables();
|
|
}
|
|
|
|
void Servatrice_DatabaseInterface::lockSessionTables()
|
|
{
|
|
QSqlQuery *query = prepareQuery("lock tables {prefix}_sessions write, {prefix}_users read");
|
|
execSqlQuery(query);
|
|
}
|
|
|
|
void Servatrice_DatabaseInterface::unlockSessionTables()
|
|
{
|
|
QSqlQuery *query = prepareQuery("unlock tables");
|
|
execSqlQuery(query);
|
|
}
|
|
|
|
bool Servatrice_DatabaseInterface::userSessionExists(const QString &userName)
|
|
{
|
|
// Call only after lockSessionTables().
|
|
|
|
QSqlQuery *query = prepareQuery(
|
|
"select 1 from {prefix}_sessions where user_name = :user_name and id_server = :id_server and end_time is null");
|
|
query->bindValue(":id_server", server->getServerID());
|
|
query->bindValue(":user_name", userName);
|
|
if (!execSqlQuery(query)) {
|
|
return false;
|
|
};
|
|
return query->next();
|
|
}
|
|
|
|
qint64 Servatrice_DatabaseInterface::startSession(const QString &userName,
|
|
const QString &address,
|
|
const QString &clientId,
|
|
const QString &connectionType)
|
|
{
|
|
if (server->getAuthenticationMethod() == Servatrice::AuthenticationNone)
|
|
return -1;
|
|
|
|
if (!checkSql())
|
|
return -1;
|
|
|
|
QSqlQuery *query = prepareQuery("insert into {prefix}_sessions (user_name, id_server, ip_address, start_time, "
|
|
"clientid, connection_type) values(:user_name, :id_server, :ip_address, NOW(), "
|
|
":client_id, :connection_type)");
|
|
query->bindValue(":user_name", userName);
|
|
query->bindValue(":id_server", server->getServerID());
|
|
query->bindValue(":ip_address", address);
|
|
query->bindValue(":client_id", clientId);
|
|
query->bindValue(":connection_type", connectionType);
|
|
if (execSqlQuery(query))
|
|
return query->lastInsertId().toInt();
|
|
return -1;
|
|
}
|
|
|
|
void Servatrice_DatabaseInterface::endSession(qint64 sessionId)
|
|
{
|
|
if (server->getAuthenticationMethod() == Servatrice::AuthenticationNone)
|
|
return;
|
|
|
|
if (!checkSql())
|
|
return;
|
|
|
|
auto *query = prepareQuery("update {prefix}_sessions set end_time=NOW() where id = :id_session");
|
|
query->bindValue(":id_session", sessionId);
|
|
execSqlQuery(query);
|
|
}
|
|
|
|
QMap<QString, ServerInfo_User> Servatrice_DatabaseInterface::getBuddyList(const QString &name)
|
|
{
|
|
QMap<QString, ServerInfo_User> result;
|
|
|
|
if (server->getAuthenticationMethod() == Servatrice::AuthenticationSql) {
|
|
checkSql();
|
|
|
|
QSqlQuery *query = prepareQuery("select a.id, a.name, a.admin, a.country, a.privlevel, "
|
|
"a.leftPawnColorOverride, a.rightPawnColorOverride from {prefix}_users a "
|
|
"left join {prefix}_buddylist b on a.id = b.id_user2 left join {prefix}_users "
|
|
"c on b.id_user1 = c.id where c.name = :name");
|
|
query->bindValue(":name", name);
|
|
if (!execSqlQuery(query))
|
|
return result;
|
|
|
|
while (query->next()) {
|
|
const ServerInfo_User &temp = evalUserQueryResult(query, false);
|
|
result.insert(QString::fromStdString(temp.name()), temp);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
QMap<QString, ServerInfo_User> Servatrice_DatabaseInterface::getIgnoreList(const QString &name)
|
|
{
|
|
QMap<QString, ServerInfo_User> result;
|
|
|
|
if (server->getAuthenticationMethod() == Servatrice::AuthenticationSql) {
|
|
checkSql();
|
|
|
|
QSqlQuery *query = prepareQuery("select a.id, a.name, a.admin, a.country, a.privlevel, "
|
|
"a.leftPawnColorOverride, a.rightPawnColorOverride from {prefix}_users a "
|
|
"left join {prefix}_ignorelist b on a.id = b.id_user2 left join {prefix}_users "
|
|
"c on b.id_user1 = c.id where c.name = :name");
|
|
query->bindValue(":name", name);
|
|
if (!execSqlQuery(query))
|
|
return result;
|
|
|
|
while (query->next()) {
|
|
ServerInfo_User temp = evalUserQueryResult(query, false);
|
|
result.insert(QString::fromStdString(temp.name()), temp);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
int Servatrice_DatabaseInterface::getNextGameId()
|
|
{
|
|
if (!sqlDatabase.isValid())
|
|
return server->getNextLocalGameId();
|
|
|
|
if (!checkSql())
|
|
return -1;
|
|
|
|
QSqlQuery *query = prepareQuery("insert into {prefix}_games (time_started) values (now())");
|
|
|
|
if (!execSqlQuery(query)) {
|
|
return -1;
|
|
}
|
|
|
|
return query->lastInsertId().toInt();
|
|
}
|
|
|
|
int Servatrice_DatabaseInterface::getNextReplayId()
|
|
{
|
|
if (!checkSql())
|
|
return -1;
|
|
|
|
QSqlQuery *query = prepareQuery("insert into {prefix}_replays (id_game) values (NULL)");
|
|
|
|
if (!execSqlQuery(query)) {
|
|
return -1;
|
|
}
|
|
|
|
return query->lastInsertId().toInt();
|
|
}
|
|
|
|
void Servatrice_DatabaseInterface::storeGameInformation(const QString &roomName,
|
|
const QStringList &roomGameTypes,
|
|
const ServerInfo_Game &gameInfo,
|
|
const QSet<QString> &allPlayersEver,
|
|
const QSet<QString> &allSpectatorsEver,
|
|
const QList<GameReplay *> &replayList)
|
|
{
|
|
if (!checkSql())
|
|
return;
|
|
|
|
if (!settingsCache->value("game/store_replays", 1).toBool())
|
|
return;
|
|
|
|
QVariantList gameIds1, playerNames, gameIds2, userIds, replayNames;
|
|
QSetIterator<QString> playerIterator(allPlayersEver);
|
|
while (playerIterator.hasNext()) {
|
|
gameIds1.append(gameInfo.game_id());
|
|
const QString &playerName = playerIterator.next();
|
|
playerNames.append(playerName);
|
|
}
|
|
QSet<QString> allUsersInGame = allPlayersEver + allSpectatorsEver;
|
|
QSetIterator<QString> allUsersIterator(allUsersInGame);
|
|
while (allUsersIterator.hasNext()) {
|
|
int id = getUserIdInDB(allUsersIterator.next());
|
|
if (id == -1)
|
|
continue;
|
|
gameIds2.append(gameInfo.game_id());
|
|
userIds.append(id);
|
|
replayNames.append(QString::fromStdString(gameInfo.description()));
|
|
}
|
|
|
|
QVariantList replayIds, replayGameIds, replayDurations, replayBlobs;
|
|
for (int i = 0; i < replayList.size(); ++i) {
|
|
QByteArray blob;
|
|
#if GOOGLE_PROTOBUF_VERSION > 3001000
|
|
const unsigned int size = static_cast<unsigned int>(replayList[i]->ByteSizeLong());
|
|
#else
|
|
const unsigned int size = static_cast<unsigned int>(replayList[i]->ByteSize());
|
|
#endif
|
|
blob.resize(size);
|
|
qulonglong replayId = replayList[i]->replay_id();
|
|
if (replayList[i]->SerializeToArray(blob.data(), size)) {
|
|
|
|
replayIds.append(QVariant(replayId));
|
|
replayGameIds.append(gameInfo.game_id());
|
|
replayDurations.append(replayList[i]->duration_seconds());
|
|
replayBlobs.append(blob);
|
|
} else {
|
|
qCWarning(DatabaseInterfaceLog)
|
|
<< "failed to serialise replay, id:" << replayId << "game:" << gameInfo.game_id();
|
|
}
|
|
}
|
|
|
|
{
|
|
QSqlQuery *query = prepareQuery("update {prefix}_games set room_name=:room_name, descr=:descr, "
|
|
"creator_name=:creator_name, password=:password, game_types=:game_types, "
|
|
"player_count=:player_count, time_finished=now() where id=:id_game");
|
|
query->bindValue(":room_name", roomName);
|
|
query->bindValue(":id_game", gameInfo.game_id());
|
|
query->bindValue(":descr", QString::fromStdString(gameInfo.description()));
|
|
query->bindValue(":creator_name", QString::fromStdString(gameInfo.creator_info().name()));
|
|
query->bindValue(":password", gameInfo.with_password() ? 1 : 0);
|
|
query->bindValue(":game_types", roomGameTypes.isEmpty() ? QString("") : roomGameTypes.join(", "));
|
|
query->bindValue(":player_count", gameInfo.max_players());
|
|
if (!execSqlQuery(query))
|
|
return;
|
|
}
|
|
{
|
|
QSqlQuery *query =
|
|
prepareQuery("insert into {prefix}_games_players (id_game, player_name) values (:id_game, :player_name)");
|
|
query->bindValue(":id_game", gameIds1);
|
|
query->bindValue(":player_name", playerNames);
|
|
query->execBatch();
|
|
}
|
|
{
|
|
QSqlQuery *query = prepareQuery(
|
|
"update {prefix}_replays set id_game=:id_game, duration=:duration, replay=:replay where id=:id_replay");
|
|
query->bindValue(":id_replay", replayIds);
|
|
query->bindValue(":id_game", replayGameIds);
|
|
query->bindValue(":duration", replayDurations);
|
|
query->bindValue(":replay", replayBlobs);
|
|
query->execBatch();
|
|
}
|
|
{
|
|
QSqlQuery *query = prepareQuery("insert into {prefix}_replays_access (id_game, id_player, replay_name) values "
|
|
"(:id_game, :id_player, :replay_name)");
|
|
query->bindValue(":id_game", gameIds2);
|
|
query->bindValue(":id_player", userIds);
|
|
query->bindValue(":replay_name", replayNames);
|
|
query->execBatch();
|
|
}
|
|
}
|
|
|
|
DeckList *Servatrice_DatabaseInterface::getDeckFromDatabase(int deckId, int userId)
|
|
{
|
|
checkSql();
|
|
|
|
QSqlQuery *query =
|
|
prepareQuery("select content from {prefix}_decklist_files where id = :id and id_user = :id_user");
|
|
query->bindValue(":id", deckId);
|
|
query->bindValue(":id_user", userId);
|
|
execSqlQuery(query);
|
|
if (!query->next())
|
|
throw Response::RespNameNotFound;
|
|
|
|
DeckList *deck = new DeckList;
|
|
deck->loadFromString_Native(query->value(0).toString());
|
|
|
|
return deck;
|
|
}
|
|
|
|
void Servatrice_DatabaseInterface::logMessage(const int senderId,
|
|
const QString &senderName,
|
|
const QString &senderIp,
|
|
const QString &logMessage,
|
|
LogMessage_TargetType targetType,
|
|
const int targetId,
|
|
const QString &targetName)
|
|
{
|
|
QString targetTypeString;
|
|
switch (targetType) {
|
|
case MessageTargetRoom:
|
|
if (!settingsCache->value("logging/log_user_msg_room", 0).toBool())
|
|
return;
|
|
targetTypeString = "room";
|
|
break;
|
|
case MessageTargetGame:
|
|
if (!settingsCache->value("logging/log_user_msg_game", 0).toBool())
|
|
return;
|
|
targetTypeString = "game";
|
|
break;
|
|
case MessageTargetChat:
|
|
if (!settingsCache->value("logging/log_user_msg_chat", 0).toBool())
|
|
return;
|
|
targetTypeString = "chat";
|
|
break;
|
|
case MessageTargetIslRoom:
|
|
if (!settingsCache->value("logging/log_user_msg_isl", 0).toBool())
|
|
return;
|
|
targetTypeString = "room";
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
QSqlQuery *query = prepareQuery("insert into {prefix}_log (log_time, sender_id, sender_name, sender_ip, "
|
|
"log_message, target_type, target_id, target_name) values (now(), :sender_id, "
|
|
":sender_name, :sender_ip, :log_message, :target_type, :target_id, :target_name)");
|
|
query->bindValue(":sender_id", senderId < 1 ? QVariant() : senderId);
|
|
query->bindValue(":sender_name", senderName);
|
|
query->bindValue(":sender_ip", senderIp);
|
|
query->bindValue(":log_message", logMessage);
|
|
query->bindValue(":target_type", targetTypeString);
|
|
query->bindValue(":target_id", (targetType == MessageTargetChat && targetId < 1) ? QVariant() : targetId);
|
|
query->bindValue(":target_name", targetName);
|
|
execSqlQuery(query);
|
|
}
|
|
|
|
bool Servatrice_DatabaseInterface::changeUserPassword(const QString &user,
|
|
const QString &password,
|
|
bool passwordNeedsHash)
|
|
{
|
|
QString passwordSha512 = password;
|
|
if (passwordNeedsHash) {
|
|
passwordSha512 = PasswordHasher::computeHash(password, PasswordHasher::generateRandomSalt());
|
|
}
|
|
|
|
QSqlQuery *passwordQuery = prepareQuery("update {prefix}_users set password_sha512=:password, "
|
|
"passwordLastChangedDate = NOW() where name = :name");
|
|
passwordQuery->bindValue(":password", passwordSha512);
|
|
passwordQuery->bindValue(":name", user);
|
|
if (execSqlQuery(passwordQuery))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool Servatrice_DatabaseInterface::changeUserPassword(const QString &user,
|
|
const QString &oldPassword,
|
|
bool oldPasswordNeedsHash,
|
|
const QString &newPassword,
|
|
bool newPasswordNeedsHash)
|
|
{
|
|
if (server->getAuthenticationMethod() != Servatrice::AuthenticationSql)
|
|
return false;
|
|
|
|
if (!checkSql())
|
|
return false;
|
|
|
|
QString error;
|
|
if (!usernameIsValid(user, error))
|
|
return false;
|
|
|
|
QSqlQuery *passwordQuery = prepareQuery("select password_sha512 from {prefix}_users where name = :name");
|
|
passwordQuery->bindValue(":name", user);
|
|
|
|
if (!execSqlQuery(passwordQuery)) {
|
|
qCWarning(DatabaseInterfaceLog) << "Change password denied: SQL error";
|
|
return false;
|
|
}
|
|
|
|
if (!passwordQuery->next())
|
|
return false;
|
|
|
|
const QString correctPasswordSha512 = passwordQuery->value(0).toString();
|
|
QString oldPasswordSha512 = oldPassword;
|
|
if (oldPasswordNeedsHash) {
|
|
QString salt = correctPasswordSha512.left(16);
|
|
oldPasswordSha512 = PasswordHasher::computeHash(oldPassword, salt);
|
|
}
|
|
if (correctPasswordSha512 != oldPasswordSha512)
|
|
return false;
|
|
|
|
return changeUserPassword(user, newPassword, newPasswordNeedsHash);
|
|
}
|
|
|
|
int Servatrice_DatabaseInterface::getActiveUserCount(QString connectionType)
|
|
{
|
|
int userCount = 0;
|
|
|
|
if (!checkSql())
|
|
return userCount;
|
|
|
|
QString text = "select count(*) from {prefix}_sessions where id_server = :serverid AND end_time is NULL";
|
|
if (!connectionType.isEmpty())
|
|
text += " AND connection_type = :connection_type";
|
|
QSqlQuery *query = prepareQuery(text);
|
|
|
|
query->bindValue(":serverid", server->getServerID());
|
|
if (!connectionType.isEmpty())
|
|
query->bindValue(":connection_type", connectionType);
|
|
|
|
if (!execSqlQuery(query))
|
|
return userCount;
|
|
|
|
if (query->next())
|
|
userCount = query->value(0).toInt();
|
|
|
|
return userCount;
|
|
}
|
|
|
|
void Servatrice_DatabaseInterface::updateUsersClientID(const QString &userName, const QString &userClientID)
|
|
{
|
|
|
|
if (!checkSql())
|
|
return;
|
|
|
|
QSqlQuery *query = prepareQuery("update {prefix}_users set clientid = :clientid where name = :username");
|
|
query->bindValue(":clientid", userClientID);
|
|
query->bindValue(":username", userName);
|
|
execSqlQuery(query);
|
|
}
|
|
|
|
void Servatrice_DatabaseInterface::updateUsersLastLoginData(const QString &userName, const QString &clientVersion)
|
|
{
|
|
|
|
if (!checkSql())
|
|
return;
|
|
|
|
int usersID = 0;
|
|
|
|
QSqlQuery *query = prepareQuery("select id from {prefix}_users where name = :user_name");
|
|
query->bindValue(":user_name", userName);
|
|
if (!execSqlQuery(query)) {
|
|
qCWarning(DatabaseInterfaceLog) << "Failed to locate user id when updating users last login data: SQL Error";
|
|
return;
|
|
}
|
|
|
|
if (query->next()) {
|
|
usersID = query->value(0).toInt();
|
|
}
|
|
|
|
if (usersID) {
|
|
int userCount = 0;
|
|
query = prepareQuery("select count(id) from {prefix}_user_analytics where id = :user_id");
|
|
query->bindValue(":user_id", usersID);
|
|
if (!execSqlQuery(query))
|
|
return;
|
|
|
|
if (query->next()) {
|
|
userCount = query->value(0).toInt();
|
|
}
|
|
|
|
if (!userCount) {
|
|
query = prepareQuery(
|
|
"insert into {prefix}_user_analytics (id,client_ver,last_login) values (:user_id,:client_ver,NOW())");
|
|
query->bindValue(":user_id", usersID);
|
|
query->bindValue(":client_ver", clientVersion);
|
|
execSqlQuery(query);
|
|
} else {
|
|
query = prepareQuery(
|
|
"update {prefix}_user_analytics set last_login = NOW(), client_ver = :client_ver where id = :user_id");
|
|
query->bindValue(":client_ver", clientVersion);
|
|
query->bindValue(":user_id", usersID);
|
|
execSqlQuery(query);
|
|
}
|
|
}
|
|
}
|
|
|
|
QList<ServerInfo_Ban> Servatrice_DatabaseInterface::getUserBanHistory(const QString userName)
|
|
{
|
|
QList<ServerInfo_Ban> results;
|
|
ServerInfo_Ban banDetails;
|
|
|
|
if (!checkSql())
|
|
return results;
|
|
|
|
QSqlQuery *query =
|
|
prepareQuery("SELECT A.id_admin, A.time_from, A.minutes, A.reason, A.visible_reason, B.name AS name_admin FROM "
|
|
"{prefix}_bans A LEFT JOIN {prefix}_users B ON A.id_admin=B.id WHERE A.user_name = :user_name");
|
|
query->bindValue(":user_name", userName);
|
|
|
|
if (!execSqlQuery(query)) {
|
|
qCWarning(DatabaseInterfaceLog) << "Failed to collect ban history information: SQL Error";
|
|
return results;
|
|
}
|
|
|
|
while (query->next()) {
|
|
banDetails.set_admin_id(QString(query->value(0).toString()).toStdString());
|
|
banDetails.set_admin_name(QString(query->value(5).toString()).toStdString());
|
|
banDetails.set_ban_time(QString(query->value(1).toString()).toStdString());
|
|
banDetails.set_ban_length(QString(query->value(2).toString()).toStdString());
|
|
banDetails.set_ban_reason(QString(query->value(3).toString()).toStdString());
|
|
banDetails.set_visible_reason(QString(query->value(4).toString()).toStdString());
|
|
results << banDetails;
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
bool Servatrice_DatabaseInterface::addWarning(const QString userName,
|
|
const QString adminName,
|
|
const QString warningReason,
|
|
const QString clientID)
|
|
{
|
|
if (!checkSql())
|
|
return false;
|
|
|
|
int userID = getUserIdInDB(userName);
|
|
QSqlQuery *query =
|
|
prepareQuery("insert into {prefix}_warnings (user_id,user_name,mod_name,reason,time_of,clientid) values "
|
|
"(:user_id,:user_name,:mod_name,:warn_reason,NOW(),:client_id)");
|
|
query->bindValue(":user_id", userID);
|
|
query->bindValue(":user_name", userName);
|
|
query->bindValue(":mod_name", adminName);
|
|
query->bindValue(":warn_reason", warningReason);
|
|
query->bindValue(":client_id", clientID);
|
|
if (!execSqlQuery(query)) {
|
|
qCWarning(DatabaseInterfaceLog) << "Failed to collect create warning history information: SQL Error";
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
QList<ServerInfo_Warning> Servatrice_DatabaseInterface::getUserWarnHistory(const QString userName)
|
|
{
|
|
QList<ServerInfo_Warning> results;
|
|
ServerInfo_Warning warnDetails;
|
|
|
|
if (!checkSql())
|
|
return results;
|
|
|
|
int userID = getUserIdInDB(userName);
|
|
QSqlQuery *query =
|
|
prepareQuery("SELECT user_name, mod_name, reason, time_of FROM {prefix}_warnings WHERE user_id = :user_id");
|
|
query->bindValue(":user_id", userID);
|
|
|
|
if (!execSqlQuery(query)) {
|
|
qCWarning(DatabaseInterfaceLog) << "Failed to collect warning history information: SQL Error";
|
|
return results;
|
|
}
|
|
|
|
while (query->next()) {
|
|
warnDetails.set_user_name(QString(query->value(0).toString()).toStdString());
|
|
warnDetails.set_admin_name(QString(query->value(1).toString()).toStdString());
|
|
warnDetails.set_reason(QString(query->value(2).toString()).toStdString());
|
|
warnDetails.set_time_of(QString(query->value(3).toString()).toStdString());
|
|
results << warnDetails;
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
QList<ServerInfo_ChatMessage> Servatrice_DatabaseInterface::getMessageLogHistory(const QString &user,
|
|
const QString &ipaddress,
|
|
const QString &gamename,
|
|
const QString &gameid,
|
|
const QString &message,
|
|
bool &chat,
|
|
bool &game,
|
|
bool &room,
|
|
int &range,
|
|
int &maxresults)
|
|
{
|
|
|
|
QList<ServerInfo_ChatMessage> results;
|
|
ServerInfo_ChatMessage chatMessage;
|
|
|
|
if (!checkSql())
|
|
return results;
|
|
|
|
if (user.isEmpty() && ipaddress.isEmpty() && gameid.isEmpty() && gamename.isEmpty()) {
|
|
// To ensure quick results and minimal lag, require an indexed field
|
|
return results;
|
|
}
|
|
|
|
// BUILD QUERY STRING BASED ON PASSED IN VALUES
|
|
QString queryString = "SELECT * FROM {prefix}_log WHERE `sender_ip` IS NOT NULL";
|
|
if (!user.isEmpty())
|
|
queryString.append(" AND (`sender_name` = :user_name OR `target_name` = :user_name)");
|
|
|
|
if (!ipaddress.isEmpty())
|
|
queryString.append(" AND `sender_ip` = :ip_to_find");
|
|
|
|
if (!gameid.isEmpty())
|
|
queryString.append(" AND (`target_id` = :game_id AND `target_type` = 'game')");
|
|
|
|
if (!gamename.isEmpty())
|
|
queryString.append(" AND (`target_name` = :game_name AND `target_type` = 'game')");
|
|
|
|
if (!message.isEmpty())
|
|
queryString.append(" AND `log_message` LIKE :log_message");
|
|
|
|
if (chat || game || room) {
|
|
queryString.append(" AND (");
|
|
|
|
if (chat)
|
|
queryString.append("`target_type` = 'chat'");
|
|
|
|
if (game) {
|
|
if (chat)
|
|
queryString.append(" OR `target_type` = 'game'");
|
|
else
|
|
queryString.append("`target_type` = 'game'");
|
|
}
|
|
|
|
if (room) {
|
|
if (game || chat)
|
|
queryString.append(" OR `target_type` = 'room'");
|
|
else
|
|
queryString.append("`target_type` = 'room'");
|
|
}
|
|
queryString.append(")");
|
|
}
|
|
|
|
if (range)
|
|
queryString.append(" AND log_time >= DATE_SUB(now(), INTERVAL :range_time HOUR)");
|
|
|
|
if (maxresults)
|
|
queryString.append(" LIMIT :limit_size");
|
|
|
|
QSqlQuery *query = prepareQuery(queryString);
|
|
if (!user.isEmpty()) {
|
|
query->bindValue(":user_name", user);
|
|
}
|
|
if (!ipaddress.isEmpty()) {
|
|
query->bindValue(":ip_to_find", ipaddress);
|
|
}
|
|
if (!gameid.isEmpty()) {
|
|
query->bindValue(":game_id", gameid);
|
|
}
|
|
if (!gamename.isEmpty()) {
|
|
query->bindValue(":game_name", gamename);
|
|
}
|
|
if (!message.isEmpty()) {
|
|
query->bindValue(":log_message", message);
|
|
}
|
|
if (range) {
|
|
query->bindValue(":range_time", range);
|
|
}
|
|
if (maxresults) {
|
|
query->bindValue(":limit_size", maxresults);
|
|
}
|
|
|
|
if (!execSqlQuery(query)) {
|
|
qCWarning(DatabaseInterfaceLog) << "Failed to collect log history information: SQL Error";
|
|
return results;
|
|
}
|
|
|
|
while (query->next()) {
|
|
chatMessage.set_time(QString(query->value(0).toString()).toStdString());
|
|
chatMessage.set_sender_id(QString(query->value(1).toString()).toStdString());
|
|
chatMessage.set_sender_name(QString(query->value(2).toString()).toStdString());
|
|
chatMessage.set_sender_ip(QString(query->value(3).toString()).toStdString());
|
|
chatMessage.set_message(QString(query->value(4).toString()).toStdString());
|
|
chatMessage.set_target_type(QString(query->value(5).toString()).toStdString());
|
|
chatMessage.set_target_id(QString(query->value(6).toString()).toStdString());
|
|
chatMessage.set_target_name(QString(query->value(7).toString()).toStdString());
|
|
results << chatMessage;
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
int Servatrice_DatabaseInterface::checkNumberOfUserAccounts(const QString &email)
|
|
{
|
|
if (!checkSql())
|
|
return 0;
|
|
|
|
QSqlQuery *query = prepareQuery("SELECT count(email) FROM {prefix}_users WHERE email = :user_email");
|
|
query->bindValue(":user_email", email);
|
|
|
|
if (!execSqlQuery(query)) {
|
|
qCWarning(DatabaseInterfaceLog)
|
|
<< "Failed to identify the number of users accounts for users email address: SQL Error";
|
|
return 0;
|
|
}
|
|
|
|
if (query->next())
|
|
return query->value(0).toInt();
|
|
|
|
return 0;
|
|
}
|
|
|
|
bool Servatrice_DatabaseInterface::addForgotPassword(const QString &user)
|
|
{
|
|
if (!checkSql())
|
|
return false;
|
|
|
|
if (!updateUserToken(PasswordHasher::generateActivationToken(), user))
|
|
return false;
|
|
|
|
QSqlQuery *query = prepareQuery("insert into {prefix}_forgot_password (name,requestDate) values (:username,NOW())");
|
|
query->bindValue(":username", user);
|
|
if (execSqlQuery(query))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool Servatrice_DatabaseInterface::removeForgotPassword(const QString &user)
|
|
{
|
|
if (!checkSql())
|
|
return false;
|
|
|
|
QSqlQuery *query = prepareQuery("delete from {prefix}_forgot_password where name = :username");
|
|
query->bindValue(":username", user);
|
|
if (execSqlQuery(query))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool Servatrice_DatabaseInterface::doesForgotPasswordExist(const QString &user)
|
|
{
|
|
if (!checkSql())
|
|
return false;
|
|
|
|
QSqlQuery *query = prepareQuery("select count(name) from {prefix}_forgot_password where name = :user_name AND "
|
|
"requestDate > (now() - interval :minutes minute)");
|
|
query->bindValue(":user_name", user);
|
|
query->bindValue(":minutes", QString::number(server->getForgotPasswordTokenLife()));
|
|
|
|
if (!execSqlQuery(query))
|
|
return false;
|
|
|
|
if (query->next())
|
|
if (query->value("count(name)").toInt() > 0)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool Servatrice_DatabaseInterface::updateUserToken(const QString &token, const QString &user)
|
|
{
|
|
if (!checkSql())
|
|
return false;
|
|
|
|
if (token.isEmpty() || user.isEmpty())
|
|
return false;
|
|
|
|
QSqlQuery *query = prepareQuery("update {prefix}_users set token = :token where name = :user_name");
|
|
query->bindValue(":user_name", user);
|
|
query->bindValue(":token", token);
|
|
|
|
if (execSqlQuery(query))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool Servatrice_DatabaseInterface::validateTableColumnStringData(const QString &table,
|
|
const QString &column,
|
|
const QString &_user,
|
|
const QString &_datatocheck)
|
|
{
|
|
if (!checkSql())
|
|
return false;
|
|
|
|
if (table.isEmpty() || column.isEmpty() || _user.isEmpty() || _datatocheck.isEmpty())
|
|
return false;
|
|
|
|
QString formatedQuery = QString("select %1 from %2 where name = :user_name").arg(column).arg(table);
|
|
QSqlQuery *query = prepareQuery(formatedQuery);
|
|
query->bindValue(":user_name", _user);
|
|
|
|
if (!execSqlQuery(query))
|
|
return false;
|
|
|
|
if (query->next())
|
|
if (query->value(column).toString().toLower() == _datatocheck.toLower())
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
void Servatrice_DatabaseInterface::addAuditRecord(const QString &user,
|
|
const QString &ipaddress,
|
|
const QString &clientid,
|
|
const QString &action,
|
|
const QString &details,
|
|
const bool &results = false)
|
|
{
|
|
if (!checkSql())
|
|
return;
|
|
|
|
if (!server->getEnableAudit())
|
|
return;
|
|
|
|
if (user.isEmpty() || ipaddress.isEmpty() || clientid.isEmpty() || action.isEmpty())
|
|
return;
|
|
|
|
QSqlQuery *query = prepareQuery("insert into {prefix}_audit "
|
|
"(id_server,name,ip_address,clientid,incidentDate,action,results,details) values "
|
|
"(:idserver,:username,:ipaddress,:clientid,NOW(),:action,:results,:details)");
|
|
query->bindValue(":idserver", server->getServerID());
|
|
query->bindValue(":username", user);
|
|
query->bindValue(":ipaddress", ipaddress);
|
|
query->bindValue(":clientid", clientid);
|
|
query->bindValue(":action", action);
|
|
query->bindValue(":results", results ? "success" : "fail");
|
|
|
|
query->bindValue(":details", details);
|
|
execSqlQuery(query);
|
|
}
|