Merge branch 'master' into ci/use-ninja-win-clean

This commit is contained in:
Bruno Alexandre Rosa 2026-04-11 14:09:03 -03:00
commit eb62b30411
28 changed files with 3063 additions and 2357 deletions

View file

@ -0,0 +1,29 @@
FROM ubuntu:26.04
RUN apt-get update && \
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
build-essential \
ca-certificates \
ccache \
clang-format \
cmake \
file \
g++ \
git \
libgl-dev \
liblzma-dev \
libmariadb-dev-compat \
libprotobuf-dev \
libqt6multimedia6 \
libqt6sql6-mysql \
ninja-build \
protobuf-compiler \
qt6-image-formats-plugins \
qt6-l10n-tools \
qt6-multimedia-dev \
qt6-svg-dev \
qt6-tools-dev \
qt6-tools-dev-tools \
qt6-websockets-dev \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*

View file

@ -17,6 +17,7 @@ Available pre-compiled binaries for installation:
<kbd>macOS 13+</kbd> <sub><i>Ventura</i></sub> <sub>Intel</sub>
<b>Linux</b>
<kbd>Ubuntu 26.04 LTS</kbd> <sub><i>Resolute Racoon</i></sub>
<kbd>Ubuntu 24.04 LTS</kbd> <sub><i>Noble Numbat</i></sub>
<kbd>Ubuntu 22.04 LTS</kbd> <sub><i>Jammy Jellyfish</i></sub>
<kbd>Debian 13</kbd> <sub><i>Trixie</i></sub>

View file

@ -9,6 +9,9 @@ updates:
- package-ecosystem: "gitsubmodule"
# Look for `.gitmodules` in the `root` directory
directory: "/"
ignore:
# Ignore updates for vcpkg (Bump to latest tag not working (no SemVer used) & macOS Intel triplet broken with newer releases)
- dependency-name: "vcpkg"
# Check for updates once a month
schedule:
interval: "monthly"

View file

@ -142,15 +142,21 @@ jobs:
- distro: Ubuntu
version: 22.04
package: DEB
test: skip # Running tests on all distros is superfluous
- distro: Ubuntu
version: 24.04
package: DEB
- distro: Ubuntu
version: 26.04
package: DEB
name: ${{matrix.distro}} ${{matrix.version}}
needs: configure
runs-on: ubuntu-latest
continue-on-error: ${{matrix.allow-failure == 'yes'}}
timeout-minutes: 70
env:
NAME: ${{matrix.distro}}${{matrix.version}}
CACHE: ${{github.workspace}}/.cache/${{matrix.distro}}${{matrix.version}} # directory for caching docker image and ccache
@ -202,9 +208,10 @@ jobs:
--ccache "$CCACHE_SIZE" $NO_CLIENT
.ci/name_build.sh
# Delete used cache to emulate a ccache update. See https://github.com/actions/cache/issues/342.
# Delete used cache to emulate a ccache update. See https://github.com/actions/cache/issues/342
- name: Delete remote compiler cache (ccache)
if: github.ref == 'refs/heads/master' && steps.ccache_restore.outputs.cache-hit
continue-on-error: true
env:
GH_TOKEN: ${{ github.token }}
run: gh cache delete --repo ${{ github.repository }} ${{ steps.ccache_restore.outputs.cache-primary-key }}
@ -265,7 +272,7 @@ jobs:
override_target: 13
make_package: 1
package_suffix: "-macOS13_Intel"
qt_version: 6.10.*
qt_version: 6.11.*
qt_arch: clang_64
qt_modules: qtimageformats qtmultimedia qtwebsockets
cmake_generator: Ninja
@ -279,7 +286,7 @@ jobs:
type: Release
make_package: 1
package_suffix: "-macOS14"
qt_version: 6.10.*
qt_version: 6.11.*
qt_arch: clang_64
qt_modules: qtimageformats qtmultimedia qtwebsockets
cmake_generator: Ninja
@ -293,7 +300,7 @@ jobs:
type: Release
make_package: 1
package_suffix: "-macOS15"
qt_version: 6.10.*
qt_version: 6.11.*
qt_arch: clang_64
qt_modules: qtimageformats qtmultimedia qtwebsockets
cmake_generator: Ninja
@ -305,7 +312,7 @@ jobs:
soc: Apple
xcode: "16.4"
type: Debug
qt_version: 6.10.*
qt_version: 6.11.*
qt_arch: clang_64
qt_modules: qtimageformats qtmultimedia qtwebsockets
cmake_generator: Ninja
@ -317,7 +324,7 @@ jobs:
type: Release
make_package: 1
package_suffix: "-Win10"
qt_version: 6.10.*
qt_version: 6.11.*
qt_arch: win64_msvc2022_64
qt_modules: qtimageformats qtmultimedia qtwebsockets
cmake_generator: Ninja
@ -325,6 +332,7 @@ jobs:
name: ${{matrix.os}} ${{matrix.target}}${{ matrix.soc == 'Intel' && ' Intel' || '' }}${{ matrix.type == 'Debug' && ' Debug' || '' }}
needs: configure
runs-on: ${{matrix.runner}}
timeout-minutes: 100
env:
CCACHE_DIR: ${{github.workspace}}/.cache/
# Cache size over the entire repo is 10Gi:
@ -396,6 +404,8 @@ jobs:
if: matrix.os == 'Windows'
uses: jurplel/install-qt-action@v4
with:
# qt 6.11.0 only works with aqtinstall directly from git until aqtinstall 3.4 is released
aqtsource: git+https://github.com/miurahr/aqtinstall.git
version: ${{ steps.resolve_qt_version.outputs.version }}
arch: ${{matrix.qt_arch}}
modules: ${{matrix.qt_modules}}
@ -433,9 +443,10 @@ jobs:
TARGET_MACOS_VERSION: ${{ matrix.override_target }}
run: .ci/compile.sh --server --test --vcpkg
# Delete used cache to emulate a ccache update. See https://github.com/actions/cache/issues/342.
# Delete used cache to emulate a ccache update. See https://github.com/actions/cache/issues/342
- name: Delete remote compiler cache (ccache)
if: github.ref == 'refs/heads/master' && matrix.use_ccache == 1 && steps.ccache_restore.outputs.cache-hit
continue-on-error: true
env:
GH_TOKEN: ${{ github.token }}
run: gh cache delete --repo ${{ github.repository }} ${{ steps.ccache_restore.outputs.cache-primary-key }}

View file

@ -564,6 +564,9 @@ if(WIN32)
PATTERN "styles/qopensslbackend.dll"
PATTERN "styles/qschannelbackend.dll"
PATTERN "styles/qwindowsvistastyle.dll"
PATTERN "styles/qwindows11style.dll"
PATTERN "styles/qmodernwindowsstyle.dll"
PATTERN "styles/qmodernwindowsstyled.dll"
PATTERN "tls/qcertonlybackend.dll"
PATTERN "tls/qopensslbackend.dll"
PATTERN "tls/qschannelbackend.dll"

File diff suppressed because it is too large Load diff

View file

