diff --git a/src/browser/BrowserAccessControlDialog.cpp b/src/browser/BrowserAccessControlDialog.cpp index 51abf2e95..e4205f811 100644 --- a/src/browser/BrowserAccessControlDialog.cpp +++ b/src/browser/BrowserAccessControlDialog.cpp @@ -22,13 +22,13 @@ BrowserAccessControlDialog::BrowserAccessControlDialog(QWidget* parent) : QDialog(parent) - , ui(new Ui::BrowserAccessControlDialog()) + , m_ui(new Ui::BrowserAccessControlDialog()) { this->setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint); - ui->setupUi(this); - connect(ui->allowButton, SIGNAL(clicked()), this, SLOT(accept())); - connect(ui->denyButton, SIGNAL(clicked()), this, SLOT(reject())); + m_ui->setupUi(this); + connect(m_ui->allowButton, SIGNAL(clicked()), this, SLOT(accept())); + connect(m_ui->denyButton, SIGNAL(clicked()), this, SLOT(reject())); } BrowserAccessControlDialog::~BrowserAccessControlDialog() @@ -37,7 +37,7 @@ BrowserAccessControlDialog::~BrowserAccessControlDialog() void BrowserAccessControlDialog::setUrl(const QString& url) { - ui->label->setText(QString(tr("%1 has requested access to passwords for the following item(s).\n" + m_ui->label->setText(QString(tr("%1 has requested access to passwords for the following item(s).\n" "Please select whether you want to allow access.")) .arg(QUrl(url).host())); } @@ -45,16 +45,16 @@ void BrowserAccessControlDialog::setUrl(const QString& url) void BrowserAccessControlDialog::setItems(const QList& items) { for (Entry* entry : items) { - ui->itemsList->addItem(entry->title() + " - " + entry->username()); + m_ui->itemsList->addItem(entry->title() + " - " + entry->username()); } } bool BrowserAccessControlDialog::remember() const { - return ui->rememberDecisionCheckBox->isChecked(); + return m_ui->rememberDecisionCheckBox->isChecked(); } void BrowserAccessControlDialog::setRemember(bool r) { - ui->rememberDecisionCheckBox->setChecked(r); + m_ui->rememberDecisionCheckBox->setChecked(r); } diff --git a/src/browser/BrowserAccessControlDialog.h b/src/browser/BrowserAccessControlDialog.h index 9f24a1e16..65cfe5e97 100644 --- a/src/browser/BrowserAccessControlDialog.h +++ b/src/browser/BrowserAccessControlDialog.h @@ -43,7 +43,7 @@ public: void setRemember(bool r); private: - QScopedPointer ui; + QScopedPointer m_ui; }; #endif // BROWSERACCESSCONTROLDIALOG_H diff --git a/src/browser/BrowserAction.cpp b/src/browser/BrowserAction.cpp index 60cf8506a..c073bb4b2 100644 --- a/src/browser/BrowserAction.cpp +++ b/src/browser/BrowserAction.cpp @@ -322,7 +322,7 @@ QJsonObject BrowserAction::handleSetLogin(const QJsonObject& json, const QString if (uuid.isEmpty()) { m_browserService.addEntry(id, login, password, url, submitUrl, realm); } else { - m_browserService.updateEntry(id, uuid, login, password, url); + m_browserService.updateEntry(id, uuid, login, password, url, submitUrl); } const QString newNonce = incrementNonce(nonce); diff --git a/src/browser/BrowserEntrySaveDialog.cpp b/src/browser/BrowserEntrySaveDialog.cpp new file mode 100644 index 000000000..384482b84 --- /dev/null +++ b/src/browser/BrowserEntrySaveDialog.cpp @@ -0,0 +1,79 @@ +/* +* Copyright (C) 2013 Francois Ferrand +* Copyright (C) 2018 KeePassXC Team +* +* 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 3 of the License, or +* (at your option) any later version. +* +* 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 "BrowserEntrySaveDialog.h" +#include "ui_BrowserEntrySaveDialog.h" + +BrowserEntrySaveDialog::BrowserEntrySaveDialog(QWidget* parent) + : QDialog(parent) + , m_ui(new Ui::BrowserEntrySaveDialog()) +{ + this->setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint); + + m_ui->setupUi(this); + connect(m_ui->okButton, SIGNAL(clicked()), this, SLOT(accept())); + connect(m_ui->cancelButton, SIGNAL(clicked()), this, SLOT(reject())); + + m_ui->itemsList->setSelectionMode(QAbstractItemView::SingleSelection); + m_ui->label->setText(QString(tr("You have multiple databases open.\n" + "Please select the correct database for saving credentials."))); +} + +BrowserEntrySaveDialog::~BrowserEntrySaveDialog() +{ +} + +int BrowserEntrySaveDialog::setItems(QList& databaseWidgets, DatabaseWidget* currentWidget) const +{ + uint counter = 0; + int activeIndex = -1; + for (const auto dbWidget : databaseWidgets) { + QString databaseName = dbWidget->getDatabaseName(); + QString databaseFileName = dbWidget->getDatabaseFileName(); + + QListWidgetItem* item = new QListWidgetItem(); + item->setData(Qt::UserRole, counter); + + // Show database name (and filename if the name has been set in metadata) + if (databaseName == databaseFileName) { + item->setText(databaseFileName); + } else { + item->setText(QString("%1 (%2)").arg(databaseName, databaseFileName)); + } + + if (currentWidget == dbWidget) { + activeIndex = counter; + } + + m_ui->itemsList->addItem(item); + ++counter; + } + + // This must be done after the whole list is filled + if (activeIndex >= 0) { + m_ui->itemsList->item(activeIndex)->setSelected(true); + } + + m_ui->itemsList->selectAll(); + return databaseWidgets.length(); +} + + QList BrowserEntrySaveDialog::getSelected() const + { + return m_ui->itemsList->selectedItems(); + } diff --git a/src/browser/BrowserEntrySaveDialog.h b/src/browser/BrowserEntrySaveDialog.h new file mode 100644 index 000000000..0b1db7271 --- /dev/null +++ b/src/browser/BrowserEntrySaveDialog.h @@ -0,0 +1,49 @@ +/* +* Copyright (C) 2013 Francois Ferrand +* Copyright (C) 2018 KeePassXC Team +* +* 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 3 of the License, or +* (at your option) any later version. +* +* 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 BROWSERENTRYSAVEDIALOG_H +#define BROWSERENTRYSAVEDIALOG_H + +#include +#include +#include +#include "gui/DatabaseTabWidget.h" + +class Entry; + +namespace Ui +{ + class BrowserEntrySaveDialog; +} + +class BrowserEntrySaveDialog : public QDialog +{ + Q_OBJECT + +public: + explicit BrowserEntrySaveDialog(QWidget* parent = nullptr); + ~BrowserEntrySaveDialog() override; + + int setItems(QList& databaseWidgets, DatabaseWidget* currentWidget) const; + QList getSelected() const; + +private: + QScopedPointer m_ui; +}; + +#endif // BROWSERENTRYSAVEDIALOG_H diff --git a/src/browser/BrowserEntrySaveDialog.ui b/src/browser/BrowserEntrySaveDialog.ui new file mode 100755 index 000000000..5beb6fab8 --- /dev/null +++ b/src/browser/BrowserEntrySaveDialog.ui @@ -0,0 +1,62 @@ + + + BrowserEntrySaveDialog + + + + 0 + 0 + 400 + 221 + + + + KeePassXC-Browser Save Entry + + + + + + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Ok + + + + + + + Cancel + + + + + + + + + + diff --git a/src/browser/BrowserService.cpp b/src/browser/BrowserService.cpp index 25c9f21b2..9a0f07f28 100644 --- a/src/browser/BrowserService.cpp +++ b/src/browser/BrowserService.cpp @@ -26,6 +26,7 @@ #include "BrowserService.h" #include "BrowserAccessControlDialog.h" #include "BrowserEntryConfig.h" +#include "BrowserEntrySaveDialog.h" #include "BrowserSettings.h" #include "core/Database.h" #include "core/EntrySearcher.h" @@ -289,14 +290,33 @@ QJsonArray BrowserService::findMatchingEntries(const QString& id, return result; } -void BrowserService::addEntry(const QString&, +void BrowserService::addEntry(const QString& id, const QString& login, const QString& password, const QString& url, const QString& submitUrl, - const QString& realm) + const QString& realm, + Database* selectedDb) { - Group* group = findCreateAddEntryGroup(); + if (thread() != QThread::currentThread()) { + QMetaObject::invokeMethod(this, + "addEntry", + Qt::BlockingQueuedConnection, + Q_ARG(const QString&, id), + Q_ARG(const QString&, login), + Q_ARG(const QString&, password), + Q_ARG(const QString&, url), + Q_ARG(const QString&, submitUrl), + Q_ARG(const QString&, realm), + Q_ARG(Database*, selectedDb)); + } + + Database* db = selectedDb ? selectedDb : selectedDatabase(); + if (!db) { + return; + } + + Group* group = findCreateAddEntryGroup(db); if (!group) { return; } @@ -328,7 +348,8 @@ void BrowserService::updateEntry(const QString& id, const QString& uuid, const QString& login, const QString& password, - const QString& url) + const QString& url, + const QString& submitUrl) { if (thread() != QThread::currentThread()) { QMetaObject::invokeMethod(this, @@ -338,16 +359,19 @@ void BrowserService::updateEntry(const QString& id, Q_ARG(const QString&, uuid), Q_ARG(const QString&, login), Q_ARG(const QString&, password), - Q_ARG(const QString&, url)); + Q_ARG(const QString&, url), + Q_ARG(const QString&, submitUrl)); } - Database* db = getDatabase(); + Database* db = selectedDatabase(); if (!db) { return; } Entry* entry = db->resolveEntry(QUuid::fromRfc4122(QByteArray::fromHex(uuid.toLatin1()))); if (!entry) { + // If entry is not found for update, add a new one to the selected database + addEntry(id, login, password, url, submitUrl, "", db); return; } @@ -679,9 +703,9 @@ BrowserService::checkAccess(const Entry* entry, const QString& host, const QStri return Unknown; } -Group* BrowserService::findCreateAddEntryGroup() +Group* BrowserService::findCreateAddEntryGroup(Database* selectedDb) { - Database* db = getDatabase(); + Database* db = selectedDb ? selectedDb : getDatabase(); if (!db) { return nullptr; } @@ -813,6 +837,42 @@ Database* BrowserService::getDatabase() return nullptr; } +Database* BrowserService::selectedDatabase() +{ + QList databaseWidgets; + for (int i = 0;; ++i) { + const auto dbStruct = m_dbTabWidget->indexDatabaseManagerStruct(i); + // Add only open databases + if (dbStruct.dbWidget && dbStruct.dbWidget->dbHasKey() && + (dbStruct.dbWidget->currentMode() == DatabaseWidget::ViewMode || + dbStruct.dbWidget->currentMode() == DatabaseWidget::EditMode)) { + databaseWidgets.push_back(dbStruct.dbWidget); + continue; + } + + // Break out if dbStruct.dbWidget is nullptr + break; + } + + BrowserEntrySaveDialog browserEntrySaveDialog; + int openDatabaseCount = browserEntrySaveDialog.setItems(databaseWidgets, m_dbTabWidget->currentDatabaseWidget()); + if (openDatabaseCount > 1) { + int res = browserEntrySaveDialog.exec(); + if (res == QDialog::Accepted) { + const auto selectedDatabase = browserEntrySaveDialog.getSelected(); + if (selectedDatabase.length() > 0) { + int index = selectedDatabase[0]->data(Qt::UserRole).toUInt(); + return databaseWidgets[index]->database(); + } + } else { + return nullptr; + } + } + + // Return current database + return getDatabase(); +} + void BrowserService::databaseLocked(DatabaseWidget* dbWidget) { if (dbWidget) { diff --git a/src/browser/BrowserService.h b/src/browser/BrowserService.h index 56f74bd38..165901a87 100644 --- a/src/browser/BrowserService.h +++ b/src/browser/BrowserService.h @@ -51,7 +51,8 @@ public: const QString& password, const QString& url, const QString& submitUrl, - const QString& realm); + const QString& realm, + Database* selectedDb = nullptr); QList searchEntries(Database* db, const QString& hostname, const QString& url); QList searchEntries(const QString& url, const StringPairList& keyList); void removeSharedEncryptionKeys(); @@ -68,7 +69,8 @@ public slots: const QString& uuid, const QString& login, const QString& password, - const QString& url); + const QString& url, + const QString& submitUrl); void databaseLocked(DatabaseWidget* dbWidget); void databaseUnlocked(DatabaseWidget* dbWidget); void activateDatabaseChanged(DatabaseWidget* dbWidget); @@ -96,13 +98,14 @@ private: const QString& realm); QJsonObject prepareEntry(const Entry* entry); Access checkAccess(const Entry* entry, const QString& host, const QString& submitHost, const QString& realm); - Group* findCreateAddEntryGroup(); + Group* findCreateAddEntryGroup(Database* selectedDb = nullptr); int sortPriority(const Entry* entry, const QString& host, const QString& submitUrl, const QString& baseSubmitUrl) const; bool matchUrlScheme(const QString& url); bool removeFirstDomain(QString& hostname); QString baseDomain(const QString& url) const; Database* getDatabase(); + Database* selectedDatabase(); private: DatabaseTabWidget* const m_dbTabWidget; diff --git a/src/browser/CMakeLists.txt b/src/browser/CMakeLists.txt index 61215c181..bb5a01908 100755 --- a/src/browser/CMakeLists.txt +++ b/src/browser/CMakeLists.txt @@ -23,6 +23,7 @@ if(WITH_XC_BROWSER) BrowserAction.cpp BrowserClients.cpp BrowserEntryConfig.cpp + BrowserEntrySaveDialog.cpp BrowserOptionDialog.cpp BrowserService.cpp BrowserSettings.cpp diff --git a/src/gui/DatabaseTabWidget.cpp b/src/gui/DatabaseTabWidget.cpp index 71bd74814..dd36ddae4 100644 --- a/src/gui/DatabaseTabWidget.cpp +++ b/src/gui/DatabaseTabWidget.cpp @@ -555,6 +555,7 @@ void DatabaseTabWidget::updateTabName(Database* db) const DatabaseManagerStruct& dbStruct = m_dbList.value(db); QString tabName; + QString fileName; if (dbStruct.fileInfo.exists()) { if (db->metadata()->name().isEmpty()) { @@ -563,6 +564,7 @@ void DatabaseTabWidget::updateTabName(Database* db) tabName = db->metadata()->name(); } + fileName = dbStruct.fileInfo.fileName(); setTabToolTip(index, dbStruct.fileInfo.absoluteFilePath()); } else { if (db->metadata()->name().isEmpty()) { @@ -580,6 +582,9 @@ void DatabaseTabWidget::updateTabName(Database* db) tabName.append("*"); } + dbStruct.dbWidget->setDatabaseName(tabName); + dbStruct.dbWidget->setDatabaseFileName(fileName); + setTabText(index, tabName); emit tabNameChanged(); } diff --git a/src/gui/DatabaseTabWidget.h b/src/gui/DatabaseTabWidget.h index dcb4a62da..87e171ab0 100644 --- a/src/gui/DatabaseTabWidget.h +++ b/src/gui/DatabaseTabWidget.h @@ -56,6 +56,7 @@ public: void mergeDatabase(const QString& fileName); DatabaseWidget* currentDatabaseWidget(); bool hasLockableDatabases() const; + DatabaseManagerStruct indexDatabaseManagerStruct(int index); static const int LastDatabasesCount; @@ -110,7 +111,6 @@ private: void deleteDatabase(Database* db); int databaseIndex(Database* db); Database* indexDatabase(int index); - DatabaseManagerStruct indexDatabaseManagerStruct(int index); Database* databaseFromDatabaseWidget(DatabaseWidget* dbWidget); void insertDatabase(Database* db, const DatabaseManagerStruct& dbStruct); void updateLastDatabases(const QString& filename); diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index 869afbd24..c90fc52f7 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -1487,6 +1487,26 @@ bool DatabaseWidget::isRecycleBinSelected() const return m_groupView->currentGroup() && m_groupView->currentGroup() == m_db->metadata()->recycleBin(); } +QString DatabaseWidget::getDatabaseName() const +{ + return m_databaseName; +} + +void DatabaseWidget::setDatabaseName(const QString& databaseName) +{ + m_databaseName = databaseName; +} + +QString DatabaseWidget::getDatabaseFileName() const +{ + return m_databaseFileName; +} + +void DatabaseWidget::setDatabaseFileName(const QString& databaseFileName) +{ + m_databaseFileName = databaseFileName; +} + void DatabaseWidget::emptyRecycleBin() { if (!isRecycleBinSelected()) { diff --git a/src/gui/DatabaseWidget.h b/src/gui/DatabaseWidget.h index a15158c2c..0e2bc96e8 100644 --- a/src/gui/DatabaseWidget.h +++ b/src/gui/DatabaseWidget.h @@ -112,6 +112,10 @@ public: void blockAutoReload(bool block = true); void refreshSearch(); bool isRecycleBinSelected() const; + QString getDatabaseName() const; + void setDatabaseName(const QString& databaseName); + QString getDatabaseFileName() const; + void setDatabaseFileName(const QString& databaseFileName); signals: void closeRequest(); @@ -238,6 +242,8 @@ private: QUuid m_entryBeforeLock; MessageWidget* m_messageWidget; EntryPreviewWidget* m_previewView; + QString m_databaseName; + QString m_databaseFileName; // Search state QString m_lastSearchText;