Additional Auto-Type improvements based on PR feedback

* Improve documentation and remove external links to keepass.info documentation
This commit is contained in:
Jonathan White 2021-02-15 17:26:23 -05:00
parent 02446af743
commit 8d058cbd04
No known key found for this signature in database
GPG Key ID: 440FC65F2E0C6E01
11 changed files with 292 additions and 194 deletions

View File

@ -34,3 +34,5 @@ include::topics/AutoType.adoc[tags=*]
include::topics/KeeShare.adoc[tags=*]
include::topics/SSHAgent.adoc[tags=*]
include::topics/Reference.adoc[tags=*]

View File

@ -31,7 +31,7 @@ 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. 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.
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: xref:UserGuide.adoc#_auto_type_actions[Auto-Type Actions Reference] and xref:UserGuide.adoc#_entry_placeholders[Entry Placeholders Reference]. Action codes and placeholders are not case sensitive.
+
[grid=rows, frame=none, width=90%]
|===
@ -43,10 +43,7 @@ image::autotype_entry_sequences.png[]
|{URL} |URL
|{NOTES} |Notes
|{TOTP} |Current TOTP value (if configured)
|{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. https://keepass.info/help/base/fieldrefs.html[Read more…, window=_blank]
|===
+
[grid=rows, frame=none, width=90%]
@ -57,22 +54,13 @@ image::autotype_entry_sequences.png[]
|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)
|{<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.

View File

@ -120,18 +120,7 @@ image::clone_entry_dialog.png[,70%]
.References in a cloned entry
image::clone_entry_references.png[]
4. You can create your own references using the following syntax:
+
`{REF:<ShortCode>@I:<UUID>}`
+
Where `<UUID>` is the Unique Identifier of the entry to pull data from and `<ShortCode>` is from the following:
+
* T - Title
* U - Username
* P - Password
* A - URL
* N - Notes
* I - UUID
4. You can create your own references using the xref:UserGuide.adoc#_entry_cross_reference[Entry Reference Syntax]
== Searching the Database
KeePassXC provides an enhanced and granular search features the enables you to search for specific entries in the databases using the different modifiers, wild card characters, and logical operators.
@ -287,7 +276,7 @@ image::database_security.png[]
+
.Database credentials
image::database_security_credentials.png[]
+
WARNING: Consider creating a backup of your YubiKey. Please refer to <<Creating a YubiKey backup>>
5. Encryption settings allows you to change the average time it takes to encrypt and decrypt the database. The longer time that is chosen, the harder it will be to brute force attack your database. *We recommend a setting of one second.*

View File

@ -0,0 +1,94 @@
= KeePassXC - Reference
include::.sharedheader[]
:imagesdir: ../images
// tag::content[]
== Reference
This section contains full details on advanced features available in KeePassXC.
=== Entry Placeholders
[grid=rows, frame=none, width=90%]
|===
|Placeholder |Description
|{TITLE} |Entry Title
|{USERNAME} |Username
|{PASSWORD} |Password
|{URL} |URL
|{NOTES} |Notes
|{TOTP} |Current TOTP value (if configured)
|{S:<ATTRIBUTE_NAME>} |Value for the given attribute (case sensitive)
|{URL:RMVSCM} |URL without scheme (e.g., https)
|{URL:WITHOUTSCHEME} |URL without scheme
|{URL:SCM} |URL Scheme
|{URL:SCHEME} |URL Scheme
|{URL:HOST} |URL Host (e.g., example.com)
|{URL:PORT} |URL Port
|{URL:PATH} |URL Path (e.g., /path/to/page.html)
|{URL:QUERY} |URL Query String
|{URL:FRAGMENT} |URL Fragment
|{URL:USERINFO} |URL Username:Password
|{URL:USERNAME} |URL Username
|{URL:PASSWORD} |URL Password
|{DT_SIMPLE} |Current Date-Time (yyyyMMddhhmmss)
|{DT_YEAR} |Current Year (yyyy)
|{DT_MONTH} |Current Month (MM)
|{DT_DAY} |Current Day (dd)
|{DT_HOUR} |Current Hour (hh)
|{DT_MINUTE} |Current Minutes (mm)
|{DT_SECOND} |Current Seconds (ss)
|{DT_UTC_SIMPLE} |Current UTC Date-Time (yyyyMMddhhmmss)
|{DT_UTC_YEAR} |Current UTC Year (yyyy)
|{DT_UTC_MONTH} |Current UTC Month (MM)
|{DT_UTC_DAY} |Current UTC Day (dd)
|{DT_UTC_HOUR} |Current UTC Hour (hh)
|{DT_UTC_MINUTE} |Current UTC Minutes (mm)
|{DT_UTC_SECOND} |Current UTC Seconds (ss)
|{DB_DIR} |Absolute directory path of database file
|===
=== Entry Cross-Reference
A reference to another entry's field is possible using the short-hand syntax:
`{REF:<FIELD>@<SEARCH_IN>:<SEARCH_TEXT>}`
`<FIELD>` and `<SEARCH_IN>` can be one of following:
* T - Title
* U - Username
* P - Password
* A - URL
* N - Notes
* I - UUID (found on entry properties page)
* O - Custom Attribute _(SEARCH_IN only)_
Examples: +
`{REF:U@I:033054D445C648C59092CC1D661B1B71}` +
`{REF:P@T:Other Entry}` +
`{REF:A@O:Attribute 1}`
=== Auto-Type Actions
[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.
*{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.
// end::content[]

View File

@ -20,7 +20,6 @@
#include <QApplication>
#include <QPluginLoader>
#include <QRegularExpression>
#include <QWindow>
#include "config-keepassx.h"
@ -32,7 +31,6 @@
#include "core/Database.h"
#include "core/Entry.h"
#include "core/Group.h"
#include "core/ListDeleter.h"
#include "core/Resources.h"
#include "core/Tools.h"
#include "gui/MainWindow.h"
@ -131,7 +129,7 @@ AutoType::AutoType(QObject* parent, bool test)
// prevent crash when the plugin has unresolved symbols
m_pluginLoader->setLoadHints(QLibrary::ResolveAllSymbolsHint);
QString pluginName = "keepassx-autotype-";
QString pluginName = "keepassxc-autotype-";
if (!test) {
pluginName += QApplication::platformName();
} else {
@ -169,7 +167,7 @@ void AutoType::loadPlugin(const QString& pluginPath)
if (m_plugin) {
if (m_plugin->isAvailable()) {
m_executor = m_plugin->createExecutor();
connect(osUtils, &OSUtilsBase::globalShortcutTriggered, this, [this](QString name) {
connect(osUtils, &OSUtilsBase::globalShortcutTriggered, this, [this](const QString& name) {
if (name == "autotype") {
startGlobalAutoType();
}
@ -249,6 +247,20 @@ void AutoType::unregisterGlobalShortcut()
*/
void AutoType::executeAutoTypeActions(const Entry* entry, QWidget* hideWindow, const QString& sequence, WId window)
{
QString error;
auto actions = parseSequence(sequence, entry, error);
if (!error.isEmpty()) {
auto errorMsg = tr("The requested Auto-Type sequence cannot be used due to an error:");
errorMsg.append(QString("\n%1\n%2").arg(sequence, error));
if (getMainWindow()) {
MessageBox::critical(getMainWindow(), tr("Auto-Type Error"), errorMsg);
}
qWarning() << errorMsg;
emit autotypeRejected();
return;
}
if (!m_inAutoType.tryLock()) {
return;
}
@ -272,11 +284,9 @@ void AutoType::executeAutoTypeActions(const Entry* entry, QWidget* hideWindow, c
#endif
}
auto actions = parseActions(sequence, entry);
// Restore window state in case app stole focus
restoreWindowState();
QApplication::processEvents();
QCoreApplication::processEvents();
m_plugin->raiseWindow(m_windowForGlobal);
// Used only for selected entry auto-type
@ -286,7 +296,7 @@ void AutoType::executeAutoTypeActions(const Entry* entry, QWidget* hideWindow, c
Tools::wait(qMax(100, config()->get(Config::AutoTypeStartDelay).toInt()));
for (auto action : asConst(actions)) {
for (const auto& action : asConst(actions)) {
if (m_plugin->activeWindow() != window) {
qWarning("Active window changed, interrupting auto-type.");
emit autotypeRejected();
@ -301,7 +311,6 @@ void AutoType::executeAutoTypeActions(const Entry* entry, QWidget* hideWindow, c
m_windowForGlobal = 0;
m_windowTitleForGlobal.clear();
// emit signal only if autotype performed correctly
emit autotypePerformed();
m_inAutoType.unlock();
}
@ -426,7 +435,7 @@ void AutoType::performGlobalAutoType(const QList<QSharedPointer<Database>>& dbLi
selectDialog->setMatches(matchList, dbList);
connect(getMainWindow(), &MainWindow::databaseLocked, selectDialog, &AutoTypeSelectDialog::reject);
connect(selectDialog, &AutoTypeSelectDialog::matchActivated, this, [this](AutoTypeMatch match) {
connect(selectDialog, &AutoTypeSelectDialog::matchActivated, this, [this](const AutoTypeMatch& match) {
executeAutoTypeActions(match.first, nullptr, match.second, m_windowForGlobal);
resetAutoTypeState();
});
@ -480,7 +489,7 @@ void AutoType::resetAutoTypeState()
* If error is provided then syntax checking will be performed.
*/
QList<QSharedPointer<AutoTypeAction>>
AutoType::parseActions(const QString& entrySequence, const Entry* entry, QString* error)
AutoType::parseSequence(const QString& entrySequence, const Entry* entry, QString& error, bool syntaxOnly)
{
const int maxTypeDelay = 100;
const int maxWaitDelay = 10000;
@ -495,8 +504,8 @@ AutoType::parseActions(const QString& entrySequence, const Entry* entry, QString
sequence.replace("{}}", "{RIGHTBRACE}");
// Quick test for bracket syntax
if ((sequence.count("{") + sequence.count("}")) % 2 != 0 && error) {
*error = tr("Bracket imbalance detected, found extra { or }");
if (sequence.count("{") != sequence.count("}")) {
error = tr("Bracket imbalance detected, found extra { or }");
return {};
}
@ -530,147 +539,163 @@ AutoType::parseActions(const QString& entrySequence, const Entry* entry, QString
}
auto character = match.captured(5);
if (!placeholder.isEmpty()) {
if (g_placeholderToKey.contains(placeholder)) {
// Basic key placeholder, allow repeat
if (repeat > maxRepetition && error) {
*error = tr("Too many repetitions detected, max is %1: %2").arg(maxRepetition).arg(fullPlaceholder);
return {};
}
auto action = QSharedPointer<AutoTypeKey>::create(g_placeholderToKey[placeholder], modifiers);
for (int i = 1; i <= repeat && i <= maxRepetition; ++i) {
actions << action;
}
} else if (placeholder.startsWith("delay=")) {
// Change keypress delay
int delay = placeholder.replace("delay=", "").toInt();
if (delay > maxTypeDelay && error) {
*error = tr("Very slow key press detected, max is %1: %2").arg(maxTypeDelay).arg(fullPlaceholder);
return {};
}
actions << QSharedPointer<AutoTypeDelay>::create(qBound(0, delay, maxTypeDelay), true);
} else if (placeholder == "delay") {
// Mid typing delay (wait)
if (repeat > maxWaitDelay && error) {
*error = tr("Very long delay detected, max is %1: %2").arg(maxWaitDelay).arg(fullPlaceholder);
return {};
}
actions << QSharedPointer<AutoTypeDelay>::create(qBound(0, repeat, maxWaitDelay));
} else if (placeholder == "clearfield") {
// Platform-specific field clearing
actions << QSharedPointer<AutoTypeClearField>::create();
} else if (placeholder == "totp") {
// Entry totp (requires special handling)
QString totp = entry->totp();
for (const auto& ch : totp) {
actions << QSharedPointer<AutoTypeKey>::create(ch);
}
} else if (placeholder == "pickchars") {
if (error) {
// Ignore this if we are syntax checking
continue;
}
// Show pickchars dialog for entry's password
auto password = entry->resolvePlaceholder(entry->password());
if (!password.isEmpty()) {
PickcharsDialog pickcharsDialog(password);
if (pickcharsDialog.exec() == QDialog::Accepted && !pickcharsDialog.selectedChars().isEmpty()) {
auto chars = pickcharsDialog.selectedChars();
auto iter = chars.begin();
while (iter != chars.end()) {
actions << QSharedPointer<AutoTypeKey>::create(*iter);
++iter;
if (pickcharsDialog.pressTab() && iter != chars.end()) {
actions << QSharedPointer<AutoTypeKey>::create(g_placeholderToKey["tab"]);
}
}
}
}
} 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 (placeholder.isEmpty()) {
if (!character.isEmpty()) {
// Type a single character with modifier
actions << QSharedPointer<AutoTypeKey>::create(character[0], modifiers);
}
continue;
}
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
auto resolved = entry->resolvePlaceholder(fullPlaceholder);
if (resolved != fullPlaceholder) {
// Attribute resolved, add characters to action list
for (const QChar& ch : resolved) {
if (ch == '\n') {
actions << QSharedPointer<AutoTypeKey>::create(g_placeholderToKey["enter"]);
} else if (ch == '\t') {
if (g_placeholderToKey.contains(placeholder)) {
// Basic key placeholder, allow repeat
if (repeat > maxRepetition) {
error = tr("Too many repetitions detected, max is %1: %2").arg(maxRepetition).arg(fullPlaceholder);
return {};
}
auto action = QSharedPointer<AutoTypeKey>::create(g_placeholderToKey[placeholder], modifiers);
for (int i = 1; i <= repeat && i <= maxRepetition; ++i) {
actions << action;
}
} else if (placeholder.startsWith("delay=")) {
// Change keypress delay
int delay = placeholder.replace("delay=", "").toInt();
if (delay > maxTypeDelay) {
error = tr("Very slow key press detected, max is %1: %2").arg(maxTypeDelay).arg(fullPlaceholder);
return {};
}
actions << QSharedPointer<AutoTypeDelay>::create(qBound(0, delay, maxTypeDelay), true);
} else if (placeholder == "delay") {
// Mid typing delay (wait)
if (repeat > maxWaitDelay) {
error = tr("Very long delay detected, max is %1: %2").arg(maxWaitDelay).arg(fullPlaceholder);
return {};
}
actions << QSharedPointer<AutoTypeDelay>::create(qBound(0, repeat, maxWaitDelay));
} else if (placeholder == "clearfield") {
// Platform-specific field clearing
actions << QSharedPointer<AutoTypeClearField>::create();
} else if (placeholder == "totp") {
// Entry totp (requires special handling)
QString totp = entry->totp();
for (const auto& ch : totp) {
actions << QSharedPointer<AutoTypeKey>::create(ch);
}
} else if (placeholder == "pickchars") {
// Ignore this if we are syntax checking
if (syntaxOnly) {
continue;
}
// Show pickchars dialog for entry's password
auto password = entry->resolvePlaceholder(entry->password());
if (!password.isEmpty()) {
PickcharsDialog pickcharsDialog(password);
if (pickcharsDialog.exec() == QDialog::Accepted && !pickcharsDialog.selectedChars().isEmpty()) {
for (const auto& ch : pickcharsDialog.selectedChars()) {
actions << QSharedPointer<AutoTypeKey>::create(ch);
if (pickcharsDialog.pressTab()) {
actions << QSharedPointer<AutoTypeKey>::create(g_placeholderToKey["tab"]);
} else {
actions << QSharedPointer<AutoTypeKey>::create(ch);
}
}
} else if (error) {
// Invalid placeholder, issue error and stop processing
*error = tr("Invalid placeholder: %1").arg(fullPlaceholder);
return {};
if (pickcharsDialog.pressTab()) {
// Remove extra tab
actions.removeLast();
}
}
}
} else if (!character.isEmpty()) {
// Type a single character with modifier
actions << QSharedPointer<AutoTypeKey>::create(character[0], modifiers);
} 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 {
error = tr("Invalid conversion type: %1").arg(type);
return {};
}
for (const QChar& ch : resolved) {
actions << QSharedPointer<AutoTypeKey>::create(ch);
}
} else {
error = tr("Invalid conversion syntax: %1").arg(fullPlaceholder);
return {};
}
} else {
error = tr("Invalid conversion syntax: %1").arg(fullPlaceholder);
return {};
}
} 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> to support Qt substitutions
resolvedReplace.replace(QRegularExpression(R"(\$(\d+))"), R"(\\1)");
auto searchRegex = QRegularExpression(resolvedSearch);
if (!searchRegex.isValid()) {
error = tr("Invalid regular expression syntax %1\n%2")
.arg(resolvedSearch, searchRegex.errorString());
return {};
}
auto resolved = resolvedText.replace(searchRegex, resolvedReplace);
for (const QChar& ch : resolved) {
actions << QSharedPointer<AutoTypeKey>::create(ch);
}
} else {
error = tr("Invalid conversion syntax: %1").arg(fullPlaceholder);
return {};
}
} else {
error = tr("Invalid conversion syntax: %1").arg(fullPlaceholder);
return {};
}
} else if (placeholder == "beep" || placeholder.startsWith("vkey") || placeholder.startsWith("appactivate")
|| placeholder.startsWith("c:")) {
// Ignore these commands
} else {
// Attempt to resolve an entry attribute
auto resolved = entry->resolvePlaceholder(fullPlaceholder);
if (resolved != fullPlaceholder) {
// Attribute resolved, add characters to action list
for (const QChar& ch : resolved) {
if (ch == '\n') {
actions << QSharedPointer<AutoTypeKey>::create(g_placeholderToKey["enter"]);
} else if (ch == '\t') {
actions << QSharedPointer<AutoTypeKey>::create(g_placeholderToKey["tab"]);
} else {
actions << QSharedPointer<AutoTypeKey>::create(ch);
}
}
} else {
// Invalid placeholder, issue error and stop processing
error = tr("Invalid placeholder: %1").arg(fullPlaceholder);
return {};
}
}
}
@ -684,7 +709,7 @@ bool AutoType::verifyAutoTypeSyntax(const QString& sequence, const Entry* entry,
{
error.clear();
if (!sequence.isEmpty()) {
parseActions(sequence, entry, &error);
parseSequence(sequence, entry, error, true);
}
return error.isEmpty();
}

View File

@ -86,7 +86,7 @@ private:
void resetAutoTypeState();
static QList<QSharedPointer<AutoTypeAction>>
parseActions(const QString& entrySequence, const Entry* entry, QString* error = nullptr);
parseSequence(const QString& entrySequence, const Entry* entry, QString& error, bool syntaxOnly = false);
QMutex m_inAutoType;
QMutex m_inGlobalAutoTypeDialog;

View File

@ -1,18 +1,18 @@
set(autotype_mac_SOURCES AutoTypeMac.cpp)
add_library(keepassx-autotype-cocoa MODULE ${autotype_mac_SOURCES})
set_target_properties(keepassx-autotype-cocoa PROPERTIES LINK_FLAGS "-framework Foundation -framework AppKit -framework Carbon")
target_link_libraries(keepassx-autotype-cocoa ${PROGNAME} Qt5::Core Qt5::Widgets)
add_library(keepassxc-autotype-cocoa MODULE ${autotype_mac_SOURCES})
set_target_properties(keepassxc-autotype-cocoa PROPERTIES LINK_FLAGS "-framework Foundation -framework AppKit -framework Carbon")
target_link_libraries(keepassxc-autotype-cocoa ${PROGNAME} Qt5::Core Qt5::Widgets)
if(WITH_APP_BUNDLE)
add_custom_command(TARGET keepassx-autotype-cocoa
add_custom_command(TARGET keepassxc-autotype-cocoa
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/libkeepassx-autotype-cocoa.so ${PLUGIN_INSTALL_DIR}/libkeepassx-autotype-cocoa.so
COMMAND ${MACDEPLOYQT_EXE} ${PROGNAME}.app -executable=${PLUGIN_INSTALL_DIR}/libkeepassx-autotype-cocoa.so -no-plugins 2> /dev/null
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/libkeepassxc-autotype-cocoa.so ${PLUGIN_INSTALL_DIR}/libkeepassxc-autotype-cocoa.so
COMMAND ${MACDEPLOYQT_EXE} ${PROGNAME}.app -executable=${PLUGIN_INSTALL_DIR}/libkeepassxc-autotype-cocoa.so -no-plugins 2> /dev/null
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/src
COMMENT "Deploying autotype plugin")
else()
install(TARGETS keepassx-autotype-cocoa
install(TARGETS keepassxc-autotype-cocoa
BUNDLE DESTINATION . COMPONENT Runtime
LIBRARY DESTINATION ${PLUGIN_INSTALL_DIR} COMPONENT Runtime)
endif()

View File

@ -1,4 +1,4 @@
set(autotype_test_SOURCES AutoTypeTest.cpp)
add_library(keepassx-autotype-test MODULE ${autotype_test_SOURCES})
target_link_libraries(keepassx-autotype-test keepassx_core ${autotype_LIB} Qt5::Core Qt5::Widgets)
add_library(keepassxc-autotype-test MODULE ${autotype_test_SOURCES})
target_link_libraries(keepassxc-autotype-test keepassx_core ${autotype_LIB} Qt5::Core Qt5::Widgets)

View File

@ -1,7 +1,7 @@
set(autotype_win_SOURCES AutoTypeWindows.cpp)
add_library(keepassx-autotype-windows MODULE ${autotype_win_SOURCES})
target_link_libraries(keepassx-autotype-windows keepassx_core ${autotype_LIB} Qt5::Core Qt5::Widgets)
install(TARGETS keepassx-autotype-windows
add_library(keepassxc-autotype-windows MODULE ${autotype_win_SOURCES})
target_link_libraries(keepassxc-autotype-windows keepassx_core ${autotype_LIB} Qt5::Core Qt5::Widgets)
install(TARGETS keepassxc-autotype-windows
BUNDLE DESTINATION . COMPONENT Runtime
LIBRARY DESTINATION ${PLUGIN_INSTALL_DIR} COMPONENT Runtime)

View File

@ -2,8 +2,8 @@ include_directories(SYSTEM ${X11_X11_INCLUDE_PATH})
set(autotype_XCB_SOURCES AutoTypeXCB.cpp)
add_library(keepassx-autotype-xcb MODULE ${autotype_XCB_SOURCES})
target_link_libraries(keepassx-autotype-xcb keepassx_core Qt5::Core Qt5::Widgets Qt5::X11Extras ${X11_X11_LIB} ${X11_Xi_LIB} ${X11_XTest_LIB})
install(TARGETS keepassx-autotype-xcb
add_library(keepassxc-autotype-xcb MODULE ${autotype_XCB_SOURCES})
target_link_libraries(keepassxc-autotype-xcb keepassx_core Qt5::Core Qt5::Widgets Qt5::X11Extras ${X11_X11_LIB} ${X11_Xi_LIB} ${X11_XTest_LIB})
install(TARGETS keepassxc-autotype-xcb
BUNDLE DESTINATION . COMPONENT Runtime
LIBRARY DESTINATION ${PLUGIN_INSTALL_DIR} COMPONENT Runtime)

View File

@ -40,7 +40,7 @@ void TestAutoType::initTestCase()
config()->set(Config::Security_AutoTypeAsk, false);
AutoType::createTestInstance();
QPluginLoader loader(resources()->pluginPath("keepassx-autotype-test"));
QPluginLoader loader(resources()->pluginPath("keepassxc-autotype-test"));
loader.setLoadHints(QLibrary::ResolveAllSymbolsHint);
QVERIFY(loader.instance());
@ -312,7 +312,7 @@ void TestAutoType::testAutoTypeResults_data()
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");
QTest::newRow("T-REPLACE-RX") << QString("{T-REPLACE-RX:/{USERNAME}/(User)/$1Pass/}") << QString("UserPassname");
}
void TestAutoType::testAutoTypeSyntaxChecks()