mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2024-10-01 01:26:01 -04:00
Pass (using IPC) command line filenames to already running instance
This commit is contained in:
parent
7b9b23b143
commit
165d664524
@ -22,13 +22,15 @@
|
|||||||
#include "core/Config.h"
|
#include "core/Config.h"
|
||||||
|
|
||||||
#include <QAbstractNativeEventFilter>
|
#include <QAbstractNativeEventFilter>
|
||||||
|
#include <QFileInfo>
|
||||||
#include <QFileOpenEvent>
|
#include <QFileOpenEvent>
|
||||||
#include <QSocketNotifier>
|
|
||||||
#include <QLockFile>
|
#include <QLockFile>
|
||||||
|
#include <QSocketNotifier>
|
||||||
#include <QStandardPaths>
|
#include <QStandardPaths>
|
||||||
#include <QtNetwork/QLocalSocket>
|
#include <QtNetwork/QLocalSocket>
|
||||||
|
|
||||||
#include "autotype/AutoType.h"
|
#include "autotype/AutoType.h"
|
||||||
|
#include "core/Global.h"
|
||||||
|
|
||||||
#if defined(Q_OS_UNIX)
|
#if defined(Q_OS_UNIX)
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
@ -36,6 +38,11 @@
|
|||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
constexpr int WaitTimeoutMSec = 150;
|
||||||
|
const char BlockSizeProperty[] = "blockSize";
|
||||||
|
}
|
||||||
|
|
||||||
#if defined(Q_OS_UNIX) && !defined(Q_OS_OSX)
|
#if defined(Q_OS_UNIX) && !defined(Q_OS_OSX)
|
||||||
class XcbEventFilter : public QAbstractNativeEventFilter
|
class XcbEventFilter : public QAbstractNativeEventFilter
|
||||||
{
|
{
|
||||||
@ -105,8 +112,8 @@ Application::Application(int& argc, char** argv)
|
|||||||
// In DEBUG mode don't interfere with Release instances
|
// In DEBUG mode don't interfere with Release instances
|
||||||
identifier += "-DEBUG";
|
identifier += "-DEBUG";
|
||||||
#endif
|
#endif
|
||||||
QString socketName = identifier + ".socket";
|
|
||||||
QString lockName = identifier + ".lock";
|
QString lockName = identifier + ".lock";
|
||||||
|
m_socketName = identifier + ".socket";
|
||||||
|
|
||||||
// According to documentation we should use RuntimeLocation on *nixes, but even Qt doesn't respect
|
// According to documentation we should use RuntimeLocation on *nixes, but even Qt doesn't respect
|
||||||
// this and creates sockets in TempLocation, so let's be consistent.
|
// this and creates sockets in TempLocation, so let's be consistent.
|
||||||
@ -114,20 +121,22 @@ Application::Application(int& argc, char** argv)
|
|||||||
m_lockFile->setStaleLockTime(0);
|
m_lockFile->setStaleLockTime(0);
|
||||||
m_lockFile->tryLock();
|
m_lockFile->tryLock();
|
||||||
|
|
||||||
|
m_lockServer.setSocketOptions(QLocalServer::UserAccessOption);
|
||||||
|
connect(&m_lockServer, SIGNAL(newConnection()), this, SIGNAL(anotherInstanceStarted()));
|
||||||
|
connect(&m_lockServer, SIGNAL(newConnection()), this, SLOT(processIncomingConnection()));
|
||||||
|
|
||||||
switch (m_lockFile->error()) {
|
switch (m_lockFile->error()) {
|
||||||
case QLockFile::NoError:
|
case QLockFile::NoError:
|
||||||
// No existing lock was found, start listener
|
// No existing lock was found, start listener
|
||||||
m_lockServer.setSocketOptions(QLocalServer::UserAccessOption);
|
m_lockServer.listen(m_socketName);
|
||||||
m_lockServer.listen(socketName);
|
|
||||||
connect(&m_lockServer, SIGNAL(newConnection()), this, SIGNAL(anotherInstanceStarted()));
|
|
||||||
break;
|
break;
|
||||||
case QLockFile::LockFailedError: {
|
case QLockFile::LockFailedError: {
|
||||||
if (config()->get("SingleInstance").toBool()) {
|
if (config()->get("SingleInstance").toBool()) {
|
||||||
// Attempt to connect to the existing instance
|
// Attempt to connect to the existing instance
|
||||||
QLocalSocket client;
|
QLocalSocket client;
|
||||||
for (int i = 0; i < 3; i++) {
|
for (int i = 0; i < 3; ++i) {
|
||||||
client.connectToServer(socketName);
|
client.connectToServer(m_socketName);
|
||||||
if (client.waitForConnected(150)) {
|
if (client.waitForConnected(WaitTimeoutMSec)) {
|
||||||
// Connection succeeded, this will raise the existing window if minimized
|
// Connection succeeded, this will raise the existing window if minimized
|
||||||
client.abort();
|
client.abort();
|
||||||
m_alreadyRunning = true;
|
m_alreadyRunning = true;
|
||||||
@ -145,9 +154,7 @@ Application::Application(int& argc, char** argv)
|
|||||||
m_lockFile->removeStaleLockFile();
|
m_lockFile->removeStaleLockFile();
|
||||||
m_lockFile->tryLock();
|
m_lockFile->tryLock();
|
||||||
// start the listen server
|
// start the listen server
|
||||||
m_lockServer.setSocketOptions(QLocalServer::UserAccessOption);
|
m_lockServer.listen(m_socketName);
|
||||||
m_lockServer.listen(socketName);
|
|
||||||
connect(&m_lockServer, SIGNAL(newConnection()), this, SIGNAL(anotherInstanceStarted()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -187,12 +194,8 @@ bool Application::event(QEvent* event)
|
|||||||
}
|
}
|
||||||
#ifdef Q_OS_MAC
|
#ifdef Q_OS_MAC
|
||||||
// restore main window when clicking on the docker icon
|
// restore main window when clicking on the docker icon
|
||||||
else if ((event->type() == QEvent::ApplicationActivate) && m_mainWindow) {
|
else if (event->type() == QEvent::ApplicationActivate) {
|
||||||
m_mainWindow->ensurePolished();
|
emit applicationActivated();
|
||||||
m_mainWindow->setWindowState(m_mainWindow->windowState() & ~Qt::WindowMinimized);
|
|
||||||
m_mainWindow->show();
|
|
||||||
m_mainWindow->raise();
|
|
||||||
m_mainWindow->activateWindow();
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -247,12 +250,54 @@ void Application::quitBySignal()
|
|||||||
m_unixSignalNotifier->setEnabled(false);
|
m_unixSignalNotifier->setEnabled(false);
|
||||||
char buf;
|
char buf;
|
||||||
Q_UNUSED(::read(unixSignalSocket[1], &buf, sizeof(buf)));
|
Q_UNUSED(::read(unixSignalSocket[1], &buf, sizeof(buf)));
|
||||||
|
emit quitSignalReceived();
|
||||||
if (nullptr != m_mainWindow)
|
|
||||||
static_cast<MainWindow*>(m_mainWindow)->appExit();
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
void Application::processIncomingConnection()
|
||||||
|
{
|
||||||
|
if (m_lockServer.hasPendingConnections()) {
|
||||||
|
QLocalSocket* socket = m_lockServer.nextPendingConnection();
|
||||||
|
socket->setProperty(BlockSizeProperty, 0);
|
||||||
|
connect(socket, SIGNAL(readyRead()), this, SLOT(socketReadyRead()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::socketReadyRead()
|
||||||
|
{
|
||||||
|
QLocalSocket* socket = qobject_cast<QLocalSocket*>(sender());
|
||||||
|
if (!socket) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDataStream in(socket);
|
||||||
|
in.setVersion(QDataStream::Qt_5_0);
|
||||||
|
|
||||||
|
int blockSize = socket->property(BlockSizeProperty).toInt();
|
||||||
|
if (blockSize == 0) {
|
||||||
|
// Relies on the fact that QDataStream format streams a quint32 into sizeof(quint32) bytes
|
||||||
|
if (socket->bytesAvailable() < qint64(sizeof(quint32))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
in >> blockSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (socket->bytesAvailable() < blockSize || in.atEnd()) {
|
||||||
|
socket->setProperty(BlockSizeProperty, blockSize);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList fileNames;
|
||||||
|
in >> fileNames;
|
||||||
|
for (const QString& fileName: asConst(fileNames)) {
|
||||||
|
const QFileInfo fInfo(fileName);
|
||||||
|
if (fInfo.isFile() && fInfo.suffix().toLower() == "kdbx") {
|
||||||
|
emit openFile(fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
socket->deleteLater();
|
||||||
|
}
|
||||||
|
|
||||||
bool Application::isAlreadyRunning() const
|
bool Application::isAlreadyRunning() const
|
||||||
{
|
{
|
||||||
#ifdef QT_DEBUG
|
#ifdef QT_DEBUG
|
||||||
@ -262,3 +307,26 @@ bool Application::isAlreadyRunning() const
|
|||||||
return config()->get("SingleInstance").toBool() && m_alreadyRunning;
|
return config()->get("SingleInstance").toBool() && m_alreadyRunning;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Application::sendFileNamesToRunningInstance(const QStringList& fileNames)
|
||||||
|
{
|
||||||
|
QLocalSocket client;
|
||||||
|
client.connectToServer(m_socketName);
|
||||||
|
const bool connected = client.waitForConnected(WaitTimeoutMSec);
|
||||||
|
if (!connected) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QByteArray data;
|
||||||
|
QDataStream out(&data, QIODevice::WriteOnly);
|
||||||
|
out.setVersion(QDataStream::Qt_5_0);
|
||||||
|
out << quint32(0)
|
||||||
|
<< fileNames;
|
||||||
|
out.device()->seek(0);
|
||||||
|
out << quint32(data.size() - sizeof(quint32));
|
||||||
|
|
||||||
|
const bool writeOk = client.write(data) != -1 && client.waitForBytesWritten(WaitTimeoutMSec);
|
||||||
|
client.disconnectFromServer();
|
||||||
|
const bool disconnected = client.waitForDisconnected(WaitTimeoutMSec);
|
||||||
|
return writeOk && disconnected;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -39,14 +39,20 @@ public:
|
|||||||
bool event(QEvent* event) override;
|
bool event(QEvent* event) override;
|
||||||
bool isAlreadyRunning() const;
|
bool isAlreadyRunning() const;
|
||||||
|
|
||||||
|
bool sendFileNamesToRunningInstance(const QStringList& fileNames);
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void openFile(const QString& filename);
|
void openFile(const QString& filename);
|
||||||
void anotherInstanceStarted();
|
void anotherInstanceStarted();
|
||||||
|
void applicationActivated();
|
||||||
|
void quitSignalReceived();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
#if defined(Q_OS_UNIX)
|
#if defined(Q_OS_UNIX)
|
||||||
void quitBySignal();
|
void quitBySignal();
|
||||||
#endif
|
#endif
|
||||||
|
void processIncomingConnection();
|
||||||
|
void socketReadyRead();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QWidget* m_mainWindow;
|
QWidget* m_mainWindow;
|
||||||
@ -63,6 +69,7 @@ private:
|
|||||||
bool m_alreadyRunning;
|
bool m_alreadyRunning;
|
||||||
QLockFile* m_lockFile;
|
QLockFile* m_lockFile;
|
||||||
QLocalServer m_lockServer;
|
QLocalServer m_lockServer;
|
||||||
|
QString m_socketName;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // KEEPASSX_APPLICATION_H
|
#endif // KEEPASSX_APPLICATION_H
|
||||||
|
@ -881,11 +881,7 @@ void MainWindow::toggleWindow()
|
|||||||
if ((QApplication::activeWindow() == this) && isVisible() && !isMinimized()) {
|
if ((QApplication::activeWindow() == this) && isVisible() && !isMinimized()) {
|
||||||
hideWindow();
|
hideWindow();
|
||||||
} else {
|
} else {
|
||||||
ensurePolished();
|
bringToFront();
|
||||||
setWindowState(windowState() & ~Qt::WindowMinimized);
|
|
||||||
show();
|
|
||||||
raise();
|
|
||||||
activateWindow();
|
|
||||||
|
|
||||||
#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC) && !defined(QT_NO_DBUS) && (QT_VERSION < QT_VERSION_CHECK(5, 9, 0))
|
#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC) && !defined(QT_NO_DBUS) && (QT_VERSION < QT_VERSION_CHECK(5, 9, 0))
|
||||||
// re-register global D-Bus menu (needed on Ubuntu with Unity)
|
// re-register global D-Bus menu (needed on Ubuntu with Unity)
|
||||||
@ -993,6 +989,15 @@ void MainWindow::hideYubiKeyPopup()
|
|||||||
setEnabled(true);
|
setEnabled(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MainWindow::bringToFront()
|
||||||
|
{
|
||||||
|
ensurePolished();
|
||||||
|
setWindowState(windowState() & ~Qt::WindowMinimized);
|
||||||
|
show();
|
||||||
|
raise();
|
||||||
|
activateWindow();
|
||||||
|
}
|
||||||
|
|
||||||
void MainWindow::handleScreenLock()
|
void MainWindow::handleScreenLock()
|
||||||
{
|
{
|
||||||
if (config()->get("security/lockdatabasescreenlock").toBool()){
|
if (config()->get("security/lockdatabasescreenlock").toBool()){
|
||||||
@ -1030,7 +1035,7 @@ void MainWindow::dropEvent(QDropEvent* event)
|
|||||||
const QMimeData* mimeData = event->mimeData();
|
const QMimeData* mimeData = event->mimeData();
|
||||||
if (mimeData->hasUrls()) {
|
if (mimeData->hasUrls()) {
|
||||||
const QStringList kdbxFiles = kdbxFilesFromUrls(mimeData->urls());
|
const QStringList kdbxFiles = kdbxFilesFromUrls(mimeData->urls());
|
||||||
for(const QString &kdbxFile: kdbxFiles) {
|
for (const QString& kdbxFile: kdbxFiles) {
|
||||||
openDatabase(kdbxFile);
|
openDatabase(kdbxFile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,6 +61,7 @@ public slots:
|
|||||||
void hideGlobalMessage();
|
void hideGlobalMessage();
|
||||||
void showYubiKeyPopup();
|
void showYubiKeyPopup();
|
||||||
void hideYubiKeyPopup();
|
void hideYubiKeyPopup();
|
||||||
|
void bringToFront();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void closeEvent(QCloseEvent* event) override;
|
void closeEvent(QCloseEvent* event) override;
|
||||||
|
61
src/main.cpp
61
src/main.cpp
@ -57,22 +57,6 @@ int main(int argc, char** argv)
|
|||||||
// don't set organizationName as that changes the return value of
|
// don't set organizationName as that changes the return value of
|
||||||
// QStandardPaths::writableLocation(QDesktopServices::DataLocation)
|
// QStandardPaths::writableLocation(QDesktopServices::DataLocation)
|
||||||
|
|
||||||
if (app.isAlreadyRunning()) {
|
|
||||||
qWarning() << QCoreApplication::translate("Main", "Another instance of KeePassXC is already running.").toUtf8().constData();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
QApplication::setQuitOnLastWindowClosed(false);
|
|
||||||
|
|
||||||
if (!Crypto::init()) {
|
|
||||||
QString error = QCoreApplication::translate("Main",
|
|
||||||
"Fatal error while testing the cryptographic functions.");
|
|
||||||
error.append("\n");
|
|
||||||
error.append(Crypto::errorString());
|
|
||||||
MessageBox::critical(nullptr, QCoreApplication::translate("Main", "KeePassXC - Error"), error);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
QCommandLineParser parser;
|
QCommandLineParser parser;
|
||||||
parser.setApplicationDescription(QCoreApplication::translate("main", "KeePassXC - cross-platform password manager"));
|
parser.setApplicationDescription(QCoreApplication::translate("main", "KeePassXC - cross-platform password manager"));
|
||||||
parser.addPositionalArgument("filename", QCoreApplication::translate("main", "filenames of the password databases to open (*.kdbx)"), "[filename(s)]");
|
parser.addPositionalArgument("filename", QCoreApplication::translate("main", "filenames of the password databases to open (*.kdbx)"), "[filename(s)]");
|
||||||
@ -93,7 +77,26 @@ int main(int argc, char** argv)
|
|||||||
parser.addOption(pwstdinOption);
|
parser.addOption(pwstdinOption);
|
||||||
|
|
||||||
parser.process(app);
|
parser.process(app);
|
||||||
const QStringList args = parser.positionalArguments();
|
const QStringList fileNames = parser.positionalArguments();
|
||||||
|
|
||||||
|
if (app.isAlreadyRunning()) {
|
||||||
|
if (!fileNames.isEmpty()) {
|
||||||
|
app.sendFileNamesToRunningInstance(fileNames);
|
||||||
|
}
|
||||||
|
qWarning() << QCoreApplication::translate("Main", "Another instance of KeePassXC is already running.").toUtf8().constData();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
QApplication::setQuitOnLastWindowClosed(false);
|
||||||
|
|
||||||
|
if (!Crypto::init()) {
|
||||||
|
QString error = QCoreApplication::translate("Main",
|
||||||
|
"Fatal error while testing the cryptographic functions.");
|
||||||
|
error.append("\n");
|
||||||
|
error.append(Crypto::errorString());
|
||||||
|
MessageBox::critical(nullptr, QCoreApplication::translate("Main", "KeePassXC - Error"), error);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (parser.isSet(configOption)) {
|
if (parser.isSet(configOption)) {
|
||||||
Config::createConfigFromFile(parser.value(configOption));
|
Config::createConfigFromFile(parser.value(configOption));
|
||||||
@ -109,15 +112,10 @@ int main(int argc, char** argv)
|
|||||||
MainWindow mainWindow;
|
MainWindow mainWindow;
|
||||||
app.setMainWindow(&mainWindow);
|
app.setMainWindow(&mainWindow);
|
||||||
|
|
||||||
QObject::connect(&app, &Application::anotherInstanceStarted,
|
QObject::connect(&app, SIGNAL(anotherInstanceStarted()), &mainWindow, SLOT(bringToFront()));
|
||||||
[&]() {
|
QObject::connect(&app, SIGNAL(applicationActivated()), &mainWindow, SLOT(bringToFront()));
|
||||||
mainWindow.ensurePolished();
|
|
||||||
mainWindow.setWindowState(mainWindow.windowState() & ~Qt::WindowMinimized);
|
|
||||||
mainWindow.show();
|
|
||||||
mainWindow.raise();
|
|
||||||
mainWindow.activateWindow();
|
|
||||||
});
|
|
||||||
QObject::connect(&app, SIGNAL(openFile(QString)), &mainWindow, SLOT(openDatabase(QString)));
|
QObject::connect(&app, SIGNAL(openFile(QString)), &mainWindow, SLOT(openDatabase(QString)));
|
||||||
|
QObject::connect(&app, SIGNAL(quitSignalReceived()), &mainWindow, SLOT(appExit()), Qt::DirectConnection);
|
||||||
|
|
||||||
// start minimized if configured
|
// start minimized if configured
|
||||||
bool minimizeOnStartup = config()->get("GUI/MinimizeOnStartup").toBool();
|
bool minimizeOnStartup = config()->get("GUI/MinimizeOnStartup").toBool();
|
||||||
@ -130,20 +128,19 @@ int main(int argc, char** argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (config()->get("OpenPreviousDatabasesOnStartup").toBool()) {
|
if (config()->get("OpenPreviousDatabasesOnStartup").toBool()) {
|
||||||
const QStringList filenames = config()->get("LastOpenedDatabases").toStringList();
|
const QStringList fileNames = config()->get("LastOpenedDatabases").toStringList();
|
||||||
for (int ii = filenames.size()-1; ii >= 0; ii--) {
|
for (const QString& filename: fileNames) {
|
||||||
QString filename = filenames.at(ii);
|
|
||||||
if (!filename.isEmpty() && QFile::exists(filename)) {
|
if (!filename.isEmpty() && QFile::exists(filename)) {
|
||||||
mainWindow.openDatabase(filename, QString(), QString());
|
mainWindow.openDatabase(filename);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int ii=0; ii < args.length(); ii++) {
|
const bool pwstdin = parser.isSet(pwstdinOption);
|
||||||
QString filename = args[ii];
|
for (const QString& filename: fileNames) {
|
||||||
if (!filename.isEmpty() && QFile::exists(filename)) {
|
if (!filename.isEmpty() && QFile::exists(filename)) {
|
||||||
QString password;
|
QString password;
|
||||||
if (parser.isSet(pwstdinOption)) {
|
if (pwstdin) {
|
||||||
static QTextStream in(stdin, QIODevice::ReadOnly);
|
static QTextStream in(stdin, QIODevice::ReadOnly);
|
||||||
password = in.readLine();
|
password = in.readLine();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user