2025-03-06 22:37:37 +00:00
|
|
|
/*
|
|
|
|
|
* Deskflow -- mouse and keyboard sharing utility
|
|
|
|
|
* SPDX-FileCopyrightText: (C) 2025 Chris Rizzitello <sithlord48@gmail.com>
|
|
|
|
|
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "Settings.h"
|
|
|
|
|
|
2025-03-21 00:36:09 +00:00
|
|
|
#include "UrlConstants.h"
|
|
|
|
|
|
2025-03-30 12:06:41 +00:00
|
|
|
#include <QCoreApplication>
|
2025-03-06 22:37:37 +00:00
|
|
|
#include <QFile>
|
2025-03-05 05:28:05 +00:00
|
|
|
#include <QRect>
|
2025-11-08 01:39:34 +00:00
|
|
|
#include <QRegularExpression>
|
2025-11-11 19:44:28 +00:00
|
|
|
#include <QStandardPaths>
|
2025-03-06 22:37:37 +00:00
|
|
|
|
|
|
|
|
Settings *Settings::instance()
|
|
|
|
|
{
|
|
|
|
|
static Settings m;
|
|
|
|
|
return &m;
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-15 12:42:55 +00:00
|
|
|
void Settings::setSettingsFile(const QString &settingsFile)
|
2025-03-06 22:37:37 +00:00
|
|
|
{
|
2025-10-15 18:42:21 +00:00
|
|
|
if (Settings::settingsFile() == settingsFile) {
|
2025-10-09 13:56:34 +00:00
|
|
|
qDebug("settings file already set, skipping");
|
2025-03-06 22:37:37 +00:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (instance()->m_settings)
|
|
|
|
|
instance()->m_settings->deleteLater();
|
2025-10-09 13:56:34 +00:00
|
|
|
|
2025-11-13 21:25:22 +00:00
|
|
|
instance()->m_settings = new QSettings(settingsFile, QSettings::IniFormat, instance());
|
2025-10-09 13:56:34 +00:00
|
|
|
instance()->m_settingsProxy->load(settingsFile);
|
|
|
|
|
qInfo().noquote() << "settings file changed:" << instance()->m_settings->fileName();
|
2025-11-14 14:47:29 +00:00
|
|
|
|
|
|
|
|
instance()->setupScreenName();
|
2025-03-06 22:37:37 +00:00
|
|
|
}
|
|
|
|
|
|
2025-11-11 19:44:28 +00:00
|
|
|
void Settings::setStateFile(const QString &stateFile)
|
|
|
|
|
{
|
|
|
|
|
if (instance()->m_stateSettings->fileName() == stateFile) {
|
2025-12-17 03:25:32 +00:00
|
|
|
qDebug("state file already set, skipping");
|
2025-11-11 19:44:28 +00:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (instance()->m_stateSettings)
|
|
|
|
|
instance()->m_stateSettings->deleteLater();
|
|
|
|
|
|
2025-11-13 21:25:22 +00:00
|
|
|
instance()->m_stateSettings = new QSettings(stateFile, QSettings::IniFormat, instance());
|
2025-12-17 03:25:32 +00:00
|
|
|
qInfo().noquote() << "state file changed:" << instance()->m_stateSettings->fileName();
|
2025-11-11 19:44:28 +00:00
|
|
|
}
|
|
|
|
|
|
2025-03-06 22:37:37 +00:00
|
|
|
Settings::Settings(QObject *parent) : QObject(parent)
|
|
|
|
|
{
|
2025-03-15 03:24:22 +00:00
|
|
|
QString fileToLoad;
|
2025-03-31 17:38:36 +00:00
|
|
|
#ifdef Q_OS_WIN
|
2025-10-09 13:56:34 +00:00
|
|
|
const auto portableFile = portableSettingsFile();
|
|
|
|
|
qDebug().noquote() << "checking for portable settings file at:" << portableFile;
|
|
|
|
|
if (QFile(portableFile).exists())
|
|
|
|
|
fileToLoad = portableFile;
|
2025-03-31 17:38:36 +00:00
|
|
|
#else
|
2025-11-14 14:03:59 +00:00
|
|
|
if (const auto xdgConfigHome = qEnvironmentVariable("XDG_CONFIG_HOME"); !xdgConfigHome.isEmpty())
|
|
|
|
|
fileToLoad = QStringLiteral("%1/%2/%2.conf").arg(xdgConfigHome, kAppName);
|
2025-10-09 13:56:34 +00:00
|
|
|
#endif
|
2025-03-31 17:38:36 +00:00
|
|
|
else if (QFile(UserSettingFile).exists())
|
|
|
|
|
fileToLoad = UserSettingFile;
|
|
|
|
|
else if (QFile(SystemSettingFile).exists())
|
|
|
|
|
fileToLoad = SystemSettingFile;
|
|
|
|
|
else
|
|
|
|
|
fileToLoad = UserSettingFile;
|
|
|
|
|
|
2025-11-13 21:25:22 +00:00
|
|
|
m_settings = new QSettings(fileToLoad, QSettings::IniFormat, this);
|
2025-03-15 03:24:22 +00:00
|
|
|
m_settingsProxy = std::make_shared<QSettingsProxy>();
|
|
|
|
|
m_settingsProxy->load(fileToLoad);
|
2025-10-09 13:56:34 +00:00
|
|
|
qInfo().noquote() << "initial settings file:" << m_settings->fileName();
|
2025-11-11 19:44:28 +00:00
|
|
|
|
2025-11-14 14:03:59 +00:00
|
|
|
const auto xdgStateHome = qEnvironmentVariable("XDG_STATE_HOME");
|
|
|
|
|
const auto stateBase = !xdgStateHome.isEmpty()
|
|
|
|
|
? xdgStateHome
|
2025-11-11 19:44:28 +00:00
|
|
|
: QStandardPaths::standardLocations(QStandardPaths::GenericStateLocation).at(0);
|
|
|
|
|
const auto stateFile = QStringLiteral("%1/%2.state").arg(stateBase, kAppName);
|
|
|
|
|
|
2025-11-13 21:25:22 +00:00
|
|
|
m_stateSettings = new QSettings(stateFile, QSettings::IniFormat, this);
|
2025-11-14 14:47:29 +00:00
|
|
|
|
|
|
|
|
setupScreenName();
|
2025-03-06 22:37:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Settings::cleanSettings()
|
|
|
|
|
{
|
|
|
|
|
const QStringList keys = m_settings->allKeys();
|
|
|
|
|
for (const QString &key : keys) {
|
2026-01-18 14:37:16 +00:00
|
|
|
if (key.startsWith(QStringLiteral("internalConfig")))
|
|
|
|
|
continue;
|
2025-03-06 22:37:37 +00:00
|
|
|
if (!m_validKeys.contains(key))
|
|
|
|
|
m_settings->remove(key);
|
2026-01-18 14:38:38 +00:00
|
|
|
if (m_settings->value(key).toString().isEmpty())
|
2025-03-06 22:37:37 +00:00
|
|
|
m_settings->remove(key);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-11 19:44:28 +00:00
|
|
|
void Settings::cleanStateSettings()
|
|
|
|
|
{
|
|
|
|
|
const QStringList keys = m_stateKeys;
|
|
|
|
|
for (const QString &key : keys) {
|
|
|
|
|
if (!m_stateKeys.contains(key))
|
|
|
|
|
m_stateSettings->remove(key);
|
2026-01-18 14:38:38 +00:00
|
|
|
if (m_stateSettings->value(key).toString().isEmpty())
|
2025-11-11 19:44:28 +00:00
|
|
|
m_stateSettings->remove(key);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-14 14:47:29 +00:00
|
|
|
void Settings::setupScreenName()
|
|
|
|
|
{
|
2026-01-18 14:23:50 +00:00
|
|
|
if (m_settings->value(Settings::Core::ScreenName).toString().isEmpty())
|
2025-11-14 14:47:29 +00:00
|
|
|
m_settings->setValue(Settings::Core::ScreenName, cleanScreenName(QSysInfo::machineHostName()));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString Settings::cleanScreenName(const QString &name)
|
|
|
|
|
{
|
|
|
|
|
static const auto hyphen = QStringLiteral("-");
|
|
|
|
|
static const auto space = QStringLiteral(" ");
|
|
|
|
|
static const auto underscore = QStringLiteral("_");
|
2025-12-17 03:13:11 +00:00
|
|
|
static const auto period = QStringLiteral(".");
|
2025-11-14 14:47:29 +00:00
|
|
|
static const auto nothing = QStringLiteral("");
|
|
|
|
|
static const auto nameRegex = QRegularExpression(QStringLiteral("[^\\w\\-\\.]"));
|
|
|
|
|
|
|
|
|
|
QString cleanName = name.simplified();
|
|
|
|
|
cleanName.replace(space, underscore);
|
|
|
|
|
cleanName.replace(nameRegex, nothing);
|
2025-12-17 03:13:11 +00:00
|
|
|
while (cleanName.startsWith(hyphen) || cleanName.startsWith(underscore) || cleanName.startsWith(period))
|
2025-11-14 14:47:29 +00:00
|
|
|
cleanName.removeFirst();
|
2025-12-17 03:13:11 +00:00
|
|
|
while (cleanName.endsWith(hyphen) || cleanName.endsWith(underscore) || cleanName.endsWith(period))
|
2025-11-14 14:47:29 +00:00
|
|
|
cleanName.removeLast();
|
|
|
|
|
if (cleanName.length() > 255) {
|
|
|
|
|
cleanName.truncate(255);
|
|
|
|
|
cleanName = cleanScreenName(cleanName);
|
|
|
|
|
}
|
|
|
|
|
return cleanName;
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-23 05:58:37 +00:00
|
|
|
int Settings::logLevelToInt(const QString &level)
|
|
|
|
|
{
|
2025-12-17 03:02:25 +00:00
|
|
|
if (level.isEmpty() || !m_logLevels.contains(level, Qt::CaseInsensitive))
|
2025-08-23 05:58:37 +00:00
|
|
|
return 4;
|
2025-12-17 03:02:25 +00:00
|
|
|
return static_cast<int>(m_logLevels.indexOf(level, 0, Qt::CaseInsensitive));
|
2025-08-23 05:58:37 +00:00
|
|
|
}
|
|
|
|
|
|
2025-03-06 22:37:37 +00:00
|
|
|
QVariant Settings::defaultValue(const QString &key)
|
|
|
|
|
{
|
2025-12-17 03:35:48 +00:00
|
|
|
if (m_defaultFalseValues.contains(key))
|
2025-03-06 22:37:37 +00:00
|
|
|
return false;
|
|
|
|
|
|
2025-12-17 03:35:48 +00:00
|
|
|
if (m_defaultTrueValues.contains(key))
|
2025-03-08 15:10:42 +00:00
|
|
|
return true;
|
|
|
|
|
|
2025-03-07 00:42:16 +00:00
|
|
|
if (key == Security::Certificate)
|
2025-12-22 04:43:16 +00:00
|
|
|
return QStringLiteral("%1/%2.pem").arg(Settings::tlsDir(), kAppId);
|
2025-03-07 00:04:55 +00:00
|
|
|
|
2025-03-09 00:25:57 +00:00
|
|
|
if (key == Security::KeySize)
|
|
|
|
|
return 2048;
|
|
|
|
|
|
2025-03-09 01:56:29 +00:00
|
|
|
if (key == Log::File)
|
2025-12-22 04:38:38 +00:00
|
|
|
return QStringLiteral("%1/%2.log").arg(QDir::homePath(), kAppId);
|
2025-03-09 01:56:29 +00:00
|
|
|
|
2025-03-09 01:13:26 +00:00
|
|
|
if (key == Log::Level)
|
2025-08-23 17:31:04 +00:00
|
|
|
return 4; // INFO
|
2025-03-09 01:13:26 +00:00
|
|
|
|
2025-04-02 22:00:06 +00:00
|
|
|
if (key == Daemon::Elevate)
|
2025-10-09 13:56:34 +00:00
|
|
|
return !Settings::isPortableMode();
|
2025-03-09 05:54:47 +00:00
|
|
|
|
2025-09-12 09:02:26 +00:00
|
|
|
if (key == Gui::UpdateCheckUrl)
|
2025-03-21 00:36:09 +00:00
|
|
|
return kUrlUpdateCheck;
|
|
|
|
|
|
2025-03-09 00:25:57 +00:00
|
|
|
if (key == Server::ExternalConfigFile)
|
2025-07-11 17:00:50 +00:00
|
|
|
return QStringLiteral("%1/%2-server.conf").arg(Settings::settingsPath(), kAppId);
|
2025-03-09 00:25:57 +00:00
|
|
|
|
|
|
|
|
if (key == Core::Port)
|
|
|
|
|
return 24800;
|
2025-03-08 16:37:04 +00:00
|
|
|
|
2025-03-31 18:47:22 +00:00
|
|
|
if (key == Core::ProcessMode) {
|
2025-10-09 13:56:34 +00:00
|
|
|
#ifdef Q_OS_WIN
|
|
|
|
|
if (!Settings::isPortableMode())
|
2025-03-31 18:47:22 +00:00
|
|
|
return Settings::ProcessMode::Service;
|
2025-10-09 13:56:34 +00:00
|
|
|
#endif
|
|
|
|
|
return Settings::ProcessMode::Desktop;
|
2025-03-31 18:47:22 +00:00
|
|
|
}
|
2025-03-09 04:14:26 +00:00
|
|
|
|
2025-12-17 03:35:48 +00:00
|
|
|
if (key == Daemon::LogFile)
|
2025-12-22 04:34:26 +00:00
|
|
|
return QStringLiteral("%1/%2-daemon.log").arg(Settings::settingsPath(), kAppId);
|
2025-03-14 21:18:54 +00:00
|
|
|
|
2025-12-17 03:35:48 +00:00
|
|
|
if (key == Client::ScrollSpeed)
|
2025-09-25 23:17:01 +00:00
|
|
|
return 120;
|
|
|
|
|
|
2025-03-06 22:37:37 +00:00
|
|
|
return QVariant();
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-19 22:30:32 +00:00
|
|
|
QString Settings::logLevelText()
|
2025-03-09 01:13:26 +00:00
|
|
|
{
|
2025-07-11 17:00:50 +00:00
|
|
|
return Settings::m_logLevels.at(Settings::value(Log::Level).toInt());
|
2025-03-09 01:13:26 +00:00
|
|
|
}
|
|
|
|
|
|
2025-03-15 02:17:08 +00:00
|
|
|
QSettingsProxy &Settings::proxy()
|
|
|
|
|
{
|
|
|
|
|
return *instance()->m_settingsProxy;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Settings::save(bool emitSaving)
|
|
|
|
|
{
|
|
|
|
|
if (emitSaving)
|
|
|
|
|
Q_EMIT instance()->serverSettingsChanged();
|
|
|
|
|
instance()->m_settings->sync();
|
2025-11-11 19:44:28 +00:00
|
|
|
instance()->m_stateSettings->sync();
|
2025-03-15 02:17:08 +00:00
|
|
|
}
|
|
|
|
|
|
2025-06-19 22:30:32 +00:00
|
|
|
QStringList Settings::validKeys()
|
2025-03-30 11:59:13 +00:00
|
|
|
{
|
2025-07-11 17:00:50 +00:00
|
|
|
return Settings::m_validKeys;
|
2025-03-30 11:59:13 +00:00
|
|
|
}
|
|
|
|
|
|
2025-03-06 22:37:37 +00:00
|
|
|
bool Settings::isWritable()
|
|
|
|
|
{
|
|
|
|
|
return instance()->m_settings->isWritable();
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-09 13:56:34 +00:00
|
|
|
bool Settings::isPortableMode()
|
2025-03-31 17:38:36 +00:00
|
|
|
{
|
2025-10-09 13:56:34 +00:00
|
|
|
// Enable portable mode only if the portable settings file exists in the expected location.
|
|
|
|
|
return QFile(portableSettingsFile()).exists();
|
2025-03-31 17:38:36 +00:00
|
|
|
}
|
|
|
|
|
|
2025-06-19 22:30:32 +00:00
|
|
|
QString Settings::settingsFile()
|
2025-03-06 22:37:37 +00:00
|
|
|
{
|
|
|
|
|
return instance()->m_settings->fileName();
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-19 22:30:32 +00:00
|
|
|
QString Settings::settingsPath()
|
2025-03-06 22:37:37 +00:00
|
|
|
{
|
2025-10-09 13:56:34 +00:00
|
|
|
#ifdef Q_OS_WIN
|
|
|
|
|
if (!isPortableMode())
|
2025-03-31 17:38:36 +00:00
|
|
|
return SystemDir;
|
2025-10-09 13:56:34 +00:00
|
|
|
#endif
|
|
|
|
|
|
2025-03-06 22:37:37 +00:00
|
|
|
return QFileInfo(instance()->m_settings->fileName()).absolutePath();
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-19 22:30:32 +00:00
|
|
|
QString Settings::tlsDir()
|
2025-03-18 01:18:37 +00:00
|
|
|
{
|
2025-12-22 04:41:01 +00:00
|
|
|
return QStringLiteral("%1/tls").arg(instance()->settingsPath());
|
2025-03-18 01:18:37 +00:00
|
|
|
}
|
|
|
|
|
|
2025-06-19 22:30:32 +00:00
|
|
|
QString Settings::tlsTrustedServersDb()
|
2025-03-18 01:41:14 +00:00
|
|
|
{
|
2025-12-22 04:46:06 +00:00
|
|
|
return QStringLiteral("%1/trusted-servers").arg(instance()->tlsDir());
|
2025-03-18 01:41:14 +00:00
|
|
|
}
|
|
|
|
|
|
2025-06-19 22:30:32 +00:00
|
|
|
QString Settings::tlsTrustedClientsDb()
|
2025-03-18 01:47:21 +00:00
|
|
|
{
|
2025-12-22 04:49:51 +00:00
|
|
|
return QStringLiteral("%1/trusted-clients").arg(instance()->tlsDir());
|
2025-03-18 01:47:21 +00:00
|
|
|
}
|
|
|
|
|
|
2025-03-06 22:37:37 +00:00
|
|
|
void Settings::setValue(const QString &key, const QVariant &value)
|
|
|
|
|
{
|
2025-11-11 19:44:28 +00:00
|
|
|
const bool useState = Settings::m_stateKeys.contains(key) && !instance()->isPortableMode();
|
|
|
|
|
auto settings = useState ? instance()->m_stateSettings : instance()->m_settings;
|
|
|
|
|
|
|
|
|
|
if (settings->value(key) == value)
|
2025-03-06 22:37:37 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (!value.isValid())
|
2025-11-11 19:44:28 +00:00
|
|
|
settings->remove(key);
|
2025-11-14 14:47:29 +00:00
|
|
|
else {
|
|
|
|
|
if (key == Settings::Core::ScreenName)
|
|
|
|
|
settings->setValue(key, cleanScreenName(value.toString()));
|
|
|
|
|
else
|
|
|
|
|
settings->setValue(key, value);
|
|
|
|
|
}
|
2025-03-06 22:37:37 +00:00
|
|
|
|
2025-11-11 19:44:28 +00:00
|
|
|
settings->sync();
|
2025-03-06 22:37:37 +00:00
|
|
|
Q_EMIT instance()->settingsChanged(key);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QVariant Settings::value(const QString &key)
|
|
|
|
|
{
|
2025-11-11 19:44:28 +00:00
|
|
|
const bool useState = Settings::m_stateKeys.contains(key) && !instance()->isPortableMode();
|
|
|
|
|
auto settings = useState ? instance()->m_stateSettings : instance()->m_settings;
|
|
|
|
|
return settings->value(key, defaultValue(key));
|
2025-03-06 22:37:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Settings::restoreDefaultSettings()
|
|
|
|
|
{
|
|
|
|
|
for (const auto &key : m_validKeys) {
|
|
|
|
|
instance()->setValue(key, defaultValue(key));
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-10-09 13:56:34 +00:00
|
|
|
|
|
|
|
|
QString Settings::portableSettingsFile()
|
|
|
|
|
{
|
|
|
|
|
static const auto filename =
|
|
|
|
|
QStringLiteral("%1/settings/%2.conf").arg(QCoreApplication::applicationDirPath(), kAppName);
|
|
|
|
|
return filename;
|
|
|
|
|
}
|