Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cleanup config initialization, add local config options #5452

Merged
merged 1 commit into from
Sep 26, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/styles/dark.css
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,7 @@ p{font-family: "Noto Sans",sans-serif !important}
blockquote{color:var(--quotecolor) !important}
.quoteblock{color:var(--textcolor)}
code{color:var(--textcoloralt);background-color: var(--sidebarbackground) !important}
pre,pre>code{line-height:1.25; color:var(--textcoloralt);}
.keyseq{color:var(--textcoloralt);}


Expand Down
2 changes: 2 additions & 0 deletions docs/topics/DownloadInstall.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ image::linux_store.png[]

The Snap and Flatpak options are sandboxed applications (more secure). The Native option is installed with the operating system files. Read more about the limitations of these options here: https://keepassxc.org/docs/#faq-appsnap-yubikey[KeePassXC Snap FAQ]

NOTE: KeePassXC stores a configuration file in `~/.cache` to remember window position, recent files, and other local settings. If you mount this folder to a tmpdisk you will lose settings after reboot.

=== macOS
To install the KeePassXC app on macOS, double click on the downloaded DMG file and use the click and drag option as shown:

Expand Down
37 changes: 37 additions & 0 deletions docs/topics/UserInterface.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,41 @@ image::compact_mode_comparison.png[]

=== Keyboard Shortcuts
include::KeyboardShortcuts.adoc[tag=content, leveloffset=+1]

// tag::advanced[]
=== Command-Line Options
You can use the following command line options to tailor the application to your preferences:

----
Usage: keepassxc.exe [options] [filename(s)]
KeePassXC - cross-platform password manager

Options:
-?, -h, --help Displays help on commandline options.
--help-all Displays help including Qt specific options.
-v, --version Displays version information.
--config <config> path to a custom config file
--localconfig <localconfig> path to a custom local config file
--keyfile <keyfile> key file of the database
--pw-stdin read password of the database from stdin
--debug-info Displays debugging information.

Arguments:
filename(s) filenames of the password databases to open (*.kdbx)
----

Additionally, the following environment variables may be useful when running the application:

[grid=rows, frame=none, width=75%]
|===
|Env Var | Description

|KPXC_CONFIG | Override default path to roaming configuration file
|KPXC_CONFIG_LOCAL | Override default path to local configuration file
|SSH_AUTH_SOCKET | Path of the unix file socket that the agent uses for communication with other processes (SSH Agent)
|QT_SCALE_FACTOR [numeric] | Defines a global scale factor for the whole application, including point-sized fonts.
|QT_SCREEN_SCALE_FACTORS [list] | Specifies scale factors for each screen. See https://doc.qt.io/qt-5/highdpi.html#high-dpi-support-in-qt
|QT_SCALE_FACTOR_ROUNDING_POLICY | Control device pixel ratio rounding to the nearest integer. See https://doc.qt.io/qt-5/highdpi.html#high-dpi-support-in-qt
|===
// end::advanced[]
// end::content[]
90 changes: 51 additions & 39 deletions src/core/Config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include <QCoreApplication>
#include <QDir>
#include <QHash>
#include <QProcessEnvironment>
#include <QSettings>
#include <QSize>
#include <QStandardPaths>
Expand Down Expand Up @@ -418,49 +419,17 @@ void Config::migrate()
sync();
}

Config::Config(const QString& fileName, QObject* parent)
Config::Config(const QString& configFileName, const QString& localConfigFileName, QObject* parent)
: QObject(parent)
{
init(fileName);
init(configFileName, localConfigFileName);
}

Config::Config(QObject* parent)
: QObject(parent)
{
// Check if we are running in portable mode, if so store the config files local to the app
auto portablePath = QCoreApplication::applicationDirPath().append("/%1");
if (QFile::exists(portablePath.arg(".portable"))) {
init(portablePath.arg("config/keepassxc.ini"), portablePath.arg("config/keepassxc_local.ini"));
return;
}

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 = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
localConfigPath = QStandardPaths::writableLocation(QStandardPaths::CacheLocation);
#else
// On case-sensitive Operating Systems, force use of lowercase app directories
configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + "/keepassxc";
localConfigPath = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) + "/keepassxc";
#endif

configPath += "/keepassxc";
localConfigPath += "/keepassxc";

#ifdef QT_DEBUG
configPath += "_debug";
localConfigPath += "_debug";
#endif

configPath += ".ini";
localConfigPath += ".ini";

init(QDir::toNativeSeparators(configPath), QDir::toNativeSeparators(localConfigPath));
auto configFiles = defaultConfigFiles();
init(configFiles.first, configFiles.second);
}

Config::~Config()
Expand Down Expand Up @@ -488,6 +457,45 @@ void Config::init(const QString& configFileName, const QString& localConfigFileN
connect(qApp, &QCoreApplication::aboutToQuit, this, &Config::sync);
}

QPair<QString, QString> Config::defaultConfigFiles()
{
// Check if we are running in portable mode, if so store the config files local to the app
auto portablePath = QCoreApplication::applicationDirPath().append("/%1");
if (QFile::exists(portablePath.arg(".portable"))) {
return {portablePath.arg("config/keepassxc.ini"), portablePath.arg("config/keepassxc_local.ini")};
}

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 = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
localConfigPath = QStandardPaths::writableLocation(QStandardPaths::CacheLocation);
#else
// On case-sensitive Operating Systems, force use of lowercase app directories
configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + "/keepassxc";
localConfigPath = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) + "/keepassxc";
#endif

QString suffix;
#ifdef QT_DEBUG
suffix = "_debug";
#endif

configPath += QString("/keepassxc%1.ini").arg(suffix);
localConfigPath += QString("/keepassxc%1.ini").arg(suffix);

// Allow overriding the default location with env vars
const auto& env = QProcessEnvironment::systemEnvironment();
configPath = env.value("KPXC_CONFIG", configPath);
localConfigPath = env.value("KPXC_CONFIG_LOCAL", localConfigPath);

return {QDir::toNativeSeparators(configPath), QDir::toNativeSeparators(localConfigPath)};
}

Config* Config::instance()
{
if (!m_instance) {
Expand All @@ -497,12 +505,16 @@ Config* Config::instance()
return m_instance;
}

void Config::createConfigFromFile(const QString& file)
void Config::createConfigFromFile(const QString& configFileName, const QString& localConfigFileName)
{
if (m_instance) {
delete m_instance;
}
m_instance = new Config(file, qApp);

auto defaultFiles = defaultConfigFiles();
m_instance = new Config(configFileName.isEmpty() ? defaultFiles.first : configFileName,
localConfigFileName.isEmpty() ? defaultFiles.second : localConfigFileName,
qApp);
}

void Config::createTempFileInstance()
Expand All @@ -514,7 +526,7 @@ void Config::createTempFileInstance()
bool openResult = tmpFile->open();
Q_ASSERT(openResult);
Q_UNUSED(openResult);
m_instance = new Config(tmpFile->fileName(), qApp);
m_instance = new Config(tmpFile->fileName(), "", qApp);
tmpFile->setParent(m_instance);
}

Expand Down
7 changes: 4 additions & 3 deletions src/core/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -198,17 +198,18 @@ class Config : public QObject
void resetToDefaults();

static Config* instance();
static void createConfigFromFile(const QString& file);
static void createConfigFromFile(const QString& configFileName, const QString& localConfigFileName = {});
static void createTempFileInstance();

signals:
void changed(ConfigKey key);

private:
Config(const QString& fileName, QObject* parent = nullptr);
Config(const QString& configFileName, const QString& localConfigFileName, QObject* parent);
explicit Config(QObject* parent);
void init(const QString& configFileName, const QString& localConfigFileName = "");
void init(const QString& configFileName, const QString& localConfigFileName);
void migrate();
static QPair<QString, QString> defaultConfigFiles();

static QPointer<Config> m_instance;

Expand Down
4 changes: 4 additions & 0 deletions src/gui/MainWindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1269,6 +1269,8 @@ bool MainWindow::saveLastDatabases()
void MainWindow::updateTrayIcon()
{
if (isTrayIconEnabled()) {
QApplication::setQuitOnLastWindowClosed(false);

if (!m_trayIcon) {
m_trayIcon = new QSystemTrayIcon(this);
auto* menu = new QMenu(this);
Expand Down Expand Up @@ -1307,6 +1309,8 @@ void MainWindow::updateTrayIcon()
m_trayIcon->setIcon(resources()->trayIconLocked());
}
} else {
QApplication::setQuitOnLastWindowClosed(true);

if (m_trayIcon) {
m_trayIcon->hide();
delete m_trayIcon;
Expand Down
53 changes: 26 additions & 27 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,51 +56,47 @@ int main(int argc, char** argv)
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
#endif

Application app(argc, argv);
Application::setApplicationName("KeePassXC");
Application::setApplicationVersion(KEEPASSXC_VERSION);
app.setProperty("KPXC_QUALIFIED_APPNAME", "org.keepassxc.KeePassXC");
app.applyTheme();
#if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0)
QGuiApplication::setDesktopFileName(app.property("KPXC_QUALIFIED_APPNAME").toString() + QStringLiteral(".desktop"));
#endif

// don't set organizationName as that changes the return value of
// QStandardPaths::writableLocation(QDesktopServices::DataLocation)
Bootstrap::bootstrapApplication();

QCommandLineParser parser;
parser.setApplicationDescription(QObject::tr("KeePassXC - cross-platform password manager"));
parser.addPositionalArgument(
"filename", QObject::tr("filenames of the password databases to open (*.kdbx)"), "[filename(s)]");
"filename(s)", QObject::tr("filenames of the password databases to open (*.kdbx)"), "[filename(s)]");

QCommandLineOption configOption("config", QObject::tr("path to a custom config file"), "config");
QCommandLineOption localConfigOption(
"localconfig", QObject::tr("path to a custom local config file"), "localconfig");
QCommandLineOption keyfileOption("keyfile", QObject::tr("key file of the database"), "keyfile");
QCommandLineOption pwstdinOption("pw-stdin", QObject::tr("read password of the database from stdin"));
// This is needed under Windows where clients send --parent-window parameter with Native Messaging connect method
QCommandLineOption parentWindowOption(QStringList() << "pw"
<< "parent-window",
QObject::tr("Parent window handle"),
"handle");

QCommandLineOption helpOption = parser.addHelpOption();
QCommandLineOption versionOption = parser.addVersionOption();
QCommandLineOption debugInfoOption(QStringList() << "debug-info", QObject::tr("Displays debugging information."));
parser.addOption(configOption);
parser.addOption(localConfigOption);
parser.addOption(keyfileOption);
parser.addOption(pwstdinOption);
parser.addOption(parentWindowOption);
parser.addOption(debugInfoOption);

Application app(argc, argv);
// don't set organizationName as that changes the return value of
// QStandardPaths::writableLocation(QDesktopServices::DataLocation)
Application::setApplicationName("KeePassXC");
Application::setApplicationVersion(KEEPASSXC_VERSION);
app.setProperty("KPXC_QUALIFIED_APPNAME", "org.keepassxc.KeePassXC");

parser.process(app);

// Don't try and do anything with the application if we're only showing the help / version
// Exit early if we're only showing the help / version
if (parser.isSet(versionOption) || parser.isSet(helpOption)) {
return EXIT_SUCCESS;
}

const QStringList fileNames = parser.positionalArguments();
// Process config file options early
if (parser.isSet(configOption) || parser.isSet(localConfigOption)) {
Config::createConfigFromFile(parser.value(configOption), parser.value(localConfigOption));
}

// Process single instance and early exit if already running
const QStringList fileNames = parser.positionalArguments();
if (app.isAlreadyRunning()) {
if (!fileNames.isEmpty()) {
app.sendFileNamesToRunningInstance(fileNames);
Expand All @@ -109,7 +105,14 @@ int main(int argc, char** argv)
return EXIT_SUCCESS;
}

QApplication::setQuitOnLastWindowClosed(false);
// Apply the configured theme before creating any GUI elements
app.applyTheme();

#if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0)
QGuiApplication::setDesktopFileName(app.property("KPXC_QUALIFIED_APPNAME").toString() + QStringLiteral(".desktop"));
#endif

Bootstrap::bootstrapApplication();

if (!Crypto::init()) {
QString error = QObject::tr("Fatal error while testing the cryptographic functions.");
Expand All @@ -128,10 +131,6 @@ int main(int argc, char** argv)
return EXIT_SUCCESS;
}

if (parser.isSet(configOption)) {
Config::createConfigFromFile(parser.value(configOption));
}

MainWindow mainWindow;
QObject::connect(&app, SIGNAL(anotherInstanceStarted()), &mainWindow, SLOT(bringToFront()));
QObject::connect(&app, SIGNAL(applicationActivated()), &mainWindow, SLOT(bringToFront()));
Expand Down