mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2024-10-01 01:26:01 -04:00
Auto-Type support for T-CONV, T-REPLACE-RX, and Comments
* Close #1825 - Add full support for T-CONV and T-REPLACE-RX placeholders. Exception is support for the "raw" type in T-CONV. * Close #5333 - Allow comment syntax to be present in the Auto-Type sequence
This commit is contained in:
parent
813ab47e29
commit
02446af743
@ -31,27 +31,9 @@ To configure Auto-Type sequences for your entries, perform the following steps:
|
||||
.Auto-Type entry sequences
|
||||
image::autotype_entry_sequences.png[]
|
||||
|
||||
2. _(Optional)_ Define a custom auto-type sequence for each window title match by selecting the _Use specific sequence for this association_ checkbox. Sequence action codes and field placeholders are detailed in the following table. A complete list of supported actions and placeholders can be found at https://keepass.info/help/base/autotype.html#autoseq[KeePass Auto-Type Action Codes] and https://keepass.info/help/base/placeholders.html[KeePass Placeholders]. Action codes and placeholders are not case sensitive.
|
||||
2. _(Optional)_ Define a custom Auto-Type sequence for each window title match by selecting the _Use specific sequence for this association_ checkbox. Sequence action codes and field placeholders are detailed in the following table. Beyond the most important ones detailed below, there are additional action codes and placeholders available: https://keepass.info/help/base/autotype.html#autoseq[KeePass Auto-Type Action Codes, window=_blank] and https://keepass.info/help/base/placeholders.html[KeePass Placeholders, window=_blank]. Action codes and placeholders are not case sensitive.
|
||||
+
|
||||
[grid=rows, frame=none, width=70%]
|
||||
|===
|
||||
|Action Code |Description
|
||||
|
||||
|{TAB}, {ENTER}, {SPACE}, {INSERT}, {DELETE}, {HOME}, {END}, {PGUP}, {PGDN}, {BACKSPACE}, {CAPSLOCK}, {ESC}
|
||||
|Press the corresponding keyboard key
|
||||
|
||||
|{UP}, {DOWN}, {LEFT}, {RIGHT} |Press the corresponding arrow key
|
||||
|{F1}, {F2}, ..., {F16} |Press F1, F2, etc.
|
||||
|{LEFTBRACE}, {RIGHTBRACE} |Press `{` or `}`, respectively
|
||||
|{DELAY=X} |Set key press delay to X milliseconds
|
||||
|{DELAY X} |Delay typing start by X milliseconds
|
||||
|{CLEARFIELD} |Clear the input field before typing
|
||||
|{TOTP} |Insert calculated TOTP value (if configured)
|
||||
|{PICKCHARS} |Pick specific password characters from a dialog
|
||||
|{<ACTION> X} |Repeat <ACTION> X times (e.g., {SPACE 5} inserts five spaces)
|
||||
|===
|
||||
+
|
||||
[grid=rows, frame=none, width=70%]
|
||||
[grid=rows, frame=none, width=90%]
|
||||
|===
|
||||
|Placeholder |Description
|
||||
|
||||
@ -64,8 +46,33 @@ image::autotype_entry_sequences.png[]
|
||||
|{DT_SIMPLE} |Current date-time
|
||||
|{DB_DIR} |Absolute directory path for database file
|
||||
|{S:<ATTRIBUTE_NAME>} |Value for the given attribute name
|
||||
|{REF:<FIELD>@<SEARCH_IN>:<SEARCH_TEXT>} |Search for a field in another entry using the reference syntax.
|
||||
|{REF:<FIELD>@<SEARCH_IN>:<SEARCH_TEXT>} |Search for a field in another entry using the reference syntax. https://keepass.info/help/base/fieldrefs.html[Read more…, window=_blank]
|
||||
|===
|
||||
+
|
||||
[grid=rows, frame=none, width=90%]
|
||||
|===
|
||||
|Action Code |Description
|
||||
|
||||
|{TAB}, {ENTER}, {SPACE}, {INSERT}, {DELETE}, {HOME}, {END}, {PGUP}, {PGDN}, {BACKSPACE}, {CAPSLOCK}, {ESC}
|
||||
|Press the corresponding keyboard key
|
||||
|
||||
|{UP}, {DOWN}, {LEFT}, {RIGHT} |Press the corresponding arrow key
|
||||
|{F1}, {F2}, ..., {F16} |Press F1, F2, etc.
|
||||
|{LEFTBRACE}, {RIGHTBRACE} |Press `{` or `}`, respectively
|
||||
|{<KEY> X} |Repeat <KEY> X times (e.g., {SPACE 5} inserts five spaces)
|
||||
|{DELAY=X} |Set delay between key presses to X milliseconds
|
||||
|{DELAY X} |Pause typing for X milliseconds
|
||||
|{CLEARFIELD} |Clear the input field
|
||||
|{PICKCHARS} |Pick specific password characters from a dialog
|
||||
|===
|
||||
+
|
||||
*Text Conversions:*
|
||||
+
|
||||
*{T-CONV:/<PLACEHOLDER>/<METHOD>/}* +
|
||||
Convert resolved placeholder (e.g., {USERNAME}, {PASSWORD}, etc.) using the following methods: UPPER, LOWER, BASE64, HEX, URI, URI-DEC. https://keepass.info/help/base/placeholders.html#conv[Read more…, window=_blank]
|
||||
+
|
||||
*{T-REPLACE-RX:/<PLACEHOLDER>/<SEARCH>/<REPLACE>/}* +
|
||||
Use regular expressions to find and replace data from a resolved placeholder. Refer to match groups using $1, $2, etc. https://keepass.info/help/base/placeholders.html#replacerx[Read more…, window=_blank]
|
||||
|
||||
=== Performing Global Auto-Type
|
||||
The global Auto-Type keyboard shortcut is used when you have focus on the window you want to type into. To make use of this feature, you must have previously configured an Auto-Type hotkey.
|
||||
|
@ -586,8 +586,67 @@ AutoType::parseActions(const QString& entrySequence, const Entry* entry, QString
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (placeholder == "beep" || placeholder.startsWith("vkey")
|
||||
|| placeholder.startsWith("appactivate")) {
|
||||
} else if (placeholder.startsWith("t-conv:")) {
|
||||
// Reset to the original capture to preserve case
|
||||
placeholder = match.captured(3);
|
||||
placeholder.replace("t-conv:", "", Qt::CaseInsensitive);
|
||||
if (!placeholder.isEmpty()) {
|
||||
auto sep = placeholder[0];
|
||||
auto parts = placeholder.split(sep);
|
||||
if (parts.size() >= 4) {
|
||||
auto resolved = entry->resolveMultiplePlaceholders(parts[1]);
|
||||
auto type = parts[2].toLower();
|
||||
|
||||
if (type == "base64") {
|
||||
resolved = resolved.toUtf8().toBase64();
|
||||
} else if (type == "hex") {
|
||||
resolved = resolved.toUtf8().toHex();
|
||||
} else if (type == "uri") {
|
||||
resolved = QUrl::toPercentEncoding(resolved.toUtf8());
|
||||
} else if (type == "uri-dec") {
|
||||
resolved = QUrl::fromPercentEncoding(resolved.toUtf8());
|
||||
} else if (type.startsWith("u")) {
|
||||
resolved = resolved.toUpper();
|
||||
} else if (type.startsWith("l")) {
|
||||
resolved = resolved.toLower();
|
||||
} else if (error) {
|
||||
*error = tr("Invalid conversion type: %1").arg(type);
|
||||
continue;
|
||||
}
|
||||
for (const QChar& ch : resolved) {
|
||||
actions << QSharedPointer<AutoTypeKey>::create(ch);
|
||||
}
|
||||
} else if (error) {
|
||||
*error = tr("Invalid conversion syntax: %1").arg(fullPlaceholder);
|
||||
}
|
||||
} else if (error) {
|
||||
*error = tr("Invalid conversion syntax: %1").arg(fullPlaceholder);
|
||||
}
|
||||
} else if (placeholder.startsWith("t-replace-rx:")) {
|
||||
// Reset to the original capture to preserve case
|
||||
placeholder = match.captured(3);
|
||||
placeholder.replace("t-replace-rx:", "", Qt::CaseInsensitive);
|
||||
if (!placeholder.isEmpty()) {
|
||||
auto sep = placeholder[0];
|
||||
auto parts = placeholder.split(sep);
|
||||
if (parts.size() >= 5) {
|
||||
auto resolvedText = entry->resolveMultiplePlaceholders(parts[1]);
|
||||
auto resolvedSearch = entry->resolveMultiplePlaceholders(parts[2]);
|
||||
auto resolvedReplace = entry->resolveMultiplePlaceholders(parts[3]);
|
||||
// Replace $<num> with \<num>s to support Qt substitutions
|
||||
resolvedReplace.replace(QRegularExpression("\\$(\\d+)"), "\\\\1");
|
||||
auto resolved = resolvedText.replace(QRegularExpression(resolvedSearch), resolvedReplace);
|
||||
for (const QChar& ch : resolved) {
|
||||
actions << QSharedPointer<AutoTypeKey>::create(ch);
|
||||
}
|
||||
} else if (error) {
|
||||
*error = tr("Invalid conversion syntax: %1").arg(fullPlaceholder);
|
||||
}
|
||||
} else if (error) {
|
||||
*error = tr("Invalid conversion syntax: %1").arg(fullPlaceholder);
|
||||
}
|
||||
} else if (placeholder == "beep" || placeholder.startsWith("vkey") || placeholder.startsWith("appactivate")
|
||||
|| placeholder.startsWith("c:")) {
|
||||
// Ignore these commands
|
||||
} else {
|
||||
// Attempt to resolve an entry attribute
|
||||
|
@ -278,6 +278,43 @@ void TestAutoType::testGlobalAutoTypeRegExp()
|
||||
m_test->clearActions();
|
||||
}
|
||||
|
||||
void TestAutoType::testAutoTypeResults()
|
||||
{
|
||||
QScopedPointer<Entry> entry(new Entry());
|
||||
entry->setUsername("Username");
|
||||
entry->setPassword("Password@1");
|
||||
entry->setUrl("https://example.com");
|
||||
entry->attributes()->set("attr1", "value1");
|
||||
entry->attributes()->set("attr2", "decode%20me");
|
||||
|
||||
QFETCH(QString, sequence);
|
||||
QFETCH(QString, expectedResult);
|
||||
|
||||
m_autoType->performAutoTypeWithSequence(entry.data(), sequence);
|
||||
QCOMPARE(m_test->actionChars(), expectedResult);
|
||||
}
|
||||
|
||||
void TestAutoType::testAutoTypeResults_data()
|
||||
{
|
||||
QTest::addColumn<QString>("sequence");
|
||||
QTest::addColumn<QString>("expectedResult");
|
||||
|
||||
// Normal Sequences
|
||||
QTest::newRow("Sequence with Attributes") << QString("{USERNAME} {PASSWORD} {URL} {S:attr1}")
|
||||
<< QString("Username Password@1 https://example.com value1");
|
||||
QTest::newRow("Sequence with Comment") << QString("{USERNAME}{TAB}{C:Extra Tab}{TAB}{S:attr1}")
|
||||
<< QString("Username[Key0x1000001][Key0x1000001]value1");
|
||||
|
||||
// Conversions and Replacements
|
||||
QTest::newRow("T-CONV UPPER") << QString("{T-CONV:/{USERNAME}/UPPER/}") << QString("USERNAME");
|
||||
QTest::newRow("T-CONV LOWER") << QString("{T-CONV:/{USERNAME}/LOWER/}") << QString("username");
|
||||
QTest::newRow("T-CONV BASE64") << QString("{T-CONV:/{USERNAME}/BASE64/}") << QString("VXNlcm5hbWU=");
|
||||
QTest::newRow("T-CONV HEX") << QString("{T-CONV:/{USERNAME}/HEX/}") << QString("557365726e616d65");
|
||||
QTest::newRow("T-CONV URI ENCODE") << QString("{T-CONV:/{URL}/URI/}") << QString("https%3A%2F%2Fexample.com");
|
||||
QTest::newRow("T-CONV URI DECODE") << QString("{T-CONV:/{S:attr2}/URI-DEC/}") << QString("decode me");
|
||||
QTest::newRow("T-REPLACE-RX") << QString("{T-REPLACE-RX:/{USERNAME}/User/Pass/}") << QString("Passname");
|
||||
}
|
||||
|
||||
void TestAutoType::testAutoTypeSyntaxChecks()
|
||||
{
|
||||
auto entry = new Entry();
|
||||
@ -321,6 +358,13 @@ void TestAutoType::testAutoTypeSyntaxChecks()
|
||||
QVERIFY2(!AutoType::verifyAutoTypeSyntax("{LEFT 50000000}", entry, error), error.toLatin1());
|
||||
QVERIFY2(AutoType::verifyAutoTypeSyntax("{SPACE 10}{TAB 3}{RIGHT 50}", entry, error), error.toLatin1());
|
||||
QVERIFY2(AutoType::verifyAutoTypeSyntax("{delay 5000000000}", entry, error), error.toLatin1());
|
||||
// Conversion and Regex
|
||||
QVERIFY2(AutoType::verifyAutoTypeSyntax("{T-CONV:/{USERNAME}/base64/}", entry, error), error.toLatin1());
|
||||
QVERIFY2(!AutoType::verifyAutoTypeSyntax("{T-CONV:/{USERNAME}/junk/}", entry, error), error.toLatin1());
|
||||
QVERIFY2(!AutoType::verifyAutoTypeSyntax("{T-CONV:}", entry, error), error.toLatin1());
|
||||
QVERIFY2(AutoType::verifyAutoTypeSyntax("{T-REPLACE-RX:/{USERNAME}/a/b/}", entry, error), error.toLatin1());
|
||||
QVERIFY2(!AutoType::verifyAutoTypeSyntax("{T-REPLACE-RX:/{USERNAME}/a/}", entry, error), error.toLatin1());
|
||||
QVERIFY2(!AutoType::verifyAutoTypeSyntax("{T-REPLACE-RX:}", entry, error), error.toLatin1());
|
||||
}
|
||||
|
||||
void TestAutoType::testAutoTypeEffectiveSequences()
|
||||
|
@ -47,6 +47,8 @@ private slots:
|
||||
void testGlobalAutoTypeUrlSubdomainMatch();
|
||||
void testGlobalAutoTypeTitleMatchDisabled();
|
||||
void testGlobalAutoTypeRegExp();
|
||||
void testAutoTypeResults();
|
||||
void testAutoTypeResults_data();
|
||||
void testAutoTypeSyntaxChecks();
|
||||
void testAutoTypeEffectiveSequences();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user