refactor autotype sequences and entry-point functions

This commit is contained in:
thez3ro 2018-01-18 23:45:09 +01:00
parent b5cabbeb43
commit a9479fd662
No known key found for this signature in database
GPG Key ID: F628F9E41DD7C073
8 changed files with 99 additions and 57 deletions

View File

@ -128,31 +128,16 @@ QStringList AutoType::windowTitles()
return m_plugin->windowTitles();
}
void AutoType::executeAutoTypeActions(const Entry* entry, QWidget* hideWindow, const QString& customSequence, WId window)
/**
* Core Autotype function that will execute actions
*/
void AutoType::executeAutoTypeActions(const Entry* entry, QWidget* hideWindow, const QString& sequence, WId window)
{
if (m_inAutoType || !m_plugin) {
// no edit to the sequence beyond this point
if (!verifyAutoTypeSyntax(sequence)) {
m_inAutoType = false; // TODO: make this automatic
return;
}
m_inAutoType = true;
QString sequence;
if (customSequence.isEmpty()) {
QList<QString> sequences = autoTypeSequences(entry);
if(sequences.isEmpty()) {
sequence = "";
} else {
sequence = sequences.first();
}
} else {
sequence = customSequence;
}
if (!checkSyntax(sequence)) {
return;
}
sequence.replace("{{}", "{LEFTBRACE}");
sequence.replace("{}}", "{RIGHTBRACE}");
QList<AutoTypeAction*> actions;
ListDeleter<AutoTypeAction*> actionsDeleter(&actions);
@ -191,6 +176,30 @@ void AutoType::executeAutoTypeActions(const Entry* entry, QWidget* hideWindow, c
m_inAutoType = false;
}
/**
* Single Autotype entry-point function
* Perfom autotype sequence in the active window
*/
void AutoType::performAutoType(const Entry* entry, QWidget* hideWindow)
{
if (m_inAutoType || !m_plugin) {
return;
}
QList<QString> sequences = autoTypeSequences(entry);
if(sequences.isEmpty()) {
return;
}
m_inAutoType = true;
executeAutoTypeActions(entry, hideWindow, sequences.first());
}
/**
* Global Autotype entry-point funcion
* Perform global autotype on the active window
*/
void AutoType::performGlobalAutoType(const QList<Database*>& dbList)
{
if (m_inAutoType || !m_plugin) {
@ -227,7 +236,7 @@ void AutoType::performGlobalAutoType(const QList<Database*>& dbList)
MessageBox::information(nullptr, tr("Auto-Type - KeePassXC"), message);
} else if ((matchList.size() == 1) && !config()->get("security/autotypeask").toBool()) {
m_inAutoType = false;
performAutoType(matchList.first().entry, nullptr, matchList.first().sequence);
executeAutoTypeActions(matchList.first().entry, nullptr, matchList.first().sequence);
} else {
m_windowFromGlobal = m_plugin->activeWindow();
AutoTypeSelectDialog* selectDialog = new AutoTypeSelectDialog();
@ -253,7 +262,7 @@ void AutoType::performAutoTypeFromGlobal(AutoTypeMatch match)
m_inAutoType = false;
performAutoType(match.entry, nullptr, match.sequence, m_windowFromGlobal);
executeAutoTypeActions(match.entry, nullptr, match.sequence, m_windowFromGlobal);
}
void AutoType::resetInAutoType()
@ -283,6 +292,7 @@ void AutoType::unloadPlugin()
}
}
bool AutoType::registerGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers)
{
Q_ASSERT(key);
@ -325,12 +335,19 @@ int AutoType::callEventFilter(void* event)
return m_plugin->platformEventFilter(event);
}
bool AutoType::parseActions(const QString& sequence, const Entry* entry, QList<AutoTypeAction*>& actions)
/**
* Parse an autotype sequence and resolve its Template/command inside as AutoTypeActions
*/
bool AutoType::parseActions(const QString& actionSequence, const Entry* entry, QList<AutoTypeAction*>& actions)
{
QString tmpl;
bool inTmpl = false;
m_autoTypeDelay = config()->get("AutoTypeDelay").toInt();
QString sequence = actionSequence;
sequence.replace("{{}", "{LEFTBRACE}");
sequence.replace("{}}", "{RIGHTBRACE}");
for (const QChar& ch : sequence) {
if (inTmpl) {
if (ch == '{') {
@ -369,6 +386,9 @@ bool AutoType::parseActions(const QString& sequence, const Entry* entry, QList<A
return true;
}
/**
* Convert an autotype Template/command to its AutoTypeAction that will be executed by the plugin executor
*/
QList<AutoTypeAction*> AutoType::createActionFromTemplate(const QString& tmpl, const Entry* entry)
{
QString tmplName = tmpl;
@ -512,6 +532,9 @@ QList<AutoTypeAction*> AutoType::createActionFromTemplate(const QString& tmpl, c
return list;
}
/**
* Retrive the autotype sequences matches for a given windowTitle
*/
QList<QString> AutoType::autoTypeSequences(const Entry* entry, const QString& windowTitle)
{
QList<QString> sequenceList;
@ -564,6 +587,9 @@ QList<QString> AutoType::autoTypeSequences(const Entry* entry, const QString& wi
return sequenceList;
}
/**
* Checks if a window title matches a pattern
*/
bool AutoType::windowMatches(const QString& windowTitle, const QString& windowPattern)
{
if (windowPattern.startsWith("//") && windowPattern.endsWith("//") && windowPattern.size() >= 4) {
@ -574,11 +600,19 @@ bool AutoType::windowMatches(const QString& windowTitle, const QString& windowPa
}
}
/**
* Checks if a window title matches an entry Title
* The entry title should be Spr-compiled by the caller
*/
bool AutoType::windowMatchesTitle(const QString& windowTitle, const QString& resolvedTitle)
{
return !resolvedTitle.isEmpty() && windowTitle.contains(resolvedTitle, Qt::CaseInsensitive);
}
/**
* Checks if a window title matches an entry URL
* The entry URL should be Spr-compiled by the caller
*/
bool AutoType::windowMatchesUrl(const QString& windowTitle, const QString& resolvedUrl)
{
if (!resolvedUrl.isEmpty() && windowTitle.contains(resolvedUrl, Qt::CaseInsensitive)) {
@ -593,6 +627,9 @@ bool AutoType::windowMatchesUrl(const QString& windowTitle, const QString& resol
return false;
}
/**
* Checks if the overall syntax of an autotype sequence is fine
*/
bool AutoType::checkSyntax(const QString& string)
{
QString allowRepetition = "(?:\\s\\d+)?";
@ -618,6 +655,9 @@ bool AutoType::checkSyntax(const QString& string)
return match.hasMatch();
}
/**
* Checks an autotype sequence for high delay
*/
bool AutoType::checkHighDelay(const QString& string)
{
// 5 digit numbers(10 seconds) are too much
@ -626,6 +666,9 @@ bool AutoType::checkHighDelay(const QString& string)
return match.hasMatch();
}
/**
* Checks an autotype sequence for slow keypress
*/
bool AutoType::checkSlowKeypress(const QString& string)
{
// 3 digit numbers(100 milliseconds) are too much
@ -634,6 +677,9 @@ bool AutoType::checkSlowKeypress(const QString& string)
return match.hasMatch();
}
/**
* Checks an autotype sequence for high repetition command
*/
bool AutoType::checkHighRepetition(const QString& string)
{
// 3 digit numbers are too much
@ -642,6 +688,9 @@ bool AutoType::checkHighRepetition(const QString& string)
return match.hasMatch();
}
/**
* Verify if the syntax of an autotype sequence is correct and doesn't have silly parameters
*/
bool AutoType::verifyAutoTypeSyntax(const QString& sequence)
{
if (!AutoType::checkSyntax(sequence)) {
@ -675,12 +724,3 @@ bool AutoType::verifyAutoTypeSyntax(const QString& sequence)
}
return true;
}
void AutoType::performAutoType(const Entry* entry, QWidget* hideWindow, const QString& customSequence, WId window)
{
auto sequence = entry->effectiveAutoTypeSequence();
if (verifyAutoTypeSyntax(sequence)) {
executeAutoTypeActions(entry, hideWindow, customSequence, window);
}
}

View File

@ -38,10 +38,6 @@ class AutoType : public QObject
public:
QStringList windowTitles();
void executeAutoTypeActions(const Entry* entry,
QWidget* hideWindow = nullptr,
const QString& customSequence = QString(),
WId window = 0);
bool registerGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers);
void unregisterGlobalShortcut();
int callEventFilter(void* event);
@ -51,9 +47,7 @@ public:
static bool checkHighDelay(const QString& string);
static bool verifyAutoTypeSyntax(const QString& sequence);
void performAutoType(const Entry* entry,
QWidget* hideWindow = nullptr,
const QString& customSequence = QString(),
WId window = 0);
QWidget* hideWindow = nullptr);
inline bool isAvailable()
{
@ -79,6 +73,10 @@ private:
explicit AutoType(QObject* parent = nullptr, bool test = false);
~AutoType();
void loadPlugin(const QString& pluginPath);
void executeAutoTypeActions(const Entry* entry,
QWidget* hideWindow = nullptr,
const QString& customSequence = QString(),
WId window = 0);
bool parseActions(const QString& sequence, const Entry* entry, QList<AutoTypeAction*>& actions);
QList<AutoTypeAction*> createActionFromTemplate(const QString& tmpl, const Entry* entry);
QList<QString> autoTypeSequences(const Entry* entry, const QString& windowTitle = QString());

View File

@ -29,6 +29,8 @@
const int Entry::DefaultIconNumber = 0;
const int Entry::ResolveMaximumDepth = 10;
const QString Entry::AutoTypeSequenceUsername = "{USERNAME}{ENTER}";
const QString Entry::AutoTypeSequencePassword = "{PASSWORD}{ENTER}";
Entry::Entry()
@ -232,7 +234,7 @@ QString Entry::effectiveAutoTypeSequence() const
if (!parent) {
return QString();
}
QString sequence = parent->effectiveAutoTypeSequence();
if (sequence.isEmpty()) {
return QString();
@ -242,6 +244,15 @@ QString Entry::effectiveAutoTypeSequence() const
return m_data.defaultAutoTypeSequence;
}
if (sequence == Group::RootAutoTypeSequence && (!username().isEmpty() || !password().isEmpty())) {
if (username().isEmpty()) {
return AutoTypeSequencePassword;
} else if (password().isEmpty()) {
return AutoTypeSequenceUsername;
}
return Group::RootAutoTypeSequence;
}
return sequence;
}

View File

@ -85,6 +85,7 @@ public:
int autoTypeObfuscation() const;
QString defaultAutoTypeSequence() const;
QString effectiveAutoTypeSequence() const;
QString effectiveNewAutoTypeSequence() const;
AutoTypeAssociations* autoTypeAssociations();
const AutoTypeAssociations* autoTypeAssociations() const;
QString title() const;
@ -109,6 +110,8 @@ public:
static const int DefaultIconNumber;
static const int ResolveMaximumDepth;
static const QString AutoTypeSequenceUsername;
static const QString AutoTypeSequencePassword;
void setUuid(const Uuid& uuid);
void setIcon(int iconNumber);

View File

@ -25,6 +25,7 @@
const int Group::DefaultIconNumber = 48;
const int Group::RecycleBinIconNumber = 43;
const QString Group::RootAutoTypeSequence = "{USERNAME}{TAB}{PASSWORD}{ENTER}";
Group::CloneFlags Group::DefaultCloneFlags = static_cast<Group::CloneFlags>(
Group::CloneNewUuid | Group::CloneResetTimeInfo | Group::CloneIncludeEntries);
@ -211,7 +212,7 @@ QString Group::effectiveAutoTypeSequence() const
} while (group && sequence.isEmpty());
if (sequence.isEmpty()) {
sequence = "{USERNAME}{TAB}{PASSWORD}{ENTER}";
sequence = RootAutoTypeSequence;
}
return sequence;

View File

@ -88,6 +88,7 @@ public:
static const int RecycleBinIconNumber;
static CloneFlags DefaultCloneFlags;
static Entry::CloneFlags DefaultEntryCloneFlags;
static const QString RootAutoTypeSequence;
Group* findChildByName(const QString& name);
Group* findChildByUuid(const Uuid& uuid);

View File

@ -136,7 +136,7 @@ void TestAutoType::testInternal()
QCOMPARE(m_platform->activeWindowTitle(), QString("Test"));
}
void TestAutoType::testAutoTypeWithoutSequence()
void TestAutoType::testSingleAutoType()
{
m_autoType->performAutoType(m_entry1, nullptr);
@ -147,17 +147,6 @@ void TestAutoType::testAutoTypeWithoutSequence()
.arg(m_test->keyToString(Qt::Key_Enter)));
}
void TestAutoType::testAutoTypeWithSequence()
{
m_autoType->performAutoType(m_entry1, nullptr, "{Username}abc{PaSsWoRd}");
QCOMPARE(m_test->actionCount(), 15);
QCOMPARE(m_test->actionChars(),
QString("%1abc%2")
.arg(m_entry1->username())
.arg(m_entry1->password()));
}
void TestAutoType::testGlobalAutoTypeWithNoMatch()
{
m_test->setActiveWindowTitle("nomatch");

View File

@ -38,8 +38,7 @@ private slots:
void cleanup();
void testInternal();
void testAutoTypeWithoutSequence();
void testAutoTypeWithSequence();
void testSingleAutoType();
void testGlobalAutoTypeWithNoMatch();
void testGlobalAutoTypeWithOneMatch();
void testGlobalAutoTypeTitleMatch();