Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Several fixes for Auto-Type #7507

Merged
merged 2 commits into from
Mar 19, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions COPYING
Original file line number Diff line number Diff line change
Expand Up @@ -175,14 +175,12 @@ Files: share/icons/application/scalable/actions/chevron-double-down.svg
share/icons/application/scalable/actions/hammer-wrench.svg
share/icons/application/scalable/actions/health.svg
share/icons/application/scalable/actions/help-about.svg
share/icons/application/scalable/actions/key-enter.svg
share/icons/application/scalable/actions/lock-question.svg
share/icons/application/scalable/actions/message-close.svg
share/icons/application/scalable/actions/move-down.svg
share/icons/application/scalable/actions/move-up.svg
share/icons/application/scalable/actions/paperclip.svg
share/icons/application/scalable/actions/password-copy.svg
share/icons/application/scalable/actions/password-generate.svg
share/icons/application/scalable/actions/password-generator.svg
share/icons/application/scalable/actions/password-show-off.svg
share/icons/application/scalable/actions/password-show-on.svg
Expand Down
Binary file modified docs/images/autotype_selection_dialog_type_menu.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions docs/topics/AutoType.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ image::autotype_entry_sequences.png[]
|{DELAY X} |Pause typing for X milliseconds
|{CLEARFIELD} |Clear the input field
|{PICKCHARS} |Pick specific password characters from a dialog
|{MODE=VIRTUAL} |(Experimental) Use virtual key presses on Windows, useful for virtual machines
|===

=== Performing Global Auto-Type
Expand Down
1 change: 0 additions & 1 deletion share/icons/application/scalable/actions/key-enter.svg

This file was deleted.

This file was deleted.

