From 2de5a9d2812c0e4f026453cd98a85cf2b4217eaf Mon Sep 17 00:00:00 2001 From: Claudio Bantaloukas Date: Sat, 24 Dec 2016 01:04:43 +0100 Subject: [PATCH] Lock database when OS is locked (Windows, DBus, macOS implementations) #134 --- src/CMakeLists.txt | 22 ++++++++ src/core/ScreenLockListener.cpp | 11 ++++ src/core/ScreenLockListener.h | 21 ++++++++ src/core/ScreenLockListenerDBus.cpp | 66 ++++++++++++++++++++++++ src/core/ScreenLockListenerDBus.h | 18 +++++++ src/core/ScreenLockListenerMac.cpp | 41 +++++++++++++++ src/core/ScreenLockListenerMac.h | 24 +++++++++ src/core/ScreenLockListenerPrivate.cpp | 26 ++++++++++ src/core/ScreenLockListenerPrivate.h | 19 +++++++ src/core/ScreenLockListenerWin.cpp | 69 ++++++++++++++++++++++++++ src/core/ScreenLockListenerWin.h | 21 ++++++++ src/gui/MainWindow.cpp | 10 ++++ src/gui/MainWindow.h | 3 ++ src/gui/SettingsWidget.cpp | 4 ++ src/gui/SettingsWidgetGeneral.ui | 7 +++ 15 files changed, 362 insertions(+) create mode 100644 src/core/ScreenLockListener.cpp create mode 100644 src/core/ScreenLockListener.h create mode 100644 src/core/ScreenLockListenerDBus.cpp create mode 100644 src/core/ScreenLockListenerDBus.h create mode 100644 src/core/ScreenLockListenerMac.cpp create mode 100644 src/core/ScreenLockListenerMac.h create mode 100644 src/core/ScreenLockListenerPrivate.cpp create mode 100644 src/core/ScreenLockListenerPrivate.h create mode 100644 src/core/ScreenLockListenerWin.cpp create mode 100644 src/core/ScreenLockListenerWin.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 42cbe36bb..dfdf96c8a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -48,6 +48,10 @@ set(keepassx_SOURCES core/PasswordGenerator.cpp core/PassphraseGenerator.cpp core/SignalMultiplexer.cpp + core/ScreenLockListener.cpp + core/ScreenLockListener.h + core/ScreenLockListenerPrivate.h + core/ScreenLockListenerPrivate.cpp core/TimeDelta.cpp core/TimeInfo.cpp core/ToDbExporter.cpp @@ -136,6 +140,24 @@ set(keepassx_SOURCES totp/totp.h totp/totp.cpp ) +if(APPLE) + set(keepassx_SOURCES ${keepassx_SOURCES} + core/ScreenLockListenerMac.h + core/ScreenLockListenerMac.cpp + ) +endif() +if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") + set(keepassx_SOURCES ${keepassx_SOURCES} + core/ScreenLockListenerDBus.h + core/ScreenLockListenerDBus.cpp + ) +endif() +if(MINGW) + set(keepassx_SOURCES ${keepassx_SOURCES} + core/ScreenLockListenerWin.h + core/ScreenLockListenerWin.cpp + ) +endif() set(keepassx_SOURCES_MAINEXE main.cpp diff --git a/src/core/ScreenLockListener.cpp b/src/core/ScreenLockListener.cpp new file mode 100644 index 000000000..2c17dfa14 --- /dev/null +++ b/src/core/ScreenLockListener.cpp @@ -0,0 +1,11 @@ +#include "ScreenLockListener.h" +#include "ScreenLockListenerPrivate.h" + +ScreenLockListener::ScreenLockListener(QWidget* parent): + QObject(parent){ + m_listener = ScreenLockListenerPrivate::instance(parent); + connect(m_listener,SIGNAL(screenLocked()), this,SIGNAL(screenLocked())); +} + +ScreenLockListener::~ScreenLockListener(){ +} diff --git a/src/core/ScreenLockListener.h b/src/core/ScreenLockListener.h new file mode 100644 index 000000000..7339054f9 --- /dev/null +++ b/src/core/ScreenLockListener.h @@ -0,0 +1,21 @@ +#ifndef SCREENLOCKLISTENER_H +#define SCREENLOCKLISTENER_H +#include + +class ScreenLockListenerPrivate; + +class ScreenLockListener : public QObject { + Q_OBJECT + +public: + ScreenLockListener(QWidget* parent=NULL); + ~ScreenLockListener(); + +Q_SIGNALS: + void screenLocked(); + +private: + ScreenLockListenerPrivate* m_listener; +}; + +#endif // SCREENLOCKLISTENER_H diff --git a/src/core/ScreenLockListenerDBus.cpp b/src/core/ScreenLockListenerDBus.cpp new file mode 100644 index 000000000..283b1e27e --- /dev/null +++ b/src/core/ScreenLockListenerDBus.cpp @@ -0,0 +1,66 @@ +#include "ScreenLockListenerDBus.h" + +#include +#include +#include + +ScreenLockListenerDBus::ScreenLockListenerDBus(QWidget *parent): + ScreenLockListenerMac(parent) +{ + QDBusConnection sessionBus = QDBusConnection::sessionBus(); + QDBusConnection systemBus = QDBusConnection::systemBus(); + sessionBus.connect( + "org.gnome.SessionManager", // service + "/org/gnome/SessionManager/Presence", // path + "org.gnome.SessionManager.Presence", // interface + "StatusChanged", // signal name + this, //receiver + SLOT(gnomeSessionStatusChanged(uint))); + + systemBus.connect( + "org.freedesktop.login1", // service + "/org/freedesktop/login1", // path + "org.freedesktop.login1.Manager", // interface + "PrepareForSleep", // signal name + this, //receiver + SLOT(logindPrepareForSleep(bool))); + + sessionBus.connect( + "com.canonical.Unity", // service + "/com/canonical/Unity/Session", // path + "com.canonical.Unity.Session", // interface + "Locked", // signal name + this, //receiver + SLOT(unityLocked())); + + /* Currently unable to get the current user session from login1.Manager + QDBusInterface login1_manager_iface("org.freedesktop.login1", "/org/freedesktop/login1", + "org.freedesktop.login1.Manager", systemBus); + if(login1_manager_iface.isValid()){ + qint64 my_pid = QCoreApplication::applicationPid(); + QDBusReply reply = login1_manager_iface.call("GetSessionByPID",static_cast(my_pid)); + if (reply.isValid()){ + QString current_session = reply.value(); + } + } + */ +} + +void ScreenLockListenerDBus::gnomeSessionStatusChanged(uint status) +{ + if(status != 0){ + Q_EMIT screenLocked(); + } +} + +void ScreenLockListenerDBus::logindPrepareForSleep(bool beforeSleep) +{ + if(beforeSleep){ + Q_EMIT screenLocked(); + } +} + +void ScreenLockListenerDBus::unityLocked() +{ + Q_EMIT screenLocked(); +} diff --git a/src/core/ScreenLockListenerDBus.h b/src/core/ScreenLockListenerDBus.h new file mode 100644 index 000000000..016932b8a --- /dev/null +++ b/src/core/ScreenLockListenerDBus.h @@ -0,0 +1,18 @@ +#ifndef SCREENLOCKLISTENERDBUS_H +#define SCREENLOCKLISTENERDBUS_H +#include +#include + +class ScreenLockListenerDBus : public ScreenLockListenerPrivate +{ + Q_OBJECT +public: + explicit ScreenLockListenerDBus(QWidget *parent = 0); + +private Q_SLOTS: + void gnomeSessionStatusChanged(uint status); + void logindPrepareForSleep(bool beforeSleep); + void unityLocked(); +}; + +#endif // SCREENLOCKLISTENERDBUS_H diff --git a/src/core/ScreenLockListenerMac.cpp b/src/core/ScreenLockListenerMac.cpp new file mode 100644 index 000000000..57dc2de3f --- /dev/null +++ b/src/core/ScreenLockListenerMac.cpp @@ -0,0 +1,41 @@ +#include "ScreenLockListenerMac.h" + +#include +#include + +ScreenLockListenerMac* ScreenLockListenerMac::instance(){ + static QMutex mutex; + QMutexLocker lock(&mutex); + + static ScreenLockListenerMac* m_ptr=NULL; + if (m_ptr==NULL){ + m_ptr = new ScreenLockListenerMac(); + } + return m_ptr; +} + +void ScreenLockListenerMac::notificationCenterCallBack(CFNotificationCenterRef /*center*/, void */*observer*/, + CFNotificationName /*name*/, const void */*object*/, CFDictionaryRef /*userInfo*/){ + instance()->onSignalReception(); +} + +ScreenLockListenerMac::ScreenLockListenerMac(QWidget* parent): + ScreenLockListenerPrivate(parent){ + CFNotificationCenterRef distCenter; + CFStringRef screenIsLockedSignal = CFSTR("com.apple.screenIsLocked"); + distCenter = CFNotificationCenterGetDistributedCenter(); + if (NULL == distCenter) + return; + + CFNotificationCenterAddObserver( + distCenter, + this, &ScreenLockListenerMac::notificationCenterCallBack, + screenIsLockedSignal, + NULL, + CFNotificationSuspensionBehaviorDeliverImmediately); +} + +void ScreenLockListenerMac::onSignalReception() +{ + Q_EMIT screenLocked(); +} diff --git a/src/core/ScreenLockListenerMac.h b/src/core/ScreenLockListenerMac.h new file mode 100644 index 000000000..5824339f1 --- /dev/null +++ b/src/core/ScreenLockListenerMac.h @@ -0,0 +1,24 @@ +#ifndef SCREENLOCKLISTENERMAC_H +#define SCREENLOCKLISTENERMAC_H +#include +#include + +#include + +#include "ScreenLockListenerPrivate.h" + +class ScreenLockListenerMac: public ScreenLockListenerPrivate { + Q_OBJECT + +public: + static ScreenLockListenerMac* instance(); + static void notificationCenterCallBack(CFNotificationCenterRef /*center*/, void */*observer*/, + CFNotificationName /*name*/, const void */*object*/, CFDictionaryRef /*userInfo*/); + +private: + ScreenLockListenerMac(QWidget* parent=NULL); + void onSignalReception(); + +}; + +#endif // SCREENLOCKLISTENERMAC_H diff --git a/src/core/ScreenLockListenerPrivate.cpp b/src/core/ScreenLockListenerPrivate.cpp new file mode 100644 index 000000000..e9355c7d0 --- /dev/null +++ b/src/core/ScreenLockListenerPrivate.cpp @@ -0,0 +1,26 @@ +#include "ScreenLockListenerPrivate.h" +#if defined(Q_OS_OSX) +#include "ScreenLockListenerMac.h" +#endif +#if defined(Q_OS_LINUX) +#include "ScreenLockListenerDBus.h" +#endif +#if defined(Q_OS_WIN) +#include "ScreenLockListenerWin.h" +#endif + +ScreenLockListenerPrivate::ScreenLockListenerPrivate(QWidget* parent): + QObject(parent){ +} + +ScreenLockListenerPrivate* ScreenLockListenerPrivate::instance(QWidget* parent){ +#if defined(Q_OS_OSX) + return ScreenLockListenerMac::instance(); +#endif +#if defined(Q_OS_LINUX) + return new ScreenLockListenerDBus(parent); +#endif +#if defined(Q_OS_WIN) + return new ScreenLockListenerWin(parent); +#endif +} diff --git a/src/core/ScreenLockListenerPrivate.h b/src/core/ScreenLockListenerPrivate.h new file mode 100644 index 000000000..aef20edcc --- /dev/null +++ b/src/core/ScreenLockListenerPrivate.h @@ -0,0 +1,19 @@ +#ifndef SCREENLOCKLISTENERPRIVATE_H +#define SCREENLOCKLISTENERPRIVATE_H +#include +#include + +class ScreenLockListenerPrivate : public QObject +{ + Q_OBJECT +public: + static ScreenLockListenerPrivate* instance(QWidget *parent = 0); + +protected: + ScreenLockListenerPrivate(QWidget *parent = 0); + +Q_SIGNALS: + void screenLocked(); +}; + +#endif // SCREENLOCKLISTENERPRIVATE_H diff --git a/src/core/ScreenLockListenerWin.cpp b/src/core/ScreenLockListenerWin.cpp new file mode 100644 index 000000000..349d5de58 --- /dev/null +++ b/src/core/ScreenLockListenerWin.cpp @@ -0,0 +1,69 @@ +#include "ScreenLockListenerWin.h" +#include +#include +#include + +/* + * See https://msdn.microsoft.com/en-us/library/windows/desktop/aa373196(v=vs.85).aspx + * See https://msdn.microsoft.com/en-us/library/aa383841(v=vs.85).aspx + * See https://blogs.msdn.microsoft.com/oldnewthing/20060104-50/?p=32783 + */ +ScreenLockListenerWin::ScreenLockListenerWin(QWidget *parent) : + ScreenLockListenerPrivate(parent), + QAbstractNativeEventFilter() +{ + Q_ASSERT(parent!=NULL); + // On windows, we need to register for platform specific messages and + // install a message handler for them + QCoreApplication::instance()->installNativeEventFilter(this); + + // This call requests a notification from windows when a laptop is closed + HPOWERNOTIFY hPnotify = RegisterPowerSettingNotification( + reinterpret_cast(parent->winId()), + &GUID_LIDSWITCH_STATE_CHANGE, DEVICE_NOTIFY_WINDOW_HANDLE); + m_powernotificationhandle = reinterpret_cast(hPnotify); + + // This call requests a notification for session changes + if (!WTSRegisterSessionNotification( + reinterpret_cast(parent->winId()), + NOTIFY_FOR_THIS_SESSION)){ + } +} + +ScreenLockListenerWin::~ScreenLockListenerWin() +{ + HWND h= reinterpret_cast(static_cast(parent())->winId()); + WTSUnRegisterSessionNotification(h); + + if(m_powernotificationhandle){ + UnregisterPowerSettingNotification(reinterpret_cast(m_powernotificationhandle)); + } +} + +bool ScreenLockListenerWin::nativeEventFilter(const QByteArray &eventType, void *message, long *) +{ + if(eventType == "windows_generic_MSG" || eventType == "windows_dispatcher_MSG"){ + MSG* m = static_cast(message); + if(m->message == WM_POWERBROADCAST){ + const POWERBROADCAST_SETTING* setting = reinterpret_cast(m->lParam); + if (setting->PowerSetting == GUID_LIDSWITCH_STATE_CHANGE){ + const DWORD* state = reinterpret_cast(&setting->Data); + if (*state == 0){ + Q_EMIT screenLocked(); + return true; + } + } + } + if(m->message == WM_WTSSESSION_CHANGE){ + if (m->wParam==WTS_CONSOLE_DISCONNECT){ + Q_EMIT screenLocked(); + return true; + } + if (m->wParam==WTS_SESSION_LOCK){ + Q_EMIT screenLocked(); + return true; + } + } + } + return false; +} diff --git a/src/core/ScreenLockListenerWin.h b/src/core/ScreenLockListenerWin.h new file mode 100644 index 000000000..ccf42ad70 --- /dev/null +++ b/src/core/ScreenLockListenerWin.h @@ -0,0 +1,21 @@ +#ifndef SCREENLOCKLISTENERWIN_H +#define SCREENLOCKLISTENERWIN_H +#include +#include +#include + +#include "ScreenLockListenerPrivate.h" + +class ScreenLockListenerWin : public ScreenLockListenerPrivate, public QAbstractNativeEventFilter +{ + Q_OBJECT +public: + explicit ScreenLockListenerWin(QWidget *parent = 0); + ~ScreenLockListenerWin(); + virtual bool nativeEventFilter(const QByteArray &eventType, void *message, long *) Q_DECL_OVERRIDE; + +private: + void * m_powernotificationhandle; +}; + +#endif // SCREENLOCKLISTENERWIN_H diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 0415f2b4e..65062fb79 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -329,6 +329,9 @@ MainWindow::MainWindow() connect(m_ui->tabWidget, SIGNAL(messageTab(QString,MessageWidget::MessageType)), this, SLOT(displayTabMessage(QString, MessageWidget::MessageType))); connect(m_ui->tabWidget, SIGNAL(messageDismissTab()), this, SLOT(hideTabMessage())); + m_screenLockListener = new ScreenLockListener(this); + connect(m_screenLockListener, SIGNAL(screenLocked()), SLOT(handleScreenLock())); + updateTrayIcon(); if (config()->hasAccessError()) { @@ -959,3 +962,10 @@ void MainWindow::hideYubiKeyPopup() hideGlobalMessage(); setEnabled(true); } + +void MainWindow::handleScreenLock() +{ + if (config()->get("AutoCloseOnScreenLock").toBool()){ + lockDatabasesAfterInactivity(); + } +} diff --git a/src/gui/MainWindow.h b/src/gui/MainWindow.h index f35cd4658..72082e4ed 100644 --- a/src/gui/MainWindow.h +++ b/src/gui/MainWindow.h @@ -23,6 +23,7 @@ #include #include "core/SignalMultiplexer.h" +#include "core/ScreenLockListener.h" #include "gui/DatabaseWidget.h" #include "gui/Application.h" @@ -91,6 +92,7 @@ private slots: void lockDatabasesAfterInactivity(); void repairDatabase(); void hideTabMessage(); + void handleScreenLock(); private: static void setShortcut(QAction* action, QKeySequence::StandardKey standard, int fallback = 0); @@ -112,6 +114,7 @@ private: InactivityTimer* m_inactivityTimer; int m_countDefaultAttributes; QSystemTrayIcon* m_trayIcon; + ScreenLockListener* m_screenLockListener; Q_DISABLE_COPY(MainWindow) diff --git a/src/gui/SettingsWidget.cpp b/src/gui/SettingsWidget.cpp index 716eb14f1..15f812a0d 100644 --- a/src/gui/SettingsWidget.cpp +++ b/src/gui/SettingsWidget.cpp @@ -141,6 +141,8 @@ void SettingsWidget::loadSettings() } } + m_generalUi->autoCloseOnScreenLockCheckBox->setChecked(config()->get("AutoCloseOnScreenLock").toBool()); + m_secUi->clearClipboardCheckBox->setChecked(config()->get("security/clearclipboard").toBool()); m_secUi->clearClipboardSpinBox->setValue(config()->get("security/clearclipboardtimeout").toInt()); @@ -186,6 +188,8 @@ void SettingsWidget::saveSettings() config()->set("AutoTypeEntryTitleMatch", m_generalUi->autoTypeEntryTitleMatchCheckBox->isChecked()); int currentLangIndex = m_generalUi->languageComboBox->currentIndex(); + config()->set("AutoCloseOnScreenLock", m_generalUi->autoCloseOnScreenLockCheckBox->isChecked()); + config()->set("GUI/Language", m_generalUi->languageComboBox->itemData(currentLangIndex).toString()); config()->set("GUI/ShowTrayIcon", m_generalUi->systrayShowCheckBox->isChecked()); diff --git a/src/gui/SettingsWidgetGeneral.ui b/src/gui/SettingsWidgetGeneral.ui index 2fe0f4089..ef947d3f0 100644 --- a/src/gui/SettingsWidgetGeneral.ui +++ b/src/gui/SettingsWidgetGeneral.ui @@ -367,6 +367,13 @@ + + + + Close database when session is locked or lid is closed + + +