Refactor Config.
Replaces all string configuration options with enum types that can be checked by the compiler. This prevents spelling errors, in-place configuration definitions, and inconsistent default values. The default value config getter signature was removed in favour of consistently and centrally default-initialised configuration values. Individual default values were adjusted for better security, such as the default password length, which was increased from 16 characters to 32. The already existing config option deprecation map was extended by a general migration procedure using configuration versioning. Settings were split into Roaming and Local settings, which go to their respective AppData locations on Windows. Fixes #2574 Fixes #2193
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2020 KeePassXC Team <team@keepassxc.org>
|
||||
* Copyright (C) 2011 Felix Geyer <debfx@fobos.de>
|
||||
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -17,40 +17,202 @@
|
||||
*/
|
||||
|
||||
#include "Config.h"
|
||||
#include "Global.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDir>
|
||||
#include <QHash>
|
||||
#include <QSettings>
|
||||
#include <QSize>
|
||||
#include <QStandardPaths>
|
||||
#include <QTemporaryFile>
|
||||
|
||||
/*
|
||||
* Map of configuration file settings that are either deprecated, or have
|
||||
* had their name changed. Entries in the map are of the form
|
||||
* {oldName, newName}
|
||||
* Set newName to empty string to remove the setting from the file.
|
||||
*/
|
||||
static const QMap<QString, QString> deprecationMap = {
|
||||
// >2.3.4
|
||||
{QStringLiteral("security/hidepassworddetails"), QStringLiteral("security/HidePasswordPreviewPanel")},
|
||||
// >2.3.4
|
||||
{QStringLiteral("GUI/HideDetailsView"), QStringLiteral("GUI/HidePreviewPanel")},
|
||||
// >2.3.4
|
||||
{QStringLiteral("GUI/DetailSplitterState"), QStringLiteral("GUI/PreviewSplitterState")},
|
||||
// >2.3.4
|
||||
{QStringLiteral("security/IconDownloadFallbackToGoogle"), QStringLiteral("security/IconDownloadFallback")},
|
||||
#define CONFIG_VERSION 1
|
||||
#define QS QStringLiteral
|
||||
|
||||
enum ConfigType
|
||||
{
|
||||
Local,
|
||||
Roaming
|
||||
};
|
||||
|
||||
struct ConfigDirective
|
||||
{
|
||||
QString name;
|
||||
ConfigType type;
|
||||
QVariant defaultValue;
|
||||
};
|
||||
|
||||
// clang-format off
|
||||
|
||||
/**
|
||||
* Map of legal config values, their type and default value.
|
||||
*/
|
||||
static const QHash<Config::ConfigKey, ConfigDirective> configStrings = {
|
||||
// General
|
||||
{Config::SingleInstance,{QS("SingleInstance"), Roaming, true}},
|
||||
{Config::RememberLastDatabases,{QS("RememberLastDatabases"), Roaming, true}},
|
||||
{Config::NumberOfRememberedLastDatabases,{QS("NumberOfRememberedLastDatabases"), Roaming, 5}},
|
||||
{Config::RememberLastKeyFiles,{QS("RememberLastKeyFiles"), Roaming, true}},
|
||||
{Config::OpenPreviousDatabasesOnStartup,{QS("OpenPreviousDatabasesOnStartup"), Roaming, true}},
|
||||
{Config::AutoSaveAfterEveryChange,{QS("AutoSaveAfterEveryChange"), Roaming, true}},
|
||||
{Config::AutoReloadOnChange,{QS("AutoReloadOnChange"), Roaming, true}},
|
||||
{Config::AutoSaveOnExit,{QS("AutoSaveOnExit"), Roaming, true}},
|
||||
{Config::BackupBeforeSave,{QS("BackupBeforeSave"), Roaming, false}},
|
||||
{Config::UseAtomicSaves,{QS("UseAtomicSaves"), Roaming, true}},
|
||||
{Config::SearchLimitGroup,{QS("SearchLimitGroup"), Roaming, false}},
|
||||
{Config::MinimizeOnOpenUrl,{QS("MinimizeOnOpenUrl"), Roaming, false}},
|
||||
{Config::HideWindowOnCopy,{QS("HideWindowOnCopy"), Roaming, false}},
|
||||
{Config::MinimizeOnCopy,{QS("MinimizeOnCopy"), Roaming, true}},
|
||||
{Config::MinimizeAfterUnlock,{QS("MinimizeAfterUnlock"), Roaming, false}},
|
||||
{Config::DropToBackgroundOnCopy,{QS("DropToBackgroundOnCopy"), Roaming, false}},
|
||||
{Config::UseGroupIconOnEntryCreation,{QS("UseGroupIconOnEntryCreation"), Roaming, true}},
|
||||
{Config::AutoTypeEntryTitleMatch,{QS("AutoTypeEntryTitleMatch"), Roaming, true}},
|
||||
{Config::AutoTypeEntryURLMatch,{QS("AutoTypeEntryURLMatch"), Roaming, true}},
|
||||
{Config::AutoTypeDelay,{QS("AutoTypeDelay"), Roaming, 25}},
|
||||
{Config::AutoTypeStartDelay,{QS("AutoTypeStartDelay"), Roaming, 500}},
|
||||
{Config::GlobalAutoTypeKey,{QS("GlobalAutoTypeKey"), Roaming, 0}},
|
||||
{Config::GlobalAutoTypeModifiers,{QS("GlobalAutoTypeModifiers"), Roaming, 0}},
|
||||
{Config::IgnoreGroupExpansion,{QS("IgnoreGroupExpansion"), Roaming, true}},
|
||||
{Config::FaviconDownloadTimeout,{QS("FaviconDownloadTimeout"), Roaming, 10}},
|
||||
{Config::UpdateCheckMessageShown,{QS("UpdateCheckMessageShown"), Roaming, true}},
|
||||
{Config::UseTouchID,{QS("UseTouchID"), Roaming, false}},
|
||||
|
||||
{Config::LastDatabases, {QS("LastDatabases"), Local, {}}},
|
||||
{Config::LastKeyFiles, {QS("LastKeyFiles"), Local, {}}},
|
||||
{Config::LastChallengeResponse, {QS("LastChallengeResponse"), Local, {}}},
|
||||
{Config::LastActiveDatabase, {QS("LastActiveDatabase"), Local, {}}},
|
||||
{Config::LastOpenedDatabases, {QS("LastOpenedDatabases"), Local, {}}},
|
||||
{Config::LastDir, {QS("LastDir"), Local, QDir::homePath()}},
|
||||
{Config::LastAttachmentDir, {QS("LastAttachmentDir"), Local, {}}},
|
||||
|
||||
// GUI
|
||||
{Config::GUI_Language, {QS("GUI/Language"), Roaming, QS("system")}},
|
||||
{Config::GUI_HideToolbar, {QS("GUI/HideToolbar"), Roaming, false}},
|
||||
{Config::GUI_MovableToolbar, {QS("GUI/MovableToolbar"), Roaming, false}},
|
||||
{Config::GUI_HidePreviewPanel, {QS("GUI/HidePreviewPanel"), Roaming, false}},
|
||||
{Config::GUI_ToolButtonStyle, {QS("GUI/ToolButtonStyle"), Roaming, Qt::ToolButtonIconOnly}},
|
||||
{Config::GUI_ShowTrayIcon, {QS("GUI/ShowTrayIcon"), Roaming, false}},
|
||||
{Config::GUI_DarkTrayIcon, {QS("GUI/DarkTrayIcon"), Roaming, false}},
|
||||
{Config::GUI_MinimizeToTray, {QS("GUI/MinimizeToTray"), Roaming, false}},
|
||||
{Config::GUI_MinimizeOnStartup, {QS("GUI/MinimizeOnStartup"), Roaming, false}},
|
||||
{Config::GUI_MinimizeOnClose, {QS("GUI/MinimizeOnClose"), Roaming, false}},
|
||||
{Config::GUI_HideUsernames, {QS("GUI/HideUsernames"), Roaming, false}},
|
||||
{Config::GUI_HidePasswords, {QS("GUI/HidePasswords"), Roaming, true}},
|
||||
{Config::GUI_AdvancedSettings, {QS("GUI/AdvancedSettings"), Roaming, false}},
|
||||
{Config::GUI_MonospaceNotes, {QS("GUI/MonospaceNotes"), Roaming, false}},
|
||||
{Config::GUI_ApplicationTheme, {QS("GUI/ApplicationTheme"), Roaming, QS("auto")}},
|
||||
{Config::GUI_CheckForUpdates, {QS("GUI/CheckForUpdates"), Roaming, true}},
|
||||
{Config::GUI_CheckForUpdatesIncludeBetas, {QS("GUI/CheckForUpdatesIncludeBetas"), Roaming, false}},
|
||||
|
||||
{Config::GUI_MainWindowGeometry, {QS("GUI/MainWindowGeometry"), Local, {}}},
|
||||
{Config::GUI_MainWindowState, {QS("GUI/MainWindowState"), Local, {}}},
|
||||
{Config::GUI_ListViewState, {QS("GUI/ListViewState"), Local, {}}},
|
||||
{Config::GUI_SearchViewState, {QS("GUI/SearchViewState"), Local, {}}},
|
||||
{Config::GUI_SplitterState, {QS("GUI/SplitterState"), Local, {}}},
|
||||
{Config::GUI_PreviewSplitterState, {QS("GUI/PreviewSplitterState"), Local, {}}},
|
||||
{Config::GUI_AutoTypeSelectDialogSize, {QS("GUI/AutoTypeSelectDialogSize"), Local, QSize(600, 250)}},
|
||||
{Config::GUI_CheckForUpdatesNextCheck, {QS("GUI/AutoTypeSelectDialogSize"), Local, 0}},
|
||||
|
||||
// Security
|
||||
{Config::Security_ClearClipboard, {QS("Security/ClearClipboard"), Roaming, true}},
|
||||
{Config::Security_ClearClipboardTimeout, {QS("Security/ClearClipboardTimeout"), Roaming, 10}},
|
||||
{Config::Security_ClearSearch, {QS("Security/ClearSearch"), Roaming, true}},
|
||||
{Config::Security_ClearSearchTimeout, {QS("Security/ClearSearchTimeout"), Roaming, 5}},
|
||||
{Config::Security_HideNotes, {QS("Security/Security_HideNotes"), Roaming, false}},
|
||||
{Config::Security_LockDatabaseIdle, {QS("Security/LockDatabaseIdle"), Roaming, false}},
|
||||
{Config::Security_LockDatabaseIdleSeconds, {QS("Security/LockDatabaseIdleSeconds"), Roaming, 240}},
|
||||
{Config::Security_LockDatabaseMinimize, {QS("Security/LockDatabaseMinimize"), Roaming, false}},
|
||||
{Config::Security_LockDatabaseScreenLock, {QS("Security/LockDatabaseScreenLock"), Roaming, true}},
|
||||
{Config::Security_RelockAutoType, {QS("Security/RelockAutoType"), Roaming, false}},
|
||||
{Config::Security_PasswordsRepeat, {QS("Security/PasswordsRepeat"), Roaming, false}},
|
||||
{Config::Security_PasswordsCleartext, {QS("Security/PasswordsCleartext"), Roaming, false}},
|
||||
{Config::Security_PasswordEmptyNoDots, {QS("Security/PasswordEmptyNoDots"), Roaming, true}},
|
||||
{Config::Security_HidePasswordPreviewPanel, {QS("Security/HidePasswordPreviewPanel"), Roaming, true}},
|
||||
{Config::Security_AutoTypeAsk, {QS("Security/AutotypeAsk"), Roaming, true}},
|
||||
{Config::Security_IconDownloadFallback, {QS("Security/IconDownloadFallback"), Roaming, false}},
|
||||
{Config::Security_ResetTouchId, {QS("Security/ResetTouchId"), Roaming, false}},
|
||||
{Config::Security_ResetTouchIdTimeout, {QS("Security/ResetTouchIdTimeout"), Roaming, 30}},
|
||||
{Config::Security_ResetTouchIdScreenlock,{QS("Security/ResetTouchIdScreenlock"), Roaming, true}},
|
||||
|
||||
// Browser
|
||||
{Config::Browser_Enabled, {QS("Browser/Enabled"), Roaming, false}},
|
||||
{Config::Browser_ShowNotification, {QS("Browser/ShowNotification"), Roaming, true}},
|
||||
{Config::Browser_BestMatchOnly, {QS("Browser/BestMatchOnly"), Roaming, false}},
|
||||
{Config::Browser_UnlockDatabase, {QS("Browser/UnlockDatabase"), Roaming, true}},
|
||||
{Config::Browser_MatchUrlScheme, {QS("Browser/MatchUrlScheme"), Roaming, true}},
|
||||
{Config::Browser_SortByUsername, {QS("Browser/SortByUsername"), Roaming, false}},
|
||||
{Config::Browser_SupportBrowserProxy, {QS("Browser/SupportBrowserProxy"), Roaming, true}},
|
||||
{Config::Browser_UseCustomProxy, {QS("Browser/UseCustomProxy"), Roaming, false}},
|
||||
{Config::Browser_CustomProxyLocation, {QS("Browser/CustomProxyLocation"), Roaming, {}}},
|
||||
{Config::Browser_UpdateBinaryPath, {QS("Browser/UpdateBinaryPath"), Roaming, true}},
|
||||
{Config::Browser_AllowExpiredCredentials, {QS("Browser/AllowExpiredCredentials"), Roaming, false}},
|
||||
{Config::Browser_AlwaysAllowAccess, {QS("Browser/AlwaysAllowAccess"), Roaming, false}},
|
||||
{Config::Browser_AlwaysAllowUpdate, {QS("Browser/AlwaysAllowUpdate"), Roaming, false}},
|
||||
{Config::Browser_HttpAuthPermission, {QS("Browser/HttpAuthPermission"), Roaming, false}},
|
||||
{Config::Browser_SearchInAllDatabases, {QS("Browser/SearchInAllDatabases"), Roaming, false}},
|
||||
{Config::Browser_SupportKphFields, {QS("Browser/SupportKphFields"), Roaming, true}},
|
||||
{Config::Browser_NoMigrationPrompt, {QS("Browser/NoMigrationPrompt"), Roaming, false}},
|
||||
|
||||
// SSHAgent
|
||||
{Config::SSHAgent_Enabled, {QS("SSHAgent/Enabled"), Roaming, false}},
|
||||
{Config::SSHAgent_UseOpenSSH, {QS("SSHAgent/UseOpenSSH"), Roaming, false}},
|
||||
{Config::SSHAgent_AuthSockOverride, {QS("SSHAgent/AuthSockOverride"), Local, {}}},
|
||||
|
||||
// FdoSecrets
|
||||
{Config::FdoSecrets_Enabled, {QS("FdoSecrets/Enabled"), Roaming, false}},
|
||||
{Config::FdoSecrets_ShowNotification, {QS("FdoSecrets/ShowNotification"), Roaming, true}},
|
||||
{Config::FdoSecrets_NoConfirmDeleteItem, {QS("FdoSecrets/NoConfirmDeleteItem"), Roaming, false}},
|
||||
|
||||
// KeeShare
|
||||
{Config::KeeShare_QuietSuccess, {QS("KeeShare/QuietSuccess"), Roaming, false}},
|
||||
{Config::KeeShare_Own, {QS("KeeShare/Own"), Roaming, {}}},
|
||||
{Config::KeeShare_Foreign, {QS("KeeShare/Foreign"), Roaming, {}}},
|
||||
{Config::KeeShare_Active, {QS("KeeShare/Active"), Roaming, {}}},
|
||||
{Config::KeeShare_LastDir, {QS("KeeShare/LastDir"), Local, QDir::homePath()}},
|
||||
{Config::KeeShare_LastKeyDir, {QS("KeeShare/LastKeyDir"), Local, QDir::homePath()}},
|
||||
{Config::KeeShare_LastShareDir, {QS("KeeShare/LastShareDir"), Local, QDir::homePath()}},
|
||||
|
||||
// PasswordGenerator
|
||||
{Config::PasswordGenerator_LowerCase, {QS("PasswordGenerator/LowerCase"), Roaming, true}},
|
||||
{Config::PasswordGenerator_UpperCase, {QS("PasswordGenerator/UpperCase"), Roaming, true}},
|
||||
{Config::PasswordGenerator_Numbers, {QS("PasswordGenerator/Numbers"), Roaming, true}},
|
||||
{Config::PasswordGenerator_EASCII, {QS("PasswordGenerator/EASCII"), Roaming, false}},
|
||||
{Config::PasswordGenerator_AdvancedMode, {QS("PasswordGenerator/AdvancedMode"), Roaming, false}},
|
||||
{Config::PasswordGenerator_SpecialChars, {QS("PasswordGenerator/SpecialChars"), Roaming, true}},
|
||||
{Config::PasswordGenerator_AdditionalChars, {QS("PasswordGenerator/AdditionalChars"), Roaming, true}},
|
||||
{Config::PasswordGenerator_Braces, {QS("PasswordGenerator/Braces"), Roaming, false}},
|
||||
{Config::PasswordGenerator_Punctuation, {QS("PasswordGenerator/Punctuation"), Roaming, false}},
|
||||
{Config::PasswordGenerator_Quotes, {QS("PasswordGenerator/Quotes"), Roaming, false}},
|
||||
{Config::PasswordGenerator_Dashes, {QS("PasswordGenerator/Dashes"), Roaming, false}},
|
||||
{Config::PasswordGenerator_Math, {QS("PasswordGenerator/Math"), Roaming, false}},
|
||||
{Config::PasswordGenerator_Logograms, {QS("PasswordGenerator/Logograms"), Roaming, false}},
|
||||
{Config::PasswordGenerator_ExcludedChars, {QS("PasswordGenerator/ExcludedChars"), Roaming, {}}},
|
||||
{Config::PasswordGenerator_ExcludeAlike, {QS("PasswordGenerator/ExcludeAlike"), Roaming, true}},
|
||||
{Config::PasswordGenerator_EnsureEvery, {QS("PasswordGenerator/EnsureEvery"), Roaming, true}},
|
||||
{Config::PasswordGenerator_Length, {QS("PasswordGenerator/Length"), Roaming, 20}},
|
||||
{Config::PasswordGenerator_WordCount, {QS("PasswordGenerator/WordCount"), Roaming, 7}},
|
||||
{Config::PasswordGenerator_WordSeparator, {QS("PasswordGenerator/WordSeparator"), Roaming, QS(" ")}},
|
||||
{Config::PasswordGenerator_WordList, {QS("PasswordGenerator/WordList"), Roaming, QS("eff_large.wordlist")}},
|
||||
{Config::PasswordGenerator_WordCase, {QS("PasswordGenerator/WordCase"), Roaming, 0}},
|
||||
{Config::PasswordGenerator_Type, {QS("PasswordGenerator/Type"), Roaming, 0}},
|
||||
|
||||
// Messages
|
||||
{Config::Messages_NoLegacyKeyFileWarning, {QS("Messages/NoLegacyKeyFileWarning"), Roaming, false}},
|
||||
{Config::Messages_Qt55CompatibilityWarning, {QS("Messages/Messages_Qt55CompatibilityWarning"), Local, false}}};
|
||||
|
||||
// clang-format on
|
||||
|
||||
QPointer<Config> Config::m_instance(nullptr);
|
||||
|
||||
QVariant Config::get(const QString& key)
|
||||
QVariant Config::get(ConfigKey key)
|
||||
{
|
||||
return m_settings->value(key, m_defaults.value(key));
|
||||
}
|
||||
|
||||
QVariant Config::get(const QString& key, const QVariant& defaultValue)
|
||||
{
|
||||
return m_settings->value(key, defaultValue);
|
||||
auto cfg = configStrings[key];
|
||||
auto defaultValue = configStrings[key].defaultValue;
|
||||
if (m_localSettings && cfg.type == Local) {
|
||||
return m_localSettings->value(cfg.name, defaultValue);
|
||||
}
|
||||
return m_settings->value(cfg.name, defaultValue);
|
||||
}
|
||||
|
||||
bool Config::hasAccessError()
|
||||
@@ -63,18 +225,32 @@ QString Config::getFileName()
|
||||
return m_settings->fileName();
|
||||
}
|
||||
|
||||
void Config::set(const QString& key, const QVariant& value)
|
||||
void Config::set(ConfigKey key, const QVariant& value)
|
||||
{
|
||||
if (m_settings->contains(key) && m_settings->value(key) == value) {
|
||||
if (get(key) == value) {
|
||||
return;
|
||||
}
|
||||
const bool surpressSignal = !m_settings->contains(key) && m_defaults.value(key) == value;
|
||||
|
||||
m_settings->setValue(key, value);
|
||||
|
||||
if (!surpressSignal) {
|
||||
emit changed(key);
|
||||
auto cfg = configStrings[key];
|
||||
if (cfg.type == Local && m_localSettings) {
|
||||
m_localSettings->setValue(cfg.name, value);
|
||||
} else {
|
||||
m_settings->setValue(cfg.name, value);
|
||||
}
|
||||
|
||||
emit changed(key);
|
||||
}
|
||||
|
||||
void Config::remove(ConfigKey key)
|
||||
{
|
||||
auto cfg = configStrings[key];
|
||||
if (cfg.type == Local && m_localSettings) {
|
||||
m_localSettings->remove(cfg.name);
|
||||
} else {
|
||||
m_settings->remove(cfg.name);
|
||||
}
|
||||
|
||||
emit changed(key);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -87,38 +263,143 @@ void Config::set(const QString& key, const QVariant& value)
|
||||
void Config::sync()
|
||||
{
|
||||
m_settings->sync();
|
||||
if (m_localSettings) {
|
||||
m_localSettings->sync();
|
||||
}
|
||||
}
|
||||
|
||||
void Config::resetToDefaults()
|
||||
{
|
||||
for (const auto& setting : m_defaults.keys()) {
|
||||
m_settings->setValue(setting, m_defaults.value(setting));
|
||||
m_settings->clear();
|
||||
if (m_localSettings) {
|
||||
m_localSettings->clear();
|
||||
}
|
||||
}
|
||||
|
||||
void Config::upgrade()
|
||||
/**
|
||||
* Map of configuration file settings that are either deprecated, or have
|
||||
* had their name changed to their new config enum values.
|
||||
*
|
||||
* Set a value to Deleted to remove the setting.
|
||||
*/
|
||||
static const QHash<QString, Config::ConfigKey> deprecationMap = {
|
||||
// 2.3.4
|
||||
{QS("security/hidepassworddetails"), Config::Security_HidePasswordPreviewPanel},
|
||||
{QS("GUI/HideDetailsView"), Config::GUI_HidePreviewPanel},
|
||||
{QS("GUI/DetailSplitterState"), Config::GUI_PreviewSplitterState},
|
||||
{QS("security/IconDownloadFallbackToGoogle"), Config::Security_IconDownloadFallback},
|
||||
|
||||
// 2.6.0
|
||||
{QS("security/autotypeask"), Config::Security_AutoTypeAsk},
|
||||
{QS("security/clearclipboard"), Config::Security_ClearClipboard},
|
||||
{QS("security/clearclipboardtimeout"), Config::Security_ClearClipboardTimeout},
|
||||
{QS("security/clearsearch"), Config::Security_ClearSearch},
|
||||
{QS("security/clearsearchtimeout"), Config::Security_ClearSearchTimeout},
|
||||
{QS("security/lockdatabaseidle"), Config::Security_LockDatabaseIdle},
|
||||
{QS("security/lockdatabaseidlesec"), Config::Security_LockDatabaseIdleSeconds},
|
||||
{QS("security/lockdatabaseminimize"), Config::Security_LockDatabaseMinimize},
|
||||
{QS("security/lockdatabasescreenlock"), Config::Security_LockDatabaseScreenLock},
|
||||
{QS("security/relockautotype"), Config::Security_RelockAutoType},
|
||||
{QS("security/IconDownloadFallback"), Config::Security_IconDownloadFallback},
|
||||
{QS("security/passwordscleartext"), Config::Security_PasswordsCleartext},
|
||||
{QS("security/passwordemptynodots"), Config::Security_PasswordEmptyNoDots},
|
||||
{QS("security/HidePasswordPreviewPanel"), Config::Security_HidePasswordPreviewPanel},
|
||||
{QS("security/passwordsrepeat"), Config::Security_PasswordsRepeat},
|
||||
{QS("security/hidenotes"), Config::Security_HideNotes},
|
||||
{QS("security/resettouchid"), Config::Security_ResetTouchId},
|
||||
{QS("security/resettouchidtimeout"), Config::Security_ResetTouchIdTimeout},
|
||||
{QS("security/resettouchidscreenlock"), Config::Security_ResetTouchIdScreenlock},
|
||||
{QS("KeeShare/Settings.own"), Config::KeeShare_Own},
|
||||
{QS("KeeShare/Settings.foreign"), Config::KeeShare_Foreign},
|
||||
{QS("KeeShare/Settings.active"), Config::KeeShare_Active},
|
||||
{QS("SSHAgent"), Config::SSHAgent_Enabled},
|
||||
{QS("SSHAgentOpenSSH"), Config::SSHAgent_UseOpenSSH},
|
||||
{QS("SSHAuthSockOverride"), Config::SSHAgent_AuthSockOverride},
|
||||
{QS("generator/LowerCase"), Config::PasswordGenerator_LowerCase},
|
||||
{QS("generator/UpperCase"), Config::PasswordGenerator_UpperCase},
|
||||
{QS("generator/Numbers"), Config::PasswordGenerator_Numbers},
|
||||
{QS("generator/EASCII"), Config::PasswordGenerator_EASCII},
|
||||
{QS("generator/AdvancedMode"), Config::PasswordGenerator_AdvancedMode},
|
||||
{QS("generator/SpecialChars"), Config::PasswordGenerator_SpecialChars},
|
||||
{QS("generator/AdditionalChars"), Config::PasswordGenerator_AdditionalChars},
|
||||
{QS("generator/Braces"), Config::PasswordGenerator_Braces},
|
||||
{QS("generator/Punctuation"), Config::PasswordGenerator_Punctuation},
|
||||
{QS("generator/Quotes"), Config::PasswordGenerator_Quotes},
|
||||
{QS("generator/Dashes"), Config::PasswordGenerator_Dashes},
|
||||
{QS("generator/Math"), Config::PasswordGenerator_Math},
|
||||
{QS("generator/Logograms"), Config::PasswordGenerator_Logograms},
|
||||
{QS("generator/ExcludedChars"), Config::PasswordGenerator_ExcludedChars},
|
||||
{QS("generator/ExcludeAlike"), Config::PasswordGenerator_ExcludeAlike},
|
||||
{QS("generator/EnsureEvery"), Config::PasswordGenerator_EnsureEvery},
|
||||
{QS("generator/Length"), Config::PasswordGenerator_Length},
|
||||
{QS("generator/WordCount"), Config::PasswordGenerator_WordCount},
|
||||
{QS("generator/WordSeparator"), Config::PasswordGenerator_WordSeparator},
|
||||
{QS("generator/WordList"), Config::PasswordGenerator_WordList},
|
||||
{QS("generator/WordCase"), Config::PasswordGenerator_WordCase},
|
||||
{QS("generator/Type"), Config::PasswordGenerator_Type},
|
||||
{QS("QtErrorMessageShown"), Config::Messages_Qt55CompatibilityWarning}};
|
||||
|
||||
/**
|
||||
* Migrate settings from previous versions.
|
||||
*/
|
||||
void Config::migrate()
|
||||
{
|
||||
const auto keys = deprecationMap.keys();
|
||||
for (const auto& setting : keys) {
|
||||
int previousVersion = m_settings->value("ConfigVersion").toInt();
|
||||
if (CONFIG_VERSION <= previousVersion) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update renamed keys and drop obsolete keys
|
||||
for (const auto& setting : deprecationMap.keys()) {
|
||||
QVariant value;
|
||||
if (m_settings->contains(setting)) {
|
||||
if (!deprecationMap.value(setting).isEmpty()) {
|
||||
// Add entry with new name and old entry's value
|
||||
m_settings->setValue(deprecationMap.value(setting), m_settings->value(setting));
|
||||
}
|
||||
value = m_settings->value(setting);
|
||||
m_settings->remove(setting);
|
||||
} else if (m_localSettings && m_localSettings->contains(setting)) {
|
||||
value = m_localSettings->value(setting);
|
||||
m_localSettings->remove(setting);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (deprecationMap[setting] == Config::Deleted) {
|
||||
continue;
|
||||
}
|
||||
|
||||
set(deprecationMap[setting], value);
|
||||
}
|
||||
|
||||
// > 2.3.4
|
||||
if (m_settings->value("AutoSaveAfterEveryChange").toBool()) {
|
||||
m_settings->setValue("AutoSaveOnExit", true);
|
||||
// Move local settings to separate file
|
||||
if (m_localSettings)
|
||||
for (const auto& setting : asConst(configStrings)) {
|
||||
if (setting.type == Local && m_settings->contains(setting.name)) {
|
||||
m_localSettings->setValue(setting.name, m_settings->value(setting.name));
|
||||
m_settings->remove(setting.name);
|
||||
}
|
||||
}
|
||||
|
||||
// Detailed version migrations
|
||||
|
||||
// pre 2.6.0 (no versioned configs)
|
||||
if (previousVersion < 1) {
|
||||
|
||||
// 2.3.4
|
||||
if (get(AutoSaveAfterEveryChange).toBool()) {
|
||||
set(AutoSaveOnExit, true);
|
||||
}
|
||||
|
||||
// Setting defaults for 'hide window on copy' behavior, keeping the user's original setting
|
||||
if (get(HideWindowOnCopy).isNull()) {
|
||||
set(HideWindowOnCopy, get(MinimizeOnCopy).toBool());
|
||||
set(MinimizeOnCopy, true);
|
||||
}
|
||||
|
||||
// Reset database columns if upgrading from pre 2.6.0
|
||||
remove(GUI_ListViewState);
|
||||
}
|
||||
|
||||
// Setting defaults for 'hide window on copy' behavior, keeping the user's original setting
|
||||
if (m_settings->value("HideWindowOnCopy").isNull()) {
|
||||
m_settings->setValue("HideWindowOnCopy", m_settings->value("MinimizeOnCopy").toBool());
|
||||
m_settings->setValue("MinimizeOnCopy", true);
|
||||
}
|
||||
m_settings->setValue("ConfigVersion", CONFIG_VERSION);
|
||||
sync();
|
||||
}
|
||||
|
||||
Config::Config(const QString& fileName, QObject* parent)
|
||||
@@ -130,109 +411,64 @@ Config::Config(const QString& fileName, QObject* parent)
|
||||
Config::Config(QObject* parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
// Check if portable config is present. If not, find it in user's directory
|
||||
QString portablePath = QCoreApplication::applicationDirPath() + "/keepassxc.ini";
|
||||
// Check if portable config is present (use it also to store local config)
|
||||
QString portablePath = QDir::fromNativeSeparators(QCoreApplication::applicationDirPath()) + "/keepassxc.ini";
|
||||
if (QFile::exists(portablePath)) {
|
||||
init(portablePath);
|
||||
} else {
|
||||
QString userPath;
|
||||
QString homePath = QDir::homePath();
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)
|
||||
// we can't use QStandardPaths on X11 as it uses XDG_DATA_HOME instead of XDG_CONFIG_HOME
|
||||
QByteArray env = qgetenv("XDG_CONFIG_HOME");
|
||||
if (env.isEmpty()) {
|
||||
userPath = homePath;
|
||||
userPath += "/.config";
|
||||
} else if (env[0] == '/') {
|
||||
userPath = QFile::decodeName(env);
|
||||
} else {
|
||||
userPath = homePath;
|
||||
userPath += '/';
|
||||
userPath += QFile::decodeName(env);
|
||||
}
|
||||
QString configPath;
|
||||
QString localConfigPath;
|
||||
|
||||
userPath += "/keepassxc/";
|
||||
#if defined(Q_OS_WIN)
|
||||
configPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
|
||||
localConfigPath = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation);
|
||||
#elif defined(Q_OS_MACOS)
|
||||
configPath = QDir::fromNativeSeparators(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation));
|
||||
localConfigPath = QStandardPaths::writableLocation(QStandardPaths::CacheLocation);
|
||||
#else
|
||||
userPath = QDir::fromNativeSeparators(QStandardPaths::writableLocation(QStandardPaths::DataLocation));
|
||||
// storageLocation() appends the application name ("/keepassxc") to the end
|
||||
userPath += "/";
|
||||
configPath = QDir::fromNativeSeparators(QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation));
|
||||
localConfigPath = QStandardPaths::writableLocation(QStandardPaths::CacheLocation);
|
||||
#endif
|
||||
|
||||
configPath += "/keepassxc";
|
||||
localConfigPath += "/keepassxc";
|
||||
|
||||
#ifdef QT_DEBUG
|
||||
userPath += "keepassxc_debug.ini";
|
||||
#else
|
||||
userPath += "keepassxc.ini";
|
||||
configPath += "_debug";
|
||||
localConfigPath += "_debug";
|
||||
#endif
|
||||
|
||||
init(userPath);
|
||||
}
|
||||
configPath += ".ini";
|
||||
localConfigPath += ".ini";
|
||||
|
||||
init(QDir::toNativeSeparators(configPath), QDir::toNativeSeparators(localConfigPath));
|
||||
}
|
||||
|
||||
Config::~Config()
|
||||
{
|
||||
}
|
||||
|
||||
void Config::init(const QString& fileName)
|
||||
void Config::init(const QString& configFileName, const QString& localConfigFileName)
|
||||
{
|
||||
m_settings.reset(new QSettings(fileName, QSettings::IniFormat));
|
||||
upgrade();
|
||||
connect(qApp, &QCoreApplication::aboutToQuit, this, &Config::sync);
|
||||
// Upgrade from previous KeePassXC version which stores its config
|
||||
// in AppData/Local on Windows instead of AppData/Roaming.
|
||||
// Move file to correct location before continuing.
|
||||
if (!localConfigFileName.isEmpty() && QFile::exists(localConfigFileName) && !QFile::exists(configFileName)) {
|
||||
QDir().mkpath(QFileInfo(configFileName).absolutePath());
|
||||
QFile::copy(localConfigFileName, configFileName);
|
||||
QFile::remove(localConfigFileName);
|
||||
QDir().rmdir(QFileInfo(localConfigFileName).absolutePath());
|
||||
}
|
||||
|
||||
m_defaults.insert("SingleInstance", true);
|
||||
m_defaults.insert("RememberLastDatabases", true);
|
||||
m_defaults.insert("NumberOfRememberedLastDatabases", 5);
|
||||
m_defaults.insert("RememberLastKeyFiles", true);
|
||||
m_defaults.insert("OpenPreviousDatabasesOnStartup", true);
|
||||
m_defaults.insert("AutoSaveAfterEveryChange", true);
|
||||
m_defaults.insert("AutoReloadOnChange", true);
|
||||
m_defaults.insert("AutoSaveOnExit", true);
|
||||
m_defaults.insert("BackupBeforeSave", false);
|
||||
m_defaults.insert("UseAtomicSaves", true);
|
||||
m_defaults.insert("SearchLimitGroup", false);
|
||||
m_defaults.insert("MinimizeOnOpenUrl", false);
|
||||
m_defaults.insert("HideWindowOnCopy", false);
|
||||
m_defaults.insert("MinimizeOnCopy", true);
|
||||
m_defaults.insert("MinimizeAfterUnlock", false);
|
||||
m_defaults.insert("DropToBackgroundOnCopy", false);
|
||||
m_defaults.insert("UseGroupIconOnEntryCreation", false);
|
||||
m_defaults.insert("AutoTypeEntryTitleMatch", true);
|
||||
m_defaults.insert("AutoTypeEntryURLMatch", true);
|
||||
m_defaults.insert("AutoTypeDelay", 25);
|
||||
m_defaults.insert("AutoTypeStartDelay", 500);
|
||||
m_defaults.insert("UseGroupIconOnEntryCreation", true);
|
||||
m_defaults.insert("IgnoreGroupExpansion", true);
|
||||
m_defaults.insert("FaviconDownloadTimeout", 10);
|
||||
m_defaults.insert("security/clearclipboard", true);
|
||||
m_defaults.insert("security/clearclipboardtimeout", 10);
|
||||
m_defaults.insert("security/clearsearch", true);
|
||||
m_defaults.insert("security/clearsearchtimeout", 5);
|
||||
m_defaults.insert("security/lockdatabaseidle", false);
|
||||
m_defaults.insert("security/lockdatabaseidlesec", 240);
|
||||
m_defaults.insert("security/lockdatabaseminimize", false);
|
||||
m_defaults.insert("security/lockdatabasescreenlock", true);
|
||||
m_defaults.insert("security/passwordsrepeat", false);
|
||||
m_defaults.insert("security/passwordscleartext", false);
|
||||
m_defaults.insert("security/passwordemptynodots", true);
|
||||
m_defaults.insert("security/HidePasswordPreviewPanel", true);
|
||||
m_defaults.insert("security/autotypeask", true);
|
||||
m_defaults.insert("security/IconDownloadFallback", false);
|
||||
m_defaults.insert("security/resettouchid", false);
|
||||
m_defaults.insert("security/resettouchidtimeout", 30);
|
||||
m_defaults.insert("security/resettouchidscreenlock", true);
|
||||
m_defaults.insert("GUI/Language", "system");
|
||||
m_defaults.insert("GUI/HideToolbar", false);
|
||||
m_defaults.insert("GUI/MovableToolbar", false);
|
||||
m_defaults.insert("GUI/ToolButtonStyle", Qt::ToolButtonIconOnly);
|
||||
m_defaults.insert("GUI/ShowTrayIcon", false);
|
||||
m_defaults.insert("GUI/DarkTrayIcon", false);
|
||||
m_defaults.insert("GUI/MinimizeToTray", false);
|
||||
m_defaults.insert("GUI/MinimizeOnClose", false);
|
||||
m_defaults.insert("GUI/HideUsernames", false);
|
||||
m_defaults.insert("GUI/HidePasswords", true);
|
||||
m_defaults.insert("GUI/AdvancedSettings", false);
|
||||
m_defaults.insert("GUI/MonospaceNotes", false);
|
||||
m_defaults.insert("GUI/ApplicationTheme", "auto");
|
||||
m_settings.reset(new QSettings(configFileName, QSettings::IniFormat));
|
||||
if (!localConfigFileName.isEmpty() && configFileName != localConfigFileName) {
|
||||
m_localSettings.reset(new QSettings(localConfigFileName, QSettings::IniFormat));
|
||||
}
|
||||
|
||||
migrate();
|
||||
connect(qApp, &QCoreApplication::aboutToQuit, this, &Config::sync);
|
||||
}
|
||||
|
||||
Config* Config::instance()
|
||||
@@ -264,3 +500,5 @@ void Config::createTempFileInstance()
|
||||
m_instance = new Config(tmpFile->fileName(), qApp);
|
||||
tmpFile->setParent(m_instance);
|
||||
}
|
||||
|
||||
#undef QS
|
||||
|
||||
Reference in New Issue
Block a user