Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/keepassxreboot/keepassxc.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
authorJanek Bevendorff <janek@jbev.net>2020-04-26 02:31:38 +0300
committerJanek Bevendorff <janek@jbev.net>2020-05-02 23:30:27 +0300
commit596d2cf425c3c8495b4c4a58c61798afe9fdd536 (patch)
treef550255e5355098dd91ba9cd73c7289ac5b352d3 /src/core
parent5add01243d6c86c6d59d5018f860add377575259 (diff)
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
Diffstat (limited to 'src/core')
-rw-r--r--src/core/Bootstrap.cpp8
-rw-r--r--src/core/Config.cpp502
-rw-r--r--src/core/Config.h166
-rw-r--r--src/core/Group.cpp4
-rw-r--r--src/core/IconDownloader.cpp4
-rw-r--r--src/core/PasswordGenerator.h16
-rw-r--r--src/core/Resources.cpp2
-rw-r--r--src/core/Translator.cpp2
8 files changed, 539 insertions, 165 deletions
diff --git a/src/core/Bootstrap.cpp b/src/core/Bootstrap.cpp
index 99b950928..3f5a67a05 100644
--- a/src/core/Bootstrap.cpp
+++ b/src/core/Bootstrap.cpp
@@ -100,7 +100,7 @@ namespace Bootstrap
void restoreMainWindowState(MainWindow& mainWindow)
{
// start minimized if configured
- if (config()->get("GUI/MinimizeOnStartup").toBool()) {
+ if (config()->get(Config::GUI_MinimizeOnStartup).toBool()) {
#ifdef Q_OS_WIN
mainWindow.showMinimized();
#else
@@ -110,14 +110,14 @@ namespace Bootstrap
mainWindow.bringToFront();
}
- if (config()->get("OpenPreviousDatabasesOnStartup").toBool()) {
- const QStringList fileNames = config()->get("LastOpenedDatabases").toStringList();
+ if (config()->get(Config::OpenPreviousDatabasesOnStartup).toBool()) {
+ const QStringList fileNames = config()->get(Config::LastOpenedDatabases).toStringList();
for (const QString& filename : fileNames) {
if (!filename.isEmpty() && QFile::exists(filename)) {
mainWindow.openDatabase(filename);
}
}
- auto lastActiveFile = config()->get("LastActiveDatabase").toString();
+ auto lastActiveFile = config()->get(Config::LastActiveDatabase).toString();
if (!lastActiveFile.isEmpty()) {
mainWindow.openDatabase(lastActiveFile);
}
diff --git a/src/core/Config.cpp b/src/core/Config.cpp
index 5965367b1..f874d8fb2 100644
--- a/src/core/Config.cpp
+++ b/src/core/Config.cpp
@@ -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
-QPointer<Config> Config::m_instance(nullptr);
+enum ConfigType
+{
+ Local,
+ Roaming
+};
-QVariant Config::get(const QString& key)
+struct ConfigDirective
{
- return m_settings->value(key, m_defaults.value(key));
-}
+ 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}},
-QVariant Config::get(const QString& key, const QVariant& defaultValue)
+ // 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(ConfigKey key)
{
- 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);
+ auto cfg = configStrings[key];
+ if (cfg.type == Local && m_localSettings) {
+ m_localSettings->setValue(cfg.name, value);
+ } else {
+ m_settings->setValue(cfg.name, value);
+ }
- if (!surpressSignal) {
- emit changed(key);
+ 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;
}
- }
- // > 2.3.4
- if (m_settings->value("AutoSaveAfterEveryChange").toBool()) {
- m_settings->setValue("AutoSaveOnExit", true);
+ if (deprecationMap[setting] == Config::Deleted) {
+ continue;
+ }
+
+ set(deprecationMap[setting], value);
}
- // 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);
+ // 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);
}
+
+ 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();
-
-#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);
- }
+ return;
+ }
- userPath += "/keepassxc/";
+ QString configPath;
+ QString localConfigPath;
+
+#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
diff --git a/src/core/Config.h b/src/core/Config.h
index ef6dd6af1..0914f1cbe 100644
--- a/src/core/Config.h
+++ b/src/core/Config.h
@@ -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
@@ -32,11 +32,160 @@ class Config : public QObject
public:
Q_DISABLE_COPY(Config)
+ enum ConfigKey
+ {
+ SingleInstance,
+ RememberLastDatabases,
+ NumberOfRememberedLastDatabases,
+ RememberLastKeyFiles,
+ OpenPreviousDatabasesOnStartup,
+ AutoSaveAfterEveryChange,
+ AutoReloadOnChange,
+ AutoSaveOnExit,
+ BackupBeforeSave,
+ UseAtomicSaves,
+ SearchLimitGroup,
+ MinimizeOnOpenUrl,
+ HideWindowOnCopy,
+ MinimizeOnCopy,
+ MinimizeAfterUnlock,
+ DropToBackgroundOnCopy,
+ UseGroupIconOnEntryCreation,
+ AutoTypeEntryTitleMatch,
+ AutoTypeEntryURLMatch,
+ AutoTypeDelay,
+ AutoTypeStartDelay,
+ GlobalAutoTypeKey,
+ GlobalAutoTypeModifiers,
+ IgnoreGroupExpansion,
+ FaviconDownloadTimeout,
+ UpdateCheckMessageShown,
+ UseTouchID,
+
+ LastDatabases,
+ LastKeyFiles,
+ LastChallengeResponse,
+ LastActiveDatabase,
+ LastOpenedDatabases,
+ LastDir,
+ LastAttachmentDir,
+
+ GUI_Language,
+ GUI_HideToolbar,
+ GUI_MovableToolbar,
+ GUI_HidePreviewPanel,
+ GUI_ToolButtonStyle,
+ GUI_ShowTrayIcon,
+ GUI_DarkTrayIcon,
+ GUI_MinimizeToTray,
+ GUI_MinimizeOnStartup,
+ GUI_MinimizeOnClose,
+ GUI_HideUsernames,
+ GUI_HidePasswords,
+ GUI_AdvancedSettings,
+ GUI_MonospaceNotes,
+ GUI_ApplicationTheme,
+ GUI_CheckForUpdates,
+ GUI_CheckForUpdatesIncludeBetas,
+
+ GUI_MainWindowGeometry,
+ GUI_MainWindowState,
+ GUI_ListViewState,
+ GUI_SearchViewState,
+ GUI_PreviewSplitterState,
+ GUI_SplitterState,
+ GUI_AutoTypeSelectDialogSize,
+ GUI_CheckForUpdatesNextCheck,
+
+ Security_ClearClipboard,
+ Security_ClearClipboardTimeout,
+ Security_ClearSearch,
+ Security_ClearSearchTimeout,
+ Security_HideNotes,
+ Security_LockDatabaseIdle,
+ Security_LockDatabaseIdleSeconds,
+ Security_LockDatabaseMinimize,
+ Security_LockDatabaseScreenLock,
+ Security_RelockAutoType,
+ Security_PasswordsRepeat,
+ Security_PasswordsCleartext,
+ Security_PasswordEmptyNoDots,
+ Security_HidePasswordPreviewPanel,
+ Security_AutoTypeAsk,
+ Security_IconDownloadFallback,
+ Security_ResetTouchId,
+ Security_ResetTouchIdTimeout,
+ Security_ResetTouchIdScreenlock,
+
+ Browser_Enabled,
+ Browser_ShowNotification,
+ Browser_BestMatchOnly,
+ Browser_UnlockDatabase,
+ Browser_MatchUrlScheme,
+ Browser_SortByUsername,
+ Browser_SupportBrowserProxy,
+ Browser_UseCustomProxy,
+ Browser_CustomProxyLocation,
+ Browser_UpdateBinaryPath,
+ Browser_AllowExpiredCredentials,
+ Browser_AlwaysAllowAccess,
+ Browser_AlwaysAllowUpdate,
+ Browser_HttpAuthPermission,
+ Browser_SearchInAllDatabases,
+ Browser_SupportKphFields,
+ Browser_NoMigrationPrompt,
+
+ SSHAgent_Enabled,
+ SSHAgent_UseOpenSSH,
+ SSHAgent_AuthSockOverride,
+
+ FdoSecrets_Enabled,
+ FdoSecrets_ShowNotification,
+ FdoSecrets_NoConfirmDeleteItem,
+
+ KeeShare_QuietSuccess,
+ KeeShare_Own,
+ KeeShare_Foreign,
+ KeeShare_Active,
+ KeeShare_LastDir,
+ KeeShare_LastKeyDir,
+ KeeShare_LastShareDir,
+
+ PasswordGenerator_LowerCase,
+ PasswordGenerator_UpperCase,
+ PasswordGenerator_Numbers,
+ PasswordGenerator_EASCII,
+ PasswordGenerator_AdvancedMode,
+ PasswordGenerator_SpecialChars,
+ PasswordGenerator_AdditionalChars,
+ PasswordGenerator_Braces,
+ PasswordGenerator_Punctuation,
+ PasswordGenerator_Quotes,
+ PasswordGenerator_Dashes,
+ PasswordGenerator_Math,
+ PasswordGenerator_Logograms,
+ PasswordGenerator_ExcludedChars,
+ PasswordGenerator_ExcludeAlike,
+ PasswordGenerator_EnsureEvery,
+ PasswordGenerator_Length,
+ PasswordGenerator_WordCount,
+ PasswordGenerator_WordSeparator,
+ PasswordGenerator_WordList,
+ PasswordGenerator_WordCase,
+ PasswordGenerator_Type,
+
+ Messages_NoLegacyKeyFileWarning,
+ Messages_Qt55CompatibilityWarning,
+
+ // Special internal value
+ Deleted
+ };
+
~Config() override;
- QVariant get(const QString& key);
- QVariant get(const QString& key, const QVariant& defaultValue);
+ QVariant get(ConfigKey key);
QString getFileName();
- void set(const QString& key, const QVariant& value);
+ void set(ConfigKey key, const QVariant& value);
+ void remove(ConfigKey key);
bool hasAccessError();
void sync();
void resetToDefaults();
@@ -46,17 +195,18 @@ public:
static void createTempFileInstance();
signals:
- void changed(const QString& key);
+ void changed(ConfigKey key);
private:
- Config(const QString& fileName, QObject* parent);
+ Config(const QString& fileName, QObject* parent = nullptr);
explicit Config(QObject* parent);
- void init(const QString& fileName);
- void upgrade();
+ void init(const QString& configFileName, const QString& localConfigFileName = "");
+ void migrate();
static QPointer<Config> m_instance;
QScopedPointer<QSettings> m_settings;
+ QScopedPointer<QSettings> m_localSettings;
QHash<QString, QVariant> m_defaults;
};
diff --git a/src/core/Group.cpp b/src/core/Group.cpp
index eb795f950..acb6d114d 100644
--- a/src/core/Group.cpp
+++ b/src/core/Group.cpp
@@ -364,7 +364,7 @@ void Group::setExpanded(bool expanded)
{
if (m_data.isExpanded != expanded) {
m_data.isExpanded = expanded;
- if (config()->get("IgnoreGroupExpansion").toBool()) {
+ if (config()->get(Config::IgnoreGroupExpansion).toBool()) {
updateTimeinfo();
return;
}
@@ -1112,7 +1112,7 @@ void Group::applyGroupIconOnCreateTo(Entry* entry)
{
Q_ASSERT(entry);
- if (!config()->get("UseGroupIconOnEntryCreation").toBool()) {
+ if (!config()->get(Config::UseGroupIconOnEntryCreation).toBool()) {
return;
}
diff --git a/src/core/IconDownloader.cpp b/src/core/IconDownloader.cpp
index 1d9bd01ad..1dccc554c 100644
--- a/src/core/IconDownloader.cpp
+++ b/src/core/IconDownloader.cpp
@@ -103,7 +103,7 @@ void IconDownloader::setUrl(const QString& entryUrl)
}
// Start with the "fallback" url (if enabled) to try to get the best favicon
- if (config()->get("security/IconDownloadFallback", false).toBool()) {
+ if (config()->get(Config::Security_IconDownloadFallback).toBool()) {
QUrl fallbackUrl = QUrl("https://icons.duckduckgo.com");
fallbackUrl.setPath("/ip3/" + QUrl::toPercentEncoding(fullyQualifiedDomain) + ".ico");
m_urlsToTry.append(fallbackUrl);
@@ -131,7 +131,7 @@ void IconDownloader::download()
}
if (!m_timeout.isActive()) {
- int timeout = config()->get("FaviconDownloadTimeout", 10).toInt();
+ int timeout = config()->get(Config::FaviconDownloadTimeout).toInt();
m_timeout.start(timeout * 1000);
// Use the first URL to start the download process
diff --git a/src/core/PasswordGenerator.h b/src/core/PasswordGenerator.h
index 20681c233..308142563 100644
--- a/src/core/PasswordGenerator.h
+++ b/src/core/PasswordGenerator.h
@@ -67,23 +67,9 @@ public:
QString generatePassword() const;
- static const int DefaultLength = 16;
+ static const int DefaultLength = 32;
static const char* DefaultAdditionalChars;
static const char* DefaultExcludedChars;
- static constexpr bool DefaultLower = (DefaultCharset & LowerLetters) != 0;
- static constexpr bool DefaultUpper = (DefaultCharset & UpperLetters) != 0;
- static constexpr bool DefaultNumbers = (DefaultCharset & Numbers) != 0;
- static constexpr bool DefaultSpecial = (DefaultCharset & SpecialCharacters) != 0;
- static constexpr bool DefaultAdvancedMode = (DefaultFlags & AdvancedMode) != 0;
- static constexpr bool DefaultBraces = (DefaultCharset & Braces) != 0;
- static constexpr bool DefaultPunctuation = (DefaultCharset & Punctuation) != 0;
- static constexpr bool DefaultQuotes = (DefaultCharset & Quotes) != 0;
- static constexpr bool DefaultDashes = (DefaultCharset & Dashes) != 0;
- static constexpr bool DefaultMath = (DefaultCharset & Math) != 0;
- static constexpr bool DefaultLogograms = (DefaultCharset & Logograms) != 0;
- static constexpr bool DefaultEASCII = (DefaultCharset & EASCII) != 0;
- static constexpr bool DefaultLookAlike = (DefaultFlags & ExcludeLookAlike) != 0;
- static constexpr bool DefaultFromEveryGroup = (DefaultFlags & CharFromEveryGroup) != 0;
private:
QVector<PasswordGroup> passwordGroups() const;
diff --git a/src/core/Resources.cpp b/src/core/Resources.cpp
index 90bc117ae..5f13a7cdf 100644
--- a/src/core/Resources.cpp
+++ b/src/core/Resources.cpp
@@ -217,7 +217,7 @@ bool Resources::testResourceDir(const QString& dir)
bool Resources::useDarkIcon()
{
- return config()->get("GUI/DarkTrayIcon").toBool();
+ return config()->get(Config::GUI_DarkTrayIcon).toBool();
}
Resources* Resources::instance()
diff --git a/src/core/Translator.cpp b/src/core/Translator.cpp
index 28dc6aa68..e90d63837 100644
--- a/src/core/Translator.cpp
+++ b/src/core/Translator.cpp
@@ -35,7 +35,7 @@
void Translator::installTranslators()
{
QStringList languages;
- QString languageSetting = config()->get("GUI/Language").toString();
+ QString languageSetting = config()->get(Config::GUI_Language).toString();
if (languageSetting.isEmpty() || languageSetting == "system") {
// NOTE: this is a workaround for the terrible way Qt loads languages
// using the QLocale::uiLanguages() approach. Instead, we search each