From 59a47471131b69dc5469a22aac89a7a19bdc61c8 Mon Sep 17 00:00:00 2001
From: Nick Bolton
Date: Thu, 26 Mar 2026 14:17:56 +0000
Subject: [PATCH] feat(ipc): Unrecognised IPC client, retry messages, and TLS
fingerprint
Also adds version checking for IPC server and client
---
src/lib/client/Client.cpp | 9 +-
src/lib/client/Client.h | 3 +-
src/lib/client/ServerProxy.cpp | 9 +-
src/lib/common/Enums.h | 10 ++
src/lib/deskflow/ipc/IpcServer.cpp | 16 ++-
src/lib/gui/CMakeLists.txt | 7 -
src/lib/gui/MainWindow.cpp | 121 ++++++++--------
src/lib/gui/MainWindow.h | 20 +--
src/lib/gui/config/IServerConfig.h | 28 ----
src/lib/gui/config/ServerConfig.h | 25 +---
src/lib/gui/core/ClientConnection.cpp | 75 ----------
src/lib/gui/core/ClientConnection.h | 48 -------
src/lib/gui/core/CoreProcess.cpp | 20 ++-
src/lib/gui/core/CoreProcess.h | 12 +-
src/lib/gui/core/ServerConnection.cpp | 108 ---------------
src/lib/gui/core/ServerConnection.h | 49 -------
src/lib/gui/core/ServerMessage.cpp | 54 --------
src/lib/gui/core/ServerMessage.h | 32 -----
src/lib/gui/dialogs/SettingsDialog.cpp | 2 +-
src/lib/gui/dialogs/SettingsDialog.h | 8 +-
src/lib/gui/ipc/IpcClient.cpp | 38 +++--
src/lib/gui/ipc/IpcClient.h | 1 +
src/lib/server/Server.cpp | 3 +-
src/unittests/gui/core/CMakeLists.txt | 16 ---
.../gui/core/ClientConnectionTests.cpp | 130 ------------------
.../gui/core/ClientConnectionTests.h | 27 ----
.../gui/core/ServerConnectionTests.cpp | 101 --------------
.../gui/core/ServerConnectionTests.h | 24 ----
28 files changed, 170 insertions(+), 826 deletions(-)
delete mode 100644 src/lib/gui/config/IServerConfig.h
delete mode 100644 src/lib/gui/core/ClientConnection.cpp
delete mode 100644 src/lib/gui/core/ClientConnection.h
delete mode 100644 src/lib/gui/core/ServerConnection.cpp
delete mode 100644 src/lib/gui/core/ServerConnection.h
delete mode 100644 src/lib/gui/core/ServerMessage.cpp
delete mode 100644 src/lib/gui/core/ServerMessage.h
delete mode 100644 src/unittests/gui/core/ClientConnectionTests.cpp
delete mode 100644 src/unittests/gui/core/ClientConnectionTests.h
delete mode 100644 src/unittests/gui/core/ServerConnectionTests.cpp
delete mode 100644 src/unittests/gui/core/ServerConnectionTests.h
diff --git a/src/lib/client/Client.cpp b/src/lib/client/Client.cpp
index 64a2a95cc..5952013f7 100644
--- a/src/lib/client/Client.cpp
+++ b/src/lib/client/Client.cpp
@@ -14,7 +14,6 @@
#include "base/NetworkProtocol.h"
#include "client/ServerProxy.h"
#include "common/Settings.h"
-#include "deskflow/ipc/CoreIpc.h"
#include "deskflow/Clipboard.h"
#include "deskflow/IPlatformScreen.h"
#include "deskflow/PacketStreamFilter.h"
@@ -22,11 +21,14 @@
#include "deskflow/ProtocolUtil.h"
#include "deskflow/Screen.h"
#include "deskflow/StreamChunker.h"
+#include "deskflow/ipc/CoreIpc.h"
#include "net/IDataSocket.h"
#include "net/ISocketFactory.h"
#include "net/SecureSocket.h"
#include "net/TCPSocket.h"
+#include
+
#include
#include
@@ -133,8 +135,11 @@ void Client::disconnect(const char *msg)
}
}
-void Client::refuseConnection(const char *msg)
+void Client::refuseConnection(deskflow::core::ConnectionRefusal reason, const char *msg)
{
+ const auto metaEnum = QMetaEnum::fromType();
+ ipcSendToClient("connectionRefused", metaEnum.valueToKey(static_cast(reason)));
+
cleanup();
if (msg) {
diff --git a/src/lib/client/Client.h b/src/lib/client/Client.h
index 7249ac284..f2f892a36 100644
--- a/src/lib/client/Client.h
+++ b/src/lib/client/Client.h
@@ -11,6 +11,7 @@
#include "deskflow/IClient.h"
#include "base/EventTypes.h"
+#include "common/Enums.h"
#include "deskflow/IClipboard.h"
#include "net/NetworkAddress.h"
@@ -88,7 +89,7 @@ public:
Disconnects from the server with an optional error message.
Unlike disconnect this function doesn't try to use other ip addresses
*/
- void refuseConnection(const char *msg);
+ void refuseConnection(deskflow::core::ConnectionRefusal reason, const char *msg);
//! Notify of handshake complete
/*!
diff --git a/src/lib/client/ServerProxy.cpp b/src/lib/client/ServerProxy.cpp
index d2e38860e..600f05697 100644
--- a/src/lib/client/ServerProxy.cpp
+++ b/src/lib/client/ServerProxy.cpp
@@ -124,6 +124,7 @@ void ServerProxy::handleData()
ServerProxy::ConnectionResult ServerProxy::parseHandshakeMessage(const uint8_t *code)
{
using enum ConnectionResult;
+ using enum deskflow::core::ConnectionRefusal;
if (memcmp(code, kMsgQInfo, 4) == 0) {
queryInfo();
@@ -168,25 +169,25 @@ ServerProxy::ConnectionResult ServerProxy::parseHandshakeMessage(const uint8_t *
int32_t minor;
ProtocolUtil::readf(m_stream, kMsgEIncompatible + 4, &major, &minor);
LOG_ERR("server has incompatible version %d.%d", major, minor);
- m_client->refuseConnection("server has incompatible version");
+ m_client->refuseConnection(IncompatibleVersion, "server has incompatible version");
return Disconnect;
}
else if (memcmp(code, kMsgEBusy, 4) == 0) {
LOG_ERR("server already has a connected client with name \"%s\"", m_client->getName().c_str());
- m_client->refuseConnection("server already has a connected client with our name");
+ m_client->refuseConnection(AlreadyConnected, "server already has a connected client with our name");
return Disconnect;
}
else if (memcmp(code, kMsgEUnknown, 4) == 0) {
LOG_ERR("server refused client with name \"%s\"", m_client->getName().c_str());
- m_client->refuseConnection("server refused client with our name");
+ m_client->refuseConnection(UnknownClient, "server refused client with our name");
return Disconnect;
}
else if (memcmp(code, kMsgEBad, 4) == 0) {
LOG_ERR("server disconnected due to a protocol error");
- m_client->refuseConnection("server reported a protocol error");
+ m_client->refuseConnection(ProtocolError, "server reported a protocol error");
return Disconnect;
} else if (memcmp(code, kMsgDLanguageSynchronisation, 4) == 0) {
setServerLanguages();
diff --git a/src/lib/common/Enums.h b/src/lib/common/Enums.h
index b9af3c698..7daf1eb77 100644
--- a/src/lib/common/Enums.h
+++ b/src/lib/common/Enums.h
@@ -43,4 +43,14 @@ enum class ConnectionState
Listening
};
Q_ENUM_NS(ConnectionState)
+
+enum class ConnectionRefusal
+{
+ IncompatibleVersion,
+ AlreadyConnected,
+ UnknownClient,
+ ProtocolError
+};
+Q_ENUM_NS(ConnectionRefusal)
+
} // namespace deskflow::core
diff --git a/src/lib/deskflow/ipc/IpcServer.cpp b/src/lib/deskflow/ipc/IpcServer.cpp
index 131c1d93d..39eff2971 100644
--- a/src/lib/deskflow/ipc/IpcServer.cpp
+++ b/src/lib/deskflow/ipc/IpcServer.cpp
@@ -7,6 +7,7 @@
#include "IpcServer.h"
#include "base/Log.h"
+#include "common/VersionInfo.h"
#include
#include
@@ -110,8 +111,21 @@ void IpcServer::processMessage(QLocalSocket *clientSocket, const QString &messag
}
if (const auto &command = parts.at(0); command == "hello") {
+ const auto versionId = QStringLiteral("%1+%2").arg(kVersion, kVersionGitSha);
+ const auto clientVersion = parts.size() >= 2 ? parts.at(1) : QString();
+ if (clientVersion != versionId) {
+ LOG_ERR(
+ "ipc client version mismatch (client: %s, server: %s)",
+ clientVersion.isEmpty() ? "unknown" : clientVersion.toUtf8().constData(), versionId.toUtf8().constData()
+ );
+ writeToClientSocket(clientSocket, "error");
+ clientSocket->flush();
+ clientSocket->disconnectFromServer();
+ return;
+ }
+
LOG_DEBUG("ipc server got hello message, sending hello back");
- writeToClientSocket(clientSocket, "hello");
+ writeToClientSocket(clientSocket, QString("hello=%1").arg(versionId));
// Replay messages that were queued before any clients connected.
LOG_DEBUG1("ipc server replaying %d pending messages", m_pendingMessages.size());
diff --git a/src/lib/gui/CMakeLists.txt b/src/lib/gui/CMakeLists.txt
index 5d46fea13..e3f7bd9e7 100644
--- a/src/lib/gui/CMakeLists.txt
+++ b/src/lib/gui/CMakeLists.txt
@@ -39,7 +39,6 @@ add_library(${target} STATIC
TlsUtility.h
VersionChecker.cpp
VersionChecker.h
- config/IServerConfig.h
config/Screen.cpp
config/Screen.h
config/ScreenConfig.cpp
@@ -48,16 +47,10 @@ add_library(${target} STATIC
config/ScreenList.h
config/ServerConfig.cpp
config/ServerConfig.h
- core/ClientConnection.cpp
- core/ClientConnection.h
core/CoreProcess.cpp
core/CoreProcess.h
core/NetworkMonitor.cpp
core/NetworkMonitor.h
- core/ServerConnection.cpp
- core/ServerConnection.h
- core/ServerMessage.cpp
- core/ServerMessage.h
dialogs/AboutDialog.cpp
dialogs/AboutDialog.h
dialogs/AboutDialog.ui
diff --git a/src/lib/gui/MainWindow.cpp b/src/lib/gui/MainWindow.cpp
index f2f396740..bd8b7177a 100644
--- a/src/lib/gui/MainWindow.cpp
+++ b/src/lib/gui/MainWindow.cpp
@@ -58,8 +58,6 @@ using namespace deskflow::gui;
MainWindow::MainWindow()
: ui{std::make_unique()},
m_coreProcess(m_serverConfig),
- m_serverConnection(this, m_serverConfig),
- m_clientConnection(this),
m_trayIcon{new QSystemTrayIcon(this)},
m_guiDupeChecker{new QLocalServer(this)},
m_daemonIpcClient{new ipc::DaemonIpcClient(this)},
@@ -269,15 +267,11 @@ void MainWindow::connectSlots()
if (!deskflow::platform::isMac())
connect(m_trayIcon, &QSystemTrayIcon::activated, this, &MainWindow::trayIconActivated);
- connect(&m_serverConnection, &ServerConnection::configureClient, this, &MainWindow::serverConnectionConfigureClient);
- connect(&m_serverConnection, &ServerConnection::clientsChanged, this, &MainWindow::serverClientsChanged);
connect(&m_coreProcess, &CoreProcess::connectedClientsChanged, this, &MainWindow::serverClientsChanged);
- connect(
- &m_serverConnection, &ServerConnection::requestNewClientPrompt, this, &MainWindow::handleNewClientPromptRequest
- );
-
- connect(&m_clientConnection, &ClientConnection::requestShowError, this, &MainWindow::showClientError);
- connect(&m_clientConnection, &ClientConnection::updateTimeoutDelay, this, &MainWindow::updateTimeoutDelay);
+ connect(&m_coreProcess, &CoreProcess::unrecognisedClient, this, &MainWindow::handleUnrecognisedClient);
+ connect(&m_coreProcess, &CoreProcess::connectionRefused, this, &MainWindow::handleConnectionRefused);
+ connect(&m_coreProcess, &CoreProcess::retryIn, this, &MainWindow::updateTimeoutDelay);
+ connect(&m_coreProcess, &CoreProcess::peerFingerprint, this, &MainWindow::handlePeerFingerprint);
if (Settings::value(Settings::Gui::AutoStartCore).toBool()) {
connect(ui->btnToggleCore, &QPushButton::clicked, m_actionStopCore, &QAction::trigger, Qt::UniqueConnection);
@@ -430,8 +424,6 @@ void MainWindow::clearSettings()
m_networkMonitor->stopMonitoring();
disconnect(&m_coreProcess, nullptr, this, nullptr);
- disconnect(&m_serverConnection, nullptr, this, nullptr);
- disconnect(&m_clientConnection, nullptr, this, nullptr);
disconnect(&m_versionChecker, nullptr, this, nullptr);
disconnect(m_guiDupeChecker, nullptr, this, nullptr);
disconnect(m_trayIcon, nullptr, this, nullptr);
@@ -598,12 +590,12 @@ void MainWindow::updateNetworkInfo()
void MainWindow::serverConnectionConfigureClient(const QString &clientName)
{
- m_serverConnection.serverConfigDialogVisible(true);
+ m_serverConfigDialogVisible = true;
ServerConfigDialog dialog(this, m_serverConfig);
if (dialog.addClient(clientName) && dialog.exec() == QDialog::Accepted) {
m_coreProcess.restart();
}
- m_serverConnection.serverConfigDialogVisible(false);
+ m_serverConfigDialogVisible = false;
}
//////////////////////////////////////////////////////////////////////////////
@@ -755,35 +747,64 @@ void MainWindow::setTrayIcon()
void MainWindow::handleLogLine(const QString &line)
{
m_logDock->appendLine(line);
- updateFromLogLine(line);
}
-void MainWindow::updateFromLogLine(const QString &line)
+void MainWindow::handleUnrecognisedClient(const QString &clientName)
{
- checkConnected(line);
- checkFingerprint(line);
-}
-
-void MainWindow::checkConnected(const QString &line)
-{
- if (ui->rbModeServer->isChecked()) {
- m_serverConnection.handleLogLine(line);
- } else {
- m_clientConnection.handleLogLine(line);
+ if (m_ignoredClients.contains(clientName)) {
+ qDebug("ignoring %s:", qPrintable(clientName));
+ return;
}
-}
-void MainWindow::checkFingerprint(const QString &line)
-{
- static const auto tlsPeerMessage = QStringLiteral("peer fingerprint: ");
- static const qsizetype msgLen = QString(tlsPeerMessage).length();
-
- const qsizetype midStart = line.indexOf(tlsPeerMessage);
- if (midStart == -1)
+ if (m_newClientPromptShowing || m_serverConfigDialogVisible)
return;
- const auto sha256Text = line.mid(midStart + msgLen).remove(':');
+ if (Settings::value(Settings::Server::ExternalConfig).toBool())
+ return;
+ if (m_serverConfig.isFull() || m_serverConfig.screenExists(clientName))
+ return;
+
+ m_newClientPromptShowing = true;
+
+ showAndActivate();
+
+ if (deskflow::gui::messages::showNewClientPrompt(this, clientName)) {
+ serverConnectionConfigureClient(clientName);
+ } else {
+ m_ignoredClients.insert(clientName);
+ }
+
+ m_newClientPromptShowing = false;
+}
+
+void MainWindow::handleConnectionRefused(deskflow::core::ConnectionRefusal reason)
+{
+ if (reason != deskflow::core::ConnectionRefusal::AlreadyConnected)
+ return;
+
+ if (!isVisible() || m_clientErrorVisible)
+ return;
+
+ m_clientErrorVisible = true;
+ showAndActivate();
+
+ const auto address = Settings::value(Settings::Client::RemoteHost).toString();
+ QMessageBox::warning(
+ this, tr("%1 Connection Error").arg(kAppName),
+ tr("Failed to connect to the server '%1'.
"
+ "A Client with your name is already connected to the server.
"
+ "Please ensure that you're using a unique name and that only a "
+ "single instance of the client process is running.
")
+ .arg(address)
+ );
+
+ m_clientErrorVisible = false;
+}
+
+void MainWindow::handlePeerFingerprint(const QString &fingerprint)
+{
+ const auto sha256Text = QString(fingerprint).remove(':');
const Fingerprint sha256 = {QCryptographicHash::Sha256, QByteArray::fromHex(sha256Text.toLatin1())};
const bool isClient = m_coreProcess.mode() == CoreMode::Client;
@@ -959,7 +980,7 @@ void MainWindow::changeEvent(QEvent *e)
updateModeControlLabels();
updateNetworkInfo();
updateStatus();
- serverClientsChanged(m_serverConnection.connectedClients());
+ serverClientsChanged({});
updateText();
}
}
@@ -1167,34 +1188,6 @@ void MainWindow::remoteHostChanged(const QString &newRemoteHost)
}
}
-void MainWindow::showClientError(deskflow::client::ErrorType error, const QString &address)
-{
- if (!isVisible() || m_clientErrorVisible || error != deskflow::client::ErrorType::AlreadyConnected)
- return;
-
- m_clientErrorVisible = true;
-
- showAndActivate();
-
- QMessageBox::warning(
- this, tr("%1 Connection Error").arg(kAppName),
- tr("Failed to connect to the server '%1'.
"
- "A Client with your name is already connected to the server.
"
- "Please ensure that you're using a unique name and that only a "
- "single instance of the client process is running.")
- .arg(address)
- );
-
- m_clientErrorVisible = false;
-}
-
-void MainWindow::handleNewClientPromptRequest(const QString &clientName)
-{
- showAndActivate();
- bool result = deskflow::gui::messages::showNewClientPrompt(this, clientName);
- m_serverConnection.handleNewClientResult(clientName, result);
-}
-
void MainWindow::updateIpLabel(const QStringList &addresses)
{
if (m_coreProcess.mode() != CoreMode::Server) {
diff --git a/src/lib/gui/MainWindow.h b/src/lib/gui/MainWindow.h
index 21c688f70..fbac2fcc8 100644
--- a/src/lib/gui/MainWindow.h
+++ b/src/lib/gui/MainWindow.h
@@ -16,10 +16,8 @@
#include "VersionChecker.h"
#include "config/ServerConfig.h"
-#include "gui/core/ClientConnection.h"
#include "gui/core/CoreProcess.h"
#include "gui/core/NetworkMonitor.h"
-#include "gui/core/ServerConnection.h"
#include "net/Fingerprint.h"
#ifdef Q_OS_MACOS
@@ -118,9 +116,9 @@ private:
void setupTrayIcon();
void applyConfig();
void setTrayIcon();
- void updateFromLogLine(const QString &line);
- void checkConnected(const QString &line);
- void checkFingerprint(const QString &line);
+ void handleUnrecognisedClient(const QString &clientName);
+ void handleConnectionRefused(deskflow::core::ConnectionRefusal reason);
+ void handlePeerFingerprint(const QString &fingerprint);
void closeEvent(QCloseEvent *event) override;
void secureSocket(bool secureSocket);
void connectSlots();
@@ -140,17 +138,10 @@ private:
void daemonIpcClientConnectionFailed();
void toggleCanRunCore(bool enableButtons);
void remoteHostChanged(const QString &newRemoteHost);
- void handleNewClientPromptRequest(const QString &clientName);
void updateIpLabel(const QStringList &addresses);
void updateTimeoutDelay(int newDelay);
bool canRunCore() const;
- /**
- * @brief showClientError
- * @param error Error Type
- * @param address
- */
- void showClientError(deskflow::client::ErrorType error, const QString &address);
/**
* @brief trustedFingerprintDatabase get the FingerprintDatabase for the trusted clients or trusted servers.
@@ -177,8 +168,9 @@ private:
bool m_clientErrorVisible = false;
ServerConfig m_serverConfig;
deskflow::gui::CoreProcess m_coreProcess;
- deskflow::gui::ServerConnection m_serverConnection;
- deskflow::gui::ClientConnection m_clientConnection;
+ QSet m_ignoredClients;
+ bool m_newClientPromptShowing = false;
+ bool m_serverConfigDialogVisible = false;
QSize m_expandedSize = QSize();
QStringList m_checkedClients;
QStringList m_checkedServers;
diff --git a/src/lib/gui/config/IServerConfig.h b/src/lib/gui/config/IServerConfig.h
deleted file mode 100644
index 940918305..000000000
--- a/src/lib/gui/config/IServerConfig.h
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Deskflow -- mouse and keyboard sharing utility
- * SPDX-FileCopyrightText: (C) 2024 Symless Ltd.
- * SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
- */
-
-#pragma once
-
-#include
-#include
-
-#include "ScreenList.h"
-
-namespace deskflow::gui {
-
-// TODO: Remove this class, when the ServerConnectionTests are ported to QTests
-class IServerConfig
-{
-public:
- virtual ~IServerConfig() = default;
- virtual bool isFull() const = 0;
- virtual bool screenExists(const QString &screenName) const = 0;
- virtual bool save(const QString &fileName) const = 0;
- virtual void save(QFile &file) const = 0;
- virtual const ScreenList &screens() const = 0;
-};
-
-} // namespace deskflow::gui
diff --git a/src/lib/gui/config/ServerConfig.h b/src/lib/gui/config/ServerConfig.h
index ca6f70cf6..80e86966b 100644
--- a/src/lib/gui/config/ServerConfig.h
+++ b/src/lib/gui/config/ServerConfig.h
@@ -9,7 +9,6 @@
#include "base/NetworkProtocol.h"
#include "gui/Hotkey.h"
-#include "gui/config/IServerConfig.h"
#include "gui/config/ScreenConfig.h"
#include "gui/config/ScreenList.h"
@@ -31,21 +30,18 @@ const auto kDefaultProtocol = NetworkProtocol::Barrier;
} // namespace deskflow::gui
-class ServerConfig : public ScreenConfig, public deskflow::gui::IServerConfig
+class ServerConfig : public ScreenConfig
{
friend class ServerConfigDialog;
friend QTextStream &operator<<(QTextStream &outStream, const ServerConfig &config);
public:
explicit ServerConfig(int columns = kDefaultColumns, int rows = kDefaultRows);
- ~ServerConfig() override = default;
+ ~ServerConfig() = default;
bool operator==(const ServerConfig &sc) const;
- //
- // Overrides
- //
- const ScreenList &screens() const override
+ const ScreenList &screens() const
{
return m_Screens;
}
@@ -131,17 +127,10 @@ public:
}
static size_t defaultClipboardSharingSize();
- //
- // Overrides
- //
- bool save(const QString &fileName) const override;
- bool screenExists(const QString &screenName) const override;
- void save(QFile &file) const override;
- bool isFull() const override;
-
- //
- // New methods
- //
+ bool save(const QString &fileName) const;
+ bool screenExists(const QString &screenName) const;
+ void save(QFile &file) const;
+ bool isFull() const;
void commit();
int numScreens() const;
QString getServerName() const;
diff --git a/src/lib/gui/core/ClientConnection.cpp b/src/lib/gui/core/ClientConnection.cpp
deleted file mode 100644
index 499cc1e0e..000000000
--- a/src/lib/gui/core/ClientConnection.cpp
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Deskflow -- mouse and keyboard sharing utility
- * SPDX-FileCopyrightText: (C) 2025 Deskflow Developers
- * SPDX-FileCopyrightText: (C) 2021 Symless Ltd.
- * SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
- */
-
-#include "ClientConnection.h"
-
-#include "common/Settings.h"
-
-#include
-
-namespace deskflow::gui {
-
-void ClientConnection::handleLogLine(const QString &logLine)
-{
- if (logLine.contains("disconnected from server")) {
- m_supressMessage = false;
- return;
- }
-
- if (logLine.contains("retry in ") && Settings::value(Settings::Client::DynamicConnectionRetry).toBool()) {
- auto line = logLine.mid(logLine.indexOf("in ") + 3);
- int seconds = line.mid(0, line.indexOf(" ")).toInt();
- Q_EMIT updateTimeoutDelay(seconds);
- return;
- }
- if (logLine.contains("connected to server")) {
- m_supressMessage = true;
- return;
- }
-
- if (logLine.contains("failed to connect to server")) {
- if (m_supressMessage) {
- qDebug("message already shown, skipping");
- return;
- }
- // ignore the message if it's about the server refusing by name as
- // this will trigger the server to show an 'add client' dialog.
- if (logLine.contains("server refused client with our name")) {
- qDebug("ignoring client name refused message");
- return;
- }
- showMessage(logLine);
- }
-}
-
-void ClientConnection::showMessage(const QString &logLine)
-{
- using enum deskflow::client::ErrorType;
-
- if (logLine.isEmpty())
- return;
-
- const auto address = Settings::value(Settings::Client::RemoteHost).toString();
- auto error = NoError;
-
- if (logLine.contains("server already has a connected client with our name")) {
- error = AlreadyConnected;
- } else if (QHostAddress a(address); a.isNull()) {
- qDebug("ip not detected, showing hostname error");
- error = HostnameError;
- } else {
- qDebug("ip detected, showing generic error");
- error = GenericError;
- }
-
- if (error == NoError)
- return;
-
- Q_EMIT requestShowError(error, address);
-}
-
-} // namespace deskflow::gui
diff --git a/src/lib/gui/core/ClientConnection.h b/src/lib/gui/core/ClientConnection.h
deleted file mode 100644
index 377cbeea0..000000000
--- a/src/lib/gui/core/ClientConnection.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Deskflow -- mouse and keyboard sharing utility
- * SPDX-FileCopyrightText: (C) 2025 Deskflow Developers
- * SPDX-FileCopyrightText: (C) 2021 Symless Ltd.
- * SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
- */
-
-#pragma once
-
-#include "common/Enums.h"
-
-#include
-#include
-
-class QWidget;
-
-namespace deskflow::gui {
-
-class ClientConnection : public QObject
-{
- Q_OBJECT
-
-public:
- explicit ClientConnection(QWidget *parent) : m_pParent(parent)
- {
- // do nothing
- }
-
- void handleLogLine(const QString &line);
-
-Q_SIGNALS:
- /**
- * @brief requestShowError, This signal is emitted when the client
- * connection would like the owning process to report an error message
- * @param error the type of error being reported
- * @param address of the host
- */
- void requestShowError(deskflow::client::ErrorType error, const QString &address);
- void updateTimeoutDelay(int newTimeout);
-
-private:
- void showMessage(const QString &logLine);
-
- QWidget *m_pParent;
- bool m_supressMessage = false;
-};
-
-} // namespace deskflow::gui
diff --git a/src/lib/gui/core/CoreProcess.cpp b/src/lib/gui/core/CoreProcess.cpp
index ef61cbe57..935f36081 100644
--- a/src/lib/gui/core/CoreProcess.cpp
+++ b/src/lib/gui/core/CoreProcess.cpp
@@ -99,7 +99,7 @@ QString CoreProcess::wrapIpv6(const QString &address)
// CoreProcess
//
-CoreProcess::CoreProcess(const IServerConfig &serverConfig)
+CoreProcess::CoreProcess(const ServerConfig &serverConfig)
: m_serverConfig(serverConfig),
m_daemonIpcClient{new ipc::DaemonIpcClient(this)}
{
@@ -430,7 +430,7 @@ void CoreProcess::stop(std::optional processModeOption)
if (m_coreIpcClient) {
m_coreIpcClient->disconnectFromServer();
- delete m_coreIpcClient;
+ m_coreIpcClient->deleteLater();
m_coreIpcClient = nullptr;
}
@@ -548,6 +548,22 @@ void CoreProcess::onCoreIpcMessageReceived(const QString &command, const QString
m_secureSocketVersion = args;
Q_EMIT securityLevelChanged(args);
}
+ } else if (command == "unrecognisedClient") {
+ Q_EMIT unrecognisedClient(args);
+ } else if (command == "connectionRefused") {
+ const auto metaEnum = QMetaEnum::fromType();
+ bool ok = false;
+ const auto reason =
+ static_cast(metaEnum.keyToValue(args.toUtf8().constData(), &ok));
+ if (ok) {
+ Q_EMIT connectionRefused(reason);
+ } else {
+ qWarning("core ipc got unknown connection refusal: %s", args.toUtf8().constData());
+ }
+ } else if (command == "retryIn") {
+ Q_EMIT retryIn(args.toInt());
+ } else if (command == "peerFingerprint") {
+ Q_EMIT peerFingerprint(args);
}
}
diff --git a/src/lib/gui/core/CoreProcess.h b/src/lib/gui/core/CoreProcess.h
index 25abd0ac7..45cfa34be 100644
--- a/src/lib/gui/core/CoreProcess.h
+++ b/src/lib/gui/core/CoreProcess.h
@@ -10,7 +10,7 @@
#include "common/Enums.h"
#include "common/Settings.h"
#include "gui/FileTail.h"
-#include "gui/config/IServerConfig.h"
+#include "gui/config/ServerConfig.h"
#include
#include
@@ -29,8 +29,6 @@ class CoreProcess : public QObject
using ConnectionState = deskflow::core::ConnectionState;
using ProcessMode = Settings::ProcessMode;
using ProcessState = deskflow::core::ProcessState;
- using IServerConfig = deskflow::gui::IServerConfig;
-
Q_OBJECT
public:
@@ -40,7 +38,7 @@ public:
StartFailed
};
- explicit CoreProcess(const IServerConfig &serverConfig);
+ explicit CoreProcess(const ServerConfig &serverConfig);
void start(std::optional processMode = std::nullopt);
void stop(std::optional processMode = std::nullopt);
@@ -91,6 +89,10 @@ Q_SIGNALS:
void daemonIpcClientConnectionFailed();
void connectedClientsChanged(const QStringList &clients);
void securityLevelChanged(QString securityLevel);
+ void unrecognisedClient(const QString &clientName);
+ void connectionRefused(deskflow::core::ConnectionRefusal reason);
+ void retryIn(int seconds);
+ void peerFingerprint(const QString &fingerprint);
private Q_SLOTS:
void onProcessFinished(int exitCode, QProcess::ExitStatus);
@@ -116,7 +118,7 @@ private:
static QString processStateToString(const CoreProcess::ProcessState state);
static QString wrapIpv6(const QString &address);
- const IServerConfig &m_serverConfig;
+ const ServerConfig &m_serverConfig;
QString m_address;
ProcessState m_processState = ProcessState::Stopped;
ConnectionState m_connectionState = ConnectionState::Disconnected;
diff --git a/src/lib/gui/core/ServerConnection.cpp b/src/lib/gui/core/ServerConnection.cpp
deleted file mode 100644
index f1ba9357d..000000000
--- a/src/lib/gui/core/ServerConnection.cpp
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Deskflow -- mouse and keyboard sharing utility
- * SPDX-FileCopyrightText: (C) 2025 Deskflow Developers
- * SPDX-FileCopyrightText: (C) 2021 Symless Ltd.
- * SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
- */
-
-#include "ServerConnection.h"
-
-#include "ServerMessage.h"
-#include "common/Settings.h"
-
-#include
-
-namespace deskflow::gui {
-
-ServerConnection::ServerConnection(QWidget *parent, IServerConfig &serverConfig)
- : m_pParent(parent),
- m_serverConfig(serverConfig)
-{
-}
-
-void ServerConnection::handleLogLine(const QString &logLine)
-{
- ServerMessage message(logLine);
- const auto &clientName = message.getClientName();
-
- if (m_ignoredClients.contains(clientName)) {
- qDebug("ignoring %s:", qPrintable(clientName));
- return;
- }
-
- if (message.isDisconnectedMessage()) {
- m_connectedClients.remove(clientName);
- Q_EMIT clientsChanged(connectedClients());
- return;
- }
-
- if (message.isConnectedMessage()) {
- m_connectedClients.insert(clientName);
- Q_EMIT clientsChanged(connectedClients());
- return;
- }
-
- if (!message.isNewClientMessage()) {
- return;
- }
-
- if (m_messageShowing) {
- qDebug("new client message already shown, skipping for now");
- return;
- }
-
- if (m_serverConfigDialogVisible) {
- qDebug("server config dialog visible, skipping new client prompt");
- return;
- }
-
- if (Settings::value(Settings::Server::ExternalConfig).toBool()) {
- qDebug("external config enabled, skipping new client prompt");
- return;
- }
-
- if (m_connectedClients.contains(clientName)) {
- qDebug("already got request, skipping new client prompt for: %s", qPrintable(clientName));
- return;
- }
-
- handleNewClient(clientName);
-}
-
-void ServerConnection::handleNewClient(const QString &clientName)
-{
- if (m_serverConfig.isFull()) {
- qDebug("server config full, skipping new client prompt for: %s", qPrintable(clientName));
- return;
- }
-
- if (m_serverConfig.screenExists(clientName)) {
- qDebug("client already added, skipping new client prompt for: %s", qPrintable(clientName));
- return;
- }
-
- m_messageShowing = true;
- Q_EMIT requestNewClientPrompt(clientName);
-}
-
-void ServerConnection::handleNewClientResult(const QString &clientName, bool acceptClient)
-{
- m_messageShowing = false;
- if (!acceptClient) {
- qDebug("declined dialog, ignoring client: %s", qPrintable(clientName));
- m_ignoredClients.insert(clientName);
- return;
- }
-
- qDebug("accepted dialog, adding client: %s", qPrintable(clientName));
- Q_EMIT configureClient(clientName);
- m_connectedClients.insert(clientName);
- Q_EMIT clientsChanged(connectedClients());
-}
-
-QStringList ServerConnection::connectedClients() const
-{
- return QStringList(m_connectedClients.begin(), m_connectedClients.end());
-}
-
-} // namespace deskflow::gui
diff --git a/src/lib/gui/core/ServerConnection.h b/src/lib/gui/core/ServerConnection.h
deleted file mode 100644
index fa656479f..000000000
--- a/src/lib/gui/core/ServerConnection.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Deskflow -- mouse and keyboard sharing utility
- * SPDX-FileCopyrightText: (C) 2025 Deskflow Developers
- * SPDX-FileCopyrightText: (C) 2021 Symless Ltd.
- * SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
- */
-
-#pragma once
-
-#include
-
-#include "gui/Messages.h"
-#include "gui/config/IServerConfig.h"
-
-namespace deskflow::gui {
-
-class ServerConnection : public QObject
-{
- Q_OBJECT
- using IServerConfig = deskflow::gui::IServerConfig;
-
-public:
- explicit ServerConnection(QWidget *parent, IServerConfig &serverConfig);
- void handleLogLine(const QString &logLine);
- void serverConfigDialogVisible(bool visible)
- {
- m_serverConfigDialogVisible = visible;
- }
-
- QStringList connectedClients() const;
- void handleNewClientResult(const QString &clientName, bool acceptClient);
-
-Q_SIGNALS:
- void requestNewClientPrompt(const QString &clientName);
- void configureClient(const QString &clientName);
- void clientsChanged(const QStringList &clients);
-
-private:
- void handleNewClient(const QString &clientName);
-
- QWidget *m_pParent;
- IServerConfig &m_serverConfig;
- QSet m_connectedClients;
- QSet m_ignoredClients;
- bool m_messageShowing = false;
- bool m_serverConfigDialogVisible = false;
-};
-
-} // namespace deskflow::gui
diff --git a/src/lib/gui/core/ServerMessage.cpp b/src/lib/gui/core/ServerMessage.cpp
deleted file mode 100644
index f29308f88..000000000
--- a/src/lib/gui/core/ServerMessage.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Deskflow -- mouse and keyboard sharing utility
- * SPDX-FileCopyrightText: (C) 2021 Symless Ltd.
- * SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
- */
-
-#include "ServerMessage.h"
-
-namespace deskflow::gui {
-
-ServerMessage::ServerMessage(const QString &message) : m_message(message), m_clientName(parseClientName(message))
-{
- // do nothing
-}
-
-bool ServerMessage::isNewClientMessage() const
-{
- return m_message.contains("unrecognised client name");
-}
-
-bool ServerMessage::isExitMessage() const
-{
- return m_message.contains("process exited");
-}
-
-bool ServerMessage::isConnectedMessage() const
-{
- return m_message.contains("has connected");
-}
-
-bool ServerMessage::isDisconnectedMessage() const
-{
- return m_message.contains("has disconnected");
-}
-
-const QString &ServerMessage::getClientName() const
-{
- return m_clientName;
-}
-
-QString ServerMessage::parseClientName(const QString &line) const
-{
- QString clientName("Unknown");
-
- auto nameStart = line.indexOf('"') + 1;
-
- if (auto nameEnd = line.indexOf('"', nameStart); nameEnd > nameStart) {
- clientName = line.mid(nameStart, nameEnd - nameStart);
- }
-
- return clientName;
-}
-
-} // namespace deskflow::gui
diff --git a/src/lib/gui/core/ServerMessage.h b/src/lib/gui/core/ServerMessage.h
deleted file mode 100644
index f08cbad64..000000000
--- a/src/lib/gui/core/ServerMessage.h
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Deskflow -- mouse and keyboard sharing utility
- * SPDX-FileCopyrightText: (C) 2021 Symless Ltd.
- * SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
- */
-
-#pragma once
-
-#include
-
-namespace deskflow::gui {
-
-class ServerMessage
-{
- QString m_message;
- QString m_clientName;
-
-public:
- explicit ServerMessage(const QString &message);
-
- bool isNewClientMessage() const;
- bool isExitMessage() const;
- bool isConnectedMessage() const;
- bool isDisconnectedMessage() const;
-
- const QString &getClientName() const;
-
-private:
- QString parseClientName(const QString &line) const;
-};
-
-} // namespace deskflow::gui
diff --git a/src/lib/gui/dialogs/SettingsDialog.cpp b/src/lib/gui/dialogs/SettingsDialog.cpp
index 12409e378..b18623b4e 100644
--- a/src/lib/gui/dialogs/SettingsDialog.cpp
+++ b/src/lib/gui/dialogs/SettingsDialog.cpp
@@ -23,7 +23,7 @@
using namespace deskflow::gui;
-SettingsDialog::SettingsDialog(QWidget *parent, const IServerConfig &serverConfig)
+SettingsDialog::SettingsDialog(QWidget *parent, const ServerConfig &serverConfig)
: QDialog(parent),
ui{std::make_unique()},
m_serverConfig(serverConfig)
diff --git a/src/lib/gui/dialogs/SettingsDialog.h b/src/lib/gui/dialogs/SettingsDialog.h
index a20bb6ea2..92075919c 100644
--- a/src/lib/gui/dialogs/SettingsDialog.h
+++ b/src/lib/gui/dialogs/SettingsDialog.h
@@ -9,7 +9,7 @@
#pragma once
#include
-#include "gui/config/IServerConfig.h"
+#include "gui/config/ServerConfig.h"
namespace Ui {
class SettingsDialog;
@@ -17,13 +17,11 @@ class SettingsDialog;
class SettingsDialog : public QDialog
{
- using IServerConfig = deskflow::gui::IServerConfig;
-
Q_OBJECT
public:
void extracted();
- SettingsDialog(QWidget *parent, const IServerConfig &serverConfig);
+ SettingsDialog(QWidget *parent, const ServerConfig &serverConfig);
~SettingsDialog() override;
Q_SIGNALS:
@@ -86,5 +84,5 @@ private:
bool m_interfaceSetOnLoad = false;
std::unique_ptr ui;
- const IServerConfig &m_serverConfig;
+ const ServerConfig &m_serverConfig;
};
diff --git a/src/lib/gui/ipc/IpcClient.cpp b/src/lib/gui/ipc/IpcClient.cpp
index 1dfdd482b..644d94705 100644
--- a/src/lib/gui/ipc/IpcClient.cpp
+++ b/src/lib/gui/ipc/IpcClient.cpp
@@ -6,15 +6,14 @@
#include "IpcClient.h"
+#include "common/VersionInfo.h"
+
#include
#include
#include
namespace deskflow::gui::ipc {
-const auto kTimeout = 1000;
-const auto kRetryLimit = 3;
-
IpcClient::IpcClient(QObject *parent, const QString &socketName)
: QObject(parent),
m_socket{new QLocalSocket(this)},
@@ -48,6 +47,8 @@ void IpcClient::connectToServer()
void IpcClient::attemptConnection()
{
+ const auto kRetryLimit = 3;
+
if (m_retryCount >= kRetryLimit) {
qWarning() << "ipc client failed to connect after" << kRetryLimit << "attempts";
m_state = State::Unconnected;
@@ -67,8 +68,9 @@ void IpcClient::attemptConnection()
connect(
m_socket, &QLocalSocket::connected, this,
[this] {
- m_socket->write("hello\n");
- qDebug() << "ipc client sent hello";
+ const auto versionId = QStringLiteral("%1+%2").arg(kVersion, kVersionGitSha);
+ m_socket->write(QString("hello=%1\n").arg(versionId).toUtf8());
+ qDebug() << "ipc client sent hello with version:" << versionId;
},
Qt::SingleShotConnection
);
@@ -141,10 +143,8 @@ void IpcClient::handleReadyRead()
continue;
}
- if (m_state == State::Connecting && parts.at(0) == "hello") {
- m_state = State::Connected;
- qDebug() << "ipc client connected";
- Q_EMIT connected();
+ if (m_state == State::Connecting) {
+ handleHandshakeMessage(parts);
continue;
}
@@ -156,6 +156,26 @@ void IpcClient::handleReadyRead()
}
}
+void IpcClient::handleHandshakeMessage(const QStringList &parts)
+{
+ if (parts.at(0) != "hello") {
+ return;
+ }
+
+ const auto versionId = QStringLiteral("%1+%2").arg(kVersion, kVersionGitSha);
+ const auto serverVersion = parts.size() >= 2 ? parts.at(1) : QString();
+ if (serverVersion != versionId) {
+ qCritical() << "ipc version mismatch (client:" << versionId << "server:" << serverVersion << ")";
+ disconnectFromServer();
+ Q_EMIT connectionFailed();
+ return;
+ }
+
+ m_state = State::Connected;
+ qDebug() << "ipc client connected";
+ Q_EMIT connected();
+}
+
void IpcClient::sendMessage(const QString &message)
{
if (m_state != State::Connected) {
diff --git a/src/lib/gui/ipc/IpcClient.h b/src/lib/gui/ipc/IpcClient.h
index 8e4068c5c..1f2b296d6 100644
--- a/src/lib/gui/ipc/IpcClient.h
+++ b/src/lib/gui/ipc/IpcClient.h
@@ -55,6 +55,7 @@ protected:
private:
void attemptConnection();
+ void handleHandshakeMessage(const QStringList &parts);
QLocalSocket *m_socket;
State m_state{State::Unconnected};
diff --git a/src/lib/server/Server.cpp b/src/lib/server/Server.cpp
index df505ea95..8e55e6b58 100644
--- a/src/lib/server/Server.cpp
+++ b/src/lib/server/Server.cpp
@@ -11,7 +11,6 @@
#include "base/IEventQueue.h"
#include "base/Log.h"
#include "deskflow/AppUtil.h"
-#include "deskflow/ipc/CoreIpc.h"
#include "deskflow/DeskflowException.h"
#include "deskflow/IPlatformScreen.h"
#include "deskflow/OptionTypes.h"
@@ -19,6 +18,7 @@
#include "deskflow/ProtocolTypes.h"
#include "deskflow/Screen.h"
#include "deskflow/StreamChunker.h"
+#include "deskflow/ipc/CoreIpc.h"
#include "net/TCPSocket.h"
#include "server/ClientListener.h"
#include "server/ClientProxy.h"
@@ -235,6 +235,7 @@ void Server::adoptClient(BaseClientProxy *client)
// name must be in our configuration
if (!m_config->isScreen(client->getName())) {
LOG_WARN("unrecognised client name \"%s\", check server config", client->getName().c_str());
+ ipcSendToClient("unrecognisedClient", QString::fromStdString(client->getName()));
closeClient(client, kMsgEUnknown);
return;
}
diff --git a/src/unittests/gui/core/CMakeLists.txt b/src/unittests/gui/core/CMakeLists.txt
index 73da17091..0484b134b 100644
--- a/src/unittests/gui/core/CMakeLists.txt
+++ b/src/unittests/gui/core/CMakeLists.txt
@@ -1,25 +1,9 @@
# SPDX-FileCopyrightText: (C) 2025 Deskflow Developers
# SPDX-License-Identifier: MIT
-create_test(
- NAME ClientConnectionTests
- DEPENDS gui
- SOURCE ClientConnectionTests.cpp
- WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/src/lib/gui"
-)
-
-create_test(
- NAME ServerConnectionTests
- DEPENDS gui
- SOURCE ServerConnectionTests.cpp
- WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/src/lib/gui"
-)
-
-
create_test(
NAME NetworkMonitorTests
DEPENDS gui
SOURCE NetworkMonitorTests.cpp
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/src/lib/gui"
)
-
diff --git a/src/unittests/gui/core/ClientConnectionTests.cpp b/src/unittests/gui/core/ClientConnectionTests.cpp
deleted file mode 100644
index 7c25fe90d..000000000
--- a/src/unittests/gui/core/ClientConnectionTests.cpp
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * Deskflow -- mouse and keyboard sharing utility
- * SPDX-FileCopyrightText: (C) 2025 Chris Rizzitello
- * SPDX-FileCopyrightText: (C) 2024 Symless Ltd.
- * SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
- */
-
-#include "ClientConnectionTests.h"
-
-#include "gui/core/ClientConnection.h"
-#include
-
-#include
-
-using namespace deskflow::gui;
-
-void ClientConnectionTests::initTestCase()
-{
- QDir dir;
- QVERIFY(dir.mkpath(m_settingsPath));
-
- QFile oldSettings(m_settingsFile);
- if (oldSettings.exists())
- oldSettings.remove();
-
- Settings::setSettingsFile(m_settingsFile);
- Settings::setStateFile(m_stateFile);
-}
-
-void ClientConnectionTests::handleLogLine_alreadyConnected_showError()
-{
- ClientConnection clientConnection(nullptr);
- const auto serverName = QStringLiteral("test server");
- Settings::setValue(Settings::Client::RemoteHost, serverName);
-
- QSignalSpy spy(&clientConnection, &ClientConnection::requestShowError);
- QVERIFY(spy.isValid());
-
- clientConnection.handleLogLine(
- "failed to connect to server\n"
- "server already has a connected client with our name"
- );
-
- QCOMPARE(spy.count(), 1);
-}
-
-void ClientConnectionTests::handleLogLine_withHostname_showError()
-{
- ClientConnection clientConnection(nullptr);
- const auto serverName = QStringLiteral("test server");
- Settings::setValue(Settings::Client::RemoteHost, serverName);
-
- QSignalSpy spy(&clientConnection, &ClientConnection::requestShowError);
- QVERIFY(spy.isValid());
-
- clientConnection.handleLogLine("failed to connect to server");
-
- QCOMPARE(spy.count(), 1);
-}
-
-void ClientConnectionTests::handleLogLine_withIpAddress_showError()
-{
- ClientConnection clientConnection(nullptr);
- const auto serverName = QStringLiteral("1.1.1.1");
- Settings::setValue(Settings::Client::RemoteHost, serverName);
-
- QSignalSpy spy(&clientConnection, &ClientConnection::requestShowError);
- QVERIFY(spy.isValid());
-
- clientConnection.handleLogLine("failed to connect to server");
-
- QCOMPARE(spy.count(), 1);
-}
-
-void ClientConnectionTests::handleLogLine_serverRefusedClient_shouldNotShowError()
-{
- ClientConnection clientConnection(nullptr);
-
- QSignalSpy spy(&clientConnection, &ClientConnection::requestShowError);
- QVERIFY(spy.isValid());
-
- clientConnection.handleLogLine(
- "failed to connect to server\n"
- "server refused client with our name"
- );
-
- QCOMPARE(spy.count(), 0);
-}
-
-void ClientConnectionTests::handleLogLine_connected_shouldPreventFutureError()
-{
- ClientConnection clientConnection(nullptr);
- clientConnection.handleLogLine("connected to server");
-
- QSignalSpy spy(&clientConnection, &ClientConnection::requestShowError);
- QVERIFY(spy.isValid());
-
- clientConnection.handleLogLine("failed to connect to server");
-
- QCOMPARE(spy.count(), 0);
-}
-
-void ClientConnectionTests::handleLogLine_connectToggled_showAfterDisconnect()
-{
- ClientConnection clientConnection(nullptr);
- clientConnection.handleLogLine("connected to server");
-
- QSignalSpy spy(&clientConnection, &ClientConnection::requestShowError);
- QVERIFY(spy.isValid());
-
- clientConnection.handleLogLine("failed to connect to server");
- clientConnection.handleLogLine("disconnected from server");
- clientConnection.handleLogLine("failed to connect to server");
-
- QCOMPARE(spy.count(), 1);
-}
-
-void ClientConnectionTests::handleLogLine_otherMessage_shouldNotShowError()
-{
- ClientConnection clientConnection(nullptr);
-
- QSignalSpy spy(&clientConnection, &ClientConnection::requestShowError);
- QVERIFY(spy.isValid());
-
- clientConnection.handleLogLine("hello world");
-
- QCOMPARE(spy.count(), 0);
-}
-
-QTEST_MAIN(ClientConnectionTests)
diff --git a/src/unittests/gui/core/ClientConnectionTests.h b/src/unittests/gui/core/ClientConnectionTests.h
deleted file mode 100644
index 33ff0e3cc..000000000
--- a/src/unittests/gui/core/ClientConnectionTests.h
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Deskflow -- mouse and keyboard sharing utility
- * SPDX-FileCopyrightText: (C) 2025 Chris Rizzitello
- * SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
- */
-
-#include
-
-class ClientConnectionTests : public QObject
-{
- Q_OBJECT
-private Q_SLOTS:
- // Test are run in order top to bottom
- void initTestCase();
- void handleLogLine_alreadyConnected_showError();
- void handleLogLine_withHostname_showError();
- void handleLogLine_withIpAddress_showError();
- void handleLogLine_serverRefusedClient_shouldNotShowError();
- void handleLogLine_connected_shouldPreventFutureError();
- void handleLogLine_connectToggled_showAfterDisconnect();
- void handleLogLine_otherMessage_shouldNotShowError();
-
-private:
- inline static const QString m_settingsPath = QStringLiteral("tmp/test");
- inline static const QString m_settingsFile = QStringLiteral("%1/Deskflow.conf").arg(m_settingsPath);
- inline static const QString m_stateFile = QStringLiteral("%1/Deskflow.state").arg(m_settingsPath);
-};
diff --git a/src/unittests/gui/core/ServerConnectionTests.cpp b/src/unittests/gui/core/ServerConnectionTests.cpp
deleted file mode 100644
index a40c5583f..000000000
--- a/src/unittests/gui/core/ServerConnectionTests.cpp
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Deskflow -- mouse and keyboard sharing utility
- * SPDX-FileCopyrightText: (C) 2025 Chris Rizzitello
- * SPDX-FileCopyrightText: (C) 2024 Symless Ltd.
- * SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
- */
-
-#include "ServerConnectionTests.h"
-
-#include "gui/config/ServerConfig.h"
-#include "gui/core/ServerConnection.h"
-#include
-
-#include
-
-using namespace deskflow::gui;
-
-class FullServerConfig : public ServerConfig
-{
-public:
- bool isFull() const override
- {
- return true;
- }
-};
-
-class ScreenExistsServerConfig : public ServerConfig
-{
-public:
- bool screenExists(const QString &screenName) const override
- {
- return true;
- }
-};
-
-void ServerConnectionTests::initTestCase()
-{
- QDir dir;
- QVERIFY(dir.mkpath(m_settingsPath));
-
- QFile oldSettings(m_settingsFile);
- if (oldSettings.exists())
- oldSettings.remove();
-
- Settings::setSettingsFile(m_settingsFile);
- Settings::setStateFile(m_stateFile);
-}
-
-void ServerConnectionTests::handleLogLine_newClient_shouldShowPrompt()
-{
- ServerConfig m_serverConfig;
- ServerConnection serverConnection(nullptr, m_serverConfig);
-
- QString clientName = "test client";
-
- QSignalSpy spy(&serverConnection, &ServerConnection::requestNewClientPrompt);
- QVERIFY(spy.isValid());
-
- serverConnection.handleLogLine(R"(unrecognised client name "test client")");
- QCOMPARE(spy.count(), 1);
-}
-
-void ServerConnectionTests::handleLogLine_ignoredClient_shouldNotShowPrompt()
-{
- ServerConfig m_serverConfig;
- ServerConnection serverConnection(nullptr, m_serverConfig);
- QString clientName = "test client";
- serverConnection.handleNewClientResult(clientName, false);
-
- QSignalSpy spy(&serverConnection, &ServerConnection::requestNewClientPrompt);
- QVERIFY(spy.isValid());
-
- serverConnection.handleLogLine(R"(unrecognised client name "test client")");
- QCOMPARE(spy.count(), 0);
-}
-
-void ServerConnectionTests::handleLogLine_serverConfigFull_shouldNotShowPrompt()
-{
- FullServerConfig m_serverConfig;
- ServerConnection serverConnection(nullptr, m_serverConfig);
-
- QSignalSpy spy(&serverConnection, &ServerConnection::requestNewClientPrompt);
- QVERIFY(spy.isValid());
-
- serverConnection.handleLogLine(R"(unrecognised client name "test client")");
- QCOMPARE(spy.count(), 0);
-}
-
-void ServerConnectionTests::handleLogLine_screenExists_shouldNotShowPrompt()
-{
- ScreenExistsServerConfig m_serverConfig;
- ServerConnection serverConnection(nullptr, m_serverConfig);
-
- QSignalSpy spy(&serverConnection, &ServerConnection::requestNewClientPrompt);
- QVERIFY(spy.isValid());
-
- serverConnection.handleLogLine(R"(unrecognised client name "test client")");
- QCOMPARE(spy.count(), 0);
-}
-
-QTEST_MAIN(ServerConnectionTests)
diff --git a/src/unittests/gui/core/ServerConnectionTests.h b/src/unittests/gui/core/ServerConnectionTests.h
deleted file mode 100644
index 558775a25..000000000
--- a/src/unittests/gui/core/ServerConnectionTests.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/*
- * Deskflow -- mouse and keyboard sharing utility
- * SPDX-FileCopyrightText: (C) 2025 Chris Rizzitello
- * SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
- */
-
-#include
-
-class ServerConnectionTests : public QObject
-{
- Q_OBJECT
-private Q_SLOTS:
- // Test are run in order top to bottom
- void initTestCase();
- void handleLogLine_newClient_shouldShowPrompt();
- void handleLogLine_ignoredClient_shouldNotShowPrompt();
- void handleLogLine_serverConfigFull_shouldNotShowPrompt();
- void handleLogLine_screenExists_shouldNotShowPrompt();
-
-private:
- inline static const QString m_settingsPath = QStringLiteral("tmp/test");
- inline static const QString m_settingsFile = QStringLiteral("%1/Deskflow.conf").arg(m_settingsPath);
- inline static const QString m_stateFile = QStringLiteral("%1/Deskflow.state").arg(m_settingsPath);
-};