diff --git a/share/translations/keepassxc_en.ts b/share/translations/keepassxc_en.ts
index 183837fae..b93ebac90 100644
--- a/share/translations/keepassxc_en.ts
+++ b/share/translations/keepassxc_en.ts
@@ -10158,6 +10158,10 @@ This option is deprecated, use --set-key-file instead.
Weak Passwords
+
+ TOTP Entries
+
+
TagView
diff --git a/src/core/EntrySearcher.cpp b/src/core/EntrySearcher.cpp
index 085c5bcdb..cb9e135fe 100644
--- a/src/core/EntrySearcher.cpp
+++ b/src/core/EntrySearcher.cpp
@@ -221,6 +221,13 @@ bool EntrySearcher::searchEntryImpl(const Entry* entry)
}
found = false;
break;
+ case Field::Has:
+ if (term.word.compare("totp", Qt::CaseInsensitive) == 0) {
+ found = entry->hasTotp();
+ break;
+ }
+ found = false;
+ break;
case Field::Uuid:
found = term.regex.match(entry->uuidToHex()).hasMatch();
break;
@@ -260,6 +267,7 @@ void EntrySearcher::parseSearchTerms(const QString& searchString)
{QStringLiteral("group"), Field::Group},
{QStringLiteral("tag"), Field::Tag},
{QStringLiteral("is"), Field::Is},
+ {QStringLiteral("has"), Field::Has},
{QStringLiteral("uuid"), Field::Uuid}};
// Group 1 = modifiers, Group 2 = field, Group 3 = quoted string, Group 4 = unquoted string
diff --git a/src/core/EntrySearcher.h b/src/core/EntrySearcher.h
index 0134054bf..a15916fb0 100644
--- a/src/core/EntrySearcher.h
+++ b/src/core/EntrySearcher.h
@@ -41,6 +41,7 @@ public:
Group,
Tag,
Is,
+ Has,
Uuid
};
diff --git a/src/gui/tag/TagModel.cpp b/src/gui/tag/TagModel.cpp
index dfa4e943d..a19f263d8 100644
--- a/src/gui/tag/TagModel.cpp
+++ b/src/gui/tag/TagModel.cpp
@@ -30,7 +30,8 @@ TagModel::TagModel(QObject* parent)
{
m_defaultSearches << qMakePair(tr("Clear Search"), QString("")) << qMakePair(tr("All Entries"), QString("*"))
<< qMakePair(tr("Expired"), QString("is:expired"))
- << qMakePair(tr("Weak Passwords"), QString("is:weak"));
+ << qMakePair(tr("Weak Passwords"), QString("is:weak"))
+ << qMakePair(tr("TOTP Entries"), QString("has:totp"));
}
TagModel::~TagModel() = default;
diff --git a/tests/TestEntrySearcher.cpp b/tests/TestEntrySearcher.cpp
index 7a7e45323..d002a3166 100644
--- a/tests/TestEntrySearcher.cpp
+++ b/tests/TestEntrySearcher.cpp
@@ -18,6 +18,7 @@
#include "TestEntrySearcher.h"
#include "core/Group.h"
#include "core/Tools.h"
+#include "core/Totp.h"
#include
@@ -394,3 +395,42 @@ void TestEntrySearcher::testUUIDSearch()
m_searchResult = m_entrySearcher.search("uuid:" + Tools::uuidToHex(uuid1), m_rootGroup);
QCOMPARE(m_searchResult.count(), 1);
}
+
+void TestEntrySearcher::testTotpSearch()
+{
+ auto entry1 = new Entry();
+ entry1->setGroup(m_rootGroup);
+ entry1->setTitle("Regular Entry");
+
+ auto entry2 = new Entry();
+ entry2->setGroup(m_rootGroup);
+ entry2->setTitle("TOTP Entry");
+ // Set up TOTP on entry2
+ auto totpSettings = Totp::createSettings("GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ", 6, 30);
+ entry2->setTotp(totpSettings);
+
+ auto entry3 = new Entry();
+ entry3->setGroup(m_rootGroup);
+ entry3->setTitle("Another TOTP Entry");
+ // Set up TOTP on entry3
+ auto totpSettings2 = Totp::createSettings("MFRGG43UEBUXGIDBKRWXAZLSMUQGG6LQ", 6, 30);
+ entry3->setTotp(totpSettings2);
+
+ // Test searching for TOTP entries
+ m_searchResult = m_entrySearcher.search("has:totp", m_rootGroup);
+ QCOMPARE(m_searchResult.count(), 2);
+ QVERIFY(m_searchResult.contains(entry2));
+ QVERIFY(m_searchResult.contains(entry3));
+ QVERIFY(!m_searchResult.contains(entry1));
+
+ // Test case insensitive search
+ m_searchResult = m_entrySearcher.search("has:TOTP", m_rootGroup);
+ QCOMPARE(m_searchResult.count(), 2);
+
+ // Test excluding TOTP entries
+ m_searchResult = m_entrySearcher.search("!has:totp", m_rootGroup);
+ QCOMPARE(m_searchResult.count(), 1);
+ QVERIFY(m_searchResult.contains(entry1));
+ QVERIFY(!m_searchResult.contains(entry2));
+ QVERIFY(!m_searchResult.contains(entry3));
+}
diff --git a/tests/TestEntrySearcher.h b/tests/TestEntrySearcher.h
index 2ca81a742..54a45d578 100644
--- a/tests/TestEntrySearcher.h
+++ b/tests/TestEntrySearcher.h
@@ -39,6 +39,7 @@ private slots:
void testGroup();
void testSkipProtected();
void testUUIDSearch();
+ void testTotpSearch();
private:
Group* m_rootGroup;