From 56521023b809e8884dbdcb9a73ee61acc12738fb Mon Sep 17 00:00:00 2001 From: Jonathan White Date: Sun, 8 Mar 2026 23:17:36 -0400 Subject: [PATCH] Support TIMEOTP autotype and entry placeholder (#13117) * Additional fix for #7263 to support KeePass2 placeholder for TOTP --------- Co-authored-by: Janek Bevendorff --- src/autotype/AutoType.cpp | 2 +- src/core/Entry.cpp | 1 + tests/TestAutoType.cpp | 23 ++++++++++++++++++++++- tests/TestAutoType.h | 2 +- 4 files changed, 25 insertions(+), 3 deletions(-) diff --git a/src/autotype/AutoType.cpp b/src/autotype/AutoType.cpp index 301792662..9ed838117 100644 --- a/src/autotype/AutoType.cpp +++ b/src/autotype/AutoType.cpp @@ -645,7 +645,7 @@ AutoType::parseSequence(const QString& entrySequence, const Entry* entry, QStrin } else if (placeholder == "clearfield") { // Platform-specific field clearing actions << QSharedPointer::create(); - } else if (placeholder == "totp") { + } else if (placeholder == "totp" || placeholder == "timeotp") { if (entry->hasValidTotp()) { // Calculate TOTP at the time of typing including delays bool isValid = false; diff --git a/src/core/Entry.cpp b/src/core/Entry.cpp index ef6440f88..235bcc6f7 100644 --- a/src/core/Entry.cpp +++ b/src/core/Entry.cpp @@ -1594,6 +1594,7 @@ Entry::PlaceholderType Entry::placeholderType(const QString& placeholder) const {QStringLiteral("{PASSWORD}"), PlaceholderType::Password}, {QStringLiteral("{NOTES}"), PlaceholderType::Notes}, {QStringLiteral("{TOTP}"), PlaceholderType::Totp}, + {QStringLiteral("{TIMEOTP}"), PlaceholderType::Totp}, {QStringLiteral("{URL}"), PlaceholderType::Url}, {QStringLiteral("{UUID}"), PlaceholderType::Uuid}, {QStringLiteral("{URL:RMVSCM}"), PlaceholderType::UrlWithoutScheme}, diff --git a/tests/TestAutoType.cpp b/tests/TestAutoType.cpp index 17974e8c2..9ede9d8a7 100644 --- a/tests/TestAutoType.cpp +++ b/tests/TestAutoType.cpp @@ -19,6 +19,7 @@ #include "TestAutoType.h" #include +#include #include #include "autotype/AutoType.h" @@ -475,7 +476,7 @@ void TestAutoType::testAutoTypeEmptyWindowAssociation() QVERIFY(assoc.isEmpty()); } -void TestAutoType::testAutoTypeTotpDelay() +void TestAutoType::testAutoTypeTotp() { // Get the TOTP time step in milliseconds auto totpStep = m_entry1->totpSettings()->step * 1000; @@ -494,4 +495,24 @@ void TestAutoType::testAutoTypeTotpDelay() QString("Typed TOTP (%1) should differ from current TOTP (%2) due to delay") .arg(totpParts[0], totpParts[1]) .toLatin1()); + + m_test->clearActions(); + + // Test TIMEOTP placeholder (KeePass2 Compatibility) + m_autoType->performAutoTypeWithSequence(m_entry1, "{TIMEOTP}"); + typedChars = m_test->actionChars(); + QCOMPARE(typedChars.size(), m_entry1->totpSettings()->digits); + // Verify that the typedchars are all numbers + QRegularExpression re("^\\d+$"); + QVERIFY(re.match(typedChars).hasMatch()); + + m_test->clearActions(); + + // Test that TIMEOTP also works as an entry placeholder + m_entry1->setPassword("{TIMEOTP}"); + m_autoType->performAutoTypeWithSequence(m_entry1, "{PASSWORD}"); + typedChars = m_test->actionChars(); + QCOMPARE(typedChars.size(), m_entry1->totpSettings()->digits); + // Verify that the typedchars are all numbers + QVERIFY(re.match(typedChars).hasMatch()); } diff --git a/tests/TestAutoType.h b/tests/TestAutoType.h index 0a8cac141..81ed07cba 100644 --- a/tests/TestAutoType.h +++ b/tests/TestAutoType.h @@ -52,7 +52,7 @@ private slots: void testAutoTypeSyntaxChecks(); void testAutoTypeEffectiveSequences(); void testAutoTypeEmptyWindowAssociation(); - void testAutoTypeTotpDelay(); + void testAutoTypeTotp(); private: AutoTypePlatformInterface* m_platform;