From 2ba80cb61d06a8c898e4f3ef5865427bd7d98ee0 Mon Sep 17 00:00:00 2001 From: BruebachL <44814898+BruebachL@users.noreply.github.com> Date: Tue, 10 Nov 2026 12:16:27 +0100 Subject: [PATCH] [Server] Serve report list views through the database interface (#7844) cmdReportList, cmdReportMyList and cmdReportDetails still prepared their SELECTs inline through Servatrice_DatabaseInterface and mapped the result columns to protocol records in the socket handler. Add getReportList, getReportsSubmittedBy, getReportForDetails and getReportComments to Server_DatabaseInterface with fail-closed defaults and a ReportLookupResult outcome, implement them with the previous statements, joins and null handling, and keep the detail authorization check and the tolerated comment-query failure in the handler in the same order. Response codes are unchanged. --- .../server/remote/server_database_interface.h | 33 ++ .../src/servatrice_database_interface.cpp | 281 ++++++++++++++++++ .../src/servatrice_database_interface.h | 10 + servatrice/src/serversocketinterface.cpp | 257 ++-------------- 4 files changed, 349 insertions(+), 232 deletions(-) diff --git a/libcockatrice_network/libcockatrice/network/server/remote/server_database_interface.h b/libcockatrice_network/libcockatrice/network/server/remote/server_database_interface.h index 709ad4bef..31593bb1e 100644 --- a/libcockatrice_network/libcockatrice/network/server/remote/server_database_interface.h +++ b/libcockatrice_network/libcockatrice/network/server/remote/server_database_interface.h @@ -5,6 +5,7 @@ #include "libcockatrice/protocol/pb/serverinfo_chat_message.pb.h" #include "libcockatrice/protocol/pb/serverinfo_moderator_login.pb.h" #include "libcockatrice/protocol/pb/serverinfo_private_chat_message.pb.h" +#include "libcockatrice/protocol/pb/serverinfo_report.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" @@ -391,6 +392,38 @@ public: { return ReplayWriteResult::Failed; } + /** @brief Outcome of fetching one report row for the detail view. */ + enum class ReportLookupResult + { + Ok, + NotFound, + Failed + }; + /** @brief One page of reports with their joined moderator names, plus the total row count. */ + virtual bool getReportList(bool /* unresolvedOnly */, + int /* offset */, + int /* limit */, + int & /* totalCount */, + QList & /* reports */) + { + return false; + } + /** @brief The newest reports the user filed, for their own list. */ + virtual bool getReportsSubmittedBy(int /* reporterId */, QList & /* reports */) + { + return false; + } + /** @brief One report row for the detail view; also returns the reporter id for the access check. */ + virtual ReportLookupResult + getReportForDetails(int /* reportId */, int & /* reporterId */, ServerInfo_Report & /* report */) + { + return ReportLookupResult::Failed; + } + /** @brief The comments of a report in creation order. */ + virtual bool getReportComments(int /* reportId */, QList & /* comments */) + { + return false; + } /** @brief One resolved or dismissed report whose reporter has not been told yet. */ struct PendingReportNotification { diff --git a/servatrice/src/servatrice_database_interface.cpp b/servatrice/src/servatrice_database_interface.cpp index 93b75777c..dc576a241 100644 --- a/servatrice/src/servatrice_database_interface.cpp +++ b/servatrice/src/servatrice_database_interface.cpp @@ -3562,6 +3562,287 @@ bool Servatrice_DatabaseInterface::userEmailMatches(const QString &userName, con return validateTableColumnStringData("{prefix}_users", "email", userName, email); } +namespace +{ +// The user level the protocol expects for a report's reported user, resolved from the +// joined user row: the row is absent when the reported name no longer exists. +int reportUserLevel(bool userExists, int adminFlags) +{ + int userLevel = ServerInfo_User::IsUser; + if (userExists) { + userLevel |= ServerInfo_User::IsRegistered; + if (adminFlags & 1) { + userLevel |= ServerInfo_User::IsAdmin | ServerInfo_User::IsModerator; + } else if (adminFlags & 2) { + userLevel |= ServerInfo_User::IsModerator; + } + if (adminFlags & 4) { + userLevel |= ServerInfo_User::IsJudge; + } + if (adminFlags & 8) { + userLevel |= ServerInfo_User::IsDeveloper; + } + } + return userLevel; +} +} // namespace + +bool Servatrice_DatabaseInterface::getReportList(bool unresolvedOnly, + int offset, + int limit, + int &totalCount, + QList &reports) +{ + if (!checkSql()) { + return false; + } + + QString whereClause; + if (unresolvedOnly) { + whereClause = "WHERE r.status = 'open' OR r.status = 'assigned' "; + } + + QSqlQuery *countQuery = prepareQuery("SELECT COUNT(*) FROM {prefix}_reports r " + whereClause); + totalCount = 0; + if (execSqlQuery(countQuery) && countQuery->next()) { + totalCount = countQuery->value(0).toInt(); + } + + QString queryStr = "SELECT r.id, r.reporter_name, r.reported_user_name, r.game_id, " + "r.category, r.description, r.created_at, r.status, " + "r.resolution_note, u.name AS assigned_mod_name, ru.name AS resolved_by_name, r.room_id, " + "(SELECT id FROM {prefix}_replays WHERE id_game = r.game_id ORDER BY id DESC LIMIT 1) " + "AS replay_id, u2.id AS reported_user_id, u2.admin AS reported_user_admin " + "FROM {prefix}_reports r " + "LEFT JOIN {prefix}_users u ON r.assigned_to = u.id " + "LEFT JOIN {prefix}_users ru ON r.resolved_by = ru.id " + "LEFT JOIN {prefix}_users u2 ON u2.name = r.reported_user_name "; + + if (unresolvedOnly) { + queryStr += "WHERE r.status = 'open' OR r.status = 'assigned' "; + } + + queryStr += "ORDER BY r.created_at DESC LIMIT :limit OFFSET :offset"; + + QSqlQuery *query = prepareQuery(queryStr); + query->bindValue(":limit", limit); + query->bindValue(":offset", offset); + if (!execSqlQuery(query)) { + return false; + } + + while (query->next()) { + ServerInfo_Report info; + + info.set_report_id(query->value(0).toInt()); + info.set_reporter_name(query->value(1).toString().toStdString()); + info.set_reported_user_name(query->value(2).toString().toStdString()); + + if (!query->value(3).isNull()) { + info.set_game_id(query->value(3).toInt()); + } + + info.set_category(query->value(4).toString().toStdString()); + info.set_description(query->value(5).toString().toStdString()); + info.set_report_time(query->value(6).toDateTime().toSecsSinceEpoch()); + info.set_status(query->value(7).toString().toStdString()); + + if (!query->value(8).isNull()) { + info.set_resolution_note(query->value(8).toString().toStdString()); + } + + if (!query->value(9).isNull()) { + info.set_assigned_mod_name(query->value(9).toString().toStdString()); + } + if (!query->value(10).isNull()) { + info.set_resolved_by_name(query->value(10).toString().toStdString()); + } + + if (!query->value(11).isNull()) { + info.set_room_id(query->value(11).toInt()); + } + + if (!query->value(12).isNull()) { + info.set_replay_id(query->value(12).toInt()); + } + + info.set_reported_user_level(reportUserLevel(!query->value(13).isNull(), query->value(14).toInt())); + + reports.append(info); + } + + return true; +} + +bool Servatrice_DatabaseInterface::getReportsSubmittedBy(int reporterId, QList &reports) +{ + if (!checkSql()) { + return false; + } + + QString queryStr = "SELECT r.id, r.reporter_name, r.reported_user_name, r.game_id, " + "r.category, r.description, r.created_at, r.status, " + "r.resolution_note, u.name AS assigned_mod_name, ru.name AS resolved_by_name, r.room_id, " + "(SELECT id FROM {prefix}_replays WHERE id_game = r.game_id ORDER BY id DESC LIMIT 1) " + "AS replay_id, u2.id AS reported_user_id, u2.admin AS reported_user_admin " + "FROM {prefix}_reports r " + "LEFT JOIN {prefix}_users u ON r.assigned_to = u.id " + "LEFT JOIN {prefix}_users ru ON r.resolved_by = ru.id " + "LEFT JOIN {prefix}_users u2 ON u2.name = r.reported_user_name " + "WHERE r.reporter_id = :reporter_id " + "ORDER BY r.created_at DESC LIMIT 200"; + + QSqlQuery *query = prepareQuery(queryStr); + query->bindValue(":reporter_id", reporterId); + + if (!execSqlQuery(query)) { + return false; + } + + while (query->next()) { + ServerInfo_Report info; + + info.set_report_id(query->value(0).toInt()); + info.set_reporter_name(query->value(1).toString().toStdString()); + info.set_reported_user_name(query->value(2).toString().toStdString()); + + if (!query->value(3).isNull()) { + info.set_game_id(query->value(3).toInt()); + } + + info.set_category(query->value(4).toString().toStdString()); + info.set_description(query->value(5).toString().toStdString()); + info.set_report_time(query->value(6).toDateTime().toSecsSinceEpoch()); + info.set_status(query->value(7).toString().toStdString()); + + if (!query->value(8).isNull()) { + info.set_resolution_note(query->value(8).toString().toStdString()); + } + + if (!query->value(9).isNull()) { + info.set_assigned_mod_name(query->value(9).toString().toStdString()); + } + if (!query->value(10).isNull()) { + info.set_resolved_by_name(query->value(10).toString().toStdString()); + } + + if (!query->value(11).isNull()) { + info.set_room_id(query->value(11).toInt()); + } + + if (!query->value(12).isNull()) { + info.set_replay_id(query->value(12).toInt()); + } + + info.set_reported_user_level(reportUserLevel(!query->value(13).isNull(), query->value(14).toInt())); + + reports.append(info); + } + + return true; +} + +Server_DatabaseInterface::ReportLookupResult +Servatrice_DatabaseInterface::getReportForDetails(int reportId, int &reporterId, ServerInfo_Report &report) +{ + if (!checkSql()) { + return ReportLookupResult::Failed; + } + + QString queryStr = + "SELECT r.id, r.reporter_id, r.reporter_name, r.reported_user_name, r.game_id, " + "r.category, r.description, r.created_at, r.status, " + "r.resolution_note, u.name AS assigned_mod_name, ru.name AS resolved_by_name, r.chat_log, r.room_id, " + "r.resolution_time, " + "(SELECT id FROM {prefix}_replays WHERE id_game = r.game_id ORDER BY id DESC LIMIT 1) " + "AS replay_id " + "FROM {prefix}_reports r " + "LEFT JOIN {prefix}_users u ON r.assigned_to = u.id " + "LEFT JOIN {prefix}_users ru ON r.resolved_by = ru.id " + "WHERE r.id = :id"; + QSqlQuery *query = prepareQuery(queryStr); + query->bindValue(":id", reportId); + + if (!execSqlQuery(query)) { + return ReportLookupResult::Failed; + } + + if (!query->next()) { + return ReportLookupResult::NotFound; + } + + reporterId = query->value(1).toInt(); + + report.set_report_id(query->value(0).toInt()); + report.set_reporter_name(query->value(2).toString().toStdString()); + report.set_reported_user_name(query->value(3).toString().toStdString()); + + if (!query->value(4).isNull()) { + report.set_game_id(query->value(4).toInt()); + } + + report.set_category(query->value(5).toString().toStdString()); + report.set_description(query->value(6).toString().toStdString()); + report.set_report_time(query->value(7).toDateTime().toSecsSinceEpoch()); + report.set_status(query->value(8).toString().toStdString()); + + if (!query->value(9).isNull()) { + report.set_resolution_note(query->value(9).toString().toStdString()); + } + + if (!query->value(10).isNull()) { + report.set_assigned_mod_name(query->value(10).toString().toStdString()); + } + if (!query->value(11).isNull()) { + report.set_resolved_by_name(query->value(11).toString().toStdString()); + } + + if (!query->value(12).isNull()) { + report.set_chat_log(query->value(12).toString().toStdString()); + } + + if (!query->value(13).isNull()) { + report.set_room_id(query->value(13).toInt()); + } + + if (!query->value(14).isNull()) { + report.set_resolution_time(query->value(14).toDateTime().toSecsSinceEpoch()); + } + + if (!query->value(15).isNull()) { + report.set_replay_id(query->value(15).toInt()); + } + + return ReportLookupResult::Ok; +} + +bool Servatrice_DatabaseInterface::getReportComments(int reportId, QList &comments) +{ + if (!checkSql()) { + return false; + } + + QSqlQuery *query = prepareQuery("SELECT c.author_name, c.comment_text, c.created_at, c.is_moderator " + "FROM {prefix}_report_comments c " + "WHERE c.report_id = :report_id ORDER BY c.created_at ASC"); + query->bindValue(":report_id", reportId); + + if (!execSqlQuery(query)) { + return false; + } + + while (query->next()) { + ServerInfo_ReportComment comment; + comment.set_author_name(query->value(0).toString().toStdString()); + comment.set_comment_text(query->value(1).toString().toStdString()); + comment.set_comment_time(query->value(2).toDateTime().toSecsSinceEpoch()); + comment.set_is_moderator(query->value(3).toBool()); + comments.append(comment); + } + + return true; +} + int Servatrice_DatabaseInterface::countReportsSubmittedInLastDay(int reporterId) { if (!checkSql()) { diff --git a/servatrice/src/servatrice_database_interface.h b/servatrice/src/servatrice_database_interface.h index bdaa72a62..00083ecf7 100644 --- a/servatrice/src/servatrice_database_interface.h +++ b/servatrice/src/servatrice_database_interface.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -291,6 +292,15 @@ public: bool results) override; int countReportsSubmittedInLastDay(int reporterId) override; int getReportedUserId(const QString &userName) override; + bool getReportList(bool unresolvedOnly, + int offset, + int limit, + int &totalCount, + QList &reports) override; + bool getReportsSubmittedBy(int reporterId, QList &reports) override; + Server_DatabaseInterface::ReportLookupResult + getReportForDetails(int reportId, int &reporterId, ServerInfo_Report &report) override; + bool getReportComments(int reportId, QList &comments) override; bool addReport(int reporterId, const QString &reporterName, int reportedUserId, diff --git a/servatrice/src/serversocketinterface.cpp b/servatrice/src/serversocketinterface.cpp index f0e0928a1..91e2dea7f 100644 --- a/servatrice/src/serversocketinterface.cpp +++ b/servatrice/src/serversocketinterface.cpp @@ -1819,33 +1819,10 @@ Response::ResponseCode AbstractServerSocketInterface::cmdBanFromServer(const Com return Response::RespOk; } -namespace -{ -int reportUserLevel(bool userExists, int adminFlags) -{ - int userLevel = ServerInfo_User::IsUser; - if (userExists) { - userLevel |= ServerInfo_User::IsRegistered; - if (adminFlags & 1) { - userLevel |= ServerInfo_User::IsAdmin | ServerInfo_User::IsModerator; - } else if (adminFlags & 2) { - userLevel |= ServerInfo_User::IsModerator; - } - if (adminFlags & 4) { - userLevel |= ServerInfo_User::IsJudge; - } - if (adminFlags & 8) { - userLevel |= ServerInfo_User::IsDeveloper; - } - } - return userLevel; -} -} // namespace - Response::ResponseCode AbstractServerSocketInterface::cmdReportList(const Command_ReportList &cmd, ResponseContainer &rc) { - if (!sqlInterface->checkSql()) { + if (!databaseInterface->checkSql()) { return Response::RespInternalError; } @@ -1853,85 +1830,16 @@ Response::ResponseCode AbstractServerSocketInterface::cmdReportList(const Comman const int offset = static_cast(cmd.offset()); const int limit = qMin(static_cast(cmd.limit()), 1000); - // Columns: 0=id, 1=reporter_name, 2=reported_user_name, 3=game_id, - // 4=category, 5=description, 6=created_at, 7=status, - // 8=resolution_note, 9=assigned_mod_name, 10=resolved_by_name, - // 11=room_id, 12=replay_id, - // 13=reported_user_id, 14=reported_user_admin - QString whereClause; - if (unresolvedOnly) { - whereClause = "WHERE r.status = 'open' OR r.status = 'assigned' "; - } - - // Total count query - QSqlQuery *countQuery = sqlInterface->prepareQuery("SELECT COUNT(*) FROM {prefix}_reports r " + whereClause); int totalCount = 0; - if (sqlInterface->execSqlQuery(countQuery) && countQuery->next()) { - totalCount = countQuery->value(0).toInt(); - } - - QString queryStr = "SELECT r.id, r.reporter_name, r.reported_user_name, r.game_id, " - "r.category, r.description, r.created_at, r.status, " - "r.resolution_note, u.name AS assigned_mod_name, ru.name AS resolved_by_name, r.room_id, " - "(SELECT id FROM {prefix}_replays WHERE id_game = r.game_id ORDER BY id DESC LIMIT 1) " - "AS replay_id, u2.id AS reported_user_id, u2.admin AS reported_user_admin " - "FROM {prefix}_reports r " - "LEFT JOIN {prefix}_users u ON r.assigned_to = u.id " - "LEFT JOIN {prefix}_users ru ON r.resolved_by = ru.id " - "LEFT JOIN {prefix}_users u2 ON u2.name = r.reported_user_name "; - - if (unresolvedOnly) { - queryStr += "WHERE r.status = 'open' OR r.status = 'assigned' "; - } - - queryStr += "ORDER BY r.created_at DESC LIMIT :limit OFFSET :offset"; - - QSqlQuery *query = sqlInterface->prepareQuery(queryStr); - query->bindValue(":limit", limit); - query->bindValue(":offset", offset); - if (!sqlInterface->execSqlQuery(query)) { + QList reports; + if (!databaseInterface->getReportList(unresolvedOnly, offset, limit, totalCount, reports)) { return Response::RespInternalError; } Response_ReportList *re = new Response_ReportList; re->set_total_count(totalCount); - - while (query->next()) { - ServerInfo_Report *info = re->add_reports(); - - info->set_report_id(query->value(0).toInt()); - info->set_reporter_name(query->value(1).toString().toStdString()); - info->set_reported_user_name(query->value(2).toString().toStdString()); - - if (!query->value(3).isNull()) { - info->set_game_id(query->value(3).toInt()); - } - - info->set_category(query->value(4).toString().toStdString()); - info->set_description(query->value(5).toString().toStdString()); - info->set_report_time(query->value(6).toDateTime().toSecsSinceEpoch()); - info->set_status(query->value(7).toString().toStdString()); - - if (!query->value(8).isNull()) { - info->set_resolution_note(query->value(8).toString().toStdString()); - } - - if (!query->value(9).isNull()) { - info->set_assigned_mod_name(query->value(9).toString().toStdString()); - } - if (!query->value(10).isNull()) { - info->set_resolved_by_name(query->value(10).toString().toStdString()); - } - - if (!query->value(11).isNull()) { - info->set_room_id(query->value(11).toInt()); - } - - if (!query->value(12).isNull()) { - info->set_replay_id(query->value(12).toInt()); - } - - info->set_reported_user_level(reportUserLevel(!query->value(13).isNull(), query->value(14).toInt())); + for (const ServerInfo_Report &report : reports) { + *re->add_reports() = report; } rc.setResponseExtension(re); @@ -2636,69 +2544,20 @@ Response::ResponseCode AbstractServerSocketInterface::cmdReportMyList(const Comm return Response::RespFunctionNotAllowed; } - if (!sqlInterface->checkSql()) { + if (!databaseInterface->checkSql()) { return Response::RespInternalError; } sendPendingReportNotifications(rc); - QString queryStr = "SELECT r.id, r.reporter_name, r.reported_user_name, r.game_id, " - "r.category, r.description, r.created_at, r.status, " - "r.resolution_note, u.name AS assigned_mod_name, ru.name AS resolved_by_name, r.room_id, " - "(SELECT id FROM {prefix}_replays WHERE id_game = r.game_id ORDER BY id DESC LIMIT 1) " - "AS replay_id, u2.id AS reported_user_id, u2.admin AS reported_user_admin " - "FROM {prefix}_reports r " - "LEFT JOIN {prefix}_users u ON r.assigned_to = u.id " - "LEFT JOIN {prefix}_users ru ON r.resolved_by = ru.id " - "LEFT JOIN {prefix}_users u2 ON u2.name = r.reported_user_name " - "WHERE r.reporter_id = :reporter_id " - "ORDER BY r.created_at DESC LIMIT 200"; - - QSqlQuery *query = sqlInterface->prepareQuery(queryStr); - query->bindValue(":reporter_id", userInfo->id()); - - if (!sqlInterface->execSqlQuery(query)) { + QList reports; + if (!databaseInterface->getReportsSubmittedBy(userInfo->id(), reports)) { return Response::RespInternalError; } Response_ReportMyList *re = new Response_ReportMyList; - - while (query->next()) { - ServerInfo_Report *info = re->add_reports(); - - info->set_report_id(query->value(0).toInt()); - info->set_reporter_name(query->value(1).toString().toStdString()); - info->set_reported_user_name(query->value(2).toString().toStdString()); - - if (!query->value(3).isNull()) { - info->set_game_id(query->value(3).toInt()); - } - - info->set_category(query->value(4).toString().toStdString()); - info->set_description(query->value(5).toString().toStdString()); - info->set_report_time(query->value(6).toDateTime().toSecsSinceEpoch()); - info->set_status(query->value(7).toString().toStdString()); - - if (!query->value(8).isNull()) { - info->set_resolution_note(query->value(8).toString().toStdString()); - } - - if (!query->value(9).isNull()) { - info->set_assigned_mod_name(query->value(9).toString().toStdString()); - } - if (!query->value(10).isNull()) { - info->set_resolved_by_name(query->value(10).toString().toStdString()); - } - - if (!query->value(11).isNull()) { - info->set_room_id(query->value(11).toInt()); - } - - if (!query->value(12).isNull()) { - info->set_replay_id(query->value(12).toInt()); - } - - info->set_reported_user_level(reportUserLevel(!query->value(13).isNull(), query->value(14).toInt())); + for (const ServerInfo_Report &report : reports) { + *re->add_reports() = report; } rc.setResponseExtension(re); @@ -2749,103 +2608,37 @@ Response::ResponseCode AbstractServerSocketInterface::cmdReportDetails(const Com return Response::RespFunctionNotAllowed; } - if (!sqlInterface->checkSql()) { + if (!databaseInterface->checkSql()) { return Response::RespInternalError; } const int reportId = cmd.report_id(); - // Columns: 0=id, 1=reporter_id, 2=reporter_name, 3=reported_user_name, 4=game_id, - // 5=category, 6=description, 7=created_at, 8=status, - // 9=resolution_note, 10=assigned_mod_name, 11=resolved_by_name, - // 12=chat_log, 13=room_id, 14=resolution_time, 15=replay_id - QString queryStr = - "SELECT r.id, r.reporter_id, r.reporter_name, r.reported_user_name, r.game_id, " - "r.category, r.description, r.created_at, r.status, " - "r.resolution_note, u.name AS assigned_mod_name, ru.name AS resolved_by_name, r.chat_log, r.room_id, " - "r.resolution_time, " - "(SELECT id FROM {prefix}_replays WHERE id_game = r.game_id ORDER BY id DESC LIMIT 1) " - "AS replay_id " - "FROM {prefix}_reports r " - "LEFT JOIN {prefix}_users u ON r.assigned_to = u.id " - "LEFT JOIN {prefix}_users ru ON r.resolved_by = ru.id " - "WHERE r.id = :id"; - QSqlQuery *query = sqlInterface->prepareQuery(queryStr); - query->bindValue(":id", reportId); - - if (!sqlInterface->execSqlQuery(query)) { - return Response::RespInternalError; - } - - if (!query->next()) { - return Response::RespNameNotFound; + int reporterId = -1; + ServerInfo_Report info; + switch (databaseInterface->getReportForDetails(reportId, reporterId, info)) { + case Server_DatabaseInterface::ReportLookupResult::Failed: + return Response::RespInternalError; + case Server_DatabaseInterface::ReportLookupResult::NotFound: + return Response::RespNameNotFound; + case Server_DatabaseInterface::ReportLookupResult::Ok: + break; } const bool isMod = userInfo->user_level() & ServerInfo_User::IsModerator; - const int reporterId = query->value(1).toInt(); if (reporterId != userInfo->id() && !isMod) { return Response::RespAccessDenied; } - ServerInfo_Report *info = new ServerInfo_Report; - info->set_report_id(query->value(0).toInt()); - info->set_reporter_name(query->value(2).toString().toStdString()); - info->set_reported_user_name(query->value(3).toString().toStdString()); - - if (!query->value(4).isNull()) { - info->set_game_id(query->value(4).toInt()); - } - - info->set_category(query->value(5).toString().toStdString()); - info->set_description(query->value(6).toString().toStdString()); - info->set_report_time(query->value(7).toDateTime().toSecsSinceEpoch()); - info->set_status(query->value(8).toString().toStdString()); - - if (!query->value(9).isNull()) { - info->set_resolution_note(query->value(9).toString().toStdString()); - } - - if (!query->value(10).isNull()) { - info->set_assigned_mod_name(query->value(10).toString().toStdString()); - } - if (!query->value(11).isNull()) { - info->set_resolved_by_name(query->value(11).toString().toStdString()); - } - - if (!query->value(12).isNull()) { - info->set_chat_log(query->value(12).toString().toStdString()); - } - - if (!query->value(13).isNull()) { - info->set_room_id(query->value(13).toInt()); - } - - if (!query->value(14).isNull()) { - info->set_resolution_time(query->value(14).toDateTime().toSecsSinceEpoch()); - } - - if (!query->value(15).isNull()) { - info->set_replay_id(query->value(15).toInt()); - } - - QSqlQuery *commentQuery = - sqlInterface->prepareQuery("SELECT c.author_name, c.comment_text, c.created_at, c.is_moderator " - "FROM {prefix}_report_comments c " - "WHERE c.report_id = :report_id ORDER BY c.created_at ASC"); - commentQuery->bindValue(":report_id", reportId); - - if (sqlInterface->execSqlQuery(commentQuery)) { - while (commentQuery->next()) { - ServerInfo_ReportComment *comment = info->add_comments(); - comment->set_author_name(commentQuery->value(0).toString().toStdString()); - comment->set_comment_text(commentQuery->value(1).toString().toStdString()); - comment->set_comment_time(commentQuery->value(2).toDateTime().toSecsSinceEpoch()); - comment->set_is_moderator(commentQuery->value(3).toBool()); + QList comments; + if (databaseInterface->getReportComments(reportId, comments)) { + for (const ServerInfo_ReportComment &comment : comments) { + *info.add_comments() = comment; } } Response_ReportDetails *re = new Response_ReportDetails; - re->set_allocated_report(info); + re->set_allocated_report(new ServerInfo_Report(info)); rc.setResponseExtension(re); return Response::RespOk; }