From 03e85c260e9069c1b02d894d0479a55b8c51858a Mon Sep 17 00:00:00 2001 From: Colfenor <34011017+Colfenor@users.noreply.github.com> Date: Mon, 25 Sep 2023 15:21:28 +0200 Subject: [PATCH 01/12] Fix first entry is not selected when a search is performed (#9868) --- src/gui/entry/EntryView.cpp | 3 ++- tests/gui/TestGui.cpp | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/gui/entry/EntryView.cpp b/src/gui/entry/EntryView.cpp index 868250a666..032ffc4e2d 100644 --- a/src/gui/entry/EntryView.cpp +++ b/src/gui/entry/EntryView.cpp @@ -219,11 +219,12 @@ void EntryView::displaySearch(const QList& entries) m_model->setEntries(entries); header()->showSection(EntryModel::ParentGroup); + setFirstEntryActive(); + // Reset sort column to 'Group', overrides DatabaseWidgetStateSync m_sortModel->sort(EntryModel::ParentGroup, Qt::AscendingOrder); sortByColumn(EntryModel::ParentGroup, Qt::AscendingOrder); - setFirstEntryActive(); m_inSearchMode = true; } diff --git a/tests/gui/TestGui.cpp b/tests/gui/TestGui.cpp index 178402d7eb..dbc3c11c52 100644 --- a/tests/gui/TestGui.cpp +++ b/tests/gui/TestGui.cpp @@ -1078,6 +1078,13 @@ void TestGui::testSearch() QCOMPARE(groupView->currentGroup(), m_db->rootGroup()); QVERIFY(!m_dbWidget->isSearchActive()); + // check if first entry is selected after search + QTest::keyClicks(searchTextEdit, "some"); + QTRY_VERIFY(m_dbWidget->isSearchActive()); + QTRY_COMPARE(entryView->selectedEntries().length(), 1); + QModelIndex index_current = entryView->indexFromEntry(entryView->currentEntry()); + QTRY_COMPARE(index_current.row(), 0); + // Try to edit the first entry from the search view // Refocus back to search edit QTest::mouseClick(searchTextEdit, Qt::LeftButton); From c6e66fccb3dc8fd3c7a1b9732c89b072c083e079 Mon Sep 17 00:00:00 2001 From: Jonathan White Date: Wed, 16 Aug 2023 07:38:16 -0400 Subject: [PATCH 02/12] Prevent scrollbars on entry drag/drop * Fixes #9746 --- src/gui/entry/EntryView.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/gui/entry/EntryView.cpp b/src/gui/entry/EntryView.cpp index 032ffc4e2d..419a08d753 100644 --- a/src/gui/entry/EntryView.cpp +++ b/src/gui/entry/EntryView.cpp @@ -546,6 +546,8 @@ void EntryView::startDrag(Qt::DropActions supportedActions) listWidget.addItem(item); } + listWidget.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + listWidget.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); listWidget.setStyleSheet("QListWidget { background-color: palette(highlight); border: 1px solid palette(dark); " "padding: 4px; color: palette(highlighted-text); }"); auto width = listWidget.sizeHintForColumn(0) + 2 * listWidget.frameWidth(); From f8311f974b497a20d187b5f0bfa95d4859963740 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sami=20V=C3=A4nttinen?= Date: Tue, 24 Oct 2023 06:23:20 +0300 Subject: [PATCH 03/12] Fix terminating KeePassXC processes with MSI installer (#9822) --- share/windows/wix-template.xml | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/share/windows/wix-template.xml b/share/windows/wix-template.xml index ae937ce709..add2af2974 100644 --- a/share/windows/wix-template.xml +++ b/share/windows/wix-template.xml @@ -92,6 +92,9 @@ + + + @@ -116,12 +119,17 @@ - - - + + + + + + + + From 267d961af9249163e909f335d5ffa84389986a9d Mon Sep 17 00:00:00 2001 From: Martin Buchholz Date: Sat, 4 Nov 2023 16:53:22 -0700 Subject: [PATCH 04/12] Fix typo: SSH_AUTH_SOCKET --- docs/topics/UserInterface.adoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/topics/UserInterface.adoc b/docs/topics/UserInterface.adoc index b60b28a66d..456c09ea63 100644 --- a/docs/topics/UserInterface.adoc +++ b/docs/topics/UserInterface.adoc @@ -86,7 +86,7 @@ Additionally, the following environment variables may be useful when running the |KPXC_CONFIG | Override default path to roaming configuration file |KPXC_CONFIG_LOCAL | Override default path to local configuration file |KPXC_INITIAL_DIR | Override initial location picking for databases -|SSH_AUTH_SOCKET | Path of the unix file socket that the agent uses for communication with other processes (SSH Agent) +|SSH_AUTH_SOCK | Path of the unix file socket that the agent uses for communication with other processes (SSH Agent) |QT_SCALE_FACTOR [numeric] | Defines a global scale factor for the whole application, including point-sized fonts. |QT_SCREEN_SCALE_FACTORS [list] | Specifies scale factors for each screen. See https://doc.qt.io/qt-5/highdpi.html#high-dpi-support-in-qt |QT_SCALE_FACTOR_ROUNDING_POLICY | Control device pixel ratio rounding to the nearest integer. See https://doc.qt.io/qt-5/highdpi.html#high-dpi-support-in-qt From 2fef9f167dfe80eba96e9e2c84687419ecf5b6ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Remigiusz=20=C5=BB=C4=99tkowski?= Date: Sun, 5 Nov 2023 22:10:09 +0100 Subject: [PATCH 05/12] Fix docs link anchors --- src/gui/DatabaseOpenWidget.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gui/DatabaseOpenWidget.cpp b/src/gui/DatabaseOpenWidget.cpp index f3adeee29b..fcda80e43b 100644 --- a/src/gui/DatabaseOpenWidget.cpp +++ b/src/gui/DatabaseOpenWidget.cpp @@ -513,12 +513,12 @@ void DatabaseOpenWidget::hardwareKeyResponse(bool found) void DatabaseOpenWidget::openHardwareKeyHelp() { - QDesktopServices::openUrl(QUrl("https://keepassxc.org/docs#faq-cat-yubikey")); + QDesktopServices::openUrl(QUrl("https://keepassxc.org/docs/#faq-yubikey-2fa")); } void DatabaseOpenWidget::openKeyFileHelp() { - QDesktopServices::openUrl(QUrl("https://keepassxc.org/docs#faq-cat-keyfile")); + QDesktopServices::openUrl(QUrl("https://keepassxc.org/docs/#faq-keyfile-howto")); } void DatabaseOpenWidget::setUserInteractionLock(bool state) From 88ecca58e69732023666c600dbfe39dc73766096 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Barnab=C3=A1s=20P=C5=91cze?= Date: Sun, 8 Oct 2023 20:34:14 +0200 Subject: [PATCH 06/12] Do not hard-code colors in classic stylesheet for SearchBanner/KeeShareBanner Having the green-ish hard-coded color makes the banner stand out too much when the platform native theming is used. --- src/gui/styles/base/classicstyle.qss | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/gui/styles/base/classicstyle.qss b/src/gui/styles/base/classicstyle.qss index f7d3c0fb47..d0ab2b88fc 100644 --- a/src/gui/styles/base/classicstyle.qss +++ b/src/gui/styles/base/classicstyle.qss @@ -9,8 +9,9 @@ QToolTip { DatabaseWidget #SearchBanner, DatabaseWidget #KeeShareBanner { font-weight: bold; - background-color: rgb(94, 161, 14); - border: 1px solid rgb(190, 190, 190); + background-color: palette(highlight); + color: palette(highlighted-text); + border: 1px solid palette(dark); padding: 2px; } From a0af0934c021c482417cfece1f5c64c5e13cb725 Mon Sep 17 00:00:00 2001 From: Jonathan White Date: Tue, 30 Jan 2024 18:44:43 -0500 Subject: [PATCH 07/12] Fix multiple TOTP issues * Fix #9847 - don't provide TOTP values if settings are blank or completely wrong * Fix #6838 - don't reset the ui when creating a new entry and applying TOTP to it * Move totp source into the core folder --- src/CMakeLists.txt | 4 ++-- src/core/Entry.cpp | 4 ++-- src/{totp/totp.cpp => core/Totp.cpp} | 12 +++++++++++- src/{totp/totp.h => core/Totp.h} | 0 src/format/OpVaultReaderSections.cpp | 2 +- src/gui/DatabaseWidget.cpp | 4 ++++ src/gui/EntryPreviewWidget.cpp | 2 +- src/gui/TotpDialog.cpp | 2 +- src/gui/TotpExportSettingsDialog.cpp | 2 +- src/gui/TotpSetupDialog.cpp | 2 +- src/gui/csvImport/CsvImportWidget.cpp | 4 ++-- src/gui/entry/EditEntryWidget.cpp | 12 ++++++++++-- src/gui/entry/EditEntryWidget.h | 1 + tests/TestCsvExporter.cpp | 2 +- tests/TestOpVaultReader.cpp | 2 +- tests/TestTotp.cpp | 14 +++++++++++++- 16 files changed, 52 insertions(+), 17 deletions(-) rename src/{totp/totp.cpp => core/Totp.cpp} (97%) rename src/{totp/totp.h => core/Totp.h} (100%) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 07534bbd7f..c0b62f8586 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -59,6 +59,7 @@ set(keepassx_SOURCES core/TimeDelta.cpp core/TimeInfo.cpp core/Tools.cpp + core/Totp.cpp core/Translator.cpp core/UrlTools.cpp cli/Utils.cpp @@ -193,8 +194,7 @@ set(keepassx_SOURCES streams/LayeredStream.cpp streams/qtiocompressor.cpp streams/StoreDataStream.cpp - streams/SymmetricCipherStream.cpp - totp/totp.cpp) + streams/SymmetricCipherStream.cpp) if(APPLE) set(keepassx_SOURCES ${keepassx_SOURCES} diff --git a/src/core/Entry.cpp b/src/core/Entry.cpp index 04120e90b2..d0c6f3b905 100644 --- a/src/core/Entry.cpp +++ b/src/core/Entry.cpp @@ -24,7 +24,7 @@ #include "core/Metadata.h" #include "core/PasswordHealth.h" #include "core/Tools.h" -#include "totp/totp.h" +#include "core/Totp.h" #include #include @@ -566,7 +566,7 @@ void Entry::setTotp(QSharedPointer settings) m_attributes->remove(Totp::ATTRIBUTE_SEED); m_attributes->remove(Totp::ATTRIBUTE_SETTINGS); - if (settings->key.isEmpty()) { + if (!settings || settings->key.isEmpty()) { m_data.totpSettings.reset(); } else { m_data.totpSettings = std::move(settings); diff --git a/src/totp/totp.cpp b/src/core/Totp.cpp similarity index 97% rename from src/totp/totp.cpp rename to src/core/Totp.cpp index dc58f158db..f55312a9db 100644 --- a/src/totp/totp.cpp +++ b/src/core/Totp.cpp @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -#include "totp.h" +#include "Totp.h" #include "core/Base32.h" #include "core/Clock.h" @@ -59,6 +59,11 @@ static QString getNameForHashType(const Totp::Algorithm hashType) QSharedPointer Totp::parseSettings(const QString& rawSettings, const QString& key) { + // Early out if both strings are empty + if (rawSettings.isEmpty() && key.isEmpty()) { + return {}; + } + // Create default settings auto settings = createSettings(key, DEFAULT_DIGITS, DEFAULT_STEP); @@ -96,6 +101,11 @@ QSharedPointer Totp::parseSettings(const QString& rawSettings, c settings->algorithm = getHashTypeByName(query.queryItemValue("otpHashMode")); } } else { + if (settings->key.isEmpty()) { + // Legacy format cannot work with an empty key + return {}; + } + // Parse semi-colon separated values ([step];[digits|S]) settings->format = StorageFormat::LEGACY; auto vars = rawSettings.split(";"); diff --git a/src/totp/totp.h b/src/core/Totp.h similarity index 100% rename from src/totp/totp.h rename to src/core/Totp.h diff --git a/src/format/OpVaultReaderSections.cpp b/src/format/OpVaultReaderSections.cpp index 661b9d6c3e..d05f8fca12 100644 --- a/src/format/OpVaultReaderSections.cpp +++ b/src/format/OpVaultReaderSections.cpp @@ -18,7 +18,7 @@ #include "OpVaultReader.h" #include "core/Entry.h" -#include "totp/totp.h" +#include "core/Totp.h" #include #include diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index 94fa2fbe75..2322064157 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -493,6 +493,10 @@ void DatabaseWidget::setupTotp() auto setupTotpDialog = new TotpSetupDialog(this, currentEntry); connect(setupTotpDialog, SIGNAL(totpUpdated()), SIGNAL(entrySelectionChanged())); + if (currentWidget() == m_editEntryWidget) { + // Entry is being edited, tell it when we are finished updating TOTP + connect(setupTotpDialog, SIGNAL(totpUpdated()), m_editEntryWidget, SLOT(updateTotp())); + } connect(this, &DatabaseWidget::databaseLockRequested, setupTotpDialog, &TotpSetupDialog::close); setupTotpDialog->open(); } diff --git a/src/gui/EntryPreviewWidget.cpp b/src/gui/EntryPreviewWidget.cpp index 7d7151c0db..b7c8ca1bcc 100644 --- a/src/gui/EntryPreviewWidget.cpp +++ b/src/gui/EntryPreviewWidget.cpp @@ -21,10 +21,10 @@ #include "Application.h" #include "core/Config.h" +#include "core/Totp.h" #include "gui/Clipboard.h" #include "gui/Font.h" #include "gui/Icons.h" -#include "totp/totp.h" #if defined(WITH_XC_KEESHARE) #include "keeshare/KeeShare.h" #include "keeshare/KeeShareSettings.h" diff --git a/src/gui/TotpDialog.cpp b/src/gui/TotpDialog.cpp index e856f5d6a4..577fa10563 100644 --- a/src/gui/TotpDialog.cpp +++ b/src/gui/TotpDialog.cpp @@ -20,9 +20,9 @@ #include "ui_TotpDialog.h" #include "core/Clock.h" +#include "core/Totp.h" #include "gui/Clipboard.h" #include "gui/MainWindow.h" -#include "totp/totp.h" #include #include diff --git a/src/gui/TotpExportSettingsDialog.cpp b/src/gui/TotpExportSettingsDialog.cpp index 1ac5231d40..8e56d5d2ec 100644 --- a/src/gui/TotpExportSettingsDialog.cpp +++ b/src/gui/TotpExportSettingsDialog.cpp @@ -17,11 +17,11 @@ #include "TotpExportSettingsDialog.h" +#include "core/Totp.h" #include "gui/Clipboard.h" #include "gui/MainWindow.h" #include "gui/SquareSvgWidget.h" #include "qrcode/QrCode.h" -#include "totp/totp.h" #include #include diff --git a/src/gui/TotpSetupDialog.cpp b/src/gui/TotpSetupDialog.cpp index d33d9ce398..e796e1d5e3 100644 --- a/src/gui/TotpSetupDialog.cpp +++ b/src/gui/TotpSetupDialog.cpp @@ -19,8 +19,8 @@ #include "ui_TotpSetupDialog.h" #include "core/Base32.h" +#include "core/Totp.h" #include "gui/MessageBox.h" -#include "totp/totp.h" TotpSetupDialog::TotpSetupDialog(QWidget* parent, Entry* entry) : QDialog(parent) diff --git a/src/gui/csvImport/CsvImportWidget.cpp b/src/gui/csvImport/CsvImportWidget.cpp index a3a30e4c33..08f6d6589c 100644 --- a/src/gui/csvImport/CsvImportWidget.cpp +++ b/src/gui/csvImport/CsvImportWidget.cpp @@ -22,9 +22,9 @@ #include #include "core/Clock.h" +#include "core/Totp.h" #include "format/KeePass2Writer.h" #include "gui/MessageBox.h" -#include "totp/totp.h" // I wanted to make the CSV import GUI future-proof, so if one day you need a new field, // all you have to do is add a field to m_columnHeader, and the GUI will follow: @@ -208,7 +208,7 @@ void CsvImportWidget::writeDatabase() auto otpString = m_parserModel->data(m_parserModel->index(r, 6)); if (otpString.isValid() && !otpString.toString().isEmpty()) { auto totp = Totp::parseSettings(otpString.toString()); - if (totp->key.isEmpty()) { + if (!totp || totp->key.isEmpty()) { // Bare secret, use default TOTP settings totp = Totp::parseSettings({}, otpString.toString()); } diff --git a/src/gui/entry/EditEntryWidget.cpp b/src/gui/entry/EditEntryWidget.cpp index a1297d4c0c..6d8ba5061e 100644 --- a/src/gui/entry/EditEntryWidget.cpp +++ b/src/gui/entry/EditEntryWidget.cpp @@ -114,6 +114,7 @@ EditEntryWidget::EditEntryWidget(QWidget* parent) m_entryModifiedTimer.setSingleShot(true); m_entryModifiedTimer.setInterval(0); connect(&m_entryModifiedTimer, &QTimer::timeout, this, [this] { + // TODO: Upon refactor of this widget, this needs to merge unsaved changes in the UI if (isVisible() && m_entry) { setForms(m_entry); } @@ -704,6 +705,13 @@ void EditEntryWidget::toKeeAgentSettings(KeeAgentSettings& settings) const settings.setSaveAttachmentToTempFile(m_sshAgentSettings.saveAttachmentToTempFile()); } +void EditEntryWidget::updateTotp() +{ + if (m_entry) { + m_attributesModel->setEntryAttributes(m_entry->attributes()); + } +} + void EditEntryWidget::browsePrivateKey() { auto fileName = fileDialog()->getOpenFileName(this, tr("Select private key"), FileDialog::getLastDir("sshagent")); @@ -828,8 +836,6 @@ void EditEntryWidget::loadEntry(Entry* entry, m_create = create; m_history = history; - connect(m_entry, &Entry::modified, this, [this] { m_entryModifiedTimer.start(); }); - if (history) { setHeadline(QString("%1 \u2022 %2").arg(parentName, tr("Entry history"))); } else { @@ -837,6 +843,8 @@ void EditEntryWidget::loadEntry(Entry* entry, setHeadline(QString("%1 \u2022 %2").arg(parentName, tr("Add entry"))); } else { setHeadline(QString("%1 \u2022 %2 \u2022 %3").arg(parentName, entry->title(), tr("Edit entry"))); + // Reload entry details if changed outside of the edit dialog + connect(m_entry, &Entry::modified, this, [this] { m_entryModifiedTimer.start(); }); } } diff --git a/src/gui/entry/EditEntryWidget.h b/src/gui/entry/EditEntryWidget.h index 3bd67032f3..fddf64eda8 100644 --- a/src/gui/entry/EditEntryWidget.h +++ b/src/gui/entry/EditEntryWidget.h @@ -118,6 +118,7 @@ private slots: void updateSSHAgentAttachment(); void updateSSHAgentAttachments(); void updateSSHAgentKeyInfo(); + void updateTotp(); void browsePrivateKey(); void addKeyToAgent(); void removeKeyFromAgent(); diff --git a/tests/TestCsvExporter.cpp b/tests/TestCsvExporter.cpp index 4854da1116..c4c937e5d8 100644 --- a/tests/TestCsvExporter.cpp +++ b/tests/TestCsvExporter.cpp @@ -22,9 +22,9 @@ #include #include "core/Group.h" +#include "core/Totp.h" #include "crypto/Crypto.h" #include "format/CsvExporter.h" -#include "totp/totp.h" QTEST_GUILESS_MAIN(TestCsvExporter) diff --git a/tests/TestOpVaultReader.cpp b/tests/TestOpVaultReader.cpp index a5d05e9b07..4899d335fb 100644 --- a/tests/TestOpVaultReader.cpp +++ b/tests/TestOpVaultReader.cpp @@ -20,9 +20,9 @@ #include "config-keepassx-tests.h" #include "core/Group.h" #include "core/Metadata.h" +#include "core/Totp.h" #include "crypto/Crypto.h" #include "format/OpVaultReader.h" -#include "totp/totp.h" #include #include diff --git a/tests/TestTotp.cpp b/tests/TestTotp.cpp index bb3b55dbd8..f2bb3d47a3 100644 --- a/tests/TestTotp.cpp +++ b/tests/TestTotp.cpp @@ -19,8 +19,8 @@ #include "TestTotp.h" #include "core/Entry.h" +#include "core/Totp.h" #include "crypto/Crypto.h" -#include "totp/totp.h" #include @@ -97,6 +97,14 @@ void TestTotp::testParseSecret() QCOMPARE(settings->digits, 6u); QCOMPARE(settings->step, 30u); QCOMPARE(settings->algorithm, Totp::Algorithm::Sha1); + + // Blank settings (expected failure) + settings = Totp::parseSettings("", ""); + QVERIFY(settings.isNull()); + + // TOTP Settings with blank secret (expected failure) + settings = Totp::parseSettings("30;8", ""); + QVERIFY(settings.isNull()); } void TestTotp::testTotpCode() @@ -164,4 +172,8 @@ void TestTotp::testEntryHistory() entry.setTotp(settings); QCOMPARE(entry.historyItems().size(), 2); QCOMPARE(entry.totpSettings()->key, QString("foo")); + // Nullptr Settings (expected reset of TOTP) + entry.setTotp(nullptr); + QVERIFY(!entry.hasTotp()); + QCOMPARE(entry.historyItems().size(), 3); } From 54807ce221168104efc112b95c3e780b0ffefe30 Mon Sep 17 00:00:00 2001 From: Jonathan White Date: Tue, 30 Jan 2024 18:45:26 -0500 Subject: [PATCH 08/12] Fix database merge crash when fdosecrets is enabled (#10136) * Entry: re-parent before adding to new group Adding the Entry to the Group will emit signals about the action. Present the object with the correct parent already. * fdosecrets: Item::Create() can fail If an entry cannot be registered on DBus, Item::Create() will return a nullptr. Basically, this can only happen if there is already an item with the same UUID in the collection. The only viable option here is to ignore the new entry. * Merger: prevent duplicate entry when merging histories If the source entry is newer, a copy of the entry is made. But before moving the merged entry to the target group, it must be removed. Otherwise there will be briefly two entries with the same UUID in the same group/database. Even though this is only the case during the transaction, it can still be observed because the operations emit signals. A notable problem is the fdosecrets feature that relies on the uniqueness of the UUID or will otherwise run into problems because the UUID is used as part of the DBus path. --- src/core/Entry.cpp | 4 ++-- src/core/Merger.cpp | 13 ++++++++----- src/core/Merger.h | 2 +- src/fdosecrets/objects/Collection.cpp | 4 ++++ 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/core/Entry.cpp b/src/core/Entry.cpp index d0c6f3b905..e237ae53d5 100644 --- a/src/core/Entry.cpp +++ b/src/core/Entry.cpp @@ -1279,11 +1279,11 @@ void Entry::setGroup(Group* group, bool trackPrevious) } } + QObject::setParent(group); + m_group = group; group->addEntry(this); - QObject::setParent(group); - if (m_updateTimeinfo) { m_data.timeInfo.setLocationChanged(Clock::currentDateTimeUtc()); } diff --git a/src/core/Merger.cpp b/src/core/Merger.cpp index fd30da7aa4..0ffb94b9e6 100644 --- a/src/core/Merger.cpp +++ b/src/core/Merger.cpp @@ -338,6 +338,7 @@ Merger::ChangeList Merger::resolveEntryConflict_MergeHistories(const MergeContex const int comparison = compare(targetEntry->timeInfo().lastModificationTime(), sourceEntry->timeInfo().lastModificationTime(), CompareItemIgnoreMilliseconds); + const int maxItems = targetEntry->database()->metadata()->historyMaxItems(); if (comparison < 0) { Group* currentGroup = targetEntry->group(); Entry* clonedEntry = sourceEntry->clone(Entry::CloneIncludeHistory); @@ -346,15 +347,15 @@ Merger::ChangeList Merger::resolveEntryConflict_MergeHistories(const MergeContex qPrintable(sourceEntry->title()), qPrintable(currentGroup->name())); changes << tr("Synchronizing from newer source %1 [%2]").arg(targetEntry->title(), targetEntry->uuidToHex()); - moveEntry(clonedEntry, currentGroup); - mergeHistory(targetEntry, clonedEntry, mergeMethod); + mergeHistory(targetEntry, clonedEntry, mergeMethod, maxItems); eraseEntry(targetEntry); + moveEntry(clonedEntry, currentGroup); } else { qDebug("Merge %s/%s with local on top/under %s", qPrintable(targetEntry->title()), qPrintable(sourceEntry->title()), qPrintable(targetEntry->group()->name())); - const bool changed = mergeHistory(sourceEntry, targetEntry, mergeMethod); + const bool changed = mergeHistory(sourceEntry, targetEntry, mergeMethod, maxItems); if (changed) { changes << tr("Synchronizing from older source %1 [%2]").arg(targetEntry->title(), targetEntry->uuidToHex()); @@ -400,7 +401,10 @@ Merger::resolveEntryConflict(const MergeContext& context, const Entry* sourceEnt return changes; } -bool Merger::mergeHistory(const Entry* sourceEntry, Entry* targetEntry, Group::MergeMode mergeMethod) +bool Merger::mergeHistory(const Entry* sourceEntry, + Entry* targetEntry, + Group::MergeMode mergeMethod, + const int maxItems) { Q_UNUSED(mergeMethod); const auto targetHistoryItems = targetEntry->historyItems(); @@ -473,7 +477,6 @@ bool Merger::mergeHistory(const Entry* sourceEntry, Entry* targetEntry, Group::M } bool changed = false; - const int maxItems = targetEntry->database()->metadata()->historyMaxItems(); const auto updatedHistoryItems = merged.values(); for (int i = 0; i < maxItems; ++i) { const Entry* oldEntry = targetHistoryItems.value(targetHistoryItems.count() - i); diff --git a/src/core/Merger.h b/src/core/Merger.h index 4b277f9561..ea45a6d141 100644 --- a/src/core/Merger.h +++ b/src/core/Merger.h @@ -50,7 +50,7 @@ class Merger : public QObject ChangeList mergeDeletions(const MergeContext& context); ChangeList mergeMetadata(const MergeContext& context); bool markOlderEntry(Entry* entry); - bool mergeHistory(const Entry* sourceEntry, Entry* targetEntry, Group::MergeMode mergeMethod); + bool mergeHistory(const Entry* sourceEntry, Entry* targetEntry, Group::MergeMode mergeMethod, const int maxItems); void moveEntry(Entry* entry, Group* targetGroup); void moveGroup(Group* group, Group* targetGroup); // remove an entry without a trace in the deletedObjects - needed for elemination cloned entries diff --git a/src/fdosecrets/objects/Collection.cpp b/src/fdosecrets/objects/Collection.cpp index 4cc6ca537d..be452d4299 100644 --- a/src/fdosecrets/objects/Collection.cpp +++ b/src/fdosecrets/objects/Collection.cpp @@ -495,6 +495,10 @@ namespace FdoSecrets } auto item = Item::Create(this, entry); + if (!item) { + return; + } + m_items << item; m_entryToItem[entry] = item; From 17629c4e43819af684fafba975e1f471d37620db Mon Sep 17 00:00:00 2001 From: wise0n <96984685+wise0n@users.noreply.github.com> Date: Wed, 23 Aug 2023 11:57:01 -0400 Subject: [PATCH 09/12] Fix menu location in alert --- share/translations/keepassxc_en.ts | 8 ++++---- src/gui/EditWidgetIcons.cpp | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/share/translations/keepassxc_en.ts b/share/translations/keepassxc_en.ts index 4427ca4882..884c34d9e9 100644 --- a/share/translations/keepassxc_en.ts +++ b/share/translations/keepassxc_en.ts @@ -3468,10 +3468,6 @@ Supported extensions are: %1. Unable to fetch favicon. - - You can enable the DuckDuckGo website icon service under Tools -> Settings -> Security - - Existing icon selected. @@ -3513,6 +3509,10 @@ Supported extensions are: %1. + + You can enable the DuckDuckGo website icon service under Application Settings -> Security + + EditWidgetProperties diff --git a/src/gui/EditWidgetIcons.cpp b/src/gui/EditWidgetIcons.cpp index 25542730cc..c91974c4c4 100644 --- a/src/gui/EditWidgetIcons.cpp +++ b/src/gui/EditWidgetIcons.cpp @@ -222,7 +222,7 @@ void EditWidgetIcons::iconReceived(const QString& url, const QImage& icon) QString message(tr("Unable to fetch favicon.")); if (!config()->get(Config::Security_IconDownloadFallback).toBool()) { message.append("\n").append( - tr("You can enable the DuckDuckGo website icon service under Tools -> Settings -> Security")); + tr("You can enable the DuckDuckGo website icon service under Application Settings -> Security")); } emit messageEditEntry(message, MessageWidget::Error); return; From 5f1c02fafb2b708d8307472a54f9908d9006549e Mon Sep 17 00:00:00 2001 From: f4lkensmaz3 <96619492+f4lkensmaz3@users.noreply.github.com> Date: Sat, 6 Jan 2024 21:54:30 +0000 Subject: [PATCH 10/12] Prevent duplicate characters in "Also choose from" field of password generator (#9803) * Fixes #9797 --- src/core/PasswordGenerator.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/core/PasswordGenerator.cpp b/src/core/PasswordGenerator.cpp index 01641bc802..aa0f3e7172 100644 --- a/src/core/PasswordGenerator.cpp +++ b/src/core/PasswordGenerator.cpp @@ -261,8 +261,10 @@ QVector PasswordGenerator::passwordGroups() const if (!m_custom.isEmpty()) { PasswordGroup group; - for (auto ch : m_custom) { - group.append(ch); + for (const auto& ch : m_custom) { + if (!group.contains(ch)) { + group.append(ch); + } } passwordGroups.append(group); From 8ec50c6158d52ef477f27464494541695c015025 Mon Sep 17 00:00:00 2001 From: BGM99 <45606744+BGM99@users.noreply.github.com> Date: Sun, 3 Dec 2023 02:58:41 +0100 Subject: [PATCH 11/12] Fix focus loss on save when the widget is not visible anymore --- src/gui/DatabaseWidget.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index 2322064157..deeec3662f 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -2155,7 +2155,7 @@ bool DatabaseWidget::performSave(QString& errorMessage, const QString& fileName) m_groupView->setDisabled(false); m_tagView->setDisabled(false); - if (focusWidget) { + if (focusWidget && focusWidget->isVisible()) { focusWidget->setFocus(); } From c6c95f73ab40b936f8e8ffc9295e1ae91f0bf738 Mon Sep 17 00:00:00 2001 From: qycyfjy Date: Sat, 21 Oct 2023 05:50:55 +0800 Subject: [PATCH 12/12] Fix removing entry from history and improve logic of history tab showing --- src/gui/entry/EditEntryWidget.cpp | 1 + src/gui/entry/EntryHistoryModel.cpp | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/gui/entry/EditEntryWidget.cpp b/src/gui/entry/EditEntryWidget.cpp index 6d8ba5061e..b84fa2488f 100644 --- a/src/gui/entry/EditEntryWidget.cpp +++ b/src/gui/entry/EditEntryWidget.cpp @@ -1151,6 +1151,7 @@ bool EditEntryWidget::commitEntry() } m_historyModel->setEntries(m_entry->historyItems(), m_entry); + setPageHidden(m_historyWidget, m_history || m_entry->historyItems().count() < 1); m_advancedUi->attachmentsWidget->linkAttachments(m_entry->attachments()); showMessage(tr("Entry updated successfully."), MessageWidget::Positive); diff --git a/src/gui/entry/EntryHistoryModel.cpp b/src/gui/entry/EntryHistoryModel.cpp index acde63cb52..618f4328f0 100644 --- a/src/gui/entry/EntryHistoryModel.cpp +++ b/src/gui/entry/EntryHistoryModel.cpp @@ -158,9 +158,10 @@ void EntryHistoryModel::deleteIndex(QModelIndex index) { auto entry = entryFromIndex(index); if (entry) { - beginRemoveRows(QModelIndex(), m_historyEntries.indexOf(entry), m_historyEntries.indexOf(entry)); + beginRemoveRows(QModelIndex(), index.row(), index.row()); m_historyEntries.removeAll(entry); m_deletedHistoryEntries << entry; + m_historyModifications.erase(m_historyModifications.begin() + index.row()); endRemoveRows(); } }