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:
parent
c4510bf093
commit
3e4cb4a1c7
37 changed files with 559 additions and 228 deletions
|
|
@ -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
|
||||
#
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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).
|
||||
|
|
|
|||
38
src/lib/gui/config/ServerConfigDialogState.h
Normal file
38
src/lib/gui/config/ServerConfigDialogState.h
Normal 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
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
67
src/lib/gui/diagnostic.cpp
Normal file
67
src/lib/gui/diagnostic.cpp
Normal 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
26
src/lib/gui/diagnostic.h
Normal 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);
|
||||
|
||||
}
|
||||
|
|
@ -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)) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
67
src/lib/gui/paths.h
Normal 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
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@ private:
|
|||
bool generateFingerprint(const QString &certificateFilename);
|
||||
|
||||
private:
|
||||
QString m_profileDir;
|
||||
QString m_toolStdout;
|
||||
[[no_unique_address]] CoreTool m_coreTool;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
|||
32
src/lib/license/ILicense.h
Normal file
32
src/lib/license/ILicense.h
Normal 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
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -27,5 +27,5 @@ public:
|
|||
IDataSocket *accept() override;
|
||||
|
||||
private:
|
||||
std::string getCertifcateFileName() const;
|
||||
std::string getCertificateFileName() const;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue