diff --git a/src/browser/BrowserEntryConfig.cpp b/src/browser/BrowserEntryConfig.cpp index 2fcc48a281..248e92c06c 100644 --- a/src/browser/BrowserEntryConfig.cpp +++ b/src/browser/BrowserEntryConfig.cpp @@ -21,7 +21,7 @@ #include "core/Entry.h" #include "core/EntryAttributes.h" -static const char KEEPASSBROWSER_NAME[] = "KeePassXC-Browser Settings"; +static const char KEEPASSXCBROWSER_NAME[] = "KeePassXC-Browser Settings"; BrowserEntryConfig::BrowserEntryConfig(QObject* parent) : @@ -83,7 +83,7 @@ void BrowserEntryConfig::setRealm(const QString& realm) bool BrowserEntryConfig::load(const Entry* entry) { - QString s = entry->customData()->value(KEEPASSBROWSER_NAME); + QString s = entry->customData()->value(KEEPASSXCBROWSER_NAME); if (s.isEmpty()) { return false; } @@ -105,5 +105,5 @@ void BrowserEntryConfig::save(Entry* entry) QVariantMap v = qo2qv(this); QJsonObject o = QJsonObject::fromVariantMap(v); QByteArray json = QJsonDocument(o).toJson(QJsonDocument::Compact); - entry->customData()->set(KEEPASSBROWSER_NAME, json); + entry->customData()->set(KEEPASSXCBROWSER_NAME, json); } diff --git a/src/browser/BrowserOptionDialog.cpp b/src/browser/BrowserOptionDialog.cpp index 835a8f502b..bb1b7b97a5 100755 --- a/src/browser/BrowserOptionDialog.cpp +++ b/src/browser/BrowserOptionDialog.cpp @@ -39,7 +39,7 @@ BrowserOptionDialog::BrowserOptionDialog(DatabaseTabWidget* parent) : connect(m_ui->customDataTable->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)), SLOT(toggleRemoveButton(QItemSelection))); connect(m_ui->removeCustomDataButton, SIGNAL(clicked()), SLOT(removeSelectedKey())); - + connect(m_ui->convertToCustomData, SIGNAL(clicked()), this, SIGNAL(convertAttributesToCustomData())); connect(m_ui->removeSharedEncryptionKeys, SIGNAL(clicked()), this, SIGNAL(removeSharedEncryptionKeys())); connect(m_ui->removeStoredPermissions, SIGNAL(clicked()), this, SIGNAL(removeStoredPermissions())); diff --git a/src/browser/BrowserOptionDialog.h b/src/browser/BrowserOptionDialog.h index 4f6855798d..bf4c39bb4f 100755 --- a/src/browser/BrowserOptionDialog.h +++ b/src/browser/BrowserOptionDialog.h @@ -49,6 +49,7 @@ public slots: signals: void removeSharedEncryptionKeys(); void removeStoredPermissions(); + void convertAttributesToCustomData(); private slots: void showProxyLocationFileDialog(); diff --git a/src/browser/BrowserOptionDialog.ui b/src/browser/BrowserOptionDialog.ui index 69caf693fe..f4ef971174 100755 --- a/src/browser/BrowserOptionDialog.ui +++ b/src/browser/BrowserOptionDialog.ui @@ -230,6 +230,23 @@ + + + + + + + 0 + 0 + + + + Move KeePassHTTP attributes to KeePassXC-Browser &custom data + + + + + diff --git a/src/browser/BrowserService.cpp b/src/browser/BrowserService.cpp index 472a0ddbea..814547db0c 100644 --- a/src/browser/BrowserService.cpp +++ b/src/browser/BrowserService.cpp @@ -44,6 +44,9 @@ static const char KEEPASSXCBROWSER_NAME[] = "KeePassXC-Browser Settings"; static const char ASSOCIATE_KEY_PREFIX[] = "Public Key: "; static const char KEEPASSXCBROWSER_GROUP_NAME[] = "KeePassXC-Browser Passwords"; static int KEEPASSXCBROWSER_DEFAULT_ICON = 1; +// These are for the settings and password conversion +static const char KEEPASSHTTP_NAME[] = "KeePassHttp Settings"; +static const char KEEPASSHTTP_GROUP_NAME[] = "KeePassHttp Passwords"; BrowserService::BrowserService(DatabaseTabWidget* parent) : m_dbTabWidget(parent), @@ -132,36 +135,6 @@ QString BrowserService::getDatabaseRecycleBinUuid() return recycleBin->uuid().toHex(); } -Entry* BrowserService::getConfigEntry(bool create) -{ - Entry* entry = nullptr; - Database* db = getDatabase(); - if (!db) { - return nullptr; - } - - entry = db->resolveEntry(KEEPASSXCBROWSER_UUID); - if (!entry && create) { - entry = new Entry(); - entry->setTitle(QLatin1String(KEEPASSXCBROWSER_NAME)); - entry->setUuid(KEEPASSXCBROWSER_UUID); - entry->setAutoTypeEnabled(false); - entry->setGroup(db->rootGroup()); - return entry; - } - - if (entry && entry->group() == db->metadata()->recycleBin()) { - if (!create) { - return nullptr; - } else { - entry->setGroup(db->rootGroup()); - return entry; - } - } - - return entry; -} - QString BrowserService::storeKey(const QString& key) { QString id; @@ -500,6 +473,69 @@ void BrowserService::removeStoredPermissions() } } +void BrowserService::convertAttributesToCustomData() +{ + if (!isDatabaseOpened()) { + QMessageBox::critical(0, tr("KeePassXC: Database locked!"), + tr("The active database is locked!\n" + "Please unlock the selected database or choose another one which is unlocked."), + QMessageBox::Ok); + return; + } + + Database* db = m_dbTabWidget->currentDatabaseWidget()->database(); + if (!db) { + return; + } + + QList entries = db->rootGroup()->entriesRecursive(); + + QProgressDialog progress(tr("Converting attributes to custom data…"), tr("Abort"), 0, entries.count()); + progress.setWindowModality(Qt::WindowModal); + + uint counter = 0; + for (Entry* entry : entries) { + if (progress.wasCanceled()) { + return; + } + + if (moveSettingsToCustomData(entry, KEEPASSHTTP_NAME)) { + ++counter; + } + if (moveSettingsToCustomData(entry, KEEPASSXCBROWSER_NAME)) { + ++counter; + } + progress.setValue(progress.value() + 1); + } + progress.reset(); + + if (counter > 0) { + QMessageBox::information(0, tr("KeePassXC: Converted KeePassHTTP attributes"), + tr("Successfully converted attributes from %n entry(s).", "", counter), + QMessageBox::Ok); + } else { + QMessageBox::information(0, tr("KeePassXC: No entry with KeePassHTTP attributes found!"), + tr("The active database does not contain an entry with KeePassHTTP attributes."), + QMessageBox::Ok); + } + + // Rename password groupName + Group* rootGroup = db->rootGroup(); + if (!rootGroup) { + return; + } + + const QString keePassBrowserGroupName = QLatin1String(KEEPASSXCBROWSER_GROUP_NAME); + const QString keePassHttpGroupName = QLatin1String(KEEPASSHTTP_GROUP_NAME); + + for (Group* g : rootGroup->groupsRecursive(true)) { + if (g->name() == keePassHttpGroupName) { + g->setName(keePassBrowserGroupName); + break; + } + } +} + QList BrowserService::sortEntries(QList& pwEntries, const QString& host, const QString& entryUrl) { QUrl url(entryUrl); @@ -715,6 +751,21 @@ Database* BrowserService::getDatabase() return nullptr; } +bool BrowserService::moveSettingsToCustomData(Entry* entry, const QString& name) const +{ + if (entry->attributes()->contains(name)) { + QString attr = entry->attributes()->value(name); + entry->beginUpdate(); + if (!attr.isEmpty()) { + entry->customData()->set(KEEPASSXCBROWSER_NAME, attr); + } + entry->attributes()->remove(name); + entry->endUpdate(); + return true; + } + return false; +} + void BrowserService::databaseLocked(DatabaseWidget* dbWidget) { if (dbWidget) { diff --git a/src/browser/BrowserService.h b/src/browser/BrowserService.h index 5a96e1ecd2..6e7a88a575 100644 --- a/src/browser/BrowserService.h +++ b/src/browser/BrowserService.h @@ -38,13 +38,13 @@ class BrowserService : public QObject bool openDatabase(bool triggerUnlock); QString getDatabaseRootUuid(); QString getDatabaseRecycleBinUuid(); - Entry* getConfigEntry(bool create = false); QString getKey(const QString& id); void addEntry(const QString& id, const QString& login, const QString& password, const QString& url, const QString& submitUrl, const QString& realm); QList searchEntries(Database* db, const QString& hostname); QList searchEntries(const QString& text); void removeSharedEncryptionKeys(); void removeStoredPermissions(); + void convertAttributesToCustomData(); public slots: QJsonArray findMatchingEntries(const QString& id, const QString& url, const QString& submitUrl, const QString& realm); @@ -73,6 +73,7 @@ public slots: bool matchUrlScheme(const QString& url); bool removeFirstDomain(QString& hostname); Database* getDatabase(); + bool moveSettingsToCustomData(Entry* entry, const QString& name) const; private: DatabaseTabWidget* const m_dbTabWidget; diff --git a/src/browser/NativeMessagingHost.cpp b/src/browser/NativeMessagingHost.cpp index 4dfa87d511..9d2c36136c 100755 --- a/src/browser/NativeMessagingHost.cpp +++ b/src/browser/NativeMessagingHost.cpp @@ -193,6 +193,12 @@ void NativeMessagingHost::removeStoredPermissions() m_browserService.removeStoredPermissions(); } +void NativeMessagingHost::convertAttributesToCustomData() +{ + QMutexLocker locker(&m_mutex); + m_browserService.convertAttributesToCustomData(); +} + void NativeMessagingHost::databaseLocked() { QJsonObject response; diff --git a/src/browser/NativeMessagingHost.h b/src/browser/NativeMessagingHost.h index 80825237b3..9db062badb 100755 --- a/src/browser/NativeMessagingHost.h +++ b/src/browser/NativeMessagingHost.h @@ -40,6 +40,7 @@ class NativeMessagingHost : public NativeMessagingBase public slots: void removeSharedEncryptionKeys(); void removeStoredPermissions(); + void convertAttributesToCustomData(); signals: void quit(); diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index dd0c036019..a802e757a2 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -140,6 +140,7 @@ class BrowserPlugin: public ISettingsPage BrowserOptionDialog* dlg = new BrowserOptionDialog(m_dbTabWidget); QObject::connect(dlg, SIGNAL(removeSharedEncryptionKeys()), m_nativeMessagingHost.data(), SLOT(removeSharedEncryptionKeys())); QObject::connect(dlg, SIGNAL(removeStoredPermissions()), m_nativeMessagingHost.data(), SLOT(removeStoredPermissions())); + QObject::connect(dlg, SIGNAL(convertAttributesToCustomData()), m_nativeMessagingHost.data(), SLOT(convertAttributesToCustomData())); return dlg; }