feat: Change to local user ini (not native)

Windows registry is horrible to deal with and makes it impractical to pass settings to the Core when run via daemon on Windows.

- Pass settings path to Core when launched via daemon
- Introduce portable mode detection logic on Windows
- Generalize `m_settingsFile` use
- Reduce #ifdef size for Settings ctor path logic
This commit is contained in:
Nick Bolton
2025-10-09 14:56:34 +01:00
committed by Chris Rizzitello
parent 3ece50e292
commit bb1394ceeb
7 changed files with 56 additions and 36 deletions

View File

@ -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<QSettingsProxy>();
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;
}

View File

@ -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<QSettingsProxy> m_settingsProxy;
// clang-format off

View File

@ -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());

View File

@ -349,6 +349,7 @@ void CoreProcess::start(std::optional<ProcessMode> processModeOption)
if (processMode == ProcessMode::Desktop) {
startForegroundProcess(args);
} else if (processMode == ProcessMode::Service) {
args.append({QStringLiteral("--settings"), Settings::settingsFile()});
startProcessFromDaemon(args);
}

View File

@ -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)) {

View File

@ -16,7 +16,7 @@ void SettingsTests::initTestCase()
oldSettings.remove();
}
void SettingsTests::setSettingsFile()
void SettingsTests::setSettingFile()
{
Settings::setSettingFile(m_settingsFile);
}

View File

@ -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 =