2 changes: 0 additions & 2 deletions share/icons/icons.qrc
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@
<file>application/scalable/actions/health.svg</file>
<file>application/scalable/actions/help-about.svg</file>
<file>application/scalable/actions/hibp.svg</file>
<file>application/scalable/actions/key-enter.svg</file>
<file>application/scalable/actions/lock-question.svg</file>
<file>application/scalable/actions/keyboard-shortcuts.svg</file>
<file>application/scalable/actions/message-close.svg</file>
Expand All @@ -60,7 +59,6 @@
<file>application/scalable/actions/object-unlocked.svg</file>
<file>application/scalable/actions/paperclip.svg</file>
<file>application/scalable/actions/password-copy.svg</file>
<file>application/scalable/actions/password-generate.svg</file>
<file>application/scalable/actions/password-generator.svg</file>
<file>application/scalable/actions/password-show-off.svg</file>
<file>application/scalable/actions/password-show-on.svg</file>
Expand Down
21 changes: 13 additions & 8 deletions share/translations/keepassxc_en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -705,14 +705,6 @@
<source>Double click a row to perform Auto-Type or find an entry using the search:</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;p&gt;You can use advanced search queries to find any entry in your open databases. The following shortcuts are useful:&lt;br/&gt;
Ctrl+F - Toggle database search&lt;br/&gt;
Ctrl+1 - Type username&lt;br/&gt;
Ctrl+2 - Type password&lt;br/&gt;
Ctrl+3 - Type TOTP&lt;/p&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Search all open databases</source>
<translation type="unfinished"></translation>
Expand Down Expand Up @@ -753,6 +745,19 @@ Ctrl+3 - Type TOTP&lt;/p&gt;</source>
<source>Copy TOTP</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&lt;p&gt;You can use advanced search queries to find any entry in your open databases. The following shortcuts are useful:&lt;br/&gt;
Ctrl+F - Toggle database search&lt;br/&gt;
Ctrl+1 - Type username&lt;br/&gt;
Ctrl+2 - Type password&lt;br/&gt;
Ctrl+3 - Type TOTP&lt;br/&gt;
Ctrl+4 - Use Virtual Keyboard (Windows Only)&lt;/p&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Use Virtual Keyboard</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>BrowserAccessControlDialog</name>
Expand Down
65 changes: 42 additions & 23 deletions src/autotype/AutoType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,10 @@ void AutoType::unregisterGlobalShortcut()
/**
* Core Autotype function that will execute actions
*/
void AutoType::executeAutoTypeActions(const Entry* entry, QWidget* hideWindow, const QString& sequence, WId window)
void AutoType::executeAutoTypeActions(const Entry* entry,
const QString& sequence,
WId window,
AutoTypeExecutor::Mode mode)
{
QString error;
auto actions = parseSequence(sequence, entry, error);
Expand All @@ -274,7 +277,8 @@ void AutoType::executeAutoTypeActions(const Entry* entry, QWidget* hideWindow, c
return;
}

if (hideWindow) {
// Explicitly hide the main window if no target window is specified
if (window == 0) {
#if defined(Q_OS_MACOS)
// Check for accessibility permission
if (!macUtils()->enableAccessibility()) {
Expand All @@ -289,20 +293,22 @@ void AutoType::executeAutoTypeActions(const Entry* entry, QWidget* hideWindow, c
macUtils()->raiseLastActiveWindow();
m_plugin->hideOwnWindow();
#else
getMainWindow()->minimizeOrHide();
if (getMainWindow()) {
getMainWindow()->minimizeOrHide();
}
#endif
}

// Restore window state in case app stole focus
restoreWindowState();
QCoreApplication::processEvents();
m_plugin->raiseWindow(m_windowForGlobal);

// Used only for selected entry auto-type
if (!window) {
QCoreApplication::processEvents();
window = m_plugin->activeWindow();
} else {
// Restore window state (macOS only) then raise the target window
restoreWindowState();
QCoreApplication::processEvents();
m_plugin->raiseWindow(window);
}

// Restore executor mode
m_executor->mode = mode;

int delay = qMax(100, config()->get(Config::AutoTypeStartDelay).toInt());
Tools::wait(delay);

Expand Down Expand Up @@ -346,29 +352,29 @@ void AutoType::executeAutoTypeActions(const Entry* entry, QWidget* hideWindow, c
* Single Autotype entry-point function
* Look up the Auto-Type sequence for the given entry then perfom Auto-Type in the active window
*/
void AutoType::performAutoType(const Entry* entry, QWidget* hideWindow)
void AutoType::performAutoType(const Entry* entry)
{
if (!m_plugin) {
return;
}

auto sequences = entry->autoTypeSequences();
if (!sequences.isEmpty()) {
executeAutoTypeActions(entry, hideWindow, sequences.first());
executeAutoTypeActions(entry, sequences.first());
}
}

/**
* Extra Autotype entry-point function
* Perfom Auto-Type of the directly specified sequence in the active window
*/
void AutoType::performAutoTypeWithSequence(const Entry* entry, const QString& sequence, QWidget* hideWindow)
void AutoType::performAutoTypeWithSequence(const Entry* entry, const QString& sequence)
{
if (!m_plugin) {
return;
}

executeAutoTypeActions(entry, hideWindow, sequence);
executeAutoTypeActions(entry, sequence);
}

void AutoType::startGlobalAutoType(const QString& search)
Expand Down Expand Up @@ -467,12 +473,19 @@ void AutoType::performGlobalAutoType(const QList<QSharedPointer<Database>>& dbLi
}

connect(getMainWindow(), &MainWindow::databaseLocked, selectDialog, &AutoTypeSelectDialog::reject);
connect(selectDialog, &AutoTypeSelectDialog::matchActivated, this, [this](const AutoTypeMatch& match) {
m_lastMatch = match;
m_lastMatchRetypeTimer.start(config()->get(Config::GlobalAutoTypeRetypeTime).toInt() * 1000);
executeAutoTypeActions(match.first, nullptr, match.second, m_windowForGlobal);
resetAutoTypeState();
});
connect(selectDialog,
&AutoTypeSelectDialog::matchActivated,
this,
[this](const AutoTypeMatch& match, bool virtualMode) {
m_lastMatch = match;
m_lastMatchRetypeTimer.start(config()->get(Config::GlobalAutoTypeRetypeTime).toInt() * 1000);
executeAutoTypeActions(match.first,
match.second,
m_windowForGlobal,
virtualMode ? AutoTypeExecutor::Mode::VIRTUAL
: AutoTypeExecutor::Mode::NORMAL);
resetAutoTypeState();
});
connect(selectDialog, &QDialog::rejected, this, [this] {
restoreWindowState();
resetAutoTypeState();
Expand All @@ -488,7 +501,7 @@ void AutoType::performGlobalAutoType(const QList<QSharedPointer<Database>>& dbLi
selectDialog->activateWindow();
} else if (!matchList.isEmpty()) {
// Only one match and not asking, do it!
executeAutoTypeActions(matchList.first().first, nullptr, matchList.first().second, m_windowForGlobal);
executeAutoTypeActions(matchList.first().first, matchList.first().second, m_windowForGlobal);
resetAutoTypeState();
} else {
// We should never get here
Expand Down Expand Up @@ -717,6 +730,12 @@ AutoType::parseSequence(const QString& entrySequence, const Entry* entry, QStrin
error = tr("Invalid conversion syntax: %1").arg(fullPlaceholder);
return {};
}
} else if (placeholder.startsWith("mode=")) {
auto mode = AutoTypeExecutor::Mode::NORMAL;
if (placeholder.endsWith("virtual")) {
mode = AutoTypeExecutor::Mode::VIRTUAL;
}
actions << QSharedPointer<AutoTypeMode>::create(mode);
} else if (placeholder == "beep" || placeholder.startsWith("vkey") || placeholder.startsWith("appactivate")
|| placeholder.startsWith("c:")) {
// Ignore these commands
Expand Down
16 changes: 8 additions & 8 deletions src/autotype/AutoType.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,15 @@
#ifndef KEEPASSX_AUTOTYPE_H
#define KEEPASSX_AUTOTYPE_H

#include "AutoTypeAction.h"

#include <QMutex>
#include <QObject>
#include <QTimer>
#include <QWidget>

#include "AutoTypeAction.h"
#include "AutoTypeMatch.h"

class AutoTypeAction;
class AutoTypeExecutor;
class AutoTypePlatformInterface;
class Database;
class Entry;
Expand All @@ -41,8 +41,8 @@ class AutoType : public QObject
QStringList windowTitles();
bool registerGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers, QString* error = nullptr);
void unregisterGlobalShortcut();
void performAutoType(const Entry* entry, QWidget* hideWindow = nullptr);
void performAutoTypeWithSequence(const Entry* entry, const QString& sequence, QWidget* hideWindow = nullptr);
void performAutoType(const Entry* entry);
void performAutoTypeWithSequence(const Entry* entry, const QString& sequence);

static bool verifyAutoTypeSyntax(const QString& sequence, const Entry* entry, QString& error);

Expand Down Expand Up @@ -80,9 +80,9 @@ private slots:
~AutoType() override;
void loadPlugin(const QString& pluginPath);
void executeAutoTypeActions(const Entry* entry,
QWidget* hideWindow = nullptr,
const QString& customSequence = QString(),
WId window = 0);
const QString& sequence = QString(),
WId window = 0,
AutoTypeExecutor::Mode mode = AutoTypeExecutor::Mode::NORMAL);
void restoreWindowState();
void resetAutoTypeState();

Expand Down
11 changes: 11 additions & 0 deletions src/autotype/AutoTypeAction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,14 @@ AutoTypeAction::Result AutoTypeBegin::exec(AutoTypeExecutor* executor) const
{
return executor->execBegin(this);
}

AutoTypeMode::AutoTypeMode(AutoTypeExecutor::Mode mode)
: mode(mode)
{
}

AutoTypeAction::Result AutoTypeMode::exec(AutoTypeExecutor* executor) const
{
executor->mode = mode;
return AutoTypeAction::Result::Ok();
}
16 changes: 16 additions & 0 deletions src/autotype/AutoTypeAction.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,13 +121,29 @@ class KEEPASSXC_EXPORT AutoTypeBegin : public AutoTypeAction
class KEEPASSXC_EXPORT AutoTypeExecutor
{
public:
enum class Mode
{
NORMAL,
VIRTUAL
};

virtual ~AutoTypeExecutor() = default;
virtual AutoTypeAction::Result execBegin(const AutoTypeBegin* action) = 0;
virtual AutoTypeAction::Result execType(const AutoTypeKey* action) = 0;
virtual AutoTypeAction::Result execClearField(const AutoTypeClearField* action) = 0;

int execDelayMs = 25;
Mode mode = Mode::NORMAL;
QString error;
};

class KEEPASSXC_EXPORT AutoTypeMode : public AutoTypeAction
{
public:
AutoTypeMode(AutoTypeExecutor::Mode mode = AutoTypeExecutor::Mode::NORMAL);
Result exec(AutoTypeExecutor* executor) const override;

const AutoTypeExecutor::Mode mode;
};

#endif // KEEPASSX_AUTOTYPEACTION_H
36 changes: 29 additions & 7 deletions src/autotype/AutoTypeSelectDialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ AutoTypeSelectDialog::AutoTypeSelectDialog(QWidget* parent)
}
});

m_ui->helpButton->setIcon(icons()->icon("system-help"));

m_ui->search->installEventFilter(this);

m_searchTimer.setInterval(300);
Expand Down Expand Up @@ -118,7 +120,7 @@ void AutoTypeSelectDialog::submitAutoTypeMatch(AutoTypeMatch match)
if (match.first) {
m_accepted = true;
accept();
emit matchActivated(std::move(match));
emit matchActivated(std::move(match), m_virtualMode);
}
}

Expand Down Expand Up @@ -274,34 +276,54 @@ void AutoTypeSelectDialog::buildActionMenu()
m_actionMenu->addAction(typeUsernameAction);
m_actionMenu->addAction(typePasswordAction);
m_actionMenu->addAction(typeTotpAction);
#ifdef Q_OS_WIN
auto typeVirtualAction = new QAction(icons()->icon("auto-type"), tr("Use Virtual Keyboard"));
m_actionMenu->addAction(typeVirtualAction);
#endif
m_actionMenu->addAction(copyUsernameAction);
m_actionMenu->addAction(copyPasswordAction);
m_actionMenu->addAction(copyTotpAction);

auto shortcut = new QShortcut(Qt::CTRL + Qt::Key_1, this);
connect(shortcut, &QShortcut::activated, typeUsernameAction, &QAction::trigger);
typeUsernameAction->setShortcut(Qt::CTRL + Qt::Key_1);
connect(typeUsernameAction, &QAction::triggered, this, [&] {
auto match = m_ui->view->currentMatch();
match.second = "{USERNAME}";
submitAutoTypeMatch(match);
});

shortcut = new QShortcut(Qt::CTRL + Qt::Key_2, this);
connect(shortcut, &QShortcut::activated, typePasswordAction, &QAction::trigger);
typePasswordAction->setShortcut(Qt::CTRL + Qt::Key_2);
connect(typePasswordAction, &QAction::triggered, this, [&] {
auto match = m_ui->view->currentMatch();
match.second = "{PASSWORD}";
submitAutoTypeMatch(match);
});

shortcut = new QShortcut(Qt::CTRL + Qt::Key_3, this);
connect(shortcut, &QShortcut::activated, typeTotpAction, &QAction::trigger);
typeTotpAction->setShortcut(Qt::CTRL + Qt::Key_3);
connect(typeTotpAction, &QAction::triggered, this, [&] {
auto match = m_ui->view->currentMatch();
match.second = "{TOTP}";
submitAutoTypeMatch(match);
});

#ifdef Q_OS_WIN
typeVirtualAction->setShortcut(Qt::CTRL + Qt::Key_4);
connect(typeVirtualAction, &QAction::triggered, this, [&] {
m_virtualMode = true;
activateCurrentMatch();
});
#endif

#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
// Qt 5.10 introduced a new "feature" to hide shortcuts in context menus
// Unfortunately, Qt::AA_DontShowShortcutsInContextMenus is broken, have to manually enable them
typeUsernameAction->setShortcutVisibleInContextMenu(true);
typePasswordAction->setShortcutVisibleInContextMenu(true);
typeTotpAction->setShortcutVisibleInContextMenu(true);
#ifdef Q_OS_WIN
typeVirtualAction->setShortcutVisibleInContextMenu(true);
#endif
#endif

connect(copyUsernameAction, &QAction::triggered, this, [&] {
auto entry = m_ui->view->currentMatch().first;
if (entry) {
Expand Down
Loading