From a8edad1e27aeefd41f3c3584b961b24543d3f773 Mon Sep 17 00:00:00 2001 From: Felix Geyer Date: Tue, 7 Jan 2014 21:56:58 +0100 Subject: [PATCH] Add option to lock databases after user inactivity. Closes #62 --- src/CMakeLists.txt | 2 + src/core/Config.cpp | 2 + src/core/InactivityTimer.cpp | 73 +++++++++++++++++++++++++++++++ src/core/InactivityTimer.h | 51 +++++++++++++++++++++ src/gui/MainWindow.cpp | 23 ++++++++++ src/gui/MainWindow.h | 4 ++ src/gui/SettingsWidget.cpp | 8 ++++ src/gui/SettingsWidgetSecurity.ui | 20 +++++++++ 8 files changed, 183 insertions(+) create mode 100644 src/core/InactivityTimer.cpp create mode 100644 src/core/InactivityTimer.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 176ecb34c..327ffd412 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -39,6 +39,7 @@ set(keepassx_SOURCES core/FilePath.cpp core/Global.h core/Group.cpp + core/InactivityTimer.cpp core/ListDeleter.h core/Metadata.cpp core/PasswordGenerator.cpp @@ -133,6 +134,7 @@ set(keepassx_MOC core/EntryAttachments.h core/EntryAttributes.h core/Group.h + core/InactivityTimer.h core/Metadata.h core/qsavefile.h gui/AboutDialog.h diff --git a/src/core/Config.cpp b/src/core/Config.cpp index 9fe851675..12f88f942 100644 --- a/src/core/Config.cpp +++ b/src/core/Config.cpp @@ -96,6 +96,8 @@ void Config::init(const QString& fileName) m_defaults.insert("MinimizeOnCopy", false); m_defaults.insert("security/clearclipboard", true); m_defaults.insert("security/clearclipboardtimeout", 10); + m_defaults.insert("security/lockdatabaseidle", false); + m_defaults.insert("security/lockdatabaseidlesec", 10); } Config* Config::instance() diff --git a/src/core/InactivityTimer.cpp b/src/core/InactivityTimer.cpp new file mode 100644 index 000000000..762c87200 --- /dev/null +++ b/src/core/InactivityTimer.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2014 Felix Geyer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "InactivityTimer.h" + +#include +#include + +InactivityTimer::InactivityTimer(QObject* parent) + : QObject(parent) + , m_timer(new QTimer(this)) + , m_active(false) +{ + m_timer->setSingleShot(true); + connect(m_timer, SIGNAL(timeout()), SLOT(timeout())); +} + +void InactivityTimer::setInactivityTimeout(int inactivityTimeout) +{ + Q_ASSERT(inactivityTimeout > 0); + + m_timer->setInterval(inactivityTimeout); +} + +void InactivityTimer::activate() +{ + if (!m_active) { + qApp->installEventFilter(this); + } + m_active = true; + m_timer->start(); +} + +void InactivityTimer::deactivate() +{ + qApp->removeEventFilter(this); + m_active = false; + m_timer->stop(); +} + +bool InactivityTimer::eventFilter(QObject* watched, QEvent* event) +{ + const QEvent::Type type = event->type(); + + if ( (type >= QEvent::MouseButtonPress && type <= QEvent::KeyRelease) + || (type >= QEvent::HoverEnter && type <= QEvent::HoverMove) + || (type == QEvent::Wheel) ) { + m_timer->start(); + } + + return QObject::eventFilter(watched, event); +} + +void InactivityTimer::timeout() +{ + if (m_active && !m_timer->isActive()) { + Q_EMIT inactivityDetected(); + } +} diff --git a/src/core/InactivityTimer.h b/src/core/InactivityTimer.h new file mode 100644 index 000000000..14e4852b3 --- /dev/null +++ b/src/core/InactivityTimer.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2014 Felix Geyer + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef KEEPASSX_INACTIVITYTIMER_H +#define KEEPASSX_INACTIVITYTIMER_H + +#include + +#include "core/Global.h" + +class QTimer; + +class InactivityTimer : public QObject +{ + Q_OBJECT + +public: + explicit InactivityTimer(QObject* parent = Q_NULLPTR); + void setInactivityTimeout(int inactivityTimeout); + void activate(); + void deactivate(); + +Q_SIGNALS: + void inactivityDetected(); + +protected: + bool eventFilter(QObject* watched, QEvent* event); + +private Q_SLOTS: + void timeout(); + +private: + QTimer* m_timer; + bool m_active; +}; + +#endif // KEEPASSX_INACTIVITYTIMER_H diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 2b8707aae..a51c19735 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -26,6 +26,7 @@ #include "core/Database.h" #include "core/Entry.h" #include "core/FilePath.h" +#include "core/InactivityTimer.h" #include "core/Metadata.h" #include "gui/AboutDialog.h" #include "gui/DatabaseWidget.h" @@ -66,6 +67,11 @@ MainWindow::MainWindow() autoType()->registerGlobalShortcut(globalAutoTypeKey, globalAutoTypeModifiers); } + m_inactivityTimer = new InactivityTimer(this); + connect(m_inactivityTimer, SIGNAL(inactivityDetected()), + m_ui->tabWidget, SLOT(lockDatabases())); + applySettingsChanges(); + setShortcut(m_ui->actionDatabaseOpen, QKeySequence::Open, Qt::CTRL + Qt::Key_O); setShortcut(m_ui->actionDatabaseSave, QKeySequence::Save, Qt::CTRL + Qt::Key_S); setShortcut(m_ui->actionDatabaseSaveAs, QKeySequence::SaveAs); @@ -134,6 +140,7 @@ MainWindow::MainWindow() connect(m_ui->stackedWidget, SIGNAL(currentChanged(int)), SLOT(setMenuActionState())); connect(m_ui->stackedWidget, SIGNAL(currentChanged(int)), SLOT(updateWindowTitle())); connect(m_ui->settingsWidget, SIGNAL(editFinished(bool)), SLOT(switchToDatabases())); + connect(m_ui->settingsWidget, SIGNAL(accepted()), SLOT(applySettingsChanges())); connect(m_ui->actionDatabaseNew, SIGNAL(triggered()), m_ui->tabWidget, SLOT(newDatabase())); @@ -456,3 +463,19 @@ void MainWindow::rememberOpenDatabases(const QString& filePath) { m_openDatabases.append(filePath); } + +void MainWindow::applySettingsChanges() +{ + int timeout = config()->get("security/lockdatabaseidlesec").toInt() * 1000; + if (timeout <= 0) { + timeout = 60; + } + + m_inactivityTimer->setInactivityTimeout(timeout); + if (config()->get("security/lockdatabaseidle").toBool()) { + m_inactivityTimer->activate(); + } + else { + m_inactivityTimer->deactivate(); + } +} diff --git a/src/gui/MainWindow.h b/src/gui/MainWindow.h index 74952687e..e52093a93 100644 --- a/src/gui/MainWindow.h +++ b/src/gui/MainWindow.h @@ -28,6 +28,8 @@ namespace Ui { class MainWindow; } +class InactivityTimer; + class MainWindow : public QMainWindow { Q_OBJECT @@ -58,6 +60,7 @@ private Q_SLOTS: void showGroupContextMenu(const QPoint& globalPos); void saveToolbarState(bool value); void rememberOpenDatabases(const QString& filePath); + void applySettingsChanges(); private: static void setShortcut(QAction* action, QKeySequence::StandardKey standard, int fallback = 0); @@ -70,6 +73,7 @@ private: QActionGroup* m_lastDatabasesActions; QActionGroup* m_copyAdditionalAttributeActions; QStringList m_openDatabases; + InactivityTimer* m_inactivityTimer; Q_DISABLE_COPY(MainWindow) }; diff --git a/src/gui/SettingsWidget.cpp b/src/gui/SettingsWidget.cpp index 57de0b234..fe6c954cb 100644 --- a/src/gui/SettingsWidget.cpp +++ b/src/gui/SettingsWidget.cpp @@ -47,6 +47,8 @@ SettingsWidget::SettingsWidget(QWidget* parent) connect(m_secUi->clearClipboardCheckBox, SIGNAL(toggled(bool)), m_secUi->clearClipboardSpinBox, SLOT(setEnabled(bool))); + connect(m_secUi->lockDatabaseIdleCheckBox, SIGNAL(toggled(bool)), + m_secUi->lockDatabaseIdleSpinBox, SLOT(setEnabled(bool))); } SettingsWidget::~SettingsWidget() @@ -74,6 +76,9 @@ void SettingsWidget::loadSettings() m_secUi->clearClipboardCheckBox->setChecked(config()->get("security/clearclipboard").toBool()); m_secUi->clearClipboardSpinBox->setValue(config()->get("security/clearclipboardtimeout").toInt()); + m_secUi->lockDatabaseIdleCheckBox->setChecked(config()->get("security/lockdatabaseidle").toBool()); + m_secUi->lockDatabaseIdleSpinBox->setValue(config()->get("security/lockdatabaseidlesec").toInt()); + setCurrentRow(0); } @@ -92,6 +97,9 @@ void SettingsWidget::saveSettings() config()->set("security/clearclipboard", m_secUi->clearClipboardCheckBox->isChecked()); config()->set("security/clearclipboardtimeout", m_secUi->clearClipboardSpinBox->value()); + config()->set("security/lockdatabaseidle", m_secUi->lockDatabaseIdleCheckBox->isChecked()); + config()->set("security/lockdatabaseidlesec", m_secUi->lockDatabaseIdleSpinBox->value()); + Q_EMIT editFinished(true); } diff --git a/src/gui/SettingsWidgetSecurity.ui b/src/gui/SettingsWidgetSecurity.ui index 3980b48a1..b3a913996 100644 --- a/src/gui/SettingsWidgetSecurity.ui +++ b/src/gui/SettingsWidgetSecurity.ui @@ -34,6 +34,26 @@ + + + + Lock databases after inactivity of + + + + + + + sec + + + 10 + + + 9999 + + +