Add reset settings menu action and env var (#7435)

* Add reset settings menu action and env var

* Delete profile and config dirs, reduce duplication with TLS dir and various other paths, fixed bug in new client dialog, move TLS arg to server

* Better filename

* Make method const

* Update ChangeLog

* Remove include

* Make param const

* Stop core process on reset

* Fixed: reset menu action causes config to be pulled from app/server config

* Center main window in middle of screen
This commit is contained in:
Nick Bolton 2024-08-11 00:58:24 +01:00 committed by GitHub
parent c4510bf093
commit 3e4cb4a1c7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
37 changed files with 559 additions and 228 deletions

View file

@ -21,6 +21,9 @@
# Enable verbose logging in the GUI (always off by default)
# SYNERGY_GUI_VERBOSE=true
# Reset all settings and delete all data on startup
# SYNERGY_RESET_ALL=true
#
# Build
#

View file

@ -77,6 +77,7 @@ Enhancements:
- #7431 Improve handling of Qt-related warnings and errors
- #7432 Only show close to tray reminder when not quitting the app
- #7434 Show dark logo in dark mode and improve .env loader
- #7435 Add reset settings menu action and env var
# 1.14.6

View file

@ -21,12 +21,14 @@
#include "AboutDialog.h"
#include "ActivationDialog.h"
#include "ServerConfigDialog.h"
#include "common/constants.h"
#include "gui/Logger.h"
#include "gui/TrayIcon.h"
#include "gui/VersionChecker.h"
#include "gui/config/ConfigScopes.h"
#include "gui/constants.h"
#include "gui/core/CoreProcess.h"
#include "gui/diagnostic.h"
#include "gui/dialogs/SettingsDialog.h"
#include "gui/license/LicenseHandler.h"
#include "gui/license/license_notices.h"
@ -76,13 +78,15 @@ MainWindow::MainWindow(ConfigScopes &configScopes, AppConfig &appConfig)
: m_ConfigScopes(configScopes),
m_AppConfig(appConfig),
m_ServerConfig(appConfig, *this),
m_CoreProcess(appConfig, m_ServerConfig),
m_ServerConnection(this, appConfig, m_ServerConfig),
m_CoreProcess(appConfig, m_ServerConfig, m_LicenseHandler.license()),
m_ServerConnection(
this, appConfig, m_ServerConfig, m_ServerConfigDialogState),
m_ClientConnection(this, appConfig),
m_TlsUtility(appConfig, m_LicenseHandler.license()),
m_WindowSaveTimer(this) {
setupUi(this);
createMenuBar();
setupControls();
connectSlots();
@ -90,15 +94,7 @@ MainWindow::MainWindow(ConfigScopes &configScopes, AppConfig &appConfig)
emit created();
}
MainWindow::~MainWindow() {
try {
saveWindow();
} catch (const std::exception &e) {
qFatal("failed to save window on main window close: %s", e.what());
}
m_CoreProcess.cleanup();
}
MainWindow::~MainWindow() { m_CoreProcess.cleanup(); }
void MainWindow::restoreWindow() {
@ -112,6 +108,11 @@ void MainWindow::restoreWindow() {
if (windowPosition.has_value()) {
qDebug("restoring main window position");
move(windowPosition.value());
} else {
// center main window in middle of screen
const auto screen = QGuiApplication::primaryScreen();
QRect screenGeometry = screen->geometry();
move(screenGeometry.center() - rect().center());
}
// give the window chance to restore its size and position before the window
@ -144,7 +145,6 @@ void MainWindow::setupControls() {
m_pActionActivate->setVisible(false);
}
createMenuBar();
secureSocket(false);
updateLocalFingerprint();
@ -273,7 +273,11 @@ void MainWindow::connectSlots() {
[this]() { showAndActivate(); });
}
void MainWindow::onAppAboutToQuit() { m_ConfigScopes.save(); }
void MainWindow::onAppAboutToQuit() {
if (m_SaveOnExit) {
m_ConfigScopes.save();
}
}
void MainWindow::onCreated() {
@ -398,6 +402,19 @@ void MainWindow::on_m_pActionTestCriticalError_triggered() const {
qCritical("test critical error");
}
void MainWindow::on_m_pActionClearSettings_triggered() {
if (!messages::showClearSettings(this)) {
qDebug("clear settings cancelled");
return;
}
m_CoreProcess.stop();
m_Quitting = true;
m_SaveOnExit = false;
diagnostic::clearSettings(m_ConfigScopes, true);
}
bool MainWindow::on_m_pActionSave_triggered() {
QString fileName =
QFileDialog::getSaveFileName(this, QString("Save configuration as..."));
@ -505,10 +522,12 @@ void MainWindow::on_m_pButtonConnectToClient_clicked() {
void MainWindow::onWindowSaveTimerTimeout() { saveWindow(); }
void MainWindow::onServerConnectionConfigureClient(const QString &clientName) {
m_ServerConfigDialogState.setVisible(true);
ServerConfigDialog dialog(this, m_ServerConfig, m_AppConfig);
if (dialog.addClient(clientName) && dialog.exec() == QDialog::Accepted) {
m_CoreProcess.restart();
}
m_ServerConfigDialogState.setVisible(false);
}
//////////////////////////////////////////////////////////////////////////////
@ -592,22 +611,21 @@ void MainWindow::createMenuBar() {
m_pMenuFile->addAction(m_pActionSave);
m_pMenuFile->addSeparator();
m_pMenuFile->addAction(m_pActionQuit);
m_pMenuEdit->addAction(m_pActionSettings);
m_pMenuWindow->addAction(m_pActionMinimize);
m_pMenuWindow->addAction(m_pActionRestore);
m_pMenuHelp->addAction(m_pActionAbout);
m_pMenuHelp->addAction(m_pActionHelp);
m_pMenuFile->addSeparator();
m_pMenuHelp->addAction(m_pActionClearSettings);
#ifndef NDEBUG
// always enable test menu in debug mode.
const auto enableTestMenu = true;
#else
// only enable test menu in release build if env var is true.
const auto enableTestMenu =
strToTrue(qEnvironmentVariable("SYNERGY_TEST_MENU"));
#endif
if (enableTestMenu) {
if (enableTestMenu || kDebugBuild) {
auto testMenu = new QMenu("Test", m_pMenuBar);
m_pMenuBar->addMenu(testMenu);
testMenu->addAction(m_pActionTestFatalError);

View file

@ -32,6 +32,7 @@
#include "gui/VersionChecker.h"
#include "gui/config/AppConfig.h"
#include "gui/config/ConfigScopes.h"
#include "gui/config/ServerConfigDialogState.h"
#include "gui/core/ClientConnection.h"
#include "gui/core/CoreProcess.h"
#include "gui/core/ServerConnection.h"
@ -130,6 +131,7 @@ private slots:
void on_m_pActionStopCore_triggered();
void on_m_pActionTestFatalError_triggered() const;
void on_m_pActionTestCriticalError_triggered() const;
void on_m_pActionClearSettings_triggered();
void on_m_pLineEditHostname_returnPressed();
void on_m_pLineEditClientIp_returnPressed();
void on_m_pLineEditHostname_textChanged(const QString &text);
@ -194,6 +196,8 @@ private:
bool m_SaveWindow = false;
LicenseHandler m_LicenseHandler;
bool m_Quitting = false;
synergy::gui::config::ServerConfigDialogState m_ServerConfigDialogState;
bool m_SaveOnExit = true;
synergy::gui::ConfigScopes &m_ConfigScopes;
AppConfig &m_AppConfig;

View file

@ -667,6 +667,14 @@
<enum>QAction::TextHeuristicRole</enum>
</property>
</action>
<action name="m_pActionClearSettings">
<property name="text">
<string>Clear settings</string>
</property>
<property name="menuRole">
<enum>QAction::TextHeuristicRole</enum>
</property>
</action>
</widget>
<customwidgets>
<customwidget>

View file

@ -25,8 +25,10 @@
#include "gui/Logger.h"
#include "gui/config/AppConfig.h"
#include "gui/config/ConfigScopes.h"
#include "gui/diagnostic.h"
#include "gui/dotenv.h"
#include "gui/messages.h"
#include "gui/string_utils.h"
#include <QApplication>
#include <QDebug>
@ -34,6 +36,7 @@
#include <QObject>
#include <QtCore>
#include <QtGui>
#include <qglobal.h>
#if defined(Q_OS_MAC)
#include <Carbon/Carbon.h>
@ -53,6 +56,11 @@ QString getSystemSettingPath();
bool checkMacAssistiveDevices();
#endif
bool hasArg(const QString &arg, const QStringList &args) {
return std::ranges::any_of(
args, [&arg](const QString &a) { return a == arg; });
}
int main(int argc, char *argv[]) {
#if defined(Q_OS_MAC)
@ -96,6 +104,15 @@ int main(int argc, char *argv[]) {
qRegisterMetaType<Edition>("Edition");
ConfigScopes configScopes;
// --no-reset
QStringList arguments = QCoreApplication::arguments();
const auto noReset = hasArg("--no-reset", arguments);
const auto resetEnvVar = strToTrue(qEnvironmentVariable("SYNERGY_RESET_ALL"));
if (resetEnvVar && !noReset) {
diagnostic::clearSettings(configScopes, false);
}
AppConfig appConfig(configScopes);
QObject::connect(

View file

@ -30,3 +30,9 @@ const auto kVersionGitSha = GIT_SHA_SHORT;
#else
const auto kVersionGitSha = "";
#endif
#ifndef NDEBUG
const auto kDebugBuild = true;
#else
const auto kDebugBuild = false;
#endif

View file

@ -100,7 +100,8 @@ AppConfig::AppConfig(
synergy::gui::IConfigScopes &scopes, std::shared_ptr<Deps> deps)
: m_Scopes(scopes),
m_pDeps(deps),
m_ScreenName(deps->hostname()) {
m_ScreenName(deps->hostname()),
m_TlsCertPath(deps->defaultTlsCertPath()) {
determineScope();
recall();
}
@ -314,13 +315,6 @@ void AppConfig::recallElevateMode() {
m_ElevateMode = static_cast<ElevateMode>(elevateMode.toInt());
}
QString AppConfig::defaultTlsCertPath() const {
QDir path(m_pDeps->profileDir());
path = path.filePath("SSL");
path = path.filePath("Synergy.pem");
return path.absolutePath();
}
QString AppConfig::settingName(Setting name) {
auto index = static_cast<int>(name);
return m_SettingsName[index];

View file

@ -21,7 +21,7 @@
#include "ElevateMode.h"
#include "IAppConfig.h"
#include "IConfigScopes.h"
#include "gui/core/CoreTool.h"
#include "gui/paths.h"
#include <QDir>
#include <QHostInfo>
@ -116,11 +116,10 @@ private:
public:
struct Deps {
virtual ~Deps() = default;
virtual QString profileDir() const { return m_coreTool.getProfileDir(); }
virtual QString defaultTlsCertPath() const {
return synergy::gui::paths::defaultTlsCertPath();
}
virtual QString hostname() const { return QHostInfo::localHostName(); }
private:
[[no_unique_address]] CoreTool m_coreTool;
};
explicit AppConfig(
@ -293,9 +292,16 @@ private:
*/
QString defaultTlsCertPath() const;
synergy::gui::IConfigScopes &m_Scopes;
std::shared_ptr<Deps> m_pDeps;
QString m_ScreenName;
static const char m_CoreServerName[];
static const char m_CoreClientName[];
static const char m_LogDir[];
/// @brief Contains the string values of the settings names that will be saved
static const char *const m_SettingsName[];
/// @brief Core config filename (not the Qt settings filename)
static const char m_ConfigFilename[];
int m_Port = 24800;
QString m_Interface = "";
int m_LogLevel = 0;
@ -324,7 +330,6 @@ private:
bool m_EnableService =
synergy::gui::kDefaultProcessMode == ProcessMode::kService;
bool m_CloseToTray = true;
QString m_TlsCertPath = defaultTlsCertPath();
int m_TlsKeyLength = synergy::gui::kDefaultTlsKeyLength;
std::optional<QSize> m_MainWindowSize;
std::optional<QPoint> m_MainWindowPosition;
@ -338,15 +343,10 @@ private:
*/
bool m_TlsChanged = false;
static const char m_CoreServerName[];
static const char m_CoreClientName[];
static const char m_LogDir[];
/// @brief Contains the string values of the settings names that will be saved
static const char *const m_SettingsName[];
/// @brief Core config filename (not the Qt settings filename)
static const char m_ConfigFilename[];
synergy::gui::IConfigScopes &m_Scopes;
std::shared_ptr<Deps> m_pDeps;
QString m_ScreenName;
QString m_TlsCertPath;
signals:
void tlsChanged();

View file

@ -54,11 +54,18 @@ ConfigScopes::ConfigScopes(std::shared_ptr<Deps> deps)
m_pSystemSettingsProxy->loadSystem();
}
void ConfigScopes::clear() const {
m_pUserSettingsProxy->clear();
m_pSystemSettingsProxy->clear();
}
void ConfigScopes::signalReady() { emit ready(); }
void ConfigScopes::save() {
qDebug("emitting config saving signal");
emit saving();
void ConfigScopes::save(bool emitSaving) {
if (emitSaving) {
qDebug("emitting config saving signal");
emit saving();
}
qDebug("writing config to filesystem");
m_pUserSettingsProxy->sync();

View file

@ -42,8 +42,10 @@ public:
explicit ConfigScopes(std::shared_ptr<Deps> deps = std::make_shared<Deps>());
~ConfigScopes() override = default;
void clear() const;
void signalReady() override;
void save() override;
void save(bool emitSaving = true) override;
bool scopeContains(
const QString &name, Scope scope = Scope::Current) const override;
bool isActiveScopeWritable() const override;

View file

@ -48,8 +48,10 @@ public:
* @brief Signals to listeners to save and calls `sync` on underlying Qt
* config.
*
* @param emitSaving Whether to emit the saving signal which typically
* triggers listeners to write their current state to the config.
*/
virtual void save() = 0;
virtual void save(bool emitSaving = true) = 0;
/**
* @brief Check a scope for a config value (default is current scope).

View file

@ -0,0 +1,38 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2021 Symless Ltd.
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file LICENSE that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
namespace synergy::gui::config {
/**
* @brief Represents the state of the server config dialog.
*
* Future design consideration: Once moving the server config dialog to the GUI
* lib, we can probably just pass a reference to that rather than needing an
* object to track it's state.
*/
class ServerConfigDialogState {
public:
bool isVisible() const { return m_isVisible; }
void setVisible(bool isVisible) { m_isVisible = isVisible; }
private:
bool m_isVisible = false;
};
} // namespace synergy::gui::config

View file

@ -20,6 +20,8 @@
#include "constants.h"
#include "gui/config/IAppConfig.h"
#include "gui/core/CoreTool.h"
#include "gui/paths.h"
#include "tls/TlsUtility.h"
#if defined(Q_OS_MAC)
#include "OSXHelpers.h"
@ -37,7 +39,7 @@
namespace synergy::gui {
const int kRetryDelay = 1000;
const auto kLastConfigFilename = "LastConfig.cfg";
const auto kServerConfigFilename = "synergy-server.conf";
const auto kLineSplitRegex = QRegularExpression("\r|\n|\r\n");
//
@ -135,9 +137,10 @@ QString CoreProcess::Deps::getProfileRoot() const {
CoreProcess::CoreProcess(
IAppConfig &appConfig, IServerConfig &serverConfig,
std::shared_ptr<Deps> deps)
const license::ILicense &license, std::shared_ptr<Deps> deps)
: m_appConfig(appConfig),
m_serverConfig(serverConfig),
m_license(license),
m_pDeps(deps) {
connect(
@ -339,65 +342,19 @@ void CoreProcess::start(std::optional<ProcessMode> processModeOption) {
setConnectionState(ConnectionState::Connecting);
QString app;
QStringList args;
args << "-f"
<< "--no-tray"
<< "--debug" << m_appConfig.logLevelText();
args << "--name" << m_appConfig.screenName();
if (processMode == ProcessMode::kDesktop) {
m_pDeps->process().create();
} else {
// tell client/server to talk to daemon through ipc.
args << "--ipc";
#if defined(Q_OS_WIN)
// tell the client/server to shut down when a ms windows desk
// is switched; this is because we may need to elevate or not
// based on which desk the user is in (login always needs
// elevation, where as default desk does not).
// Note that this is only enabled when synergy is set to elevate
// 'as needed' (e.g. on a UAC dialog popup) in order to prevent
// unnecessary restarts when synergy was started elevated or
// when it is not allowed to elevate. In these cases restarting
// the server is fruitless.
if (m_appConfig.elevateMode() == ElevateMode::kAutomatic) {
args << "--stop-on-desk-switch";
}
#endif
}
#ifndef Q_OS_LINUX
QString app;
QStringList args;
addGenericArgs(args, processMode);
if (m_serverConfig.enableDragAndDrop()) {
args << "--enable-drag-drop";
}
#endif
if (m_appConfig.tlsEnabled()) {
args << "--enable-crypto";
args << "--tls-cert" << m_appConfig.tlsCertPath();
}
#if defined(Q_OS_WIN)
// on windows, the profile directory changes depending on the user that
// launched the process (e.g. when launched with elevation). setting the
// profile dir on launch ensures it uses the same profile dir is used
// no matter how its relaunched.
args << "--profile-dir" << m_pDeps->getProfileRoot();
#endif
if (m_appConfig.preventSleep()) {
args << "--prevent-sleep";
}
if ((mode() == Mode::Client && !clientArgs(args, app)) ||
(mode() == Mode::Server && !serverArgs(args, app))) {
qDebug("failed to get args for core process, aborting start");
if (mode() == Mode::Server && !addServerArgs(args, app)) {
qDebug("failed to add server args for core process, aborting start");
return;
} else if (mode() == Mode::Client && !addClientArgs(args, app)) {
qDebug("failed to add client args for core process, aborting start");
return;
}
@ -483,7 +440,62 @@ void CoreProcess::cleanup() {
m_pDeps->ipcClient().disconnectFromHost();
}
bool CoreProcess::serverArgs(QStringList &args, QString &app) {
bool CoreProcess::addGenericArgs(
QStringList &args, const ProcessMode processMode) const {
args << "-f"
<< "--no-tray"
<< "--debug" << m_appConfig.logLevelText();
args << "--name" << m_appConfig.screenName();
if (processMode != ProcessMode::kDesktop) {
// tell client/server to talk to daemon through ipc.
args << "--ipc";
#if defined(Q_OS_WIN)
// tell the client/server to shut down when a ms windows desk
// is switched; this is because we may need to elevate or not
// based on which desk the user is in (login always needs
// elevation, where as default desk does not).
// Note that this is only enabled when synergy is set to elevate
// 'as needed' (e.g. on a UAC dialog popup) in order to prevent
// unnecessary restarts when synergy was started elevated or
// when it is not allowed to elevate. In these cases restarting
// the server is fruitless.
if (m_appConfig.elevateMode() == ElevateMode::kAutomatic) {
args << "--stop-on-desk-switch";
}
#endif
}
#ifndef Q_OS_LINUX
if (m_serverConfig.enableDragAndDrop()) {
args << "--enable-drag-drop";
}
#endif
if (m_appConfig.tlsEnabled()) {
args << "--enable-crypto";
}
#if defined(Q_OS_WIN)
// on windows, the profile directory changes depending on the user that
// launched the process (e.g. when launched with elevation). setting the
// profile dir on launch ensures it uses the same profile dir is used
// no matter how its relaunched.
args << "--profile-dir" << m_pDeps->getProfileRoot();
#endif
if (m_appConfig.preventSleep()) {
args << "--prevent-sleep";
}
return true;
}
bool CoreProcess::addServerArgs(QStringList &args, QString &app) {
app = m_pDeps->appPath(m_appConfig.coreServerName());
if (!m_pDeps->fileExists(app)) {
@ -497,7 +509,7 @@ bool CoreProcess::serverArgs(QStringList &args, QString &app) {
args << "--log" << m_appConfig.logFilename();
}
QString configFilename = persistConfig();
QString configFilename = persistServerConfig();
if (configFilename.isEmpty()) {
qFatal("config file name empty for server args");
return false;
@ -526,10 +538,22 @@ bool CoreProcess::serverArgs(QStringList &args, QString &app) {
args << "--serial-key" << m_appConfig.serialKey();
}
// bizarrely, the tls cert path arg was being given to the core client.
// since it's not clear why (it is only needed for the server), this has now
// been moved to server args.
if (m_appConfig.tlsEnabled()) {
TlsUtility tlsUtility(m_appConfig, m_license);
if (!tlsUtility.persistCertificate()) {
qCritical("failed to persist tls certificate");
return false;
}
args << "--tls-cert" << m_appConfig.tlsCertPath();
}
return true;
}
bool CoreProcess::clientArgs(QStringList &args, QString &app) {
bool CoreProcess::addClientArgs(QStringList &args, QString &app) {
app = m_pDeps->appPath(m_appConfig.coreClientName());
if (!m_pDeps->fileExists(app)) {
@ -568,35 +592,25 @@ bool CoreProcess::clientArgs(QStringList &args, QString &app) {
return true;
}
QString CoreProcess::persistConfig() const {
QString CoreProcess::persistServerConfig() const {
QString configFullPath;
if (m_appConfig.useExternalConfig()) {
return m_appConfig.configFile();
}
for (auto path :
{QStandardPaths::AppDataLocation, QStandardPaths::AppConfigLocation}) {
auto configDirPath = QStandardPaths::writableLocation(path);
if (!QDir().mkpath(configDirPath)) {
qWarning("failed to create config folder: %s", qPrintable(configDirPath));
continue;
}
const auto configDir = paths::configDir(true);
const auto configDirPath = configDir.absolutePath();
QFile configFile(configDirPath + "/" + kLastConfigFilename);
if (!configFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
qWarning(
"failed to open core config file: %s",
qPrintable(configFile.fileName()));
continue;
}
m_serverConfig.save(configFile);
configFile.close();
return configFile.fileName();
QFile configFile(configDirPath + "/" + kServerConfigFilename);
if (!configFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
qFatal(
"failed to open core config file for write: %s",
qPrintable(configFile.fileName()));
}
qFatal("failed to persist config file");
return "";
m_serverConfig.save(configFile);
configFile.close();
return configFile.fileName();
}
QString CoreProcess::modeString() const {

View file

@ -21,6 +21,7 @@
#include "gui/config/IServerConfig.h"
#include "gui/ipc/QIpcClient.h"
#include "gui/proxy/QProcessProxy.h"
#include "license/License.h"
#include <QMutex>
#include <QObject>
@ -58,6 +59,7 @@ public:
explicit CoreProcess(
IAppConfig &appConfig, IServerConfig &serverConfig,
const license::ILicense &license,
std::shared_ptr<Deps> deps = std::make_shared<Deps>());
void extracted(QString &app, QStringList &args);
@ -98,9 +100,10 @@ private:
void startService(const QString &app, const QStringList &args);
void stopDesktop() const;
void stopService();
bool serverArgs(QStringList &args, QString &app);
bool clientArgs(QStringList &args, QString &app);
QString persistConfig() const;
bool addGenericArgs(QStringList &args, const ProcessMode processMode) const;
bool addServerArgs(QStringList &args, QString &app);
bool addClientArgs(QStringList &args, QString &app);
QString persistServerConfig() const;
QString modeString() const;
QString processModeString() const;
void setConnectionState(ConnectionState state);
@ -117,6 +120,7 @@ private:
IAppConfig &m_appConfig;
IServerConfig &m_serverConfig;
const license::ILicense &m_license;
std::shared_ptr<Deps> m_pDeps;
QString m_address;
ProcessState m_processState = ProcessState::Stopped;

View file

@ -18,6 +18,7 @@
#include "ServerConnection.h"
#include "ServerMessage.h"
#include "gui/config/ServerConfigDialogState.h"
#include "messages.h"
#include <QMessageBox>
@ -40,10 +41,12 @@ messages::NewClientPromptResult ServerConnection::Deps::showNewClientPrompt(
ServerConnection::ServerConnection(
QWidget *parent, IAppConfig &appConfig, IServerConfig &serverConfig,
const config::ServerConfigDialogState &serverConfigDialogState,
std::shared_ptr<Deps> deps)
: m_pParent(parent),
m_appConfig(appConfig),
m_serverConfig(serverConfig),
m_serverConfigDialogState(serverConfigDialogState),
m_pDeps(deps) {}
void ServerConnection::handleLogLine(const QString &logLine) {
@ -53,6 +56,16 @@ void ServerConnection::handleLogLine(const QString &logLine) {
return;
}
if (m_messageShowing) {
qDebug("new client message already shown, skipping for now");
return;
}
if (m_serverConfigDialogState.isVisible()) {
qDebug("server config dialog visible, skipping new client prompt");
return;
}
if (m_appConfig.useExternalConfig()) {
qDebug("external config enabled, skipping new client prompt");
return;
@ -75,11 +88,6 @@ void ServerConnection::handleNewClient(const QString &clientName) {
m_receivedClients.append(clientName);
if (m_messageShowing) {
qDebug("new client message already shown, skipping");
return;
}
if (m_serverConfig.isFull()) {
qDebug(
"server config full, skipping new client prompt for: %s",

View file

@ -22,6 +22,7 @@
#include "gui/config/IAppConfig.h"
#include "gui/config/IServerConfig.h"
#include "gui/config/ServerConfigDialogState.h"
#include "gui/messages.h"
namespace synergy::gui {
@ -39,6 +40,7 @@ public:
explicit ServerConnection(
QWidget *parent, IAppConfig &appConfig, IServerConfig &serverConfig,
const config::ServerConfigDialogState &serverConfigDialogState,
std::shared_ptr<Deps> deps = std::make_shared<Deps>());
void handleLogLine(const QString &logLine);
@ -52,6 +54,7 @@ private:
QWidget *m_pParent;
IAppConfig &m_appConfig;
IServerConfig &m_serverConfig;
const config::ServerConfigDialogState &m_serverConfigDialogState;
std::shared_ptr<Deps> m_pDeps;
QStringList m_receivedClients;
bool m_messageShowing = false;

View file

@ -0,0 +1,67 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2024 Symless Ltd.
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file LICENSE that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "diagnostic.h"
#include "config/ConfigScopes.h"
#include "paths.h"
#include <QApplication>
#include <QDir>
#include <QProcess>
namespace synergy::gui::diagnostic {
void restart() {
QString program = QCoreApplication::applicationFilePath();
QStringList arguments = QCoreApplication::arguments();
// prevent infinite reset loop when env var set.
arguments << "--no-reset";
qInfo("launching new process: %s", qPrintable(program));
QProcess::startDetached(program, arguments);
qDebug("exiting current process");
QApplication::exit();
}
void clearSettings(ConfigScopes &scopes, bool enableRestart) {
qDebug("clearing settings");
scopes.clear();
// save but do not emit saving signal which will prevent the current state of
// the app config and server configs from being applied.
scopes.save(false);
auto configDir = paths::configDir();
qDebug("removing config dir: %s", qPrintable(configDir.absolutePath()));
configDir.removeRecursively();
auto profileDir = paths::coreProfileDir();
qDebug("removing profile dir: %s", qPrintable(profileDir.absolutePath()));
profileDir.removeRecursively();
if (enableRestart) {
qDebug("restarting");
restart();
} else {
qDebug("skipping restart");
}
}
} // namespace synergy::gui::diagnostic

26
src/lib/gui/diagnostic.h Normal file
View file

@ -0,0 +1,26 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2024 Symless Ltd.
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file LICENSE that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "gui/config/ConfigScopes.h"
namespace synergy::gui::diagnostic {
void clearSettings(ConfigScopes &scopes, bool enableRestart);
}

View file

@ -17,6 +17,8 @@
#include "dotenv.h"
#include "paths.h"
#include <QCoreApplication>
#include <QDebug>
#include <QDir>
@ -55,17 +57,10 @@ void dotenv(const QString &filename) {
"no %s file in dir: %s", qPrintable(filename),
qPrintable(fileInfo.absolutePath()));
// if nothing in current dir, then try the app data dir.
// if nothing in current dir, then try the config dir.
// this makes it a bit easier for engineers in the field to have an easily
// predictable location for the .env file.
QDir configDir(
QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation));
// HACK: since we have the org name set to the app name, the config dir is
// confusing. make this simple by using the org dir instead.
// use `filePath("..")` instead of `cdUp` to avoid the existence check.
QDir orgDir = configDir.filePath("..");
orgDir = orgDir.absolutePath();
const auto orgDir = paths::configDir();
filePath = orgDir.filePath(filename);
if (!open(file, filePath)) {

View file

@ -249,4 +249,17 @@ showNewClientPrompt(QWidget *parent, const QString &clientName) {
}
}
bool showClearSettings(QWidget *parent) {
QMessageBox message(parent);
message.addButton(QObject::tr("Cancel"), QMessageBox::RejectRole);
const auto clear =
message.addButton(QObject::tr("Clear settings"), QMessageBox::AcceptRole);
message.setText(
"<p>Are you sure you want to clear all settings and restart Synergy?</p>"
"<p>This action cannot be undone.</p>");
message.exec();
return message.clickedButton() == clear;
}
} // namespace synergy::gui::messages

View file

@ -46,4 +46,6 @@ void showClientConnectError(
NewClientPromptResult
showNewClientPrompt(QWidget *parent, const QString &clientName);
bool showClearSettings(QWidget *parent);
} // namespace synergy::gui::messages

67
src/lib/gui/paths.h Normal file
View file

@ -0,0 +1,67 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2024 Symless Ltd.
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file LICENSE that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "core/CoreTool.h"
#include <QDir>
#include <QStandardPaths>
#include <QString>
const auto kCertificateFilename = "Synergy.pem";
const auto kSslDir = "SSL";
namespace synergy::gui::paths {
/**
* @brief Gets the org config dir (parent of app config dir).
*/
inline QDir configDir(const bool persist = false) {
const QDir configDir(
QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation));
// HACK: since we have the org name set to the app name, the config dir is
// confusing. make this simple by using the org dir instead.
// use `filePath("..")` instead of `cdUp` to avoid the existence check.
const QDir orgDir = configDir.filePath("..");
if (persist) {
const auto orgDirPath = orgDir.absolutePath();
if (!QDir().mkpath(orgDirPath)) {
qFatal("failed to persist config dir: %s", qPrintable(orgDirPath));
}
}
return orgDir.absolutePath();
}
/**
* @brief Uses the Core tool to get the profile dir.
*/
inline QDir coreProfileDir() {
CoreTool coreTool;
return QDir(coreTool.getProfileDir());
}
inline QString defaultTlsCertPath() {
const auto root = coreProfileDir();
const auto sslDirPath = QDir(root.filePath(kSslDir));
return sslDirPath.filePath(kCertificateFilename);
}
} // namespace synergy::gui::paths

View file

@ -29,7 +29,7 @@ public:
virtual void loadUser();
virtual void loadSystem();
virtual void clear() { m_pSettings->clear(); }
virtual void sync() { m_pSettings->sync(); }
virtual int beginReadArray(const QString &prefix);
virtual void beginWriteArray(const QString &prefix);

View file

@ -18,18 +18,16 @@
#include "TlsCertificate.h"
#include "TlsFingerprint.h"
#include "gui/paths.h"
#include <QCoreApplication>
#include <QDir>
#include <QProcess>
#include <optional>
static const char *const kCertificateKeyLength = "rsa:";
static const char *const kCertificateHashAlgorithm = "-sha256";
static const char *const kCertificateLifetime = "365";
static const char *const kCertificateSubjectInfo = "/CN=Synergy";
static const char *const kCertificateFilename = "Synergy.pem";
static const char *const kSslDir = "SSL";
#if defined(Q_OS_WIN)
static const char *const kWinOpenSslDir = "OpenSSL";
@ -39,6 +37,8 @@ static const char *const kConfigFile = "synergy.conf";
static const char *const kUnixOpenSslCommand = "openssl";
#endif
using namespace synergy::gui;
#if defined(Q_OS_WIN)
namespace synergy::gui {
@ -87,12 +87,7 @@ using namespace synergy::gui;
#endif
TlsCertificate::TlsCertificate(QObject *parent) : QObject(parent) {
m_profileDir = m_coreTool.getProfileDir();
if (m_profileDir.isEmpty()) {
qCritical("empty profile directory result");
}
}
TlsCertificate::TlsCertificate(QObject *parent) : QObject(parent) {}
bool TlsCertificate::runTool(const QStringList &args) {
QString program;
@ -157,21 +152,17 @@ bool TlsCertificate::runTool(const QStringList &args) {
}
bool TlsCertificate::generateCertificate(const QString &path, int keyLength) {
QString sslDirPath =
QString("%1%2%3").arg(m_profileDir).arg(QDir::separator()).arg(kSslDir);
qDebug("generating tls certificate: %s", qUtf8Printable(path));
QString defaultPath = QString("%1%2%3")
.arg(sslDirPath)
.arg(QDir::separator())
.arg(kCertificateFilename);
QFileInfo info(path);
QDir dir(info.absolutePath());
if (!dir.exists() && !dir.mkpath(".")) {
qCritical("failed to create directory for tls certificate");
return false;
}
QString keySize = kCertificateKeyLength + QString::number(keyLength);
const QString pathToUse =
QDir::cleanPath(path.isEmpty() ? defaultPath : path);
qDebug("generating tls certificate: %s", qUtf8Printable(pathToUse));
QStringList arguments;
// self signed certificate
@ -179,7 +170,7 @@ bool TlsCertificate::generateCertificate(const QString &path, int keyLength) {
arguments.append("-x509");
arguments.append("-nodes");
// valide duration
// valid duration
arguments.append("-days");
arguments.append(kCertificateLifetime);
@ -193,22 +184,18 @@ bool TlsCertificate::generateCertificate(const QString &path, int keyLength) {
arguments.append("-newkey");
arguments.append(keySize);
if (QDir sslDir(sslDirPath); !sslDir.exists()) {
sslDir.mkpath(".");
}
// key output filename
arguments.append("-keyout");
arguments.append(pathToUse);
arguments.append(path);
// certificate output filename
arguments.append("-out");
arguments.append(pathToUse);
arguments.append(path);
if (runTool(arguments)) {
qDebug("tls certificate generated");
return generateFingerprint(pathToUse);
return generateFingerprint(path);
} else {
qCritical("failed to generate tls certificate");
return false;

View file

@ -35,7 +35,6 @@ private:
bool generateFingerprint(const QString &certificateFilename);
private:
QString m_profileDir;
QString m_toolStdout;
[[no_unique_address]] CoreTool m_coreTool;
};

View file

@ -20,12 +20,13 @@
#include "TlsCertificate.h"
#include "constants.h"
#include <QFile>
#include <QString>
namespace synergy::gui {
TlsUtility::TlsUtility(
const IAppConfig &appConfig, const license::License &license)
const IAppConfig &appConfig, const license::ILicense &license)
: m_appConfig(appConfig),
m_license(license) {}
@ -48,10 +49,20 @@ bool TlsUtility::generateCertificate() {
return false;
}
auto path = m_appConfig.tlsCertPath();
auto length = m_appConfig.tlsKeyLength();
return m_certificate.generateCertificate(path, length);
return m_certificate.generateCertificate(m_appConfig.tlsCertPath(), length);
}
bool TlsUtility::persistCertificate() {
qDebug("persisting tls certificate");
if (QFile::exists(m_appConfig.tlsCertPath())) {
qDebug("tls certificate already exists");
return true;
}
return generateCertificate();
}
} // namespace synergy::gui

View file

@ -31,9 +31,10 @@ class TlsUtility : public QObject {
public:
explicit TlsUtility(
const IAppConfig &appConfig, const license::License &license);
const IAppConfig &appConfig, const license::ILicense &license);
bool generateCertificate();
bool persistCertificate();
/**
* @brief Combines the availability and the enabled status of TLS.
@ -51,7 +52,7 @@ public:
private:
const IAppConfig &m_appConfig;
const license::License &m_license;
const license::ILicense &m_license;
TlsCertificate m_certificate;
};

View file

@ -0,0 +1,32 @@
/*
* synergy -- mouse and keyboard sharing utility
* Copyright (C) 2016 Symless Ltd.
*
* This package is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* found in the file LICENSE that should have accompanied this file.
*
* This package is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
class Server;
class LicenseHandler;
class LicenseTests;
namespace synergy::license {
class ILicense {
public:
virtual ~ILicense() = default;
virtual bool isTlsAvailable() const = 0;
};
} // namespace synergy::license

View file

@ -17,6 +17,7 @@
#pragma once
#include "ILicense.h"
#include "SerialKey.h"
#include <chrono>
@ -30,7 +31,7 @@ class LicenseTests;
namespace synergy::license {
class License {
class License : public ILicense {
friend class ::Server;
friend class ::LicenseHandler;
friend class ::LicenseTests;
@ -44,18 +45,20 @@ class License {
public:
explicit License(const SerialKey &serialKey);
explicit License(const std::string &hexString);
~License() override = default;
friend bool operator==(License const &lhs, License const &rhs) {
return lhs.m_serialKey == rhs.m_serialKey;
}
bool isTlsAvailable() const override;
bool isValid() const { return m_serialKey.isValid; }
bool isExpiringSoon() const;
bool isExpired() const;
bool isTrial() const;
bool isSubscription() const;
bool isTimeLimited() const;
bool isTlsAvailable() const;
days daysLeft() const;
Edition productEdition() const;
std::string productName() const;

View file

@ -40,7 +40,7 @@ IDataSocket *SecureServerSocket::accept() {
socket->initSsl(true);
setListeningJob();
auto certificateFilename = getCertifcateFileName();
auto certificateFilename = getCertificateFileName();
if (socket->loadCertificates(certificateFilename)) {
socket->secureAccept();
} else {
@ -64,7 +64,7 @@ IDataSocket *SecureServerSocket::accept() {
return dynamic_cast<IDataSocket *>(socket);
}
std::string SecureServerSocket::getCertifcateFileName() const {
std::string SecureServerSocket::getCertificateFileName() const {
// if the tls cert option is set use that for the certificate file
auto certificateFilename = ArgParser::argsBase().m_tlsCertFile;

View file

@ -27,5 +27,5 @@ public:
IDataSocket *accept() override;
private:
std::string getCertifcateFileName() const;
std::string getCertificateFileName() const;
};

View file

@ -48,12 +48,12 @@ public:
MOCK_METHOD(bool, isActiveScopeWritable, (), (const, override));
MOCK_METHOD(const QSettingsProxy &, activeSettings, (), (const, override));
MOCK_METHOD(QSettingsProxy &, activeSettings, (), (override));
MOCK_METHOD(void, save, (), (override));
MOCK_METHOD(void, save, (bool), (override));
};
struct DepsMock : public AppConfig::Deps {
DepsMock() {
ON_CALL(*this, profileDir()).WillByDefault(Return("stub"));
ON_CALL(*this, defaultTlsCertPath()).WillByDefault(Return("stub"));
ON_CALL(*this, hostname()).WillByDefault(Return("stub"));
}
@ -61,7 +61,7 @@ struct DepsMock : public AppConfig::Deps {
return std::make_shared<NiceMock<DepsMock>>();
}
MOCK_METHOD(QString, profileDir, (), (const, override));
MOCK_METHOD(QString, defaultTlsCertPath, (), (const, override));
MOCK_METHOD(QString, hostname, (), (const, override));
};

View file

@ -63,8 +63,7 @@ struct DepsMock : public ConfigScopes::Deps {
} // namespace
TEST(ConfigScopesTests, ctor_callsMakeUserSettings) {
std::shared_ptr<NiceMock<DepsMock>> deps =
std::make_shared<NiceMock<DepsMock>>();
auto deps = std::make_shared<NiceMock<DepsMock>>();
EXPECT_CALL(*deps, makeUserSettings()).Times(1);
@ -72,8 +71,7 @@ TEST(ConfigScopesTests, ctor_callsMakeUserSettings) {
}
TEST(ConfigScopesTests, ctor_callsMakeSystemSettings) {
std::shared_ptr<NiceMock<DepsMock>> deps =
std::make_shared<NiceMock<DepsMock>>();
auto deps = std::make_shared<NiceMock<DepsMock>>();
EXPECT_CALL(*deps, makeSystemSettings()).Times(1);
@ -81,8 +79,7 @@ TEST(ConfigScopesTests, ctor_callsMakeSystemSettings) {
}
TEST(ConfigScopesTests, save_syncsBothScopes) {
std::shared_ptr<NiceMock<DepsMock>> deps =
std::make_shared<NiceMock<DepsMock>>();
auto deps = std::make_shared<NiceMock<DepsMock>>();
ConfigScopes configScopes(deps);
@ -93,8 +90,7 @@ TEST(ConfigScopesTests, save_syncsBothScopes) {
}
TEST(ConfigScopesTests, activeSettings_returnsUserSettingsByDefault) {
std::shared_ptr<NiceMock<DepsMock>> deps =
std::make_shared<NiceMock<DepsMock>>();
auto deps = std::make_shared<NiceMock<DepsMock>>();
ConfigScopes configScopes(deps);
@ -102,8 +98,7 @@ TEST(ConfigScopesTests, activeSettings_returnsUserSettingsByDefault) {
}
TEST(ConfigScopesTests, activeSettings_returnsSystemSettingsWhenSystemScope) {
std::shared_ptr<NiceMock<DepsMock>> deps =
std::make_shared<NiceMock<DepsMock>>();
auto deps = std::make_shared<NiceMock<DepsMock>>();
ConfigScopes configScopes(deps);
configScopes.setActiveScope(ConfigScopes::Scope::System);
@ -112,8 +107,7 @@ TEST(ConfigScopesTests, activeSettings_returnsSystemSettingsWhenSystemScope) {
}
TEST(ConfigScopesTests, setActiveScope_setsCurrentScope) {
std::shared_ptr<NiceMock<DepsMock>> deps =
std::make_shared<NiceMock<DepsMock>>();
auto deps = std::make_shared<NiceMock<DepsMock>>();
ConfigScopes configScopes(deps);
@ -125,8 +119,7 @@ TEST(ConfigScopesTests, setActiveScope_setsCurrentScope) {
TEST(
ConfigScopesTests,
isActiveScopeWritable_returnsTrueWhenUserSettingsWritable) {
std::shared_ptr<NiceMock<DepsMock>> deps =
std::make_shared<NiceMock<DepsMock>>();
auto deps = std::make_shared<NiceMock<DepsMock>>();
ConfigScopes configScopes(deps);
@ -138,8 +131,7 @@ TEST(
TEST(
ConfigScopesTests,
scopeContains_byDefault_returnsTrueWhenUserSettingsContainsKey) {
std::shared_ptr<NiceMock<DepsMock>> deps =
std::make_shared<NiceMock<DepsMock>>();
auto deps = std::make_shared<NiceMock<DepsMock>>();
ON_CALL(*deps->m_pUserSettings, contains(_)).WillByDefault(Return(true));
@ -151,8 +143,7 @@ TEST(
TEST(
ConfigScopesTests,
scopeContains_userScope_returnsTrueWhenUserSettingsContainsKey) {
std::shared_ptr<NiceMock<DepsMock>> deps =
std::make_shared<NiceMock<DepsMock>>();
auto deps = std::make_shared<NiceMock<DepsMock>>();
ON_CALL(*deps->m_pUserSettings, contains(_)).WillByDefault(Return(true));
@ -164,8 +155,7 @@ TEST(
TEST(
ConfigScopesTests,
scopeContains_systemScope_returnsTrueWhenSystemSettingsContainsKey) {
std::shared_ptr<NiceMock<DepsMock>> deps =
std::make_shared<NiceMock<DepsMock>>();
auto deps = std::make_shared<NiceMock<DepsMock>>();
ON_CALL(*deps->m_pSystemSettings, contains(_)).WillByDefault(Return(true));
@ -175,8 +165,7 @@ TEST(
}
TEST(ConfigScopesTests, activeFilePath_returnsUserSettingsFileNameByDefault) {
std::shared_ptr<NiceMock<DepsMock>> deps =
std::make_shared<NiceMock<DepsMock>>();
auto deps = std::make_shared<NiceMock<DepsMock>>();
ON_CALL(*deps->m_pUserSettings, fileName()).WillByDefault(Return("test"));
ConfigScopes configScopes(deps);
@ -185,8 +174,7 @@ TEST(ConfigScopesTests, activeFilePath_returnsUserSettingsFileNameByDefault) {
}
TEST(ConfigScopesTests, getFromScope_byDefault_returnsValueFromActiveSettings) {
std::shared_ptr<NiceMock<DepsMock>> deps =
std::make_shared<NiceMock<DepsMock>>();
auto deps = std::make_shared<NiceMock<DepsMock>>();
ON_CALL(*deps->m_pUserSettings, value(_, _)).WillByDefault(Return("test"));
ConfigScopes configScopes(deps);
@ -195,8 +183,7 @@ TEST(ConfigScopesTests, getFromScope_byDefault_returnsValueFromActiveSettings) {
}
TEST(ConfigScopesTests, setInScope_byDefault_setsValueInActiveSettings) {
std::shared_ptr<NiceMock<DepsMock>> deps =
std::make_shared<NiceMock<DepsMock>>();
auto deps = std::make_shared<NiceMock<DepsMock>>();
ConfigScopes configScopes(deps);

View file

@ -19,6 +19,7 @@
#include "gui/core/CoreProcess.h"
#include "gui/ipc/IQIpcClient.h"
#include "gui/proxy/QProcessProxy.h"
#include "license/ILicense.h"
#include "shared/gui/mocks/AppConfigMock.h"
#include "shared/gui/mocks/ServerConfigMock.h"
@ -27,10 +28,8 @@
#include <gtest/gtest.h>
using namespace synergy::gui;
using ::testing::_;
using ::testing::NiceMock;
using ::testing::Return;
using ::testing::ReturnRef;
using namespace synergy::license;
using namespace testing;
namespace {
@ -92,12 +91,23 @@ public:
NiceMock<QIpcClientMock> m_ipcClient;
};
class CoreProcessTests : public ::testing::Test {
class LicenseMock : public ILicense {
public:
CoreProcessTests() : m_coreProcess(m_appConfig, m_serverConfig, m_pDeps) {}
LicenseMock() {
ON_CALL(*this, isTlsAvailable()).WillByDefault(Return(true));
}
MOCK_METHOD(bool, isTlsAvailable, (), (const, override));
};
class CoreProcessTests : public Test {
public:
CoreProcessTests()
: m_coreProcess(m_appConfig, m_serverConfig, m_license, m_pDeps) {}
NiceMock<AppConfigMock> m_appConfig;
NiceMock<ServerConfigMock> m_serverConfig;
NiceMock<LicenseMock> m_license;
std::shared_ptr<NiceMock<DepsMock>> m_pDeps =
std::make_shared<NiceMock<DepsMock>>();
CoreProcess m_coreProcess;

View file

@ -15,6 +15,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "gui/config/ServerConfigDialogState.h"
#include "gui/core/ServerConnection.h"
#include "shared/gui/mocks/AppConfigMock.h"
@ -45,11 +46,12 @@ public:
std::shared_ptr<DepsMock> m_pDeps = std::make_shared<NiceMock<DepsMock>>();
NiceMock<AppConfigMock> m_appConfig;
NiceMock<ServerConfigMock> m_serverConfig;
config::ServerConfigDialogState m_serverConfigDialogState;
};
TEST_F(ServerConnectionTests, handleLogLine_newClient_shouldShowPrompt) {
ServerConnection serverConnection(
nullptr, m_appConfig, m_serverConfig, m_pDeps);
nullptr, m_appConfig, m_serverConfig, m_serverConfigDialogState, m_pDeps);
QString clientName = "test client";
EXPECT_CALL(*m_pDeps, showNewClientPrompt(_, clientName));
@ -59,7 +61,7 @@ TEST_F(ServerConnectionTests, handleLogLine_newClient_shouldShowPrompt) {
TEST_F(ServerConnectionTests, handleLogLine_ignoredClient_shouldNotShowPrompt) {
ServerConnection serverConnection(
nullptr, m_appConfig, m_serverConfig, m_pDeps);
nullptr, m_appConfig, m_serverConfig, m_serverConfigDialogState, m_pDeps);
ON_CALL(*m_pDeps, showNewClientPrompt(_, _))
.WillByDefault(testing::Return(messages::NewClientPromptResult::Ignore));
serverConnection.handleLogLine(R"(unrecognised client name "stub")");
@ -72,7 +74,7 @@ TEST_F(ServerConnectionTests, handleLogLine_ignoredClient_shouldNotShowPrompt) {
TEST_F(
ServerConnectionTests, handleLogLine_serverConfigFull_shouldNotShowPrompt) {
ServerConnection serverConnection(
nullptr, m_appConfig, m_serverConfig, m_pDeps);
nullptr, m_appConfig, m_serverConfig, m_serverConfigDialogState, m_pDeps);
ON_CALL(m_serverConfig, isFull()).WillByDefault(testing::Return(true));
EXPECT_CALL(*m_pDeps, showNewClientPrompt(_, _)).Times(0);
@ -82,7 +84,7 @@ TEST_F(
TEST_F(ServerConnectionTests, handleLogLine_screenExists_shouldNotShowPrompt) {
ServerConnection serverConnection(
nullptr, m_appConfig, m_serverConfig, m_pDeps);
nullptr, m_appConfig, m_serverConfig, m_serverConfigDialogState, m_pDeps);
ON_CALL(m_serverConfig, screenExists(_)).WillByDefault(testing::Return(true));
EXPECT_CALL(*m_pDeps, showNewClientPrompt(_, _)).Times(0);

View file

@ -39,7 +39,7 @@ TEST(SettingsDialogTests, ctor_getsScreenName) {
serialKey.isValid = true;
License license(serialKey);
auto cpDeps = std::make_shared<CoreProcess::Deps>();
CoreProcess coreProcess(appConfig, serverConfig, cpDeps);
CoreProcess coreProcess(appConfig, serverConfig, license, cpDeps);
EXPECT_CALL(appConfig, screenName()).Times(1);