Skip to content

Commit

Permalink
Allow selecting any open database in unlock dialog
Browse files Browse the repository at this point in the history
When showing the database unlock dialog from browser or auto-type and
there's more than one open database, replace the filename label with
a drop-down box of all available files. Switching the filename here will
change the active tab of the main window and unlock it.

This is the first part of an implementation for
keepassxreboot#2322

TODO/Limitations:
  * The drop-down list should show only locked databases rather than all
    open databases.
  * The drop-down should dynamically respond if the set of open database
    tabs is changed (e.g. if the user closes a database in the main
    window while the unlock dialog is open)
  * No handling for the case where the active database is unlocked but
    has no matching credentials, and there's other locked databases open
    ("Case 2" in the github issue)
  • Loading branch information
aswild committed Sep 5, 2020
1 parent 6a35bbe commit 2d3efdf
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 1 deletion.
42 changes: 42 additions & 0 deletions src/gui/DatabaseOpenDialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

#include "DatabaseOpenDialog.h"
#include "DatabaseOpenWidget.h"
#include "DatabaseTabWidget.h"
#include "DatabaseWidget.h"
#include "core/Database.h"

Expand All @@ -38,6 +39,28 @@ DatabaseOpenDialog::DatabaseOpenDialog(QWidget* parent)
setMinimumWidth(700);
}

void DatabaseOpenDialog::setMultiFile(bool multiFile)
{
if (multiFile) {
auto* tabWidget = qobject_cast<const DatabaseTabWidget*>(parentWidget());
if (!tabWidget) {
m_view->clearMultiFileList();
return;
}
QStringList fileList;
for (int i = 0, c = tabWidget->count(); i < c; ++i) {
auto* dbWidget = tabWidget->databaseWidgetFromIndex(i);
Q_ASSERT(dbWidget);
if (dbWidget) {
fileList << dbWidget->database()->filePath();
}
}
m_view->setMultiFileList(fileList);
} else {
m_view->clearMultiFileList();
}
}

void DatabaseOpenDialog::setFilePath(const QString& filePath)
{
m_view->load(filePath);
Expand Down Expand Up @@ -67,6 +90,25 @@ DatabaseOpenDialog::Intent DatabaseOpenDialog::intent() const
return m_intent;
}

void DatabaseOpenDialog::changeFile(const QString& filePath)
{
auto* tabWidget = qobject_cast<DatabaseTabWidget*>(parentWidget());
if (!tabWidget) {
return;
}

auto* dbWidget = tabWidget->databaseWidgetFromFilePath(filePath);
if (dbWidget) {
setTargetDatabaseWidget(dbWidget);
tabWidget->setCurrentIndex(tabWidget->indexOf(dbWidget));
setFilePath(filePath);
} else {
// no matching dbWidget for the filePath, it probably got closed while this dialog was open.
// cancel and let the user start over.
complete(false);
}
}

void DatabaseOpenDialog::clearForms()
{
m_view->clearForms();
Expand Down
2 changes: 2 additions & 0 deletions src/gui/DatabaseOpenDialog.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class DatabaseOpenDialog : public QDialog
explicit DatabaseOpenDialog(QWidget* parent = nullptr);
void setFilePath(const QString& filePath);
void setTargetDatabaseWidget(DatabaseWidget* dbWidget);
void setMultiFile(bool multiFile);
void setIntent(Intent intent);
Intent intent() const;
QSharedPointer<Database> database();
Expand All @@ -54,6 +55,7 @@ class DatabaseOpenDialog : public QDialog

public slots:
void complete(bool accepted);
void changeFile(const QString& filePath);

private:
QPointer<DatabaseOpenWidget> m_view;
Expand Down
40 changes: 39 additions & 1 deletion src/gui/DatabaseOpenWidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "core/Resources.h"
#include "crypto/Random.h"
#include "format/KeePass2Reader.h"
#include "gui/DatabaseOpenDialog.h"
#include "gui/FileDialog.h"
#include "gui/MainWindow.h"
#include "gui/MessageBox.h"
Expand Down Expand Up @@ -138,14 +139,49 @@ void DatabaseOpenWidget::hideEvent(QHideEvent* event)
}
}

/**
* Set a list of files in a drop-down to choose a different database.
* Must be called before load(), and should only be called by DatabaseOpenDialog
*/
void DatabaseOpenWidget::setMultiFileList(const QStringList& filenameList)
{
m_multiFileList = filenameList;
}

void DatabaseOpenWidget::clearMultiFileList()
{
m_multiFileList.clear();
}

