From 3743d7112b1d962249d76a2d5542effecb1f85fb 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 b5cc6fa03..81566423d 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 da212cc7a..f4a5a6e70 100644 --- a/src/core/Entry.cpp +++ b/src/core/Entry.cpp @@ -1585,6 +1585,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 6edbb8042..968fbe57a 100644 --- a/tests/TestAutoType.cpp +++ b/tests/TestAutoType.cpp @@ -19,6 +19,7 @@ #include "TestAutoType.h" #include +#include #include #include "autotype/AutoType.h" @@ -473,7 +474,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; @@ -492,4 +493,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;