@ -1,8 +1,9 @@
#include "abstract_game.h"
#include "../interface/widgets/tabs/tab_game.h"
#include "player/player.h"
AbstractGame::AbstractGame(TabGame *_tab) : tab(_tab)
AbstractGame::AbstractGame(TabGame *_tab) : QObject(_tab), tab(_tab)
{
gameMetaInfo = new GameMetaInfo(this);
gameEventHandler = new GameEventHandler(this);

View file

@ -10,7 +10,7 @@
#include <libcockatrice/protocol/pb/command_reveal_cards.pb.h>
PlayerMenu::PlayerMenu(Player *_player) : player(_player)
PlayerMenu::PlayerMenu(Player *_player) : QObject(_player), player(_player)
{
playerMenu = new TearOffMenu();

View file

@ -37,7 +37,7 @@ private slots:
void refreshShortcuts();
public:
PlayerMenu(Player *player);
explicit PlayerMenu(Player *player);
/// Lifecycle methods: delegate to all managedComponents, plus counters separately via player->getCounters().
void retranslateUi();

View file

@ -3,6 +3,7 @@
#include "../../interface/widgets/tabs/tab_game.h"
#include "../../interface/widgets/utility/get_text_with_max.h"
#include "../board/card_item.h"
#include "../client/settings/card_counter_settings.h"
#include "../dialogs/dlg_move_top_cards_until.h"
#include "../dialogs/dlg_roll_dice.h"
#include "../zones/hand_zone.h"
@ -27,13 +28,15 @@
#include <libcockatrice/protocol/pb/command_shuffle.pb.h>
#include <libcockatrice/protocol/pb/command_undo_draw.pb.h>
#include <libcockatrice/protocol/pb/context_move_card.pb.h>
#include <libcockatrice/utility/expression.h>
#include <libcockatrice/utility/trice_limits.h>
#include <libcockatrice/utility/zone_names.h>
// milliseconds in between triggers of the move top cards until action
static constexpr int MOVE_TOP_CARD_UNTIL_INTERVAL = 100;
PlayerActions::PlayerActions(Player *_player) : player(_player), lastTokenTableRow(0), movingCardsUntil(false)
PlayerActions::PlayerActions(Player *_player)
: QObject(_player), player(_player), lastTokenTableRow(0), movingCardsUntil(false)
{
moveTopCardTimer = new QTimer(this);
moveTopCardTimer->setInterval(MOVE_TOP_CARD_UNTIL_INTERVAL);
@ -1572,23 +1575,34 @@ void PlayerActions::actCardCounterTrigger()
break;
}
case 11: { // set counter with dialog
bool ok;
player->setDialogSemaphore(true);
int oldValue = 0;
if (player->getGameScene()->selectedItems().size() == 1) {
auto *card = static_cast<CardItem *>(player->getGameScene()->selectedItems().first());
oldValue = card->getCounters().value(counterId, 0);
// If a single card is selected, we show the old value in the dialog. Otherwise, we show "x"
QList<QGraphicsItem *> sel = player->getGameScene()->selectedItems();
QString oldValueForDlg = "x";
if (sel.size() == 1) {
auto *card = dynamic_cast<CardItem *>(sel.first());
oldValueForDlg = QString::number(card->getCounters().value(counterId, 0));
}
int number = QInputDialog::getInt(player->getGame()->getTab(), tr("Set counters"), tr("Number:"), oldValue,
0, MAX_COUNTERS_ON_CARD, 1, &ok);
auto &cardCounterSettings = SettingsCache::instance().cardCounters();
QString counterName = cardCounterSettings.displayName(counterId);
AbstractCounterDialog dialog(counterName, oldValueForDlg, player->getGame()->getTab());
int ok = dialog.exec();
player->setDialogSemaphore(false);
if (player->clearCardsToDelete() || !ok) {
return;
}
for (const auto &item : player->getGameScene()->selectedItems()) {
auto *card = static_cast<CardItem *>(item);
for (const auto &item : sel) {
auto *card = dynamic_cast<CardItem *>(item);
int oldValue = card->getCounters().value(counterId, 0);
Expression exp(oldValue);
int number = static_cast<int>(exp.parse(dialog.textValue()));
auto *cmd = new Command_SetCardCounter;
cmd->set_zone(card->getZone()->getName().toStdString());
cmd->set_card_id(card->getId());

View file

@ -32,7 +32,7 @@
#include <libcockatrice/protocol/pb/event_shuffle.pb.h>
#include <libcockatrice/utility/zone_names.h>
PlayerEventHandler::PlayerEventHandler(Player *_player) : player(_player)
PlayerEventHandler::PlayerEventHandler(Player *_player) : QObject(_player), player(_player)
{
}

View file

@ -28,6 +28,7 @@ CardPictureLoader::CardPictureLoader() : QObject(nullptr)
connect(&SettingsCache::instance(), &SettingsCache::picDownloadChanged, this,
&CardPictureLoader::picDownloadChanged);
qRegisterMetaType<ExactCard>();
connect(worker, &CardPictureLoaderWorker::imageLoaded, this, &CardPictureLoader::imageLoaded);
statusBar = new CardPictureLoaderStatusBar(nullptr);

View file

@ -179,7 +179,7 @@ QBrush ThemeManager::loadExtraBrush(QString fileName, QBrush &fallbackBrush)
static inline QPalette createDarkGreenFusionPalette()
{
QPalette p;
QPalette p = QStyleFactory::create("Fusion")->standardPalette();
// ---------- Core backgrounds ----------
p.setColor(QPalette::Window, QColor(30, 30, 30)); // #ff1e1e1e
@ -248,7 +248,7 @@ static inline QPalette createDarkGreenFusionPalette()
static inline QPalette createLightGreenFusionPalette()
{
QPalette p;
QPalette p = QStyleFactory::create("Fusion")->standardPalette();
// ---------- Core backgrounds ----------
p.setColor(QPalette::Window, QColor(240, 240, 240)); // #fff0f0f0
@ -332,13 +332,15 @@ void ThemeManager::themeChangedSlot()
}
if (themeName == FUSION_THEME_NAME) {
qApp->setStyle(QStyleFactory::create("Fusion"));
QStyle *fusionStyle = QStyleFactory::create("Fusion");
qApp->setStyle(fusionStyle);
#if (QT_VERSION >= QT_VERSION_CHECK(6, 5, 0))
QPalette palette;
// Start from Fusion's own palette so dark mode is handled correctly,
// then apply any tweaks on top of it.
QPalette palette = fusionStyle->standardPalette();
if (QGuiApplication::styleHints()->colorScheme() == Qt::ColorScheme::Dark) {
palette.setColor(QPalette::AlternateBase, QColor(53, 53, 53));
}
qApp->setPalette(palette);
#endif
} else if (themeName == FUSION_THEME_NAME_LIGHT) {
@ -348,7 +350,7 @@ void ThemeManager::themeChangedSlot()
qApp->setStyle(QStyleFactory::create("Fusion"));
qApp->setPalette(createDarkGreenFusionPalette());
} else {
qApp->setStyle(defaultStyleName); // setting the style also sets the palette
qApp->setStyle(QStyleFactory::create(defaultStyleName)); // setting the style also sets the palette
}
if (dirPath.isEmpty()) {

View file

@ -20,7 +20,6 @@ ResizablePanel::ResizablePanel(const QString &_typeId, AbstractAnalyticsPanelWid
frame = new QFrame(this);
frame->setFrameShape(QFrame::Box);
frame->setLineWidth(2);
frame->setStyleSheet("border: none;");
auto *frameLayout = new QVBoxLayout(frame);
frameLayout->setContentsMargins(0, 0, 0, 0);
@ -30,15 +29,13 @@ ResizablePanel::ResizablePanel(const QString &_typeId, AbstractAnalyticsPanelWid
frameLayout->addWidget(analyticsPanel);
dropIndicator = new QFrame(frame);
dropIndicator->setStyleSheet("background-color: #3daee9;");
dropIndicator->setFixedHeight(3);
dropIndicator->hide(); // hidden by default
dropIndicator->raise(); // make sure it's above children
selectionOverlay = new QFrame(frame);
selectionOverlay->setStyleSheet("background-color: rgba(61,174,233,50);"); // semi-transparent blue
selectionOverlay->hide(); // hidden by default
selectionOverlay->raise(); // make sure it is above children
selectionOverlay->hide(); // hidden by default
selectionOverlay->raise(); // make sure it is above children
selectionOverlay->setAttribute(Qt::WA_TransparentForMouseEvents);
// Bottom bar with drag button and resize handle
@ -51,24 +48,41 @@ ResizablePanel::ResizablePanel(const QString &_typeId, AbstractAnalyticsPanelWid
dragButton = new QPushButton("", bottomBar);
dragButton->setFixedSize(40, 8);
dragButton->setCursor(Qt::OpenHandCursor);
dragButton->setStyleSheet("QPushButton { "
"background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #4a4a4a, stop:1 #3a3a3a); "
"border: none; color: #888; font-size: 10px; }"
"QPushButton:hover { background: #5a5a5a; }");
bottomLayout->addWidget(dragButton);
// Resize handle fills the rest
resizeHandle = new QWidget(bottomBar);
resizeHandle->setFixedHeight(8);
resizeHandle->setCursor(Qt::SizeVerCursor);
resizeHandle->setStyleSheet("background: qlineargradient(x1:0, y1:0, x2:0, y2:1, "
"stop:0 #3a3a3a, stop:1 #2a2a2a);");
bottomLayout->addWidget(resizeHandle, 1);
frameLayout->addWidget(bottomBar);
mainLayout->addWidget(frame);
const QPalette &pal = QApplication::palette();
QColor mid = pal.color(QPalette::Mid);
QColor dark = pal.color(QPalette::Dark);
QColor midLight = pal.color(QPalette::Midlight);
QColor shadow = pal.color(QPalette::Shadow);
QColor placeholderText = pal.color(QPalette::PlaceholderText);
frame->setStyleSheet("QFrame { border: none; }");
dropIndicator->setStyleSheet("QFrame { background-color: #3daee9; }");
selectionOverlay->setStyleSheet("QFrame { background-color: rgba(61,174,233,50); }");
dragButton->setStyleSheet(QString("QPushButton { "
"background: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 %1, stop:1 %2); "
"border: none; color: %3; font-size: 10px; }"
"QPushButton:hover { background: %4; }")
.arg(mid.name(), dark.name(), placeholderText.name(), midLight.name()));
resizeHandle->setStyleSheet(QString("QWidget { background: qlineargradient(x1:0, y1:0, x2:0, y2:1, "
"stop:0 %1, stop:1 %2); }")
.arg(dark.name(), shadow.name()));
// Set size policy
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);

View file

@ -30,6 +30,8 @@
#include <libcockatrice/protocol/pb/response_replay_get_code.pb.h>
#include <libcockatrice/protocol/pending_command.h>
inline Q_LOGGING_CATEGORY(TabReplaysLog, "replays_tab");
TabReplays::TabReplays(TabSupervisor *_tabSupervisor, AbstractClient *_client, const ServerInfo_User *currentUserInfo)
: Tab(_tabSupervisor), client(_client)
{
@ -265,9 +267,11 @@ void TabReplays::actOpenLocalReplay()
f.close();
GameReplay *replay = new GameReplay;
replay->ParseFromArray(_data.data(), _data.size());
emit openReplay(replay);
if (replay->ParseFromArray(_data.data(), _data.size())) {
emit openReplay(replay);
} else {
qCWarning(TabReplaysLog) << "could not parse replay!";
}
}
}
@ -379,9 +383,12 @@ void TabReplays::openRemoteReplayFinished(const Response &r)
const Response_ReplayDownload &resp = r.GetExtension(Response_ReplayDownload::ext);
GameReplay *replay = new GameReplay;
replay->ParseFromString(resp.replay_data());
if (replay->ParseFromString(resp.replay_data())) {
emit openReplay(replay);
emit openReplay(replay);
} else {
qCWarning(TabReplaysLog) << "could not parse remote replay!";
}
}
void TabReplays::actDownload()

View file

@ -84,6 +84,7 @@
const QString MainWindow::appName = "Cockatrice";
const QStringList MainWindow::fileNameFilters = QStringList() << QObject::tr("Cockatrice card database (*.xml)")
<< QObject::tr("All files (*.*)");
inline Q_LOGGING_CATEGORY(MainWindowLog, "main_window");
/**
* Replaces the tab-specific menus that are shown in the menuBar.
@ -277,9 +278,11 @@ void MainWindow::actWatchReplay()
file.close();
replay = new GameReplay;
replay->ParseFromArray(buf.data(), buf.size());
tabSupervisor->openReplay(replay);
if (replay->ParseFromArray(buf.data(), buf.size())) {
tabSupervisor->openReplay(replay);
} else {
qCWarning(MainWindowLog) << "failed to parse replay!";
}
}
void MainWindow::localGameEnded()

File diff suppressed because it is too large Load diff

View file

@ -114,5 +114,6 @@ public:
*/
void emitPixmapUpdated() const;
};
Q_DECLARE_METATYPE(ExactCard)
#endif // EXACT_CARD_H

View file

@ -390,14 +390,18 @@ void RemoteClient::readData()
return;
ServerMessage newServerMessage;
newServerMessage.ParseFromArray(inputBuffer.data(), messageLength);
qCDebug(RemoteClientLog).noquote() << "IN" << getSafeDebugString(newServerMessage);
bool ok = newServerMessage.ParseFromArray(inputBuffer.data(), messageLength);
inputBuffer.remove(0, messageLength);
messageInProgress = false;
processProtocolItem(newServerMessage);
if (ok) {
qCDebug(RemoteClientLog).noquote() << "IN" << getSafeDebugString(newServerMessage);
processProtocolItem(newServerMessage);
} else {
qCDebug(RemoteClientLog) << "parsing error!";
}
if (getStatus() == StatusDisconnecting) // use thread-safe getter
doDisconnectFromServer();
@ -408,11 +412,13 @@ void RemoteClient::websocketMessageReceived(const QByteArray &message)
{
lastDataReceived = timeRunning;
ServerMessage newServerMessage;
newServerMessage.ParseFromArray(message.data(), message.length());
if (newServerMessage.ParseFromArray(message.data(), message.length())) {
qCDebug(RemoteClientLog).noquote() << "IN" << getSafeDebugString(newServerMessage);
qCDebug(RemoteClientLog).noquote() << "IN" << getSafeDebugString(newServerMessage);
processProtocolItem(newServerMessage);
processProtocolItem(newServerMessage);
} else {
qCDebug(RemoteClientLog) << "parsing error!";
}
}
void RemoteClient::sendCommandContainer(const CommandContainer &cont)
@ -426,19 +432,27 @@ void RemoteClient::sendCommandContainer(const CommandContainer &cont)
qCDebug(RemoteClientLog).noquote() << "OUT" << getSafeDebugString(cont);
QByteArray buf;
bool ok;
if (usingWebSocket) {
buf.resize(size);
cont.SerializeToArray(buf.data(), size);
websocket->sendBinaryMessage(buf);
ok = cont.SerializeToArray(buf.data(), size);
if (ok) {
websocket->sendBinaryMessage(buf);
}
} else {
buf.resize(size + 4);
cont.SerializeToArray(buf.data() + 4, size);
buf.data()[3] = (unsigned char)size;
buf.data()[2] = (unsigned char)(size >> 8);
buf.data()[1] = (unsigned char)(size >> 16);
buf.data()[0] = (unsigned char)(size >> 24);
ok = cont.SerializeToArray(buf.data() + 4, size);
if (ok) {
buf.data()[3] = (unsigned char)size;
buf.data()[2] = (unsigned char)(size >> 8);
buf.data()[1] = (unsigned char)(size >> 16);
buf.data()[0] = (unsigned char)(size >> 24);
socket->write(buf);
socket->write(buf);
}
}
if (!ok) {
qCDebug(RemoteClientLog) << "transmit error!";
}
}

View file

@ -101,6 +101,10 @@ QString getSafeDebugString(const ::google::protobuf::Message &message)
#endif // GOOGLE_PROTOBUF_VERSION > 3004000
std::string debug_string;
printer.PrintToString(message, &debug_string);
return QString::number(size) + " bytes " + QString::fromStdString(debug_string);
bool ok = printer.PrintToString(message, &debug_string);
if (ok) {
return QString::number(size) + " bytes " + QString::fromStdString(debug_string);
} else {
return "[could not convert message to string]";
}
}

View file

@ -20,7 +20,7 @@ peg::parser math(R"(
NUMBER <- < '-'? [0-9]+ >
NAME <- < [a-z][a-z0-9]* >
VARIABLE <- < [x] >
VARIABLE <- < [xX] >
FUNCTION <- NAME '(' EXPRESSION ( [,\n] EXPRESSION )* ')'
%whitespace <- [ \t\r]*

View file

@ -278,7 +278,7 @@
<context>
<name>OracleImporter</name>
<message>
<location filename="src/oracleimporter.cpp" line="541"/>
<location filename="src/oracleimporter.cpp" line="540"/>
<source>Dummy set containing tokens</source>
<translation type="unfinished"></translation>
</message>
@ -286,7 +286,7 @@
<context>
<name>OracleWizard</name>
<message>
<location filename="src/oraclewizard.cpp" line="70"/>
<location filename="src/oraclewizard.cpp" line="97"/>
<source>Oracle Importer</source>
<translation type="unfinished"></translation>
</message>

View file

@ -360,13 +360,13 @@ int OracleImporter::importCardsFromSet(const CardSetPtr &currentSet, const QList
}
// split cards are considered a single card, enqueue for later merging
if (layout == "split" || layout == "aftermath" || layout == "adventure") {
if (layout == "split" || layout == "aftermath" || layout == "adventure" || layout == "prepare") {
auto _faceName = getStringPropertyFromMap(card, "faceName");
SplitCardPart split(_faceName, text, properties, printingInfo);
auto found_iter = splitCards.find(name + numProperty);
if (found_iter == splitCards.end()) {
splitCards.insert(name + numProperty, {{split}, name});
} else if (layout == "adventure") {
} else if (layout == "adventure" || layout == "prepare") {
found_iter->first.insert(0, split);
} else {
found_iter->first.append(split);

View file

@ -22,6 +22,7 @@ const QMap<QString, CardSet::Priority> setTypePriorities{
{"archenemy", CardSet::PriorityReprint},
{"arsenal", CardSet::PriorityReprint},
{"box", CardSet::PriorityReprint},
{"eternal", CardSet::PriorityReprint},
{"from_the_vault", CardSet::PriorityReprint},
{"masterpiece", CardSet::PriorityReprint},
{"masters", CardSet::PriorityReprint},

View file

@ -21,6 +21,10 @@ OracleWizard::OracleWizard(QWidget *parent) : QWizard(parent)
// define a dummy context that will be used where needed
QString dummy = QT_TRANSLATE_NOOP("i18n", "English");
#ifdef Q_OS_WIN
setWizardStyle(QWizard::ModernStyle);
#endif
QString oracleSettingsFile = SettingsCache::instance().getSettingsPath() + "oracle.ini";
settings = new QSettings(oracleSettingsFile, QSettings::IniFormat, this);

View file

@ -3,6 +3,7 @@
#include "main.h"
#include "server_logger.h"
#include <QLoggingCategory>
#include <QSslSocket>
#include <google/protobuf/descriptor.h>
#include <libcockatrice/protocol/debug_pb_message.h>
@ -21,6 +22,8 @@
#include <server_protocolhandler.h>
#include <server_room.h>
inline Q_LOGGING_CATEGORY(IslInterfaceLog, "isl_interface");
void IslInterface::sharedCtor(const QSslCertificate &cert, const QSslKey &privateKey)
{
socket = new QSslSocket(this);
@ -113,10 +116,11 @@ void IslInterface::initServer()
socket->startServerEncryption();
if (!socket->waitForEncrypted(5000)) {
QList<QSslError> sslErrors(socket->sslHandshakeErrors());
if (sslErrors.isEmpty())
qDebug() << "[ISL] SSL handshake timeout, terminating connection";
else
qDebug() << "[ISL] SSL errors:" << sslErrors;
if (sslErrors.isEmpty()) {
qCDebug(IslInterfaceLog) << "SSL handshake timeout, terminating connection";
} else {
qCWarning(IslInterfaceLog) << "SSL errors:" << sslErrors;
}
deleteLater();
return;
}
@ -157,7 +161,7 @@ void IslInterface::initServer()
server->islLock.lockForWrite();
if (server->islConnectionExists(serverId)) {
qDebug() << "[ISL] Duplicate connection to #" << serverId << "terminating connection";
qCDebug(IslInterfaceLog) << "Duplicate connection to #" << serverId << "terminating connection";
deleteLater();
} else {
transmitMessage(message);
@ -180,27 +184,28 @@ void IslInterface::initClient()
expectedErrors.append(QSslError(QSslError::SelfSignedCertificate, peerCert));
socket->ignoreSslErrors(expectedErrors);
qDebug() << "[ISL] Connecting to #" << serverId << ":" << peerAddress << ":" << peerPort;
qCDebug(IslInterfaceLog) << "Connecting to #" << serverId << ":" << peerAddress << ":" << peerPort;
socket->connectToHostEncrypted(peerAddress, peerPort, peerHostName);
if (!socket->waitForConnected(5000)) {
qDebug() << "[ISL] Socket error:" << socket->errorString();
qCDebug(IslInterfaceLog) << "Socket error:" << socket->errorString();
deleteLater();
return;
}
if (!socket->waitForEncrypted(5000)) {
QList<QSslError> sslErrors(socket->sslHandshakeErrors());
if (sslErrors.isEmpty())
qDebug() << "[ISL] SSL handshake timeout, terminating connection";
else
qDebug() << "[ISL] SSL errors:" << sslErrors;
if (sslErrors.isEmpty()) {
qCDebug(IslInterfaceLog) << "SSL handshake timeout, terminating connection";
} else {
qCWarning(IslInterfaceLog) << "SSL errors:" << sslErrors;
}
deleteLater();
return;
}
server->islLock.lockForWrite();
if (server->islConnectionExists(serverId)) {
qDebug() << "[ISL] Duplicate connection to #" << serverId << "terminating connection";
qCDebug(IslInterfaceLog) << "Duplicate connection to #" << serverId << "terminating connection";
deleteLater();
return;
}
@ -242,17 +247,21 @@ void IslInterface::readClient()
return;
IslMessage newMessage;
newMessage.ParseFromArray(inputBuffer.data(), messageLength);
bool ok = newMessage.ParseFromArray(inputBuffer.data(), messageLength);
inputBuffer.remove(0, messageLength);
messageInProgress = false;
processMessage(newMessage);
if (ok) {
processMessage(newMessage);
} else {
qCWarning(IslInterfaceLog) << "parsing error!";
}
} while (!inputBuffer.isEmpty());
}
void IslInterface::catchSocketError(QAbstractSocket::SocketError socketError)
{
qDebug() << "[ISL] Socket error:" << socketError;
qCWarning(IslInterfaceLog) << "Socket error:" << socketError;
server->islLock.lockForWrite();
server->removeIslInterface(serverId);
@ -270,7 +279,10 @@ void IslInterface::transmitMessage(const IslMessage &item)
unsigned int size = static_cast<unsigned int>(item.ByteSize());
#endif
buf.resize(size + 4);
item.SerializeToArray(buf.data() + 4, size);
if (!item.SerializeToArray(buf.data() + 4, size)) {
qCWarning(IslInterfaceLog) << "transmit error!";
return;
}
buf.data()[3] = (unsigned char)size;
buf.data()[2] = (unsigned char)(size >> 8);
buf.data()[1] = (unsigned char)(size >> 16);
@ -368,7 +380,7 @@ void IslInterface::processSessionEvent(const SessionEvent &event, qint64 session
QReadLocker clientsLocker(&server->clientsLock);
Server_AbstractUserInterface *client = server->getUsersBySessionId().value(sessionId);
if (!client) {
qDebug() << "IslInterface::processSessionEvent: session id" << sessionId << "not found";
qCDebug(IslInterfaceLog) << "IslInterface::processSessionEvent: session id" << sessionId << "not found";
break;
}
const Event_GameJoined &gameJoined = event.GetExtension(Event_GameJoined::ext);
@ -382,7 +394,8 @@ void IslInterface::processSessionEvent(const SessionEvent &event, qint64 session
QReadLocker clientsLocker(&server->clientsLock);
Server_AbstractUserInterface *client = server->getUsersBySessionId().value(sessionId);
if (!client) {
qDebug() << "IslInterface::processSessionEvent: session id" << sessionId << "not found";
qCWarning(IslInterfaceLog)
<< "IslInterface::processSessionEvent: session id" << sessionId << "not found";
break;
}
@ -430,7 +443,7 @@ void IslInterface::processRoomCommand(const CommandContainer &cont, qint64 sessi
void IslInterface::processMessage(const IslMessage &item)
{
qDebug() << getSafeDebugString(item);
qCDebug(IslInterfaceLog) << getSafeDebugString(item);
switch (item.message_type()) {
case IslMessage::ROOM_COMMAND_CONTAINER: {

View file

@ -7,12 +7,15 @@
#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)
{
@ -56,17 +59,16 @@ bool Servatrice_DatabaseInterface::openDatabase()
sqlDatabase.close();
const QString poolStr = instanceId == -1 ? QString("main") : QString("pool %1").arg(instanceId);
qDebug().noquote() << QString("[%1] Opening database...").arg(poolStr);
qCDebug(DatabaseInterfaceLog).noquote() << poolStr << "Opening database...";
if (!sqlDatabase.open()) {
qCritical() << QString("[%1] Error opening database: %2").arg(poolStr).arg(sqlDatabase.lastError().text());
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)) {
qCritical() << QString("[%1] Error opening database: unable to load database schema version (hint: ensure the "
"cockatrice_schema_version exists)")
.arg(poolStr);
qCCritical(DatabaseInterfaceLog) << poolStr << "Error opening database: unable to load database schema version"
<< "(hint: ensure the cockatrice_schema_version exists)";
return false;
}
@ -74,24 +76,21 @@ bool Servatrice_DatabaseInterface::openDatabase()
const int dbversion = versionQuery->value(0).toInt();
const int expectedversion = DATABASE_SCHEMA_VERSION;
if (dbversion < expectedversion) {
qCritical() << QString("[%1] Error opening database: the database schema version is too old, you need to "
"run the migrations to update it from version %2 to version %3")
.arg(poolStr)
.arg(dbversion)
.arg(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) {
qCritical() << QString("[%1] Error opening database: the database schema version %2 is too new, you need "
"to update servatrice (this servatrice actually uses version %3)")
.arg(poolStr)
.arg(dbversion)
.arg(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 {
qCritical() << QString("[%1] Error opening database: unable to load database schema version (hint: ensure the "
"cockatrice_schema_version contains a single record)")
.arg(poolStr);
qCCritical(DatabaseInterfaceLog) << poolStr
<< "Error opening database: unable to load database schema version (hint: "
"ensure the cockatrice_schema_version contains a single record)";
return false;
}
@ -114,9 +113,7 @@ bool Servatrice_DatabaseInterface::checkSql()
if (query.lastError().isValid()) {
const auto &poolStr = instanceId == -1 ? QString("main") : QString("pool %1").arg(instanceId);
qCritical() << QString("[%1] Error executing query: %2, resetting connection")
.arg(poolStr)
.arg(query.lastError().text());
qCCritical(DatabaseInterfaceLog) << poolStr << "Error executing query:" << query.lastError().text();
sqlDatabase.close();
return openDatabase();
@ -145,7 +142,7 @@ bool Servatrice_DatabaseInterface::execSqlQuery(QSqlQuery *query)
if (query->exec())
return true;
const QString poolStr = instanceId == -1 ? QString("main") : QString("pool %1").arg(instanceId);
qCritical() << QString("[%1] Error executing query: %2").arg(poolStr).arg(query->lastError().text());
qCCritical(DatabaseInterfaceLog) << poolStr << "Error executing query:" << query->lastError().text();
sqlDatabase.close();
openDatabase();
return false;
@ -252,7 +249,8 @@ bool Servatrice_DatabaseInterface::registerUser(const QString &userName,
query->bindValue(":token", token);
if (!execSqlQuery(query)) {
qDebug() << "Failed to insert user: " << query->lastError() << " sql: " << query->lastQuery();
qCWarning(DatabaseInterfaceLog) << "Failed to insert user: " << query->lastError()
<< " sql: " << query->lastQuery();
return false;
}
@ -270,8 +268,8 @@ bool Servatrice_DatabaseInterface::activateUser(const QString &userName, const Q
activateQuery->bindValue(":username", userName);
activateQuery->bindValue(":token", token);
if (!execSqlQuery(activateQuery)) {
qDebug() << "Account activation failed: SQL error." << activateQuery->lastError()
<< " sql: " << activateQuery->lastQuery();
qCWarning(DatabaseInterfaceLog) << "Account activation failed: SQL error." << activateQuery->lastError()
<< " sql: " << activateQuery->lastQuery();
return false;
}
@ -284,7 +282,8 @@ bool Servatrice_DatabaseInterface::activateUser(const QString &userName, const Q
query->bindValue(":userName", userName);
if (!execSqlQuery(query)) {
qDebug() << "Failed to activate user: " << query->lastError() << " sql: " << query->lastQuery();
qCWarning(DatabaseInterfaceLog)
<< "Failed to activate user: " << query->lastError() << " sql: " << query->lastQuery();
return false;
}
@ -326,7 +325,7 @@ AuthenticationResult Servatrice_DatabaseInterface::checkUserPassword(Server_Prot
prepareQuery("select password_sha512, active from {prefix}_users where name = :name");
passwordQuery->bindValue(":name", user);
if (!execSqlQuery(passwordQuery)) {
qDebug("Login denied: SQL error");
qCWarning(DatabaseInterfaceLog) << "Login denied: SQL error";
return NotLoggedIn;
}
@ -334,7 +333,7 @@ AuthenticationResult Servatrice_DatabaseInterface::checkUserPassword(Server_Prot
const QString correctPasswordSha512 = passwordQuery->value(0).toString();
const bool userIsActive = passwordQuery->value(1).toBool();
if (!userIsActive) {
qDebug("Login denied: user not active");
qCWarning(DatabaseInterfaceLog) << "Login denied: user not active";
return UserIsInactive;
}
QString hashedPassword;
@ -344,14 +343,14 @@ AuthenticationResult Servatrice_DatabaseInterface::checkUserPassword(Server_Prot
hashedPassword = password;
}
if (correctPasswordSha512 == hashedPassword) {
qDebug("Login accepted: password right");
qCDebug(DatabaseInterfaceLog) << "Login accepted: password right";
return PasswordRight;
} else {
qDebug("Login denied: password wrong");
qCDebug(DatabaseInterfaceLog) << "Login denied: password wrong";
return NotLoggedIn;
}
} else {
qDebug("Login accepted: unknown user");
qCDebug(DatabaseInterfaceLog) << "Login accepted: unknown user";
return UnknownUser;
}
}
@ -369,7 +368,7 @@ bool Servatrice_DatabaseInterface::checkUserIsBanned(const QString &ipAddress,
return false;
if (!checkSql()) {
qDebug("Failed to check if user is banned. Database invalid.");
qCWarning(DatabaseInterfaceLog) << "Failed to check if user is banned. Database invalid.";
return false;
}
@ -400,7 +399,7 @@ bool Servatrice_DatabaseInterface::checkUserIsIdBanned(const QString &clientId,
idBanQuery->bindValue(":id", clientId);
idBanQuery->bindValue(":id2", clientId);
if (!execSqlQuery(idBanQuery)) {
qDebug() << "Id ban check failed: SQL error." << idBanQuery->lastError();
qCWarning(DatabaseInterfaceLog) << "Id ban check failed: SQL error." << idBanQuery->lastError();
return false;
}
@ -410,7 +409,7 @@ bool Servatrice_DatabaseInterface::checkUserIsIdBanned(const QString &clientId,
if ((secondsLeft > 0) || permanentBan) {
banReason = idBanQuery->value(2).toString();
banSecondsRemaining = permanentBan ? 0 : secondsLeft;
qDebug() << "User is banned by client id" << clientId;
qCDebug(DatabaseInterfaceLog) << "User is banned by client id" << clientId;
return true;
}
}
@ -428,7 +427,7 @@ bool Servatrice_DatabaseInterface::checkUserIsNameBanned(const QString &userName
nameBanQuery->bindValue(":name1", userName);
nameBanQuery->bindValue(":name2", userName);
if (!execSqlQuery(nameBanQuery)) {
qDebug() << "Name ban check failed: SQL error" << nameBanQuery->lastError();
qCWarning(DatabaseInterfaceLog) << "Name ban check failed: SQL error" << nameBanQuery->lastError();
return false;
}
@ -438,7 +437,7 @@ bool Servatrice_DatabaseInterface::checkUserIsNameBanned(const QString &userName
if ((secondsLeft > 0) || permanentBan) {
banReason = nameBanQuery->value(2).toString();
banSecondsRemaining = permanentBan ? 0 : secondsLeft;
qDebug() << "Username" << userName << "is banned by name";
qCDebug(DatabaseInterfaceLog) << "Username" << userName << "is banned by name";
return true;
}
}
@ -464,7 +463,7 @@ bool Servatrice_DatabaseInterface::checkUserIsIpBanned(const QString &ipAddress,
ipBanQuery->bindValue(":address", ipAddress);
ipBanQuery->bindValue(":address2", ipAddress);
if (!execSqlQuery(ipBanQuery)) {
qDebug() << "IP ban check failed: SQL error." << ipBanQuery->lastError();
qCWarning(DatabaseInterfaceLog) << "IP ban check failed: SQL error." << ipBanQuery->lastError();
return false;
}
@ -474,7 +473,7 @@ bool Servatrice_DatabaseInterface::checkUserIsIpBanned(const QString &ipAddress,
if ((secondsLeft > 0) || permanentBan) {
banReason = ipBanQuery->value(2).toString();
banSecondsRemaining = permanentBan ? 0 : secondsLeft;
qDebug() << "User is banned by address" << ipAddress;
qCDebug(DatabaseInterfaceLog) << "User is banned by address" << ipAddress;
return true;
}
}
@ -865,12 +864,17 @@ void Servatrice_DatabaseInterface::storeGameInformation(const QString &roomName,
const unsigned int size = static_cast<unsigned int>(replayList[i]->ByteSize());
#endif
blob.resize(size);
replayList[i]->SerializeToArray(blob.data(), size);
qulonglong replayId = replayList[i]->replay_id();
if (replayList[i]->SerializeToArray(blob.data(), size)) {
replayIds.append(QVariant((qulonglong)replayList[i]->replay_id()));
replayGameIds.append(gameInfo.game_id());
replayDurations.append(replayList[i]->duration_seconds());
replayBlobs.append(blob);
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();
}
}
{
@ -1017,7 +1021,7 @@ bool Servatrice_DatabaseInterface::changeUserPassword(const QString &user,
passwordQuery->bindValue(":name", user);
if (!execSqlQuery(passwordQuery)) {
qDebug("Change password denied: SQL error");
qCWarning(DatabaseInterfaceLog) << "Change password denied: SQL error";
return false;
}
@ -1084,7 +1088,7 @@ void Servatrice_DatabaseInterface::updateUsersLastLoginData(const QString &userN
QSqlQuery *query = prepareQuery("select id from {prefix}_users where name = :user_name");
query->bindValue(":user_name", userName);
if (!execSqlQuery(query)) {
qDebug("Failed to locate user id when updating users last login data: SQL Error");
qCWarning(DatabaseInterfaceLog) << "Failed to locate user id when updating users last login data: SQL Error";
return;
}
@ -1133,7 +1137,7 @@ QList<ServerInfo_Ban> Servatrice_DatabaseInterface::getUserBanHistory(const QStr
query->bindValue(":user_name", userName);
if (!execSqlQuery(query)) {
qDebug("Failed to collect ban history information: SQL Error");
qCWarning(DatabaseInterfaceLog) << "Failed to collect ban history information: SQL Error";
return results;
}
@ -1168,7 +1172,7 @@ bool Servatrice_DatabaseInterface::addWarning(const QString userName,
query->bindValue(":warn_reason", warningReason);
query->bindValue(":client_id", clientID);
if (!execSqlQuery(query)) {
qDebug("Failed to collect create warning history information: SQL Error");
qCWarning(DatabaseInterfaceLog) << "Failed to collect create warning history information: SQL Error";
return false;
}
@ -1189,7 +1193,7 @@ QList<ServerInfo_Warning> Servatrice_DatabaseInterface::getUserWarnHistory(const
query->bindValue(":user_id", userID);
if (!execSqlQuery(query)) {
qDebug("Failed to collect warning history information: SQL Error");
qCWarning(DatabaseInterfaceLog) << "Failed to collect warning history information: SQL Error";
return results;
}
@ -1296,7 +1300,7 @@ QList<ServerInfo_ChatMessage> Servatrice_DatabaseInterface::getMessageLogHistory
}
if (!execSqlQuery(query)) {
qDebug("Failed to collect log history information: SQL Error");
qCWarning(DatabaseInterfaceLog) << "Failed to collect log history information: SQL Error";
return results;
}
@ -1324,7 +1328,8 @@ int Servatrice_DatabaseInterface::checkNumberOfUserAccounts(const QString &email
query->bindValue(":user_email", email);
if (!execSqlQuery(query)) {
qDebug("Failed to identify the number of users accounts for users email address: SQL Error");
qCWarning(DatabaseInterfaceLog)
<< "Failed to identify the number of users accounts for users email address: SQL Error";
return 0;
}

View file

@ -31,6 +31,7 @@
#include <QDateTime>
#include <QDebug>
#include <QHostAddress>
#include <QLoggingCategory>
#include <QRegularExpression>
#include <QSqlError>
#include <QSqlQuery>
@ -83,6 +84,10 @@
#include <server_room.h>
#include <string>
inline Q_LOGGING_CATEGORY(AbstractServerSocketInterfaceLog, "abstract_server_socket_interface");
inline Q_LOGGING_CATEGORY(TcpServerSocketInterfaceLog, "tcp_server_socket_interface");
inline Q_LOGGING_CATEGORY(WebsocketServerSocketInterfaceLog, "websocket_server_socket_interface");
static const int protocolVersion = 14;
AbstractServerSocketInterface::AbstractServerSocketInterface(Servatrice *_server,
@ -130,7 +135,7 @@ bool AbstractServerSocketInterface::initSession()
void AbstractServerSocketInterface::catchSocketError(QAbstractSocket::SocketError socketError)
{
qDebug() << "Socket error:" << socketError;
qCWarning(AbstractServerSocketInterfaceLog) << "Socket error:" << socketError;
prepareDestroy();
}
@ -1100,7 +1105,7 @@ Response::ResponseCode AbstractServerSocketInterface::cmdBanFromServer(const Com
clientIdQuery->bindValue(":client_id", nameFromStdString(cmd.clientid()));
sqlInterface->execSqlQuery(clientIdQuery);
if (!sqlInterface->execSqlQuery(clientIdQuery)) {
qDebug("ClientID username ban lookup failed: SQL Error");
qCWarning(AbstractServerSocketInterfaceLog) << "ClientID username ban lookup failed: SQL Error";
} else {
while (clientIdQuery->next()) {
userName = clientIdQuery->value(0).toString();
@ -1152,7 +1157,7 @@ Response::ResponseCode AbstractServerSocketInterface::cmdRegisterAccount(const C
{
QString userName = nameFromStdString(cmd.user_name());
QString clientId = nameFromStdString(cmd.clientid());
qDebug() << "Got register command for user:" << userName;
qCDebug(AbstractServerSocketInterfaceLog) << "Got register command for user:" << userName;
bool registrationEnabled = settingsCache->value("registration/enabled", false).toBool();
if (!registrationEnabled) {
@ -1289,7 +1294,7 @@ Response::ResponseCode AbstractServerSocketInterface::cmdRegisterAccount(const C
country, !requireEmailActivation);
if (regSucceeded) {
qDebug() << "Accepted register command for user:" << userName;
qCDebug(AbstractServerSocketInterfaceLog) << "Accepted register command for user:" << userName;
if (requireEmailActivation) {
QSqlQuery *query =
sqlInterface->prepareQuery("insert into {prefix}_activation_emails (name) values(:name)");
@ -1337,7 +1342,7 @@ Response::ResponseCode AbstractServerSocketInterface::cmdActivateAccount(const C
clientId = "UNKNOWN";
if (sqlInterface->activateUser(userName, token)) {
qDebug() << "Accepted activation for user" << userName;
qCDebug(AbstractServerSocketInterfaceLog) << "Accepted activation for user" << userName;
if (servatrice->getEnableRegistrationAudit())
sqlInterface->addAuditRecord(userName.simplified(), this->getAddress(), clientId.simplified(),
@ -1345,7 +1350,7 @@ Response::ResponseCode AbstractServerSocketInterface::cmdActivateAccount(const C
return Response::RespActivationAccepted;
} else {
qDebug() << "Failed activation for user" << userName;
qCDebug(AbstractServerSocketInterfaceLog) << "Failed activation for user" << userName;
if (servatrice->getEnableRegistrationAudit())
sqlInterface->addAuditRecord(userName.simplified(), this->getAddress(), clientId.simplified(),
@ -1493,7 +1498,7 @@ Response::ResponseCode AbstractServerSocketInterface::cmdForgotPasswordRequest(c
const QString userName = nameFromStdString(cmd.user_name());
const QString clientId = nameFromStdString(cmd.clientid());
qDebug() << "Received reset password request from user:" << userName;
qCDebug(AbstractServerSocketInterfaceLog) << "Received reset password request from user:" << userName;
if (!servatrice->getEnableForgotPassword()) {
if (servatrice->getEnableForgotPasswordAudit())
@ -1575,7 +1580,7 @@ Response::ResponseCode AbstractServerSocketInterface::cmdForgotPasswordReset(con
Q_UNUSED(rc);
QString userName = nameFromStdString(cmd.user_name());
QString clientId = nameFromStdString(cmd.clientid());
qDebug() << "Received reset password reset from user:" << userName;
qCDebug(AbstractServerSocketInterfaceLog) << "Received reset password reset from user:" << userName;
if (!sqlInterface->doesForgotPasswordExist(userName)) {
if (servatrice->getEnableForgotPasswordAudit())
@ -1626,7 +1631,7 @@ AbstractServerSocketInterface::cmdForgotPasswordChallenge(const Command_ForgotPa
const QString userName = nameFromStdString(cmd.user_name());
const QString clientId = nameFromStdString(cmd.clientid());
qDebug() << "Received reset password challenge from user:" << userName;
qCDebug(AbstractServerSocketInterfaceLog) << "Received reset password challenge from user:" << userName;
if (!servatrice->getEnableForgotPasswordChallenge()) {
if (servatrice->getEnableForgotPasswordAudit()) {
@ -1971,13 +1976,16 @@ void TcpServerSocketInterface::flushOutputQueue()
unsigned int size = static_cast<unsigned int>(item.ByteSize());
#endif
buf.resize(size + 4);
item.SerializeToArray(buf.data() + 4, size);
buf.data()[3] = (unsigned char)size;
buf.data()[2] = (unsigned char)(size >> 8);
buf.data()[1] = (unsigned char)(size >> 16);
buf.data()[0] = (unsigned char)(size >> 24);
// In case socket->write() calls catchSocketError(), the mutex must not be locked during this call.
writeToSocket(buf);
if (item.SerializeToArray(buf.data() + 4, size)) {
buf.data()[3] = (unsigned char)size;
buf.data()[2] = (unsigned char)(size >> 8);
buf.data()[1] = (unsigned char)(size >> 16);
buf.data()[0] = (unsigned char)(size >> 24);
// In case socket->write() calls catchSocketError(), the mutex must not be locked during this call.
writeToSocket(buf);
} else {
qCWarning(TcpServerSocketInterfaceLog) << "serialisation error!";
}
totalBytes += size + 4;
locker.relock();
@ -2010,41 +2018,48 @@ void TcpServerSocketInterface::readClient()
return;
CommandContainer newCommandContainer;
bool ok;
try {
newCommandContainer.ParseFromArray(inputBuffer.data(), messageLength);
ok = newCommandContainer.ParseFromArray(inputBuffer.data(), messageLength);
} catch (std::exception &e) {
qDebug() << "Caught std::exception in" << __FILE__ << __LINE__ <<
qCWarning(TcpServerSocketInterfaceLog) << "Caught std::exception in" << __FILE__ << __LINE__ <<
#ifdef _MSC_VER // Visual Studio
__FUNCTION__;
__FUNCTION__
#else
__PRETTY_FUNCTION__;
__PRETTY_FUNCTION__
#endif
qDebug() << "Exception:" << e.what();
qDebug() << "Message coming from:" << getAddress();
qDebug() << "Message length:" << messageLength;
qDebug() << "Message content:" << inputBuffer.toHex();
<< Qt::endl
<< "Exception:" << e.what() << Qt::endl
<< "Message coming from:" << getAddress() << Qt::endl
<< "Message length:" << messageLength << Qt::endl
<< "Message content:" << inputBuffer.toHex();
} catch (...) {
qDebug() << "Unhandled exception in" << __FILE__ << __LINE__ <<
qCWarning(TcpServerSocketInterfaceLog) << "Unhandled exception in" << __FILE__ << __LINE__ <<
#ifdef _MSC_VER // Visual Studio
__FUNCTION__;
__FUNCTION__
#else
__PRETTY_FUNCTION__;
__PRETTY_FUNCTION__
#endif
qDebug() << "Message coming from:" << getAddress();
<< Qt::endl
<< "Message coming from:" << getAddress();
}
inputBuffer.remove(0, messageLength);
messageInProgress = false;
// dirty hack to make v13 client display the correct error message
if (handshakeStarted)
processCommandContainer(newCommandContainer);
else if (!newCommandContainer.has_cmd_id()) {
handshakeStarted = true;
if (!initTcpSession())
prepareDestroy();
if (ok) {
// dirty hack to make v13 client display the correct error message
if (handshakeStarted)
processCommandContainer(newCommandContainer);
else if (!newCommandContainer.has_cmd_id()) {
handshakeStarted = true;
if (!initTcpSession())
prepareDestroy();
}
// end of hack
} else {
qCWarning(TcpServerSocketInterfaceLog) << "parsing error!";
}
// end of hack
} while (!inputBuffer.isEmpty());
}
@ -2172,9 +2187,12 @@ void WebsocketServerSocketInterface::flushOutputQueue()
unsigned int size = static_cast<unsigned int>(item.ByteSize());
#endif
buf.resize(size);
item.SerializeToArray(buf.data(), size);
// In case socket->write() calls catchSocketError(), the mutex must not be locked during this call.
writeToSocket(buf);
if (item.SerializeToArray(buf.data(), size)) {
// In case socket->write() calls catchSocketError(), the mutex must not be locked during this call.
writeToSocket(buf);
} else {
qCWarning(TcpServerSocketInterfaceLog) << "serialisation error!";
}
totalBytes += size;
locker.relock();
@ -2190,30 +2208,37 @@ void WebsocketServerSocketInterface::binaryMessageReceived(const QByteArray &mes
servatrice->incRxBytes(message.size());
CommandContainer newCommandContainer;
bool ok;
try {
newCommandContainer.ParseFromArray(message.data(), message.size());
ok = newCommandContainer.ParseFromArray(message.data(), message.size());
} catch (std::exception &e) {
qDebug() << "Caught std::exception in" << __FILE__ << __LINE__ <<
qCWarning(WebsocketServerSocketInterfaceLog) << "Caught std::exception in" << __FILE__ << __LINE__ <<
#ifdef _MSC_VER // Visual Studio
__FUNCTION__;
__FUNCTION__
#else
__PRETTY_FUNCTION__;
__PRETTY_FUNCTION__
#endif
qDebug() << "Exception:" << e.what();
qDebug() << "Message coming from:" << getAddress();
qDebug() << "Message length:" << message.size();
qDebug() << "Message content:" << message.toHex();
<< Qt::endl
<< "Exception:" << e.what() << Qt::endl
<< "Message coming from:" << getAddress() << Qt::endl
<< "Message length:" << message.size() << Qt::endl
<< "Message content:" << message.toHex();
} catch (...) {
qDebug() << "Unhandled exception in" << __FILE__ << __LINE__ <<
qCWarning(WebsocketServerSocketInterfaceLog) << "Unhandled exception in" << __FILE__ << __LINE__ <<
#ifdef _MSC_VER // Visual Studio
__FUNCTION__;
__FUNCTION__
#else
__PRETTY_FUNCTION__;
__PRETTY_FUNCTION__
#endif
qDebug() << "Message coming from:" << getAddress();
<< Qt::endl
<< "Message coming from:" << getAddress();
}
processCommandContainer(newCommandContainer);
if (ok) {
processCommandContainer(newCommandContainer);
} else {
qCWarning(WebsocketServerSocketInterfaceLog) << "parsing error!";
}
}
bool AbstractServerSocketInterface::isPasswordLongEnough(const int passwordLength)