/* * synergy -- mouse and keyboard sharing utility * Copyright (C) 2012 Symless Ltd. * Copyright (C) 2008 Volker Lanz (vl@fidra.de) * * 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 . */ #include "AppConfig.h" #include "ConfigScopes.h" #include #include #include #include #include #include #include using namespace synergy::gui; // this should be incremented each time the wizard is changed, // which will force it to re-run for existing installations. const int kWizardVersion = 8; static const char *const kLogLevelNames[] = { "INFO", "DEBUG", "DEBUG1", "DEBUG2"}; #if defined(Q_OS_WIN) const char AppConfig::m_CoreServerName[] = "synergys.exe"; const char AppConfig::m_CoreClientName[] = "synergyc.exe"; const char AppConfig::m_LogDir[] = "log/"; const char AppConfig::m_ConfigFilename[] = "synergy.sgc"; #else const char AppConfig::m_CoreServerName[] = "synergys"; const char AppConfig::m_CoreClientName[] = "synergyc"; const char AppConfig::m_LogDir[] = "/var/log/"; const char AppConfig::m_ConfigFilename[] = "synergy.conf"; #endif // TODO: instead, use key value pair table, which would be less fragile. const char *const AppConfig::m_SettingsName[] = { "screenName", "port", "interface", "logLevel2", "logToFile", "logFilename", "wizardLastRun", "startedBefore", "elevateMode", "elevateModeEnum", "", // 10 = edition, obsolete (using serial key instead) "cryptoEnabled", // kTlsEnabled (retain legacy string value) "autoHide", "serialKey", "lastVersion", "", // 15 = lastExpiringWarningTime, obsolete "activationHasRun", "", // 17 = minimizeToTray, obsolete "", // 18 = ActivateEmail, obsolete "loadFromSystemScope", "groupServerChecked", // kServerGroupChecked "useExternalConfig", "configFile", "useInternalConfig", "groupClientChecked", "serverHostname", "tlsCertPath", "tlsKeyLength", "preventSleep", "languageSync", "invertScrollDirection", "", // 31 = guid, obsolete "", // 32 = licenseRegistryUrl, obsolete "licenseNextCheck", "initiateConnectionFromServer", // kInvertConnection "", // 35 = clientHostMode, obsolete "", // 36 = serverClientMode, obsolete "enableService", "closeToTray", "mainWindowSize", "mainWindowPosition", "showDevThanks", "showCloseReminder", }; AppConfig::AppConfig( synergy::gui::IConfigScopes &scopes, std::shared_ptr deps) : m_Scopes(scopes), m_pDeps(deps), m_ScreenName(deps->hostname()) { determineScope(); recall(); } void AppConfig::recall() { using enum AppConfig::Setting; qDebug("recalling app config"); recallFromAllScopes(); recallFromCurrentScope(); } void AppConfig::recallFromAllScopes() { using enum Setting; m_WizardLastRun = findInAllScopes(kWizardLastRun, m_WizardLastRun).toInt(); m_LoadFromSystemScope = findInAllScopes(kLoadSystemSettings, m_LoadFromSystemScope).toBool(); m_LicenseNextCheck = findInAllScopes(kLicenseNextCheck, m_LicenseNextCheck).toULongLong(); } void AppConfig::recallFromCurrentScope() { using enum Setting; recallScreenName(); recallSerialKey(); recallElevateMode(); m_Port = getFromCurrentScope(kPort, m_Port).toInt(); m_Interface = getFromCurrentScope(kInterface, m_Interface).toString(); m_LogLevel = getFromCurrentScope(kLogLevel, m_LogLevel).toInt(); m_LogToFile = getFromCurrentScope(kLogToFile, m_LogToFile).toBool(); m_LogFilename = getFromCurrentScope(kLogFilename, m_LogFilename).toString(); m_StartedBefore = getFromCurrentScope(kStartedBefore, m_StartedBefore).toBool(); m_AutoHide = getFromCurrentScope(kAutoHide, m_AutoHide).toBool(); m_LastVersion = getFromCurrentScope(kLastVersion, m_LastVersion).toString(); m_ActivationHasRun = getFromCurrentScope(kActivationHasRun, m_ActivationHasRun).toBool(); m_ServerGroupChecked = getFromCurrentScope(kServerGroupChecked, m_ServerGroupChecked).toBool(); m_UseExternalConfig = getFromCurrentScope(kUseExternalConfig, m_UseExternalConfig).toBool(); m_ConfigFile = getFromCurrentScope(kConfigFile, m_ConfigFile).toString(); m_UseInternalConfig = getFromCurrentScope(kUseInternalConfig, m_UseInternalConfig).toBool(); m_ClientGroupChecked = getFromCurrentScope(kClientGroupChecked, m_ClientGroupChecked).toBool(); m_ServerHostname = getFromCurrentScope(kServerHostname, m_ServerHostname).toString(); m_PreventSleep = getFromCurrentScope(kPreventSleep, m_PreventSleep).toBool(); m_LanguageSync = getFromCurrentScope(kLanguageSync, m_LanguageSync).toBool(); m_InvertScrollDirection = getFromCurrentScope(kInvertScrollDirection, m_InvertScrollDirection) .toBool(); m_InvertConnection = getFromCurrentScope(kInvertConnection, m_InvertConnection).toBool(); m_EnableService = getFromCurrentScope(kEnableService, m_EnableService).toBool(); m_CloseToTray = getFromCurrentScope(kCloseToTray, m_CloseToTray).toBool(); m_TlsEnabled = getFromCurrentScope(kTlsEnabled, m_TlsEnabled).toBool(); m_TlsCertPath = getFromCurrentScope(kTlsCertPath, m_TlsCertPath).toString(); m_TlsKeyLength = getFromCurrentScope(kTlsKeyLength, m_TlsKeyLength).toInt(); m_MainWindowPosition = getFromCurrentScope( kMainWindowPosition, [](const QVariant &v) { return v.toPoint(); }); m_MainWindowSize = getFromCurrentScope( kMainWindowSize, [](const QVariant &v) { return v.toSize(); }); m_ShowDevThanks = getFromCurrentScope(kShowDevThanks, m_ShowDevThanks).toBool(); m_ShowCloseReminder = getFromCurrentScope(kShowCloseReminder, m_ShowCloseReminder).toBool(); } void AppConfig::recallScreenName() { using enum Setting; const auto &screenName = getFromCurrentScope(kScreenName, m_ScreenName).toString().trimmed(); // for some reason, the screen name can be saved as an empty string // in the config file. this is probably a bug. if this happens, then default // back to the hostname. if (screenName.isEmpty()) { qWarning("screen name was empty in config, setting to hostname"); m_ScreenName = m_pDeps->hostname(); } else { m_ScreenName = screenName; } } void AppConfig::commit() { using enum Setting; qDebug("committing app config"); saveToAllScopes(kWizardLastRun, m_WizardLastRun); saveToAllScopes(kLoadSystemSettings, m_LoadFromSystemScope); saveToAllScopes(kClientGroupChecked, m_ClientGroupChecked); saveToAllScopes(kServerGroupChecked, m_ServerGroupChecked); saveToAllScopes(kLicenseNextCheck, m_LicenseNextCheck); if (isActiveScopeWritable()) { setInCurrentScope(kScreenName, m_ScreenName); setInCurrentScope(kPort, m_Port); setInCurrentScope(kInterface, m_Interface); setInCurrentScope(kLogLevel, m_LogLevel); setInCurrentScope(kLogToFile, m_LogToFile); setInCurrentScope(kLogFilename, m_LogFilename); setInCurrentScope(kStartedBefore, m_StartedBefore); setInCurrentScope(kElevateMode, static_cast(m_ElevateMode)); setInCurrentScope( kElevateModeLegacy, m_ElevateMode == ElevateMode::kAlways); setInCurrentScope(kTlsEnabled, m_TlsEnabled); setInCurrentScope(kAutoHide, m_AutoHide); setInCurrentScope(kSerialKey, m_SerialKey); setInCurrentScope(kLastVersion, m_LastVersion); setInCurrentScope(kActivationHasRun, m_ActivationHasRun); setInCurrentScope(kUseExternalConfig, m_UseExternalConfig); setInCurrentScope(kConfigFile, m_ConfigFile); setInCurrentScope(kUseInternalConfig, m_UseInternalConfig); setInCurrentScope(kServerHostname, m_ServerHostname); setInCurrentScope(kPreventSleep, m_PreventSleep); setInCurrentScope(kLanguageSync, m_LanguageSync); setInCurrentScope(kInvertScrollDirection, m_InvertScrollDirection); setInCurrentScope(kInvertConnection, m_InvertConnection); setInCurrentScope(kEnableService, m_EnableService); setInCurrentScope(kCloseToTray, m_CloseToTray); setInCurrentScope(kShowDevThanks, m_ShowDevThanks); setInCurrentScope(kShowCloseReminder, m_ShowCloseReminder); setInCurrentScope(kMainWindowSize, m_MainWindowSize); setInCurrentScope(kMainWindowPosition, m_MainWindowPosition); } if (m_TlsChanged) { m_TlsChanged = false; emit tlsChanged(); } } void AppConfig::determineScope() { qDebug("determining config scope"); // first, try to determine if the system scope should be used according to the // user scope... if (m_Scopes.scopeContains( settingName(Setting::kLoadSystemSettings), ConfigScopes::Scope::User)) { auto loadFromSystemScope = m_Scopes .getFromScope( settingName(Setting::kLoadSystemSettings), m_LoadFromSystemScope, ConfigScopes::Scope::User) .toBool(); if (loadFromSystemScope) { qDebug("user settings indicates system scope should be used"); } else { qDebug("user settings indicates user scope should be used"); } setLoadFromSystemScope(loadFromSystemScope); } // ...failing that, check the system scope instead to see if an arbitrary // required setting is present. if it is, then we can assume that the system // scope should be used. else if (m_Scopes.scopeContains( settingName(Setting::kScreenName), ConfigScopes::Scope::System)) { qDebug("system settings scope contains screen name, using system scope"); setLoadFromSystemScope(true); } } void AppConfig::recallSerialKey() { using enum Setting; if (!m_Scopes.scopeContains(settingName(kLoadSystemSettings))) { qDebug("no serial key in current scope, skipping"); return; } const auto &serialKey = getFromCurrentScope(kSerialKey, m_SerialKey).toString().trimmed(); if (serialKey.isEmpty()) { qDebug("serial key is empty, skipping"); return; } m_SerialKey = serialKey; } void AppConfig::recallElevateMode() { using enum Setting; if (!m_Scopes.scopeContains(settingName(kElevateMode))) { qDebug("elevate mode not set yet, skipping"); return; } QVariant elevateMode = getFromCurrentScope(kElevateMode); if (!elevateMode.isValid()) { qDebug("elevate mode not valid, loading legacy setting"); elevateMode = getFromCurrentScope( kElevateModeLegacy, QVariant(static_cast(kDefaultElevateMode))); } m_ElevateMode = static_cast(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(name); return m_SettingsName[index]; } template void AppConfig::setInCurrentScope(Setting name, T value) { m_Scopes.setInScope(settingName(name), value); } template void AppConfig::saveToAllScopes(Setting name, T value) { m_Scopes.setInScope(settingName(name), value, ConfigScopes::Scope::User); m_Scopes.setInScope(settingName(name), value, ConfigScopes::Scope::System); } QVariant AppConfig::getFromCurrentScope( Setting name, const QVariant &defaultValue) const { return m_Scopes.getFromScope(settingName(name), defaultValue); } template std::optional AppConfig::getFromCurrentScope( Setting name, std::function toType) const { if (m_Scopes.scopeContains(settingName(name))) { return toType(m_Scopes.getFromScope(settingName(name))); } else { return std::nullopt; } } template void AppConfig::setInCurrentScope(Setting name, const std::optional &value) { if (value.has_value()) { m_Scopes.setInScope(settingName(name), value.value()); } } QVariant AppConfig::findInAllScopes(Setting name, const QVariant &defaultValue) const { using enum ConfigScopes::Scope; QVariant result(defaultValue); QString setting(settingName(name)); if (m_Scopes.scopeContains(setting)) { result = m_Scopes.getFromScope(setting, defaultValue); } else if (m_Scopes.activeScope() == System) { if (m_Scopes.scopeContains(setting, User)) { result = m_Scopes.getFromScope(setting, defaultValue, User); } } else if (m_Scopes.scopeContains(setting, System)) { result = m_Scopes.getFromScope(setting, defaultValue, System); } return result; } void AppConfig::loadScope(ConfigScopes::Scope scope) { using enum ConfigScopes::Scope; switch (scope) { case User: qDebug("loading user settings scope"); break; case System: qDebug("loading system settings scope"); break; default: qFatal("invalid scope"); } if (m_Scopes.activeScope() == scope) { qDebug("already in required scope, skipping"); return; } m_Scopes.setActiveScope(scope); // only signal ready if there is at least one setting in the required scope. // this prevents the current settings from being set back to default. if (m_Scopes.scopeContains( settingName(Setting::kScreenName), m_Scopes.activeScope())) { m_Scopes.signalReady(); } else { qDebug("no screen name in scope, skipping"); } } void AppConfig::setLoadFromSystemScope(bool value) { using enum ConfigScopes::Scope; if (value) { loadScope(System); } else { loadScope(User); } // set after loading scope since it may have been overridden. m_LoadFromSystemScope = value; } bool AppConfig::isActiveScopeWritable() const { return m_Scopes.isActiveScopeWritable(); } bool AppConfig::isActiveScopeSystem() const { return m_Scopes.activeScope() == ConfigScopes::Scope::System; } QString AppConfig::logDir() const { // by default log to home dir return QDir::home().absolutePath() + "/"; } void AppConfig::persistLogDir() const { QDir dir = logDir(); // persist the log directory if (!dir.exists()) { dir.mkpath(dir.path()); } } /////////////////////////////////////////////////////////////////////////////// // Begin getters /////////////////////////////////////////////////////////////////////////////// IConfigScopes &AppConfig::scopes() { return m_Scopes; } bool AppConfig::activationHasRun() const { return m_ActivationHasRun; } QString AppConfig::serialKey() const { return m_SerialKey; } const QString &AppConfig::screenName() const { return m_ScreenName; } int AppConfig::port() const { return m_Port; } const QString &AppConfig::networkInterface() const { return m_Interface; } int AppConfig::logLevel() const { return m_LogLevel; } bool AppConfig::logToFile() const { return m_LogToFile; } const QString &AppConfig::logFilename() const { return m_LogFilename; } QString AppConfig::logLevelText() const { return kLogLevelNames[logLevel()]; } ProcessMode AppConfig::processMode() const { return m_EnableService ? ProcessMode::kService : ProcessMode::kDesktop; } bool AppConfig::wizardShouldRun() const { return m_WizardLastRun < kWizardVersion; } bool AppConfig::startedBefore() const { return m_StartedBefore; } QString AppConfig::lastVersion() const { return m_LastVersion; } QString AppConfig::coreServerName() const { return m_CoreServerName; } QString AppConfig::coreClientName() const { return m_CoreClientName; } ElevateMode AppConfig::elevateMode() const { return m_ElevateMode; } bool AppConfig::tlsEnabled() const { return m_TlsEnabled; } bool AppConfig::autoHide() const { return m_AutoHide; } bool AppConfig::invertScrollDirection() const { return m_InvertScrollDirection; } unsigned long long AppConfig::licenseNextCheck() const { return m_LicenseNextCheck; } bool AppConfig::languageSync() const { return m_LanguageSync; } bool AppConfig::preventSleep() const { return m_PreventSleep; } bool AppConfig::invertConnection() const { return m_InvertConnection; } QString AppConfig::tlsCertPath() const { return m_TlsCertPath; } int AppConfig::tlsKeyLength() const { return m_TlsKeyLength; } bool AppConfig::enableService() const { return m_EnableService; } bool AppConfig::closeToTray() const { return m_CloseToTray; } bool AppConfig::serverGroupChecked() const { return m_ServerGroupChecked; } bool AppConfig::useExternalConfig() const { return m_UseExternalConfig; } const QString &AppConfig::configFile() const { return m_ConfigFile; } bool AppConfig::useInternalConfig() const { return m_UseInternalConfig; } bool AppConfig::clientGroupChecked() const { return m_ClientGroupChecked; } const QString &AppConfig::serverHostname() const { return m_ServerHostname; } void AppConfig::setActivationHasRun(bool value) { m_ActivationHasRun = value; } std::optional AppConfig::mainWindowSize() const { return m_MainWindowSize; } std::optional AppConfig::mainWindowPosition() const { return m_MainWindowPosition; } bool AppConfig::showDevThanks() const { return m_ShowDevThanks; } bool AppConfig::showCloseReminder() const { return m_ShowCloseReminder; } /////////////////////////////////////////////////////////////////////////////// // End getters /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // Begin setters /////////////////////////////////////////////////////////////////////////////// void AppConfig::clearSerialKey() { m_SerialKey.clear(); } void AppConfig::setTlsEnabled(bool value) { // we purposefully do not set the 'tls changed' flag when enabling/disabling // tls, since that would cause the certificate to regenerate, which could get // pretty annoying. m_TlsEnabled = value; } void AppConfig::setTlsCertPath(const QString &value) { if (m_TlsCertPath != value) { // deliberately only set the changed flag if there was a change. // it's important not to set this flag to false here. m_TlsChanged = true; } m_TlsCertPath = value; } void AppConfig::setTlsKeyLength(int value) { if (m_TlsKeyLength != value) { // deliberately only set the changed flag if there was a change. // it's important not to set this flag to false here. m_TlsChanged = true; } m_TlsKeyLength = value; } void AppConfig::setSerialKey(const QString &serialKey) { m_SerialKey = serialKey; saveToAllScopes(Setting::kSerialKey, m_SerialKey); } void AppConfig::setServerGroupChecked(bool newValue) { m_ServerGroupChecked = newValue; } void AppConfig::setUseExternalConfig(bool newValue) { m_UseExternalConfig = newValue; } void AppConfig::setConfigFile(const QString &newValue) { m_ConfigFile = newValue; } void AppConfig::setUseInternalConfig(bool newValue) { m_UseInternalConfig = newValue; } void AppConfig::setClientGroupChecked(bool newValue) { m_ClientGroupChecked = newValue; } void AppConfig::setServerHostname(const QString &newValue) { m_ServerHostname = newValue; } void AppConfig::setLastVersion(const QString &version) { m_LastVersion = version; } void AppConfig::setScreenName(const QString &s) { m_ScreenName = s; emit screenNameChanged(); } void AppConfig::setPort(int i) { m_Port = i; } void AppConfig::setNetworkInterface(const QString &s) { m_Interface = s; } void AppConfig::setLogLevel(int i) { m_LogLevel = i; } void AppConfig::setLogToFile(bool b) { m_LogToFile = b; } void AppConfig::setLogFilename(const QString &s) { m_LogFilename = s; } void AppConfig::setWizardHasRun() { m_WizardLastRun = kWizardVersion; } void AppConfig::setStartedBefore(bool b) { m_StartedBefore = b; } void AppConfig::setElevateMode(ElevateMode em) { m_ElevateMode = em; } void AppConfig::setAutoHide(bool b) { m_AutoHide = b; } void AppConfig::setLicenseNextCheck(unsigned long long time) { m_LicenseNextCheck = time; } void AppConfig::setInvertScrollDirection(bool newValue) { m_InvertScrollDirection = newValue; } void AppConfig::setLanguageSync(bool newValue) { m_LanguageSync = newValue; } void AppConfig::setPreventSleep(bool newValue) { m_PreventSleep = newValue; } void AppConfig::setEnableService(bool enabled) { m_EnableService = enabled; } void AppConfig::setCloseToTray(bool minimize) { m_CloseToTray = minimize; } void AppConfig::setInvertConnection(bool value) { m_InvertConnection = value; emit invertConnectionChanged(); } void AppConfig::setMainWindowSize(const QSize &size) { m_MainWindowSize = size; } void AppConfig::setMainWindowPosition(const QPoint &position) { m_MainWindowPosition = position; } void AppConfig::setShowDevThanks(bool value) { m_ShowDevThanks = value; } void AppConfig::setShowCloseReminder(bool value) { m_ShowCloseReminder = value; } /////////////////////////////////////////////////////////////////////////////// // End setters ///////////////////////////////////////////////////////////////////////////////