void DatabaseOpenWidget::load(const QString& filename)
{
clearForms();

m_filename = filename;
m_ui->fileNameLabel->setRawText(m_filename);

if (m_multiFileList.count() > 1) {
m_ui->fileNameLabel->setVisible(false);
m_ui->fileNameList->addItems(m_multiFileList);
m_ui->fileNameList->setCurrentText(filename);
m_ui->fileNameList->setVisible(true);

// changing the file in a dropdown needs to go back up the parent tree to change the current database tab.
// The handler of this signal will come back and call load() here again.
auto* parentDialog = qobject_cast<DatabaseOpenDialog*>(parentWidget());
if (parentDialog) {
// clang-format off
connect(m_ui->fileNameList, SIGNAL(textActivated(const QString&)),
parentDialog, SLOT(changeFile(const QString&)));
// clang-format on
}
} else {
m_ui->fileNameLabel->setRawText(m_filename);
m_ui->fileNameLabel->setVisible(true);
m_ui->fileNameList->setVisible(false);
}

m_ui->keyFileClearIcon->setVisible(false);
m_ui->editPassword->setFocus();

if (config()->get(Config::RememberLastKeyFiles).toBool()) {
auto lastKeyFiles = config()->get(Config::LastKeyFiles).toHash();
Expand All @@ -170,6 +206,8 @@ void DatabaseOpenWidget::load(const QString& filename)

void DatabaseOpenWidget::clearForms()
{
disconnect(m_ui->fileNameList, SIGNAL(textActivated(const QString&)), nullptr, nullptr);
m_ui->fileNameList->clear();
m_ui->editPassword->setText("");
m_ui->editPassword->setShowPassword(false);
m_ui->keyFileLineEdit->clear();
Expand Down
3 changes: 3 additions & 0 deletions src/gui/DatabaseOpenWidget.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ class DatabaseOpenWidget : public DialogyWidget
void clearForms();
void enterKey(const QString& pw, const QString& keyFile);
QSharedPointer<Database> database();
void setMultiFileList(const QStringList& filenameList);
void clearMultiFileList();

signals:
void dialogFinished(bool accepted);
Expand All @@ -57,6 +59,7 @@ class DatabaseOpenWidget : public DialogyWidget
const QScopedPointer<Ui::DatabaseOpenWidget> m_ui;
QSharedPointer<Database> m_db;
QString m_filename;
QStringList m_multiFileList;
bool m_retryUnlockWithEmptyPassword = false;

protected slots:
Expand Down
7 changes: 7 additions & 0 deletions src/gui/DatabaseOpenWidget.ui
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,13 @@
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="fileNameList">
<property name="visible">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_3">
<property name="orientation">
Expand Down
14 changes: 14 additions & 0 deletions src/gui/DatabaseTabWidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -619,6 +619,18 @@ DatabaseWidget* DatabaseTabWidget::currentDatabaseWidget()
return qobject_cast<DatabaseWidget*>(currentWidget());
}

DatabaseWidget* DatabaseTabWidget::databaseWidgetFromFilePath(const QString& filePath) const
{
for (int i = 0, c = count(); i < c; ++i) {
auto* dbWidget = databaseWidgetFromIndex(i);
Q_ASSERT(dbWidget);
if (dbWidget && (dbWidget->database()->filePath() == filePath)) {
return dbWidget;
}
}
return nullptr;
}

/**
* Attempt to lock all open databases
*
Expand Down Expand Up @@ -666,6 +678,8 @@ void DatabaseTabWidget::unlockDatabaseInDialog(DatabaseWidget* dbWidget,
{
m_databaseOpenDialog->setTargetDatabaseWidget(dbWidget);
m_databaseOpenDialog->setIntent(intent);
m_databaseOpenDialog->setMultiFile(intent == DatabaseOpenDialog::Intent::AutoType
|| intent == DatabaseOpenDialog::Intent::Browser);
m_databaseOpenDialog->setFilePath(filePath);

#ifdef Q_OS_MACOS
Expand Down
1 change: 1 addition & 0 deletions src/gui/DatabaseTabWidget.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class DatabaseTabWidget : public QTabWidget
QString tabName(int index);
DatabaseWidget* currentDatabaseWidget();
DatabaseWidget* databaseWidgetFromIndex(int index) const;
DatabaseWidget* databaseWidgetFromFilePath(const QString& filePath) const;

bool isReadOnly(int index = -1) const;
bool canSave(int index = -1) const;
Expand Down

0 comments on commit 2d3efdf

Please sign in to comment.