Skip to content
This repository was archived by the owner on Dec 9, 2021. It is now read-only.
Open
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 CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ if(WITH_TESTS)
endif(WITH_TESTS)

find_package(Qt5Core 5.2 REQUIRED)
find_package(Qt5Network 5.2 REQUIRED)
find_package(Qt5Concurrent 5.2 REQUIRED)
find_package(Qt5Widgets 5.2 REQUIRED)
find_package(Qt5Test 5.2 REQUIRED)
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ The following tools must exist within your PATH:

* make
* cmake (>= 2.8.12)
* g++ (>= 4.7) or clang++ (>= 3.0)
* g++ (>= 4.7) or clang++ (>= 3.1)

The following libraries are required:

Expand Down
3 changes: 2 additions & 1 deletion src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -163,12 +163,13 @@ qt5_wrap_ui(keepassx_SOURCES ${keepassx_FORMS})

add_library(keepassx_core STATIC ${keepassx_SOURCES})
set_target_properties(keepassx_core PROPERTIES COMPILE_DEFINITIONS KEEPASSX_BUILDING_CORE)
target_link_libraries(keepassx_core Qt5::Core Qt5::Concurrent Qt5::Widgets)
target_link_libraries(keepassx_core Qt5::Core Qt5::Network Qt5::Concurrent Qt5::Widgets)

add_executable(${PROGNAME} WIN32 MACOSX_BUNDLE ${keepassx_SOURCES_MAINEXE})
target_link_libraries(${PROGNAME}
keepassx_core
Qt5::Core
Qt5::Network
Qt5::Concurrent
Qt5::Widgets
${GCRYPT_LIBRARIES}
Expand Down
62 changes: 62 additions & 0 deletions src/gui/Application.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@

#include <QAbstractNativeEventFilter>
#include <QFileOpenEvent>
#include <QLockFile>
#include <QStandardPaths>
#include <QtNetwork/QLocalSocket>

#include "autotype/AutoType.h"

Expand All @@ -46,10 +49,64 @@ class XcbEventFilter : public QAbstractNativeEventFilter
Application::Application(int& argc, char** argv)
: QApplication(argc, argv)
, m_mainWindow(nullptr)
, alreadyRunning(false)
, lock(nullptr)
{
#if defined(Q_OS_UNIX) && !defined(Q_OS_OSX)
installNativeEventFilter(new XcbEventFilter());
#endif

QString userName = qgetenv("USER");
if (userName.isEmpty()) {
userName = qgetenv("USERNAME");
}
QString identifier = "keepassx2";
if (!userName.isEmpty()) {
identifier.append("-");
identifier.append(userName);
}
QString socketName = identifier + ".socket";
QString lockName = identifier + ".lock";

// 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.
lock = new QLockFile(QStandardPaths::writableLocation(QStandardPaths::TempLocation) + "/" + lockName);
lock->setStaleLockTime(0);
lock->tryLock();
switch (lock->error()) {
case QLockFile::NoError:
server.setSocketOptions(QLocalServer::UserAccessOption);
server.listen(socketName);
connect(&server, SIGNAL(newConnection()), this, SIGNAL(anotherInstanceStarted()));
break;
case QLockFile::LockFailedError: {
alreadyRunning = true;
// notify the other instance
// try several times, in case the other instance is still starting up
QLocalSocket client;
for (int i = 0; i < 3; i++) {
client.connectToServer(socketName);
if (client.waitForConnected(150)) {
client.abort();
break;
}
}
break;
}
default:
qWarning() << QCoreApplication::translate("Main",
"The lock file could not be created. Single-instance mode disabled.")
.toUtf8().constData();
}
}

Application::~Application()
{
server.close();
if (lock) {
lock->unlock();
delete lock;
}
}

void Application::setMainWindow(QWidget* mainWindow)
Expand Down Expand Up @@ -77,3 +134,8 @@ bool Application::event(QEvent* event)

return QApplication::event(event);
}

bool Application::isAlreadyRunning() const
{
return alreadyRunning;
}
8 changes: 8 additions & 0 deletions src/gui/Application.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,22 +20,30 @@
#define KEEPASSX_APPLICATION_H

#include <QApplication>
#include <QtNetwork/QLocalServer>
class QLockFile;

class Application : public QApplication
{
Q_OBJECT

public:
Application(int& argc, char** argv);
~Application();
void setMainWindow(QWidget* mainWindow);

bool event(QEvent* event) override;
bool isAlreadyRunning() const;

Q_SIGNALS:
void openFile(const QString& filename);
void anotherInstanceStarted();

private:
QWidget* m_mainWindow;
bool alreadyRunning;
QLockFile* lock;
QLocalServer server;
};

#endif // KEEPASSX_APPLICATION_H
13 changes: 13 additions & 0 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ int main(int argc, char** argv)
// don't set organizationName as that changes the return value of
// QStandardPaths::writableLocation(QDesktopServices::DataLocation)

if (app.isAlreadyRunning()) {
qWarning() << QCoreApplication::translate("Main", "Another instance of KeePassX 2 is already running.").toUtf8().constData();
return 0;
}

QApplication::setQuitOnLastWindowClosed(false);

if (!Crypto::init()) {
Expand Down Expand Up @@ -85,6 +90,14 @@ int main(int argc, char** argv)
mainWindow.show();
app.setMainWindow(&mainWindow);

QObject::connect(&app, &Application::anotherInstanceStarted,
[&]() {
mainWindow.ensurePolished();
mainWindow.setWindowState(mainWindow.windowState() & ~Qt::WindowMinimized);
mainWindow.show();
mainWindow.raise();
mainWindow.activateWindow();
});
QObject::connect(&app, SIGNAL(openFile(QString)), &mainWindow, SLOT(openDatabase(QString)));

if (!args.isEmpty()) {
Expand Down