Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/keepassxreboot/keepassxc.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/MainWindow.cpp')
-rw-r--r--src/gui/MainWindow.cpp250
1 files changed, 205 insertions, 45 deletions
diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp
index e37a7d28c..76f1ffb60 100644
--- a/src/gui/MainWindow.cpp
+++ b/src/gui/MainWindow.cpp
@@ -20,14 +20,10 @@
#include "ui_MainWindow.h"
#include <QCloseEvent>
+#include <QMimeData>
#include <QShortcut>
#include <QTimer>
-#if defined(Q_OS_LINUX) && ! defined(QT_NO_DBUS)
-#include <QList>
-#include <QtDBus/QtDBus>
-#endif
-
#include "config-keepassx.h"
#include "autotype/AutoType.h"
@@ -49,6 +45,23 @@
#include "http/OptionDialog.h"
#endif
+#ifdef WITH_XC_SSHAGENT
+#include "sshagent/AgentSettingsPage.h"
+#include "sshagent/SSHAgent.h"
+#endif
+
+#ifdef WITH_XC_BROWSER
+#include "browser/NativeMessagingHost.h"
+#include "browser/BrowserSettings.h"
+#include "browser/BrowserOptionDialog.h"
+#endif
+
+#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC) && !defined(QT_NO_DBUS)
+#include <QList>
+#include <QtDBus/QtDBus>
+#include "gui/MainWindowAdaptor.h"
+#endif
+
#include "gui/SettingsWidget.h"
#include "gui/PasswordGeneratorWidget.h"
@@ -56,7 +69,7 @@
class HttpPlugin: public ISettingsPage
{
public:
- HttpPlugin(DatabaseTabWidget * tabWidget)
+ HttpPlugin(DatabaseTabWidget* tabWidget)
{
m_service = new Service(tabWidget);
}
@@ -65,7 +78,7 @@ public:
QString name() override
{
- return QObject::tr("Browser Integration");
+ return QObject::tr("Legacy Browser Integration");
}
QIcon icon() override
@@ -75,18 +88,18 @@ public:
QWidget * createWidget() override
{
- OptionDialog * dlg = new OptionDialog();
+ OptionDialog* dlg = new OptionDialog();
QObject::connect(dlg, SIGNAL(removeSharedEncryptionKeys()), m_service, SLOT(removeSharedEncryptionKeys()));
QObject::connect(dlg, SIGNAL(removeStoredPermissions()), m_service, SLOT(removeStoredPermissions()));
return dlg;
}
- void loadSettings(QWidget * widget) override
+ void loadSettings(QWidget* widget) override
{
qobject_cast<OptionDialog*>(widget)->loadSettings();
}
- void saveSettings(QWidget * widget) override
+ void saveSettings(QWidget* widget) override
{
qobject_cast<OptionDialog*>(widget)->saveSettings();
if (HttpSettings::isEnabled())
@@ -95,7 +108,55 @@ public:
m_service->stop();
}
private:
- Service *m_service;
+ Service* m_service;
+};
+#endif
+
+#ifdef WITH_XC_BROWSER
+class BrowserPlugin: public ISettingsPage
+{
+ public:
+ BrowserPlugin(DatabaseTabWidget* tabWidget) {
+ m_nativeMessagingHost = QSharedPointer<NativeMessagingHost>(new NativeMessagingHost(tabWidget));
+ }
+
+ ~BrowserPlugin() {
+
+ }
+
+ QString name() override
+ {
+ return QObject::tr("Browser Integration");
+ }
+
+ QIcon icon() override
+ {
+ return FilePath::instance()->icon("apps", "internet-web-browser");
+ }
+
+ QWidget* createWidget() override {
+ BrowserOptionDialog* dlg = new BrowserOptionDialog();
+ QObject::connect(dlg, SIGNAL(removeSharedEncryptionKeys()), m_nativeMessagingHost.data(), SLOT(removeSharedEncryptionKeys()));
+ QObject::connect(dlg, SIGNAL(removeStoredPermissions()), m_nativeMessagingHost.data(), SLOT(removeStoredPermissions()));
+ return dlg;
+ }
+
+ void loadSettings(QWidget* widget) override
+ {
+ qobject_cast<BrowserOptionDialog*>(widget)->loadSettings();
+ }
+
+ void saveSettings(QWidget* widget) override
+ {
+ qobject_cast<BrowserOptionDialog*>(widget)->saveSettings();
+ if (BrowserSettings::isEnabled()) {
+ m_nativeMessagingHost->run();
+ } else {
+ m_nativeMessagingHost->stop();
+ }
+ }
+ private:
+ QSharedPointer<NativeMessagingHost> m_nativeMessagingHost;
};
#endif
@@ -108,6 +169,17 @@ MainWindow::MainWindow()
, m_appExiting(false)
{
m_ui->setupUi(this);
+
+#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC) && !defined(QT_NO_DBUS)
+ new MainWindowAdaptor(this);
+ QDBusConnection dbus = QDBusConnection::sessionBus();
+ dbus.registerObject("/keepassxc", this);
+ dbus.registerService("org.keepassxc.KeePassXC.MainWindow");
+#endif
+
+ setAcceptDrops(true);
+
+ m_ui->toolBar->setContextMenuPolicy(Qt::PreventContextMenu);
// Setup the search widget in the toolbar
SearchWidget *search = new SearchWidget();
@@ -118,18 +190,22 @@ MainWindow::MainWindow()
m_countDefaultAttributes = m_ui->menuEntryCopyAttribute->actions().size();
restoreGeometry(config()->get("GUI/MainWindowGeometry").toByteArray());
- #ifdef WITH_XC_HTTP
+#ifdef WITH_XC_BROWSER
+ m_ui->settingsWidget->addSettingsPage(new BrowserPlugin(m_ui->tabWidget));
+#endif
+#ifdef WITH_XC_HTTP
m_ui->settingsWidget->addSettingsPage(new HttpPlugin(m_ui->tabWidget));
- #endif
+#endif
+#ifdef WITH_XC_SSHAGENT
+ SSHAgent::init(this);
+ m_ui->settingsWidget->addSettingsPage(new AgentSettingsPage(m_ui->tabWidget));
+#endif
setWindowIcon(filePath()->applicationIcon());
m_ui->globalMessageWidget->setHidden(true);
- QAction* toggleViewAction = m_ui->toolBar->toggleViewAction();
- toggleViewAction->setText(tr("Show toolbar"));
- m_ui->menuView->addAction(toggleViewAction);
- bool showToolbar = config()->get("ShowToolbar").toBool();
- m_ui->toolBar->setVisible(showToolbar);
- connect(m_ui->toolBar, SIGNAL(visibilityChanged(bool)), this, SLOT(saveToolbarState(bool)));
+ connect(m_ui->globalMessageWidget, &MessageWidget::linkActivated, &MessageWidget::openHttpUrl);
+ connect(m_ui->globalMessageWidget, SIGNAL(showAnimationStarted()), m_ui->globalMessageWidgetContainer, SLOT(show()));
+ connect(m_ui->globalMessageWidget, SIGNAL(hideAnimationFinished()), m_ui->globalMessageWidgetContainer, SLOT(hide()));
m_clearHistoryAction = new QAction(tr("Clear history"), m_ui->menuFile);
m_lastDatabasesActions = new QActionGroup(m_ui->menuRecentDatabases);
@@ -167,6 +243,7 @@ MainWindow::MainWindow()
setShortcut(m_ui->actionEntryNew, QKeySequence::New, Qt::CTRL + Qt::Key_N);
m_ui->actionEntryEdit->setShortcut(Qt::CTRL + Qt::Key_E);
m_ui->actionEntryDelete->setShortcut(Qt::CTRL + Qt::Key_D);
+ m_ui->actionEntryDelete->setShortcut(Qt::Key_Delete);
m_ui->actionEntryClone->setShortcut(Qt::CTRL + Qt::Key_K);
m_ui->actionEntryTotp->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_T);
m_ui->actionEntryCopyTotp->setShortcut(Qt::CTRL + Qt::Key_T);
@@ -187,7 +264,6 @@ MainWindow::MainWindow()
m_ui->actionChangeMasterKey->setIcon(filePath()->icon("actions", "database-change-key", false));
m_ui->actionLockDatabases->setIcon(filePath()->icon("actions", "document-encrypt", false));
m_ui->actionQuit->setIcon(filePath()->icon("actions", "application-exit"));
- m_ui->actionQuit->setMenuRole(QAction::QuitRole);
m_ui->actionEntryNew->setIcon(filePath()->icon("actions", "entry-new", false));
m_ui->actionEntryClone->setIcon(filePath()->icon("actions", "entry-clone", false));
@@ -196,6 +272,7 @@ MainWindow::MainWindow()
m_ui->actionEntryAutoType->setIcon(filePath()->icon("actions", "auto-type", false));
m_ui->actionEntryCopyUsername->setIcon(filePath()->icon("actions", "username-copy", false));
m_ui->actionEntryCopyPassword->setIcon(filePath()->icon("actions", "password-copy", false));
+ m_ui->actionEntryCopyURL->setIcon(filePath()->icon("actions", "url-copy", false));
m_ui->actionGroupNew->setIcon(filePath()->icon("actions", "group-new", false));
m_ui->actionGroupEdit->setIcon(filePath()->icon("actions", "group-edit", false));
@@ -203,11 +280,9 @@ MainWindow::MainWindow()
m_ui->actionGroupEmptyRecycleBin->setIcon(filePath()->icon("actions", "group-empty-trash", false));
m_ui->actionSettings->setIcon(filePath()->icon("actions", "configure"));
- m_ui->actionSettings->setMenuRole(QAction::PreferencesRole);
m_ui->actionPasswordGenerator->setIcon(filePath()->icon("actions", "password-generator", false));
m_ui->actionAbout->setIcon(filePath()->icon("actions", "help-about"));
- m_ui->actionAbout->setMenuRole(QAction::AboutRole);
m_actionMultiplexer.connect(SIGNAL(currentModeChanged(DatabaseWidget::Mode)),
this, SLOT(setMenuActionState(DatabaseWidget::Mode)));
@@ -343,13 +418,42 @@ MainWindow::MainWindow()
m_ui->globalMessageWidget->showMessage(
tr("Access error for config file %1").arg(config()->getFileName()), MessageWidget::Error);
}
+#ifdef WITH_XC_HTTP
+ if (config()->get("Http/Enabled", false).toBool() && config()->get("Http/DeprecationNoticeShown", 0).toInt() < 3) {
+ // show message after global widget dismissed all messages
+ connect(m_ui->globalMessageWidget, SIGNAL(hideAnimationFinished()), this, SLOT(showKeePassHTTPDeprecationNotice()));
+ }
+#endif
+#ifndef KEEPASSXC_BUILD_TYPE_RELEASE
+ m_ui->globalMessageWidget->showMessage(tr("WARNING: You are using an unstable build of KeePassXC!\n"
+ "There is a high risk of corruption, maintain a backup of your databases.\n"
+ "This version is not meant for production use."),
+ MessageWidget::Warning, -1);
+#else
+ // Show the HTTP deprecation message if enabled above
+ emit m_ui->globalMessageWidget->hideAnimationFinished();
+#endif
}
MainWindow::~MainWindow()
{
}
+void MainWindow::showKeePassHTTPDeprecationNotice()
+{
+ int warningNum = config()->get("Http/DeprecationNoticeShown", 0).toInt();
+ displayGlobalMessage(tr("<p>It looks like you are using KeePassHTTP for browser integration. "
+ "This feature has been deprecated and will be removed in the future.<br>"
+ "Please switch to KeePassXC-Browser instead! For help with migration, "
+ "visit our <a class=\"link\" href=\"https://keepassxc.org/docs/keepassxc-browser-migration\">"
+ "migration guide</a> (warning %1 of 3).</p>").arg(warningNum + 1),
+ MessageWidget::Warning, true, -1);
+
+ config()->set("Http/DeprecationNoticeShown", warningNum + 1);
+ disconnect(m_ui->globalMessageWidget, SIGNAL(hideAnimationFinished()), this, SLOT(showKeePassHTTPDeprecationNotice()));
+}
+
void MainWindow::appExit()
{
m_appExitCalled = true;
@@ -429,13 +533,13 @@ void MainWindow::setMenuActionState(DatabaseWidget::Mode mode)
switch (mode) {
case DatabaseWidget::ViewMode: {
- bool inSearch = dbWidget->isInSearchMode();
+ //bool inSearch = dbWidget->isInSearchMode();
bool singleEntrySelected = dbWidget->numberOfSelectedEntries() == 1;
bool entriesSelected = dbWidget->numberOfSelectedEntries() > 0;
bool groupSelected = dbWidget->isGroupSelected();
bool recycleBinSelected = dbWidget->isRecycleBinSelected();
- m_ui->actionEntryNew->setEnabled(!inSearch);
+ m_ui->actionEntryNew->setEnabled(true);
m_ui->actionEntryClone->setEnabled(singleEntrySelected);
m_ui->actionEntryEdit->setEnabled(singleEntrySelected);
m_ui->actionEntryDelete->setEnabled(entriesSelected);
@@ -458,12 +562,13 @@ void MainWindow::setMenuActionState(DatabaseWidget::Mode mode)
m_ui->actionGroupEmptyRecycleBin->setEnabled(recycleBinSelected);
m_ui->actionChangeMasterKey->setEnabled(true);
m_ui->actionChangeDatabaseSettings->setEnabled(true);
- m_ui->actionDatabaseSave->setEnabled(true);
+ m_ui->actionDatabaseSave->setEnabled(m_ui->tabWidget->canSave());
m_ui->actionDatabaseSaveAs->setEnabled(true);
m_ui->actionExportCsv->setEnabled(true);
m_ui->actionDatabaseMerge->setEnabled(m_ui->tabWidget->currentIndex() != -1);
m_searchWidgetAction->setEnabled(true);
+
break;
}
case DatabaseWidget::EditMode:
@@ -563,6 +668,7 @@ void MainWindow::updateWindowTitle()
if (m_ui->tabWidget->readOnly(tabWidgetIndex)) {
customWindowTitlePart.append(QString(" [%1]").arg(tr("read-only")));
}
+ m_ui->actionDatabaseSave->setEnabled(m_ui->tabWidget->canSave(tabWidgetIndex));
} else if (stackedWidgetIndex == 1) {
customWindowTitlePart = tr("Settings");
}
@@ -729,7 +835,9 @@ void MainWindow::changeEvent(QEvent* event)
void MainWindow::saveWindowInformation()
{
- config()->set("GUI/MainWindowGeometry", saveGeometry());
+ if (isVisible()) {
+ config()->set("GUI/MainWindowGeometry", saveGeometry());
+ }
}
bool MainWindow::saveLastDatabases()
@@ -764,7 +872,6 @@ void MainWindow::updateTrayIcon()
if (isTrayIconEnabled()) {
if (!m_trayIcon) {
m_trayIcon = new QSystemTrayIcon(this);
-
QMenu* menu = new QMenu(this);
QAction* actionToggle = new QAction(tr("Toggle window"), menu);
@@ -784,6 +891,7 @@ void MainWindow::updateTrayIcon()
connect(actionToggle, SIGNAL(triggered()), SLOT(toggleWindow()));
m_trayIcon->setContextMenu(menu);
+
m_trayIcon->setIcon(filePath()->applicationIcon());
m_trayIcon->show();
}
@@ -813,11 +921,6 @@ void MainWindow::showGroupContextMenu(const QPoint& globalPos)
m_ui->menuGroups->popup(globalPos);
}
-void MainWindow::saveToolbarState(bool value)
-{
- config()->set("ShowToolbar", value);
-}
-
void MainWindow::setShortcut(QAction* action, QKeySequence::StandardKey standard, int fallback)
{
if (!QKeySequence::keyBindings(standard).isEmpty()) {
@@ -853,13 +956,14 @@ void MainWindow::applySettingsChanges()
void MainWindow::trayIconTriggered(QSystemTrayIcon::ActivationReason reason)
{
- if (reason == QSystemTrayIcon::Trigger) {
+ if (reason == QSystemTrayIcon::Trigger || reason == QSystemTrayIcon::MiddleClick) {
toggleWindow();
}
}
void MainWindow::hideWindow()
{
+ saveWindowInformation();
#ifndef Q_OS_MAC
setWindowState(windowState() | Qt::WindowMinimized);
#endif
@@ -875,13 +979,9 @@ void MainWindow::toggleWindow()
if ((QApplication::activeWindow() == this) && isVisible() && !isMinimized()) {
hideWindow();
} else {
- ensurePolished();
- setWindowState(windowState() & ~Qt::WindowMinimized);
- show();
- raise();
- activateWindow();
+ bringToFront();
-#if defined(Q_OS_LINUX) && ! 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)
// see https://github.com/keepassxreboot/keepassxc/issues/271
// and https://bugreports.qt.io/browse/QTBUG-58723
@@ -949,16 +1049,17 @@ bool MainWindow::isTrayIconEnabled() const
&& QSystemTrayIcon::isSystemTrayAvailable();
}
-void MainWindow::displayGlobalMessage(const QString& text, MessageWidget::MessageType type, bool showClosebutton)
+void MainWindow::displayGlobalMessage(const QString& text, MessageWidget::MessageType type, bool showClosebutton,
+ int autoHideTimeout)
{
m_ui->globalMessageWidget->setCloseButtonVisible(showClosebutton);
- m_ui->globalMessageWidget->showMessage(text, type);
+ m_ui->globalMessageWidget->showMessage(text, type, autoHideTimeout);
}
-void MainWindow::displayTabMessage(const QString& text, MessageWidget::MessageType type, bool showClosebutton)
+void MainWindow::displayTabMessage(const QString& text, MessageWidget::MessageType type, bool showClosebutton,
+ int autoHideTimeout)
{
- m_ui->globalMessageWidget->setCloseButtonVisible(showClosebutton);
- m_ui->tabWidget->currentDatabaseWidget()->showMessage(text, type);
+ m_ui->tabWidget->currentDatabaseWidget()->showMessage(text, type, showClosebutton, autoHideTimeout);
}
void MainWindow::hideGlobalMessage()
@@ -975,7 +1076,8 @@ void MainWindow::hideTabMessage()
void MainWindow::showYubiKeyPopup()
{
- displayGlobalMessage(tr("Please touch the button on your YubiKey!"), MessageWidget::Information, false);
+ displayGlobalMessage(tr("Please touch the button on your YubiKey!"), MessageWidget::Information,
+ false, MessageWidget::DisableAutoHide);
setEnabled(false);
}
@@ -985,9 +1087,67 @@ void MainWindow::hideYubiKeyPopup()
setEnabled(true);
}
+void MainWindow::bringToFront()
+{
+ ensurePolished();
+ setWindowState(windowState() & ~Qt::WindowMinimized);
+ show();
+ raise();
+ activateWindow();
+}
+
void MainWindow::handleScreenLock()
{
if (config()->get("security/lockdatabasescreenlock").toBool()){
lockDatabasesAfterInactivity();
}
}
+
+QStringList MainWindow::kdbxFilesFromUrls(const QList<QUrl>& urls)
+{
+ QStringList kdbxFiles;
+ for (const QUrl& url: urls) {
+ const QFileInfo fInfo(url.toLocalFile());
+ const bool isKdbxFile = fInfo.isFile() && fInfo.suffix().toLower() == "kdbx";
+ if (isKdbxFile) {
+ kdbxFiles.append(fInfo.absoluteFilePath());
+ }
+ }
+
+ return kdbxFiles;
+}
+
+void MainWindow::dragEnterEvent(QDragEnterEvent* event)
+{
+ const QMimeData* mimeData = event->mimeData();
+ if (mimeData->hasUrls()) {
+ const QStringList kdbxFiles = kdbxFilesFromUrls(mimeData->urls());
+ if (!kdbxFiles.isEmpty()) {
+ event->acceptProposedAction();
+ }
+ }
+}
+
+void MainWindow::dropEvent(QDropEvent* event)
+{
+ const QMimeData* mimeData = event->mimeData();
+ if (mimeData->hasUrls()) {
+ const QStringList kdbxFiles = kdbxFilesFromUrls(mimeData->urls());
+ if (!kdbxFiles.isEmpty()) {
+ event->acceptProposedAction();
+ }
+ for (const QString& kdbxFile: kdbxFiles) {
+ openDatabase(kdbxFile);
+ }
+ }
+}
+
+void MainWindow::closeAllDatabases()
+{
+ m_ui->tabWidget->closeAllDatabases();
+}
+
+void MainWindow::lockAllDatabases()
+{
+ lockDatabasesAfterInactivity();
+} \ No newline at end of file