diff --git a/src/gui/Application.cpp b/src/gui/Application.cpp index 1f9543f26..4b3b488c7 100644 --- a/src/gui/Application.cpp +++ b/src/gui/Application.cpp @@ -307,13 +307,26 @@ void Application::socketReadyRead() } QStringList fileNames; - in >> fileNames; - for (const QString& fileName : asConst(fileNames)) { - const QFileInfo fInfo(fileName); - if (fInfo.isFile() && fInfo.suffix().toLower() == "kdbx") { - emit openFile(fileName); + quint32 id; + in >> id; + + // TODO: move constants to enum + switch (id) { + case 1: + in >> fileNames; + for (const QString& fileName : asConst(fileNames)) { + const QFileInfo fInfo(fileName); + if (fInfo.isFile() && fInfo.suffix().toLower() == "kdbx") { + emit openFile(fileName); + } } + + break; + case 2: + getMainWindow()->lockAllDatabases(); + break; } + socket->deleteLater(); } @@ -326,6 +339,12 @@ bool Application::isAlreadyRunning() const return config()->get(Config::SingleInstance).toBool() && m_alreadyRunning; } +/** + * Send to-open file names to the running UI instance + * + * @param fileNames - list of file names to open + * @return true if all operations succeeded (connection made, data sent, connection closed) + */ bool Application::sendFileNamesToRunningInstance(const QStringList& fileNames) { QLocalSocket client; @@ -338,13 +357,48 @@ bool Application::sendFileNamesToRunningInstance(const QStringList& fileNames) QByteArray data; QDataStream out(&data, QIODevice::WriteOnly); out.setVersion(QDataStream::Qt_5_0); - out << quint32(0) << fileNames; + out << quint32(0); // reserve space for block size + out << quint32(1); // ID for file name send. TODO: move to enum + out << fileNames; // send file names to be opened out.device()->seek(0); - out << quint32(data.size() - sizeof(quint32)); + out << quint32(data.size() - sizeof(quint32)); // replace the previous constant 0 with block size const bool writeOk = client.write(data) != -1 && client.waitForBytesWritten(WaitTimeoutMSec); client.disconnectFromServer(); - const bool disconnected = client.waitForDisconnected(WaitTimeoutMSec); + const bool disconnected = + client.state() == QLocalSocket::UnconnectedState || client.waitForDisconnected(WaitTimeoutMSec); + return writeOk && disconnected; +} + +/** + * Locks all open databases in the running instance + * + * @return true if the "please lock" signal was sent successfully + */ +bool Application::sendLockToInstance() +{ + // Make a connection to avoid SIGSEGV + QLocalSocket client; + client.connectToServer(m_socketName); + const bool connected = client.waitForConnected(WaitTimeoutMSec); + if (!connected) { + return false; + } + + // Send lock signal + QByteArray data; + QDataStream out(&data, QIODevice::WriteOnly); + out.setVersion(QDataStream::Qt_5_0); + out << quint32(0); // reserve space for block size + out << quint32(2); // ID for database lock. TODO: move to enum + out.device()->seek(0); + out << quint32(data.size() - sizeof(quint32)); // replace the previous constant 0 with block size + + // Finish gracefully + const bool writeOk = client.write(data) != -1 && client.waitForBytesWritten(WaitTimeoutMSec); + client.disconnectFromServer(); + const bool disconnected = + client.state() == QLocalSocket::UnconnectedState || client.waitForConnected(WaitTimeoutMSec); return writeOk && disconnected; } diff --git a/src/gui/Application.h b/src/gui/Application.h index 5f79f64b2..f71ee86ae 100644 --- a/src/gui/Application.h +++ b/src/gui/Application.h @@ -50,6 +50,7 @@ public: bool isDarkTheme() const; bool sendFileNamesToRunningInstance(const QStringList& fileNames); + bool sendLockToInstance(); void restart(); diff --git a/src/main.cpp b/src/main.cpp index 5f175173e..1dba5f426 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -71,6 +71,7 @@ int main(int argc, char** argv) 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 lockOption("lock", QObject::tr("lock all open databases")); QCommandLineOption keyfileOption("keyfile", QObject::tr("key file of the database"), "keyfile"); QCommandLineOption pwstdinOption("pw-stdin", QObject::tr("read password of the database from stdin")); QCommandLineOption allowScreenCaptureOption("allow-screencapture", @@ -81,6 +82,7 @@ int main(int argc, char** argv) QCommandLineOption debugInfoOption(QStringList() << "debug-info", QObject::tr("Displays debugging information.")); parser.addOption(configOption); parser.addOption(localConfigOption); + parser.addOption(lockOption); parser.addOption(keyfileOption); parser.addOption(pwstdinOption); parser.addOption(debugInfoOption); @@ -110,12 +112,23 @@ int main(int argc, char** argv) } // Process single instance and early exit if already running + // FIXME: this is a *mess* and it is entirely my fault. --wundrweapon const QStringList fileNames = parser.positionalArguments(); if (app.isAlreadyRunning()) { - if (!fileNames.isEmpty()) { - app.sendFileNamesToRunningInstance(fileNames); + if (parser.isSet(lockOption)) { + if (app.sendLockToInstance()) { + qInfo() << QObject::tr("Locked databases.").toUtf8().constData(); + } else { + qWarning() << QObject::tr("Database failed to lock.").toUtf8().constData(); + return EXIT_FAILURE; + } + } else { + if (!fileNames.isEmpty()) { + app.sendFileNamesToRunningInstance(fileNames); + } + + qWarning() << QObject::tr("Another instance of KeePassXC is already running.").toUtf8().constData(); } - qWarning() << QObject::tr("Another instance of KeePassXC is already running.").toUtf8().constData(); return EXIT_SUCCESS; }