mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-09-29 09:22:18 -07:00
[Server] Add moderation investigation tools
Implements the server side of the moderation suite: - getUserSessions/getUserAlts/getModeratorLastLogins/removeUserAvatar DB methods - Handlers for all five new commands with audit records (PASSWORD_RESET, REMOVE_USER_AVATAR); password resets return a generated temporary password - cmdGetWarnList now reports per-category infraction levels from the officialwarnings setting; cmdReportUserInfo reports last_login - Update servatrice.ini.example with the warning taxonomy - Password/avatar mutations report RespNameNotFound when the user does not exist
This commit is contained in:
parent
96ed16abee
commit
ae58cdd2de
5 changed files with 325 additions and 9 deletions
|
|
@ -79,8 +79,10 @@ requiredfeatures=""
|
|||
|
||||
; You can define custom warnings that users are sent when the moderation staff uses the right client warn user
|
||||
; menu option. This list is comma seperated that each item will appear in the drop down list for staff members
|
||||
; to choose from. Example: "Flaming,Foul Language"
|
||||
officialwarnings="Flaming,Spamming,Causing Drama,Abusive Language"
|
||||
; to choose from. Each entry may optionally carry a recommended starting intervention level (see the moderator
|
||||
; guide) by appending "|" and the level number. Entries without an explicit level default to intervention
|
||||
; level 1. Example: "Flaming,Foul Language"
|
||||
officialwarnings="Abusive Language|1,Calling Out User|1,Causing Drama|1,Cheating|2,Disrespecting Staff|1,Disrupting a Draft|1,Inappropriate Avatar|3,Inappropriate Game Name|1,Kicking Without Valid Reason|1,Spamming|1,Targeted Harassment|2"
|
||||
|
||||
; Maximum time in seconds a player can stay connected but idle. Default is 3600 (0 = disabled)
|
||||
; Clients will be notified at the 90% time period of pending disconnection if they do not take action.
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
#include <QSqlQuery>
|
||||
#include <libcockatrice/deck_list/deck_list.h>
|
||||
#include <libcockatrice/protocol/pb/game_replay.pb.h>
|
||||
#include <libcockatrice/protocol/pb/serverinfo_user.pb.h>
|
||||
#include <libcockatrice/utility/passwordhasher.h>
|
||||
|
||||
inline Q_LOGGING_CATEGORY(DatabaseInterfaceLog, "database_interface");
|
||||
|
|
@ -1084,12 +1085,11 @@ bool Servatrice_DatabaseInterface::changeUserPassword(const QString &user,
|
|||
"passwordLastChangedDate = NOW() where name = :name");
|
||||
passwordQuery->bindValue(":password", passwordSha512);
|
||||
passwordQuery->bindValue(":name", user);
|
||||
if (execSqlQuery(passwordQuery)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!execSqlQuery(passwordQuery)) {
|
||||
return false;
|
||||
}
|
||||
return passwordQuery->numRowsAffected() > 0;
|
||||
}
|
||||
|
||||
bool Servatrice_DatabaseInterface::changeUserPassword(const QString &user,
|
||||
const QString &oldPassword,
|
||||
|
|
@ -1314,6 +1314,167 @@ QList<ServerInfo_Warning> Servatrice_DatabaseInterface::getUserWarnHistory(const
|
|||
return results;
|
||||
}
|
||||
|
||||
QList<ServerInfo_UserSession> Servatrice_DatabaseInterface::getUserSessions(const QString &userName, int limit)
|
||||
{
|
||||
QList<ServerInfo_UserSession> results;
|
||||
|
||||
if (!checkSql()) {
|
||||
return results;
|
||||
}
|
||||
|
||||
QSqlQuery *query = prepareQuery("SELECT user_name, ip_address, clientid, "
|
||||
"UNIX_TIMESTAMP(start_time), UNIX_TIMESTAMP(end_time), connection_type "
|
||||
"FROM {prefix}_sessions WHERE user_name = :user_name "
|
||||
"ORDER BY start_time DESC LIMIT :limit");
|
||||
query->bindValue(":user_name", userName);
|
||||
query->bindValue(":limit", limit);
|
||||
|
||||
if (!execSqlQuery(query)) {
|
||||
qCWarning(DatabaseInterfaceLog) << "Failed to collect session history information: SQL Error";
|
||||
return results;
|
||||
}
|
||||
|
||||
while (query->next()) {
|
||||
ServerInfo_UserSession sessionDetails;
|
||||
sessionDetails.set_user_name(query->value(0).toString().toStdString());
|
||||
sessionDetails.set_ip_address(query->value(1).toString().toStdString());
|
||||
sessionDetails.set_clientid(query->value(2).toString().toStdString());
|
||||
sessionDetails.set_start_time(query->value(3).toLongLong());
|
||||
if (!query->value(4).isNull()) {
|
||||
sessionDetails.set_end_time(query->value(4).toLongLong());
|
||||
}
|
||||
sessionDetails.set_connection_type(query->value(5).toString().toStdString());
|
||||
results << sessionDetails;
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
QList<ServerInfo_UserAlt> Servatrice_DatabaseInterface::getUserAlts(const QString &userName)
|
||||
{
|
||||
QList<ServerInfo_UserAlt> results;
|
||||
|
||||
if (!checkSql()) {
|
||||
return results;
|
||||
}
|
||||
|
||||
// Seed account identifiers used to find related accounts
|
||||
QSqlQuery *seedQuery = prepareQuery("SELECT email, clientid FROM {prefix}_users WHERE name = :user_name");
|
||||
seedQuery->bindValue(":user_name", userName);
|
||||
if (!execSqlQuery(seedQuery) || !seedQuery->next()) {
|
||||
return results;
|
||||
}
|
||||
const QString seedEmail = seedQuery->value(0).toString();
|
||||
const QString seedClientId = seedQuery->value(1).toString();
|
||||
|
||||
QString queryString = "SELECT u.name, u.email, u.clientid, UNIX_TIMESTAMP(u.registrationDate), "
|
||||
"UNIX_TIMESTAMP(a.last_login), "
|
||||
"(SELECT COUNT(*) FROM {prefix}_warnings w WHERE w.user_id = u.id), "
|
||||
"(SELECT COUNT(*) FROM {prefix}_bans b WHERE b.user_name = u.name), "
|
||||
"u.active "
|
||||
"FROM {prefix}_users u "
|
||||
"LEFT JOIN {prefix}_user_analytics a ON a.id = u.id "
|
||||
"WHERE u.name = :user_name";
|
||||
if (!seedEmail.isEmpty()) {
|
||||
queryString.append(" OR u.email = :seed_email");
|
||||
}
|
||||
if (!seedClientId.isEmpty()) {
|
||||
queryString.append(" OR u.clientid = :seed_clientid");
|
||||
}
|
||||
queryString.append(" OR u.name IN (SELECT DISTINCT s.user_name FROM {prefix}_sessions s "
|
||||
"WHERE s.ip_address IN (SELECT DISTINCT s2.ip_address FROM {prefix}_sessions s2 "
|
||||
"WHERE s2.user_name = :user_name)) "
|
||||
"ORDER BY u.name");
|
||||
|
||||
QSqlQuery *query = prepareQuery(queryString);
|
||||
query->bindValue(":user_name", userName);
|
||||
if (!seedEmail.isEmpty()) {
|
||||
query->bindValue(":seed_email", seedEmail);
|
||||
}
|
||||
if (!seedClientId.isEmpty()) {
|
||||
query->bindValue(":seed_clientid", seedClientId);
|
||||
}
|
||||
|
||||
if (!execSqlQuery(query)) {
|
||||
qCWarning(DatabaseInterfaceLog) << "Failed to collect user alt information: SQL Error";
|
||||
return results;
|
||||
}
|
||||
|
||||
while (query->next()) {
|
||||
ServerInfo_UserAlt altDetails;
|
||||
altDetails.set_user_name(query->value(0).toString().toStdString());
|
||||
altDetails.set_email(query->value(1).toString().toStdString());
|
||||
altDetails.set_clientid(query->value(2).toString().toStdString());
|
||||
altDetails.set_registration_time(query->value(3).toLongLong());
|
||||
if (!query->value(4).isNull()) {
|
||||
altDetails.set_last_login(query->value(4).toLongLong());
|
||||
}
|
||||
altDetails.set_warn_count(query->value(5).toInt());
|
||||
altDetails.set_ban_count(query->value(6).toInt());
|
||||
altDetails.set_is_active(query->value(7).toBool());
|
||||
results << altDetails;
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
QList<ServerInfo_ModeratorLogin> Servatrice_DatabaseInterface::getModeratorLastLogins()
|
||||
{
|
||||
QList<ServerInfo_ModeratorLogin> results;
|
||||
|
||||
if (!checkSql()) {
|
||||
return results;
|
||||
}
|
||||
|
||||
QSqlQuery *query = prepareQuery("SELECT u.name, u.admin, UNIX_TIMESTAMP(a.last_login) "
|
||||
"FROM {prefix}_users u "
|
||||
"LEFT JOIN {prefix}_user_analytics a ON a.id = u.id "
|
||||
"WHERE (u.admin & 7) <> 0 ORDER BY u.name");
|
||||
|
||||
if (!execSqlQuery(query)) {
|
||||
qCWarning(DatabaseInterfaceLog) << "Failed to collect moderator login information: SQL Error";
|
||||
return results;
|
||||
}
|
||||
|
||||
while (query->next()) {
|
||||
ServerInfo_ModeratorLogin loginDetails;
|
||||
loginDetails.set_user_name(query->value(0).toString().toStdString());
|
||||
|
||||
const int isAdmin = query->value(1).toInt();
|
||||
int userLevel = ServerInfo_User::IsUser | ServerInfo_User::IsRegistered;
|
||||
if (isAdmin & 1) {
|
||||
userLevel |= ServerInfo_User::IsAdmin | ServerInfo_User::IsModerator;
|
||||
} else if (isAdmin & 2) {
|
||||
userLevel |= ServerInfo_User::IsModerator;
|
||||
}
|
||||
if (isAdmin & 4) {
|
||||
userLevel |= ServerInfo_User::IsJudge;
|
||||
}
|
||||
loginDetails.set_user_level(userLevel);
|
||||
|
||||
if (!query->value(2).isNull()) {
|
||||
loginDetails.set_last_login(query->value(2).toLongLong());
|
||||
}
|
||||
results << loginDetails;
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
bool Servatrice_DatabaseInterface::removeUserAvatar(const QString &userName)
|
||||
{
|
||||
if (!checkSql()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QSqlQuery *query = prepareQuery("UPDATE {prefix}_users SET avatar_bmp = '' WHERE name = :user_name");
|
||||
query->bindValue(":user_name", userName);
|
||||
if (!execSqlQuery(query)) {
|
||||
return false;
|
||||
}
|
||||
return query->numRowsAffected() > 0;
|
||||
}
|
||||
|
||||
QList<ServerInfo_ChatMessage> Servatrice_DatabaseInterface::getMessageLogHistory(const QString &user,
|
||||
const QString &ipaddress,
|
||||
const QString &gamename,
|
||||
|
|
|
|||
|
|
@ -6,6 +6,9 @@
|
|||
#include <QObject>
|
||||
#include <QSqlDatabase>
|
||||
#include <libcockatrice/protocol/pb/serverinfo_chat_message.pb.h>
|
||||
#include <libcockatrice/protocol/pb/serverinfo_moderator_login.pb.h>
|
||||
#include <libcockatrice/protocol/pb/serverinfo_user_alt.pb.h>
|
||||
#include <libcockatrice/protocol/pb/serverinfo_user_session.pb.h>
|
||||
#include <libcockatrice/protocol/pb/serverinfo_warning.pb.h>
|
||||
#include <server.h>
|
||||
#include <server_database_interface.h>
|
||||
|
|
@ -133,6 +136,10 @@ public:
|
|||
bool &room,
|
||||
int &range,
|
||||
int &maxresults);
|
||||
QList<ServerInfo_UserSession> getUserSessions(const QString &userName, int limit);
|
||||
QList<ServerInfo_UserAlt> getUserAlts(const QString &userName);
|
||||
QList<ServerInfo_ModeratorLogin> getModeratorLastLogins();
|
||||
bool removeUserAvatar(const QString &userName);
|
||||
bool addForgotPassword(const QString &user);
|
||||
bool removeForgotPassword(const QString &user) override;
|
||||
bool doesForgotPasswordExist(const QString &user);
|
||||
|
|
|
|||
|
|
@ -80,8 +80,10 @@
|
|||
#include <libcockatrice/protocol/pb/response_deck_upload.pb.h>
|
||||
#include <libcockatrice/protocol/pb/response_forgotpasswordrequest.pb.h>
|
||||
#include <libcockatrice/protocol/pb/response_get_admin_notes.pb.h>
|
||||
#include <libcockatrice/protocol/pb/response_moderator_last_logins.pb.h>
|
||||
#include <libcockatrice/protocol/pb/response_password_salt.pb.h>
|
||||
#include <libcockatrice/protocol/pb/response_register.pb.h>
|
||||
#include <libcockatrice/protocol/pb/response_remove_user_avatar.pb.h>
|
||||
#include <libcockatrice/protocol/pb/response_replay_download.pb.h>
|
||||
#include <libcockatrice/protocol/pb/response_replay_download_by_game_id.pb.h>
|
||||
#include <libcockatrice/protocol/pb/response_replay_get_code.pb.h>
|
||||
|
|
@ -91,15 +93,23 @@
|
|||
#include <libcockatrice/protocol/pb/response_report_my_list.pb.h>
|
||||
#include <libcockatrice/protocol/pb/response_report_stats.pb.h>
|
||||
#include <libcockatrice/protocol/pb/response_report_user_info.pb.h>
|
||||
#include <libcockatrice/protocol/pb/response_reset_user_password.pb.h>
|
||||
#include <libcockatrice/protocol/pb/response_user_alts.pb.h>
|
||||
#include <libcockatrice/protocol/pb/response_user_sessions.pb.h>
|
||||
#include <libcockatrice/protocol/pb/response_viewlog_history.pb.h>
|
||||
#include <libcockatrice/protocol/pb/response_warn_history.pb.h>
|
||||
#include <libcockatrice/protocol/pb/response_warn_list.pb.h>
|
||||
#include <libcockatrice/protocol/pb/serverinfo_ban.pb.h>
|
||||
#include <libcockatrice/protocol/pb/serverinfo_chat_message.pb.h>
|
||||
#include <libcockatrice/protocol/pb/serverinfo_deckstorage.pb.h>
|
||||
#include <libcockatrice/protocol/pb/serverinfo_moderator_login.pb.h>
|
||||
#include <libcockatrice/protocol/pb/serverinfo_replay.pb.h>
|
||||
#include <libcockatrice/protocol/pb/serverinfo_user.pb.h>
|
||||
#include <libcockatrice/protocol/pb/serverinfo_user_alt.pb.h>
|
||||
#include <libcockatrice/protocol/pb/serverinfo_user_session.pb.h>
|
||||
#include <libcockatrice/utility/passwordhasher.h>
|
||||
#include <libcockatrice/utility/string_limits.h>
|
||||
#include <libcockatrice/utility/warning_categories.h>
|
||||
#include <server_response_containers.h>
|
||||
#include <server_room.h>
|
||||
#include <string>
|
||||
|
|
@ -295,6 +305,16 @@ Response::ResponseCode AbstractServerSocketInterface::processExtendedModeratorCo
|
|||
return cmdReportUserInfo(cmd.GetExtension(Command_ReportUserInfo::ext), rc);
|
||||
case ModeratorCommand::REPORT_STATS:
|
||||
return cmdReportStats(cmd.GetExtension(Command_ReportStats::ext), rc);
|
||||
case ModeratorCommand::GET_USER_SESSIONS:
|
||||
return cmdGetUserSessions(cmd.GetExtension(Command_GetUserSessions::ext), rc);
|
||||
case ModeratorCommand::GET_USER_ALTS:
|
||||
return cmdGetUserAlts(cmd.GetExtension(Command_GetUserAlts::ext), rc);
|
||||
case ModeratorCommand::GET_MODERATOR_LAST_LOGINS:
|
||||
return cmdGetModeratorLastLogins(cmd.GetExtension(Command_GetModeratorLastLogins::ext), rc);
|
||||
case ModeratorCommand::RESET_USER_PASSWORD:
|
||||
return cmdResetUserPassword(cmd.GetExtension(Command_ResetUserPassword::ext), rc);
|
||||
case ModeratorCommand::REMOVE_USER_AVATAR:
|
||||
return cmdRemoveUserAvatar(cmd.GetExtension(Command_RemoveUserAvatar::ext), rc);
|
||||
default:
|
||||
return Response::RespFunctionNotAllowed;
|
||||
}
|
||||
|
|
@ -1103,9 +1123,10 @@ Response::ResponseCode AbstractServerSocketInterface::cmdGetWarnList(const Comma
|
|||
Response_WarnList *re = new Response_WarnList;
|
||||
|
||||
QString officialWarnings = settingsCache->value("server/officialwarnings").toString();
|
||||
QStringList warningsList = officialWarnings.split(",", Qt::SkipEmptyParts);
|
||||
for (const QString &warning : warningsList) {
|
||||
re->add_warning(warning.toStdString());
|
||||
const QList<WarningCategory> categories = parseWarningCategories(officialWarnings);
|
||||
for (const WarningCategory &category : categories) {
|
||||
re->add_warning(category.name.toStdString());
|
||||
re->add_warning_il(category.startingIl);
|
||||
}
|
||||
re->set_user_name(nameFromStdString(cmd.user_name()).toStdString());
|
||||
re->set_user_clientid(nameFromStdString(cmd.user_clientid()).toStdString());
|
||||
|
|
@ -1550,6 +1571,15 @@ Response::ResponseCode AbstractServerSocketInterface::cmdReportUserInfo(const Co
|
|||
re->set_total_warns(warnCountQuery->value(0).toInt());
|
||||
}
|
||||
|
||||
QSqlQuery *lastLoginQuery = sqlInterface->prepareQuery("SELECT UNIX_TIMESTAMP(a.last_login) "
|
||||
"FROM {prefix}_user_analytics a "
|
||||
"JOIN {prefix}_users u ON u.id = a.id "
|
||||
"WHERE u.name = :name");
|
||||
lastLoginQuery->bindValue(":name", userName);
|
||||
if (sqlInterface->execSqlQuery(lastLoginQuery) && lastLoginQuery->next() && !lastLoginQuery->value(0).isNull()) {
|
||||
re->set_last_login(lastLoginQuery->value(0).toLongLong());
|
||||
}
|
||||
|
||||
QSqlQuery *recentQuery =
|
||||
sqlInterface->prepareQuery("SELECT r.id, r.reporter_name, r.reported_user_name, r.game_id, "
|
||||
"r.category, r.description, r.created_at, r.status, "
|
||||
|
|
@ -1697,6 +1727,116 @@ Response::ResponseCode AbstractServerSocketInterface::cmdReportStats(const Comma
|
|||
return Response::RespOk;
|
||||
}
|
||||
|
||||
Response::ResponseCode AbstractServerSocketInterface::cmdGetUserSessions(const Command_GetUserSessions &cmd,
|
||||
ResponseContainer &rc)
|
||||
{
|
||||
if (!sqlInterface->checkSql()) {
|
||||
return Response::RespInternalError;
|
||||
}
|
||||
|
||||
const QString userName = nameFromStdString(cmd.user_name());
|
||||
if (userName.isEmpty()) {
|
||||
return Response::RespContextError;
|
||||
}
|
||||
|
||||
Response_UserSessions *re = new Response_UserSessions;
|
||||
const QList<ServerInfo_UserSession> sessions = sqlInterface->getUserSessions(userName, cmd.limit());
|
||||
for (const ServerInfo_UserSession &session : sessions) {
|
||||
re->add_sessions()->CopyFrom(session);
|
||||
}
|
||||
rc.setResponseExtension(re);
|
||||
return Response::RespOk;
|
||||
}
|
||||
|
||||
Response::ResponseCode AbstractServerSocketInterface::cmdGetUserAlts(const Command_GetUserAlts &cmd,
|
||||
ResponseContainer &rc)
|
||||
{
|
||||
if (!sqlInterface->checkSql()) {
|
||||
return Response::RespInternalError;
|
||||
}
|
||||
|
||||
const QString userName = nameFromStdString(cmd.user_name());
|
||||
if (userName.isEmpty()) {
|
||||
return Response::RespContextError;
|
||||
}
|
||||
|
||||
Response_UserAlts *re = new Response_UserAlts;
|
||||
const QList<ServerInfo_UserAlt> alts = sqlInterface->getUserAlts(userName);
|
||||
for (const ServerInfo_UserAlt &alt : alts) {
|
||||
re->add_alts()->CopyFrom(alt);
|
||||
}
|
||||
rc.setResponseExtension(re);
|
||||
return Response::RespOk;
|
||||
}
|
||||
|
||||
Response::ResponseCode AbstractServerSocketInterface::cmdGetModeratorLastLogins(const Command_GetModeratorLastLogins &,
|
||||
ResponseContainer &rc)
|
||||
{
|
||||
if (!sqlInterface->checkSql()) {
|
||||
return Response::RespInternalError;
|
||||
}
|
||||
|
||||
Response_ModeratorLastLogins *re = new Response_ModeratorLastLogins;
|
||||
const QList<ServerInfo_ModeratorLogin> logins = sqlInterface->getModeratorLastLogins();
|
||||
for (const ServerInfo_ModeratorLogin &login : logins) {
|
||||
re->add_logins()->CopyFrom(login);
|
||||
}
|
||||
rc.setResponseExtension(re);
|
||||
return Response::RespOk;
|
||||
}
|
||||
|
||||
Response::ResponseCode AbstractServerSocketInterface::cmdResetUserPassword(const Command_ResetUserPassword &cmd,
|
||||
ResponseContainer &rc)
|
||||
{
|
||||
if (!sqlInterface->checkSql()) {
|
||||
return Response::RespInternalError;
|
||||
}
|
||||
|
||||
const QString userName = nameFromStdString(cmd.user_name()).simplified();
|
||||
if (userName.isEmpty()) {
|
||||
return Response::RespContextError;
|
||||
}
|
||||
|
||||
const QString tempPassword = PasswordHasher::generateRandomSalt();
|
||||
if (!sqlInterface->changeUserPassword(userName, tempPassword, true)) {
|
||||
return Response::RespInternalError;
|
||||
}
|
||||
|
||||
sqlInterface->addAuditRecord(userName, this->getAddress(), QString::fromStdString(userInfo->clientid()),
|
||||
"PASSWORD_RESET", "Moderator password reset", true);
|
||||
|
||||
Response_ResetUserPassword *re = new Response_ResetUserPassword;
|
||||
re->set_user_name(userName.toStdString());
|
||||
re->set_temporary_password(tempPassword.toStdString());
|
||||
rc.setResponseExtension(re);
|
||||
return Response::RespOk;
|
||||
}
|
||||
|
||||
Response::ResponseCode AbstractServerSocketInterface::cmdRemoveUserAvatar(const Command_RemoveUserAvatar &cmd,
|
||||
ResponseContainer &rc)
|
||||
{
|
||||
if (!sqlInterface->checkSql()) {
|
||||
return Response::RespInternalError;
|
||||
}
|
||||
|
||||
const QString userName = nameFromStdString(cmd.user_name()).simplified();
|
||||
if (userName.isEmpty()) {
|
||||
return Response::RespContextError;
|
||||
}
|
||||
|
||||
if (!sqlInterface->removeUserAvatar(userName)) {
|
||||
return Response::RespInternalError;
|
||||
}
|
||||
|
||||
sqlInterface->addAuditRecord(userName, this->getAddress(), QString::fromStdString(userInfo->clientid()),
|
||||
"REMOVE_USER_AVATAR", "Moderator removed user avatar", true);
|
||||
|
||||
Response_RemoveUserAvatar *re = new Response_RemoveUserAvatar;
|
||||
re->set_user_name(userName.toStdString());
|
||||
rc.setResponseExtension(re);
|
||||
return Response::RespOk;
|
||||
}
|
||||
|
||||
void AbstractServerSocketInterface::sendPendingReportNotifications(ResponseContainer &rc)
|
||||
{
|
||||
if (!sqlInterface->checkSql()) {
|
||||
|
|
|
|||
|
|
@ -166,6 +166,12 @@ private:
|
|||
Response::ResponseCode cmdGetAdminNotes(const Command_GetAdminNotes &cmd, ResponseContainer &rc);
|
||||
Response::ResponseCode cmdUpdateAdminNotes(const Command_UpdateAdminNotes &cmd, ResponseContainer &rc);
|
||||
|
||||
Response::ResponseCode cmdGetUserSessions(const Command_GetUserSessions &cmd, ResponseContainer &rc);
|
||||
Response::ResponseCode cmdGetUserAlts(const Command_GetUserAlts &cmd, ResponseContainer &rc);
|
||||
Response::ResponseCode cmdGetModeratorLastLogins(const Command_GetModeratorLastLogins &cmd, ResponseContainer &rc);
|
||||
Response::ResponseCode cmdResetUserPassword(const Command_ResetUserPassword &cmd, ResponseContainer &rc);
|
||||
Response::ResponseCode cmdRemoveUserAvatar(const Command_RemoveUserAvatar &cmd, ResponseContainer &rc);
|
||||
|
||||
bool addAdminFlagToUser(const QString &user, int flag);
|
||||
bool removeAdminFlagFromUser(const QString &user, int flag);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue