Skip to content

Commit

Permalink
Cleanup config initialization, add local config options
Browse files Browse the repository at this point in the history
* Fix #5313, allow specifying local config path using environment variable and command line flag
* Add command line flag `--localconfig <path>` to specify a file path to use for the local configuration settings.
* Add environment variable support to set config files paths: `KPXC_CONFIG` and `KPXC_CONFIG_LOCAL` to override default locations.
* Reorder startup sequence to load specified config files earlier to allow for theme settings and other early options to be picked up.
* Removed old command line option `--pw`, no longer used.

* Attempt a fix of application not closing when last window is gone. Only set `QApplication::setQuitOnLastWindowClosed(true)` when tray icon is enabled instead of always.
  • Loading branch information
droidmonkey committed Sep 20, 2020
1 parent 3c5bd0f commit 0465676
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 69 deletions.
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

0 comments on commit 0465676

Please sign in to comment.