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(); 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; 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; QList<AutoTypeAction*> actions;
ListDeleter<AutoTypeAction*> actionsDeleter(&actions); ListDeleter<AutoTypeAction*> actionsDeleter(&actions);
@ -191,6 +176,30 @@ void AutoType::executeAutoTypeActions(const Entry* entry, QWidget* hideWindow, c
m_inAutoType = false; 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) void AutoType::performGlobalAutoType(const QList<Database*>& dbList)
{ {
if (m_inAutoType || !m_plugin) { if (m_inAutoType || !m_plugin) {
@ -227,7 +236,7 @@ void AutoType::performGlobalAutoType(const QList<Database*>& dbList)
MessageBox::information(nullptr, tr("Auto-Type - KeePassXC"), message); MessageBox::information(nullptr, tr("Auto-Type - KeePassXC"), message);
} else if ((matchList.size() == 1) && !config()->get("security/autotypeask").toBool()) { } else if ((matchList.size() == 1) && !config()->get("security/autotypeask").toBool()) {
m_inAutoType = false; m_inAutoType = false;
performAutoType(matchList.first().entry, nullptr, matchList.first().sequence); executeAutoTypeActions(matchList.first().entry, nullptr, matchList.first().sequence);
} else { } else {
m_windowFromGlobal = m_plugin->activeWindow(); m_windowFromGlobal = m_plugin->activeWindow();
AutoTypeSelectDialog* selectDialog = new AutoTypeSelectDialog(); AutoTypeSelectDialog* selectDialog = new AutoTypeSelectDialog();
@ -253,7 +262,7 @@ void AutoType::performAutoTypeFromGlobal(AutoTypeMatch match)
m_inAutoType = false; m_inAutoType = false;
performAutoType(match.entry, nullptr, match.sequence, m_windowFromGlobal); executeAutoTypeActions(match.entry, nullptr, match.sequence, m_windowFromGlobal);
} }
void AutoType::resetInAutoType() void AutoType::resetInAutoType()
@ -283,6 +292,7 @@ void AutoType::unloadPlugin()
} }
} }
bool AutoType::registerGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers) bool AutoType::registerGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers)
{ {
Q_ASSERT(key); Q_ASSERT(key);
@ -325,12 +335,19 @@ int AutoType::callEventFilter(void* event)
return m_plugin->platformEventFilter(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; QString tmpl;
bool inTmpl = false; bool inTmpl = false;
m_autoTypeDelay = config()->get("AutoTypeDelay").toInt(); m_autoTypeDelay = config()->get("AutoTypeDelay").toInt();
QString sequence = actionSequence;
sequence.replace("{{}", "{LEFTBRACE}");
sequence.replace("{}}", "{RIGHTBRACE}");
for (const QChar& ch : sequence) { for (const QChar& ch : sequence) {
if (inTmpl) { if (inTmpl) {
if (ch == '{') { if (ch == '{') {
@ -369,6 +386,9 @@ bool AutoType::parseActions(const QString& sequence, const Entry* entry, QList<A
return true; 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) QList<AutoTypeAction*> AutoType::createActionFromTemplate(const QString& tmpl, const Entry* entry)
{ {
QString tmplName = tmpl; QString tmplName = tmpl;
@ -512,6 +532,9 @@ QList<AutoTypeAction*> AutoType::createActionFromTemplate(const QString& tmpl, c
return list; return list;
} }
/**
* Retrive the autotype sequences matches for a given windowTitle
*/
QList<QString> AutoType::autoTypeSequences(const Entry* entry, const QString& windowTitle) QList<QString> AutoType::autoTypeSequences(const Entry* entry, const QString& windowTitle)
{ {
QList<QString> sequenceList; QList<QString> sequenceList;
@ -564,6 +587,9 @@ QList<QString> AutoType::autoTypeSequences(const Entry* entry, const QString& wi
return sequenceList; return sequenceList;
} }
/**
* Checks if a window title matches a pattern
*/
bool AutoType::windowMatches(const QString& windowTitle, const QString& windowPattern) bool AutoType::windowMatches(const QString& windowTitle, const QString& windowPattern)
{ {
if (windowPattern.startsWith("//") && windowPattern.endsWith("//") && windowPattern.size() >= 4) { 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) bool AutoType::windowMatchesTitle(const QString& windowTitle, const QString& resolvedTitle)
{ {
return !resolvedTitle.isEmpty() && windowTitle.contains(resolvedTitle, Qt::CaseInsensitive); 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) bool AutoType::windowMatchesUrl(const QString& windowTitle, const QString& resolvedUrl)
{ {
if (!resolvedUrl.isEmpty() && windowTitle.contains(resolvedUrl, Qt::CaseInsensitive)) { if (!resolvedUrl.isEmpty() && windowTitle.contains(resolvedUrl, Qt::CaseInsensitive)) {
@ -593,6 +627,9 @@ bool AutoType::windowMatchesUrl(const QString& windowTitle, const QString& resol
return false; return false;
} }
/**
* Checks if the overall syntax of an autotype sequence is fine
*/
bool AutoType::checkSyntax(const QString& string) bool AutoType::checkSyntax(const QString& string)
{ {
QString allowRepetition = "(?:\\s\\d+)?"; QString allowRepetition = "(?:\\s\\d+)?";
@ -618,6 +655,9 @@ bool AutoType::checkSyntax(const QString& string)
return match.hasMatch(); return match.hasMatch();
} }
/**
* Checks an autotype sequence for high delay
*/
bool AutoType::checkHighDelay(const QString& string) bool AutoType::checkHighDelay(const QString& string)
{ {
// 5 digit numbers(10 seconds) are too much // 5 digit numbers(10 seconds) are too much
@ -626,6 +666,9 @@ bool AutoType::checkHighDelay(const QString& string)
return match.hasMatch(); return match.hasMatch();
} }
/**
* Checks an autotype sequence for slow keypress
*/
bool AutoType::checkSlowKeypress(const QString& string) bool AutoType::checkSlowKeypress(const QString& string)
{ {
// 3 digit numbers(100 milliseconds) are too much // 3 digit numbers(100 milliseconds) are too much
@ -634,6 +677,9 @@ bool AutoType::checkSlowKeypress(const QString& string)
return match.hasMatch(); return match.hasMatch();
} }
/**
* Checks an autotype sequence for high repetition command
*/
bool AutoType::checkHighRepetition(const QString& string) bool AutoType::checkHighRepetition(const QString& string)
{ {
// 3 digit numbers are too much // 3 digit numbers are too much
@ -642,6 +688,9 @@ bool AutoType::checkHighRepetition(const QString& string)
return match.hasMatch(); 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) bool AutoType::verifyAutoTypeSyntax(const QString& sequence)
{ {
if (!AutoType::checkSyntax(sequence)) { if (!AutoType::checkSyntax(sequence)) {
@ -675,12 +724,3 @@ bool AutoType::verifyAutoTypeSyntax(const QString& sequence)
} }
return true; 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: public:
QStringList windowTitles(); 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); bool registerGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifiers);
void unregisterGlobalShortcut(); void unregisterGlobalShortcut();
int callEventFilter(void* event); int callEventFilter(void* event);
@ -51,9 +47,7 @@ public:
static bool checkHighDelay(const QString& string); static bool checkHighDelay(const QString& string);
static bool verifyAutoTypeSyntax(const QString& sequence); static bool verifyAutoTypeSyntax(const QString& sequence);
void performAutoType(const Entry* entry, void performAutoType(const Entry* entry,
QWidget* hideWindow = nullptr, QWidget* hideWindow = nullptr);
const QString& customSequence = QString(),
WId window = 0);
inline bool isAvailable() inline bool isAvailable()
{ {
@ -79,6 +73,10 @@ private:
explicit AutoType(QObject* parent = nullptr, bool test = false); explicit AutoType(QObject* parent = nullptr, bool test = false);
~AutoType(); ~AutoType();
void loadPlugin(const QString& pluginPath); 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); bool parseActions(const QString& sequence, const Entry* entry, QList<AutoTypeAction*>& actions);
QList<AutoTypeAction*> createActionFromTemplate(const QString& tmpl, const Entry* entry); QList<AutoTypeAction*> createActionFromTemplate(const QString& tmpl, const Entry* entry);
QList<QString> autoTypeSequences(const Entry* entry, const QString& windowTitle = QString()); QList<QString> autoTypeSequences(const Entry* entry, const QString& windowTitle = QString());

View File

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

View File

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

View File

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

View File

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

View File

@ -136,7 +136,7 @@ void TestAutoType::testInternal()
QCOMPARE(m_platform->activeWindowTitle(), QString("Test")); QCOMPARE(m_platform->activeWindowTitle(), QString("Test"));
} }
void TestAutoType::testAutoTypeWithoutSequence() void TestAutoType::testSingleAutoType()
{ {
m_autoType->performAutoType(m_entry1, nullptr); m_autoType->performAutoType(m_entry1, nullptr);
@ -147,17 +147,6 @@ void TestAutoType::testAutoTypeWithoutSequence()
.arg(m_test->keyToString(Qt::Key_Enter))); .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() void TestAutoType::testGlobalAutoTypeWithNoMatch()
{ {
m_test->setActiveWindowTitle("nomatch"); m_test->setActiveWindowTitle("nomatch");

View File

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