diff --git a/src/lib/common/Settings.cpp b/src/lib/common/Settings.cpp index 544fde13c..4c6e49d0d 100644 --- a/src/lib/common/Settings.cpp +++ b/src/lib/common/Settings.cpp @@ -20,45 +20,42 @@ Settings *Settings::instance() void Settings::setSettingFile(const QString &settingsFile) { - if (instance()->m_portableSettingsFile == settingsFile) { - qDebug().noquote() << "settings file already in use"; + if (instance()->settingsFile() == settingsFile) { + qDebug("settings file already set, skipping"); return; } - instance()->m_portableSettingsFile = settingsFile; if (instance()->m_settings) instance()->m_settings->deleteLater(); - instance()->m_settings = new QSettings(instance()->m_portableSettingsFile, QSettings::IniFormat); - instance()->m_settingsProxy->load(instance()->m_portableSettingsFile); - qInfo().noquote() << "settings file:" << instance()->m_settings->fileName(); + + instance()->m_settings = new QSettings(settingsFile, QSettings::IniFormat); + instance()->m_settingsProxy->load(settingsFile); + qInfo().noquote() << "settings file changed:" << instance()->m_settings->fileName(); } Settings::Settings(QObject *parent) : QObject(parent) { QString fileToLoad; #ifdef Q_OS_WIN - m_portableSettingsFile = m_portableSettingsFile.arg(QCoreApplication::applicationDirPath(), kAppName); - if (QFile(m_portableSettingsFile).exists()) { - fileToLoad = m_portableSettingsFile; - m_settings = new QSettings(fileToLoad, QSettings::IniFormat); - } else { - m_settings = new QSettings(QSettings::NativeFormat, QSettings::UserScope, kAppName, kAppName); - } + const auto portableFile = portableSettingsFile(); + qDebug().noquote() << "checking for portable settings file at:" << portableFile; + if (QFile(portableFile).exists()) + fileToLoad = portableFile; #else if (!qEnvironmentVariable("XDG_CONFIG_HOME").isEmpty()) fileToLoad = QStringLiteral("%1/%2/%2.conf").arg(qEnvironmentVariable("XDG_CONFIG_HOME"), kAppName); +#endif else if (QFile(UserSettingFile).exists()) fileToLoad = UserSettingFile; else if (QFile(SystemSettingFile).exists()) fileToLoad = SystemSettingFile; else fileToLoad = UserSettingFile; - m_settings = new QSettings(fileToLoad, QSettings::IniFormat); -#endif + m_settings = new QSettings(fileToLoad, QSettings::IniFormat); m_settingsProxy = std::make_shared(); m_settingsProxy->load(fileToLoad); - qInfo().noquote() << "settings file:" << m_settings->fileName(); + qInfo().noquote() << "initial settings file:" << m_settings->fileName(); } void Settings::cleanSettings() @@ -143,7 +140,7 @@ QVariant Settings::defaultValue(const QString &key) return 4; // INFO if (key == Daemon::Elevate) - return Settings::isNativeMode(); + return !Settings::isPortableMode(); if (key == Core::UpdateUrl) return kUrlUpdateCheck; @@ -155,10 +152,12 @@ QVariant Settings::defaultValue(const QString &key) return 24800; if (key == Core::ProcessMode) { - if (Settings::isNativeMode()) +#ifdef Q_OS_WIN + if (!Settings::isPortableMode()) return Settings::ProcessMode::Service; - else - return Settings::ProcessMode::Desktop; +#endif + + return Settings::ProcessMode::Desktop; } if (key == Daemon::LogFile) { @@ -196,14 +195,13 @@ QStringList Settings::validKeys() bool Settings::isWritable() { - if (Settings::isNativeMode()) - return true; return instance()->m_settings->isWritable(); } -bool Settings::isNativeMode() +bool Settings::isPortableMode() { - return instance()->m_settings->format() == QSettings::NativeFormat; + // Enable portable mode only if the portable settings file exists in the expected location. + return QFile(portableSettingsFile()).exists(); } QString Settings::settingsFile() @@ -213,8 +211,11 @@ QString Settings::settingsFile() QString Settings::settingsPath() { - if (instance()->isNativeMode()) +#ifdef Q_OS_WIN + if (!isPortableMode()) return SystemDir; +#endif + return QFileInfo(instance()->m_settings->fileName()).absolutePath(); } @@ -263,3 +264,10 @@ void Settings::restoreDefaultSettings() instance()->setValue(key, defaultValue(key)); } } + +QString Settings::portableSettingsFile() +{ + static const auto filename = + QStringLiteral("%1/settings/%2.conf").arg(QCoreApplication::applicationDirPath(), kAppName); + return filename; +} diff --git a/src/lib/common/Settings.h b/src/lib/common/Settings.h index a67d1f43b..8e0973108 100644 --- a/src/lib/common/Settings.h +++ b/src/lib/common/Settings.h @@ -19,7 +19,7 @@ class Settings : public QObject Q_OBJECT public: #if defined(Q_OS_WIN) - inline const static auto UserDir = QStringLiteral("%1/AppData/Local/%2").arg(QDir::homePath(), kAppName); + inline const static auto UserDir = QStringLiteral("%1/AppData/Roaming/%2").arg(QDir::homePath(), kAppName); inline const static auto SystemDir = QStringLiteral("%1ProgramData/%2").arg(QDir::rootPath(), kAppName); #elif defined(Q_OS_MAC) inline const static auto UserDir = QStringLiteral("%1/Library/%2").arg(QDir::homePath(), kAppName); @@ -28,6 +28,7 @@ public: inline const static auto UserDir = QStringLiteral("%1/.config/%2").arg(QDir::homePath(), kAppName); inline const static auto SystemDir = QStringLiteral("/etc/%1").arg(kAppName); #endif + inline const static auto UserSettingFile = QStringLiteral("%1/%2.conf").arg(UserDir, kAppName); inline const static auto SystemSettingFile = QStringLiteral("%1/%2.conf").arg(SystemDir, kAppName); @@ -120,7 +121,7 @@ public: static void restoreDefaultSettings(); static QVariant defaultValue(const QString &key); static bool isWritable(); - static bool isNativeMode(); + static bool isPortableMode(); static QString settingsFile(); static QString settingsPath(); static QString tlsDir(); @@ -132,6 +133,7 @@ public: static void save(bool emitSaving = true); static QStringList validKeys(); static int logLevelToInt(const QString &level = "INFO"); + static QString portableSettingsFile(); Q_SIGNALS: void settingsChanged(const QString key); @@ -145,7 +147,6 @@ private: void cleanSettings(); QSettings *m_settings = nullptr; - QString m_portableSettingsFile = QStringLiteral("%1/settings/%2.conf"); std::shared_ptr m_settingsProxy; // clang-format off diff --git a/src/lib/gui/Diagnostic.cpp b/src/lib/gui/Diagnostic.cpp index 25a4744fc..91559d273 100644 --- a/src/lib/gui/Diagnostic.cpp +++ b/src/lib/gui/Diagnostic.cpp @@ -44,7 +44,7 @@ void clearSettings(bool enableRestart) profileDir.removeRecursively(); #ifdef Q_OS_WIN - if (!Settings::isNativeMode()) { + if (Settings::isPortableMode()) { // make a new empty portable settings file if (profileDir.mkpath(Settings::settingsPath())) { QFile file(Settings::settingsFile()); diff --git a/src/lib/gui/core/CoreProcess.cpp b/src/lib/gui/core/CoreProcess.cpp index 336851fd3..d95c8cc57 100644 --- a/src/lib/gui/core/CoreProcess.cpp +++ b/src/lib/gui/core/CoreProcess.cpp @@ -349,6 +349,7 @@ void CoreProcess::start(std::optional processModeOption) if (processMode == ProcessMode::Desktop) { startForegroundProcess(args); } else if (processMode == ProcessMode::Service) { + args.append({QStringLiteral("--settings"), Settings::settingsFile()}); startProcessFromDaemon(args); } diff --git a/src/lib/gui/dialogs/SettingsDialog.cpp b/src/lib/gui/dialogs/SettingsDialog.cpp index 66fcc92df..3efadad3a 100644 --- a/src/lib/gui/dialogs/SettingsDialog.cpp +++ b/src/lib/gui/dialogs/SettingsDialog.cpp @@ -271,8 +271,9 @@ void SettingsDialog::updateControls() ui->comboTlsKeyLength->setEnabled(writable); ui->cbCloseToTray->setEnabled(writable); - // Handle enable and disable of service items - if (Settings::isNativeMode()) { + // Portable mode only ever applies to Windows. + // Daemon options should only be available on Windows when *not* in portable mode. + if (!Settings::isPortableMode()) { ui->groupService->setEnabled(writable); ui->cbElevateDaemon->setEnabled(writable && serviceChecked); } else if (ui->groupService->isVisibleTo(ui->tabAdvanced)) { diff --git a/src/unittests/common/SettingsTests.cpp b/src/unittests/common/SettingsTests.cpp index 09b502f80..78540dc67 100644 --- a/src/unittests/common/SettingsTests.cpp +++ b/src/unittests/common/SettingsTests.cpp @@ -16,7 +16,7 @@ void SettingsTests::initTestCase() oldSettings.remove(); } -void SettingsTests::setSettingsFile() +void SettingsTests::setSettingFile() { Settings::setSettingFile(m_settingsFile); } diff --git a/src/unittests/common/SettingsTests.h b/src/unittests/common/SettingsTests.h index d408504a6..70cd3c74a 100644 --- a/src/unittests/common/SettingsTests.h +++ b/src/unittests/common/SettingsTests.h @@ -14,7 +14,7 @@ class SettingsTests : public QObject private Q_SLOTS: void initTestCase(); // Test are run in order top to bottom - void setSettingsFile(); + void setSettingFile(); void settingsFile(); void settingsPath(); void tlsDir(); @@ -24,9 +24,18 @@ private Q_SLOTS: void checkValidSettings(); private: - inline static const QString m_settingsPath = QStringLiteral("tmp/test"); - inline static const QString m_settingsFile = QStringLiteral("%1/Deskflow.conf").arg(m_settingsPath); - inline static const QString m_expectedTlsDir = QStringLiteral("tmp/test/%1").arg(kTlsDirName); + inline static const QString m_settingsPathTemp = QStringLiteral("tmp/test"); + inline static const QString m_settingsFile = QStringLiteral("%1/Deskflow.conf").arg(m_settingsPathTemp); + +// Gotcha: On Windows non-portable mode, additional config files such as TLS config are saved +// in 'Program Data' and are not stored in the same place as the settings file. +#ifdef Q_OS_WIN + inline static const QString m_settingsPath = Settings::SystemDir; +#else + inline static const QString m_settingsPath = m_settingsPathTemp; +#endif + + inline static const QString m_expectedTlsDir = QStringLiteral("%1/%2").arg(m_settingsPath, kTlsDirName); inline static const QString m_expectedTlsLocalDB = QStringLiteral("%1/%2").arg(m_expectedTlsDir, kTlsFingerprintLocalFilename); inline static const QString m_expectedTlsServerDB =