diff --git a/src/autotype/AutoType.cpp b/src/autotype/AutoType.cpp index 0d01a8311..2df3b4f1e 100644 --- a/src/autotype/AutoType.cpp +++ b/src/autotype/AutoType.cpp @@ -20,6 +20,7 @@ #include #include +#include #include "config-keepassx.h" @@ -54,17 +55,16 @@ AutoType::AutoType(QObject* parent, bool test) QString pluginName = "keepassx-autotype-"; if (!test) { pluginName += QApplication::platformName(); - } - else { + } else { pluginName += "test"; } QString pluginPath = filePath()->pluginPath(pluginName); if (!pluginPath.isEmpty()) { - #ifdef WITH_XC_AUTOTYPE +#ifdef WITH_XC_AUTOTYPE loadPlugin(pluginPath); - #endif +#endif } connect(qApp, SIGNAL(aboutToQuit()), SLOT(unloadPlugin())); @@ -91,8 +91,7 @@ void AutoType::loadPlugin(const QString& pluginPath) if (m_plugin->isAvailable()) { m_executor = m_plugin->createExecutor(); connect(pluginInstance, SIGNAL(globalShortcutTriggered()), SIGNAL(globalShortcutTriggered())); - } - else { + } else { unloadPlugin(); } } @@ -128,7 +127,7 @@ QStringList AutoType::windowTitles() return m_plugin->windowTitles(); } -void AutoType::performAutoType(const Entry* entry, QWidget* hideWindow, const QString& customSequence, WId window) +void AutoType::executeAutoTypeActions(const Entry* entry, QWidget* hideWindow, const QString& customSequence, WId window) { if (m_inAutoType || !m_plugin) { return; @@ -138,11 +137,14 @@ void AutoType::performAutoType(const Entry* entry, QWidget* hideWindow, const QS QString sequence; if (customSequence.isEmpty()) { sequence = autoTypeSequence(entry); - } - else { + } else { sequence = customSequence; } + if (!checkSyntax(sequence)) { + return; + } + sequence.replace("{{}", "{LEFTBRACE}"); sequence.replace("{}}", "{RIGHTBRACE}"); @@ -217,16 +219,14 @@ void AutoType::performGlobalAutoType(const QList& dbList) message.append("\n\n"); message.append(windowTitle); MessageBox::information(nullptr, tr("Auto-Type - KeePassXC"), message); - } - else if ((entryList.size() == 1) && !config()->get("security/autotypeask").toBool()) { + } else if ((entryList.size() == 1) && !config()->get("security/autotypeask").toBool()) { m_inAutoType = false; performAutoType(entryList.first(), nullptr, sequenceHash[entryList.first()]); - } - else { + } else { m_windowFromGlobal = m_plugin->activeWindow(); AutoTypeSelectDialog* selectDialog = new AutoTypeSelectDialog(); - connect(selectDialog, SIGNAL(entryActivated(Entry*,QString)), - SLOT(performAutoTypeFromGlobal(Entry*,QString))); + connect( + selectDialog, SIGNAL(entryActivated(Entry*, QString)), SLOT(performAutoTypeFromGlobal(Entry*, QString))); connect(selectDialog, SIGNAL(rejected()), SLOT(resetInAutoType())); selectDialog->setEntries(entryList, sequenceHash); #if defined(Q_OS_MAC) @@ -246,6 +246,7 @@ void AutoType::performAutoTypeFromGlobal(Entry* entry, const QString& sequence) m_plugin->raiseWindow(m_windowFromGlobal); m_inAutoType = false; + performAutoType(entry, nullptr, sequence, m_windowFromGlobal); } @@ -294,12 +295,10 @@ bool AutoType::registerGlobalShortcut(Qt::Key key, Qt::KeyboardModifiers modifie m_currentGlobalKey = key; m_currentGlobalModifiers = modifiers; return true; - } - else { + } else { return false; } - } - else { + } else { return true; } } @@ -326,30 +325,28 @@ bool AutoType::parseActions(const QString& sequence, const Entry* entry, QListget("AutoTypeDelay").toInt(); - for (const QChar& ch : sequence) { if (inTmpl) { if (ch == '{') { qWarning("Syntax error in auto-type sequence."); return false; - } - else if (ch == '}') { - actions.append(createActionFromTemplate(tmpl, entry)); + } else if (ch == '}') { + QList autoType = createActionFromTemplate(tmpl, entry); + if (autoType.isEmpty()) { + return false; + } + actions.append(autoType); inTmpl = false; tmpl.clear(); - } - else { + } else { tmpl += ch; } - } - else if (ch == '{') { + } else if (ch == '{') { inTmpl = true; - } - else if (ch == '}') { + } else if (ch == '}') { qWarning("Syntax error in auto-type sequence."); return false; - } - else { + } else { actions.append(new AutoTypeChar(ch)); } } @@ -388,120 +385,78 @@ QList AutoType::createActionFromTemplate(const QString& tmpl, c if (num == 0) { return list; } - // some safety checks - else if (tmplName.compare("delay",Qt::CaseInsensitive)==0) { - if (num > 10000) { - return list; - } - } - else if (num > 100) { - return list; - } } - if (tmplName.compare("tab",Qt::CaseInsensitive)==0) { + if (tmplName.compare("tab", Qt::CaseInsensitive) == 0) { list.append(new AutoTypeKey(Qt::Key_Tab)); - } - else if (tmplName.compare("enter",Qt::CaseInsensitive)==0) { + } else if (tmplName.compare("enter", Qt::CaseInsensitive) == 0) { list.append(new AutoTypeKey(Qt::Key_Enter)); - } - else if (tmplName.compare("space",Qt::CaseInsensitive)==0) { + } else if (tmplName.compare("space", Qt::CaseInsensitive) == 0) { list.append(new AutoTypeKey(Qt::Key_Space)); - } - else if (tmplName.compare("up",Qt::CaseInsensitive)==0) { + } else if (tmplName.compare("up", Qt::CaseInsensitive) == 0) { list.append(new AutoTypeKey(Qt::Key_Up)); - } - else if (tmplName.compare("down",Qt::CaseInsensitive)==0) { + } else if (tmplName.compare("down", Qt::CaseInsensitive) == 0) { list.append(new AutoTypeKey(Qt::Key_Down)); - } - else if (tmplName.compare("left",Qt::CaseInsensitive)==0) { + } else if (tmplName.compare("left", Qt::CaseInsensitive) == 0) { list.append(new AutoTypeKey(Qt::Key_Left)); - } - else if (tmplName.compare("right",Qt::CaseInsensitive)==0) { + } else if (tmplName.compare("right", Qt::CaseInsensitive) == 0) { list.append(new AutoTypeKey(Qt::Key_Right)); - } - else if (tmplName.compare("insert",Qt::CaseInsensitive)==0 || - tmplName.compare("ins",Qt::CaseInsensitive)==0) { + } else if (tmplName.compare("insert", Qt::CaseInsensitive) == 0 || + tmplName.compare("ins", Qt::CaseInsensitive) == 0) { list.append(new AutoTypeKey(Qt::Key_Insert)); - } - else if (tmplName.compare("delete",Qt::CaseInsensitive)==0 || - tmplName.compare("del",Qt::CaseInsensitive)==0) { + } else if (tmplName.compare("delete", Qt::CaseInsensitive) == 0 || + tmplName.compare("del", Qt::CaseInsensitive) == 0) { list.append(new AutoTypeKey(Qt::Key_Delete)); - } - else if (tmplName.compare("home",Qt::CaseInsensitive)==0) { + } else if (tmplName.compare("home", Qt::CaseInsensitive) == 0) { list.append(new AutoTypeKey(Qt::Key_Home)); - } - else if (tmplName.compare("end",Qt::CaseInsensitive)==0) { + } else if (tmplName.compare("end", Qt::CaseInsensitive) == 0) { list.append(new AutoTypeKey(Qt::Key_End)); - } - else if (tmplName.compare("pgup",Qt::CaseInsensitive)==0) { + } else if (tmplName.compare("pgup", Qt::CaseInsensitive) == 0) { list.append(new AutoTypeKey(Qt::Key_PageUp)); - } - else if (tmplName.compare("pgdown",Qt::CaseInsensitive)==0) { + } else if (tmplName.compare("pgdown", Qt::CaseInsensitive) == 0) { list.append(new AutoTypeKey(Qt::Key_PageDown)); - } - else if (tmplName.compare("backspace",Qt::CaseInsensitive)==0 || - tmplName.compare("bs",Qt::CaseInsensitive)==0 || - tmplName.compare("bksp",Qt::CaseInsensitive)==0) { + } else if (tmplName.compare("backspace", Qt::CaseInsensitive) == 0 || + tmplName.compare("bs", Qt::CaseInsensitive) == 0 || tmplName.compare("bksp", Qt::CaseInsensitive) == 0) { list.append(new AutoTypeKey(Qt::Key_Backspace)); - } - else if (tmplName.compare("break",Qt::CaseInsensitive)==0) { + } else if (tmplName.compare("break", Qt::CaseInsensitive) == 0) { list.append(new AutoTypeKey(Qt::Key_Pause)); - } - else if (tmplName.compare("capslock",Qt::CaseInsensitive)==0) { + } else if (tmplName.compare("capslock", Qt::CaseInsensitive) == 0) { list.append(new AutoTypeKey(Qt::Key_CapsLock)); - } - else if (tmplName.compare("esc",Qt::CaseInsensitive)==0) { + } else if (tmplName.compare("esc", Qt::CaseInsensitive) == 0) { list.append(new AutoTypeKey(Qt::Key_Escape)); - } - else if (tmplName.compare("help",Qt::CaseInsensitive)==0) { + } else if (tmplName.compare("help", Qt::CaseInsensitive) == 0) { list.append(new AutoTypeKey(Qt::Key_Help)); - } - else if (tmplName.compare("numlock",Qt::CaseInsensitive)==0) { + } else if (tmplName.compare("numlock", Qt::CaseInsensitive) == 0) { list.append(new AutoTypeKey(Qt::Key_NumLock)); - } - else if (tmplName.compare("ptrsc",Qt::CaseInsensitive)==0) { + } else if (tmplName.compare("ptrsc", Qt::CaseInsensitive) == 0) { list.append(new AutoTypeKey(Qt::Key_Print)); - } - else if (tmplName.compare("scrolllock",Qt::CaseInsensitive)==0) { + } else if (tmplName.compare("scrolllock", Qt::CaseInsensitive) == 0) { list.append(new AutoTypeKey(Qt::Key_ScrollLock)); } // Qt doesn't know about keypad keys so use the normal ones instead - else if (tmplName.compare("add",Qt::CaseInsensitive)==0 || - tmplName.compare("+",Qt::CaseInsensitive)==0) { + else if (tmplName.compare("add", Qt::CaseInsensitive) == 0 || tmplName.compare("+", Qt::CaseInsensitive) == 0) { list.append(new AutoTypeChar('+')); - } - else if (tmplName.compare("subtract",Qt::CaseInsensitive)==0) { + } else if (tmplName.compare("subtract", Qt::CaseInsensitive) == 0) { list.append(new AutoTypeChar('-')); - } - else if (tmplName.compare("multiply",Qt::CaseInsensitive)==0) { + } else if (tmplName.compare("multiply", Qt::CaseInsensitive) == 0) { list.append(new AutoTypeChar('*')); - } - else if (tmplName.compare("divide",Qt::CaseInsensitive)==0) { + } else if (tmplName.compare("divide", Qt::CaseInsensitive) == 0) { list.append(new AutoTypeChar('/')); - } - else if (tmplName.compare("^",Qt::CaseInsensitive)==0) { + } else if (tmplName.compare("^", Qt::CaseInsensitive) == 0) { list.append(new AutoTypeChar('^')); - } - else if (tmplName.compare("%",Qt::CaseInsensitive)==0) { + } else if (tmplName.compare("%", Qt::CaseInsensitive) == 0) { list.append(new AutoTypeChar('%')); - } - else if (tmplName.compare("~",Qt::CaseInsensitive)==0) { + } else if (tmplName.compare("~", Qt::CaseInsensitive) == 0) { list.append(new AutoTypeChar('~')); - } - else if (tmplName.compare("(",Qt::CaseInsensitive)==0) { + } else if (tmplName.compare("(", Qt::CaseInsensitive) == 0) { list.append(new AutoTypeChar('(')); - } - else if (tmplName.compare(")",Qt::CaseInsensitive)==0) { + } else if (tmplName.compare(")", Qt::CaseInsensitive) == 0) { list.append(new AutoTypeChar(')')); - } - else if (tmplName.compare("leftbrace",Qt::CaseInsensitive)==0) { + } else if (tmplName.compare("leftbrace", Qt::CaseInsensitive) == 0) { list.append(new AutoTypeChar('{')); - } - else if (tmplName.compare("rightbrace",Qt::CaseInsensitive)==0) { + } else if (tmplName.compare("rightbrace", Qt::CaseInsensitive) == 0) { list.append(new AutoTypeChar('}')); - } - else { + } else { QRegExp fnRegexp("f(\\d+)", Qt::CaseInsensitive, QRegExp::RegExp2); if (fnRegexp.exactMatch(tmplName)) { int fnNo = fnRegexp.cap(1).toInt(); @@ -515,18 +470,14 @@ QList AutoType::createActionFromTemplate(const QString& tmpl, c for (int i = 1; i < num; i++) { list.append(list.at(0)->clone()); } - return list; } - - if (tmplName.compare("delay",Qt::CaseInsensitive)==0 && num > 0) { + if (tmplName.compare("delay", Qt::CaseInsensitive) == 0 && num > 0) { list.append(new AutoTypeDelay(num)); - } - else if (tmplName.compare("clearfield",Qt::CaseInsensitive)==0) { + } else if (tmplName.compare("clearfield", Qt::CaseInsensitive) == 0) { list.append(new AutoTypeClearField()); - } - else if (tmplName.compare("totp", Qt::CaseInsensitive) == 0) { + } else if (tmplName.compare("totp", Qt::CaseInsensitive) == 0) { QString totp = entry->totp(); if (!totp.isEmpty()) { for (const QChar& ch : totp) { @@ -545,11 +496,9 @@ QList AutoType::createActionFromTemplate(const QString& tmpl, c for (const QChar& ch : resolved) { if (ch == '\n') { list.append(new AutoTypeKey(Qt::Key_Enter)); - } - else if (ch == '\t') { + } else if (ch == '\t') { list.append(new AutoTypeKey(Qt::Key_Tab)); - } - else { + } else { list.append(new AutoTypeChar(ch)); } } @@ -574,8 +523,7 @@ QString AutoType::autoTypeSequence(const Entry* entry, const QString& windowTitl if (windowMatches(windowTitle, window)) { if (!assoc.sequence.isEmpty()) { sequence = assoc.sequence; - } - else { + } else { sequence = entry->defaultAutoTypeSequence(); } match = true; @@ -583,14 +531,14 @@ QString AutoType::autoTypeSequence(const Entry* entry, const QString& windowTitl } } - if (!match && config()->get("AutoTypeEntryTitleMatch").toBool() - && windowMatchesTitle(windowTitle, entry->resolvePlaceholder(entry->title()))) { + if (!match && config()->get("AutoTypeEntryTitleMatch").toBool() && + windowMatchesTitle(windowTitle, entry->resolvePlaceholder(entry->title()))) { sequence = entry->defaultAutoTypeSequence(); match = true; } - if (!match && config()->get("AutoTypeEntryURLMatch").toBool() - && windowMatchesUrl(windowTitle, entry->resolvePlaceholder(entry->url()))) { + if (!match && config()->get("AutoTypeEntryURLMatch").toBool() && + windowMatchesUrl(windowTitle, entry->resolvePlaceholder(entry->url()))) { sequence = entry->defaultAutoTypeSequence(); match = true; } @@ -598,8 +546,7 @@ QString AutoType::autoTypeSequence(const Entry* entry, const QString& windowTitl if (!match) { return QString(); } - } - else { + } else { sequence = entry->defaultAutoTypeSequence(); } @@ -608,8 +555,7 @@ QString AutoType::autoTypeSequence(const Entry* entry, const QString& windowTitl if (!enableSet) { if (group->autoTypeEnabled() == Group::Disable) { return QString(); - } - else if (group->autoTypeEnabled() == Group::Enable) { + } else if (group->autoTypeEnabled() == Group::Enable) { enableSet = true; } } @@ -621,14 +567,13 @@ QString AutoType::autoTypeSequence(const Entry* entry, const QString& windowTitl group = group->parentGroup(); } while (group && (!enableSet || sequence.isEmpty())); - if (sequence.isEmpty() && (!entry->resolvePlaceholder(entry->username()).isEmpty() || !entry->resolvePlaceholder(entry->password()).isEmpty())) { + if (sequence.isEmpty() && (!entry->resolvePlaceholder(entry->username()).isEmpty() || + !entry->resolvePlaceholder(entry->password()).isEmpty())) { if (entry->resolvePlaceholder(entry->username()).isEmpty()) { sequence = "{PASSWORD}{ENTER}"; - } - else if (entry->resolvePlaceholder(entry->password()).isEmpty()) { + } else if (entry->resolvePlaceholder(entry->password()).isEmpty()) { sequence = "{USERNAME}{ENTER}"; - } - else { + } else { sequence = "{USERNAME}{TAB}{PASSWORD}{ENTER}"; } } @@ -641,8 +586,7 @@ bool AutoType::windowMatches(const QString& windowTitle, const QString& windowPa if (windowPattern.startsWith("//") && windowPattern.endsWith("//") && windowPattern.size() >= 4) { QRegExp regExp(windowPattern.mid(2, windowPattern.size() - 4), Qt::CaseInsensitive, QRegExp::RegExp2); return (regExp.indexIn(windowTitle) != -1); - } - else { + } else { return WildcardMatcher(windowTitle).match(windowPattern); } } @@ -665,3 +609,96 @@ bool AutoType::windowMatchesUrl(const QString& windowTitle, const QString& resol return false; } + +bool AutoType::checkSyntax(const QString& string) +{ + QString allowRepetition = "(?:\\s\\d+)?"; + // the ":" allows custom commands with syntax S:Field + // exclude BEEP otherwise will be checked as valid + QString normalCommands = "(?!BEEP\\s)[A-Z:]*" + allowRepetition; + QString specialLiterals = "[\\^\\%\\(\\)~\\{\\}\\[\\]\\+]" + allowRepetition; + QString functionKeys = "(?:F[1-9]" + allowRepetition + "|F1[0-2])" + allowRepetition; + QString numpad = "NUMPAD\\d" + allowRepetition; + QString delay = "DELAY=\\d+"; + QString beep = "BEEP\\s\\d+\\s\\d+"; + QString vkey = "VKEY(?:-[EN]X)?\\s\\w+"; + + // these chars aren't in parentheses + QString shortcutKeys = "[\\^\\%~\\+@]"; + // a normal string not in parentheses + QString fixedStrings = "[^\\^\\%~\\+@\\{\\}]*"; + + QRegularExpression autoTypeSyntax("^(?:" + shortcutKeys + "|" + fixedStrings + "|\\{(?:" + normalCommands + "|" + specialLiterals + + "|" + functionKeys + "|" + numpad + "|" + delay + "|" + beep + "|" + vkey + ")\\})*$", + QRegularExpression::CaseInsensitiveOption); + QRegularExpressionMatch match = autoTypeSyntax.match(string); + return match.hasMatch(); +} + +bool AutoType::checkHighDelay(const QString& string) +{ + // 5 digit numbers(10 seconds) are too much + QRegularExpression highDelay("\\{DELAY\\s\\d{5,}\\}", QRegularExpression::CaseInsensitiveOption); + QRegularExpressionMatch match = highDelay.match(string); + return match.hasMatch(); +} + +bool AutoType::checkSlowKeypress(const QString& string) +{ + // 3 digit numbers(100 milliseconds) are too much + QRegularExpression slowKeypress("\\{DELAY=\\d{3,}\\}", QRegularExpression::CaseInsensitiveOption); + QRegularExpressionMatch match = slowKeypress.match(string); + return match.hasMatch(); +} + +bool AutoType::checkHighRepetition(const QString& string) +{ + // 3 digit numbers are too much + QRegularExpression highRepetition("\\{(?!DELAY\\s)\\w+\\s\\d{3,}\\}", QRegularExpression::CaseInsensitiveOption); + QRegularExpressionMatch match = highRepetition.match(string); + return match.hasMatch(); +} + +bool AutoType::verifyAutoTypeSyntax(const QString& sequence) +{ + if (!AutoType::checkSyntax(sequence)) { + QMessageBox messageBox; + messageBox.critical(0, tr("Auto-Type"), tr("The Syntax of your AutoType statement is incorrect!")); + return false; + } else if (AutoType::checkHighDelay(sequence)) { + QMessageBox::StandardButton reply; + reply = QMessageBox::question(0, tr("Auto-Type"), + tr("This AutoType command contains a very long delay. Do you really want to proceed?")); + + if (reply == QMessageBox::No) { + return false; + } + } else if (AutoType::checkSlowKeypress(sequence)) { + QMessageBox::StandardButton reply; + reply = QMessageBox::question(0, tr("Auto-Type"), + tr("This AutoType command contains very slow key-press. Do you really want to proceed?")); + + if (reply == QMessageBox::No) { + return false; + } + } else if (AutoType::checkHighRepetition(sequence)) { + QMessageBox::StandardButton reply; + reply = QMessageBox::question(0, tr("Auto-Type"), + tr("This AutoType command contains arguments which are " + "repeated very often. Do you really want to proceed?")); + + if (reply == QMessageBox::No) { + return false; + } + } + 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); + } +} diff --git a/src/autotype/AutoType.h b/src/autotype/AutoType.h index e881975ac..eb366ae9c 100644 --- a/src/autotype/AutoType.h +++ b/src/autotype/AutoType.h @@ -36,13 +36,25 @@ class AutoType : public QObject public: QStringList windowTitles(); - void performAutoType(const Entry* entry, QWidget* hideWindow = nullptr, - const QString& customSequence = QString(), WId window = 0); + 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); + static bool checkSyntax(const QString& string); + static bool checkHighRepetition(const QString& string); + static bool checkSlowKeypress(const QString& string); + 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); - inline bool isAvailable() { + inline bool isAvailable() + { return m_plugin; } @@ -85,7 +97,8 @@ private: Q_DISABLE_COPY(AutoType) }; -inline AutoType* autoType() { +inline AutoType* autoType() +{ return AutoType::instance(); } diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index 1ef91a4fc..387d5d2a2 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -613,7 +613,7 @@ void DatabaseWidget::openUrlForEntry(Entry* entry) } return; } - + // otherwise ask user if (urlString.length() > 6) { QString cmdTruncated = urlString.mid(6); @@ -627,7 +627,7 @@ void DatabaseWidget::openUrlForEntry(Entry* entry) this ); msgbox.setDefaultButton(QMessageBox::No); - + QCheckBox* checkbox = new QCheckBox(tr("Remember my choice"), &msgbox); msgbox.setCheckBox(checkbox); bool remember = false; @@ -636,12 +636,12 @@ void DatabaseWidget::openUrlForEntry(Entry* entry) remember = true; } }); - + int result = msgbox.exec(); if (result == QMessageBox::Yes) { QProcess::startDetached(urlString.mid(6)); } - + if (remember) { entry->attributes()->set(EntryAttributes::RememberCmdExecAttr, result == QMessageBox::Yes ? "1" : "0"); diff --git a/src/gui/entry/EditEntryWidget.cpp b/src/gui/entry/EditEntryWidget.cpp index 3e26e3092..707d2f6d2 100644 --- a/src/gui/entry/EditEntryWidget.cpp +++ b/src/gui/entry/EditEntryWidget.cpp @@ -32,6 +32,7 @@ #include #include +#include "autotype/AutoType.h" #include "core/Config.h" #include "core/Database.h" #include "core/Entry.h" @@ -764,7 +765,9 @@ void EditEntryWidget::updateEntryData(Entry* entry) const entry->setDefaultAutoTypeSequence(QString()); } else { - entry->setDefaultAutoTypeSequence(m_autoTypeUi->sequenceEdit->text()); + if (AutoType::verifyAutoTypeSyntax(m_autoTypeUi->sequenceEdit->text())) { + entry->setDefaultAutoTypeSequence(m_autoTypeUi->sequenceEdit->text()); + } } entry->autoTypeAssociations()->copyDataFrom(m_autoTypeAssoc); diff --git a/tests/TestAutoType.cpp b/tests/TestAutoType.cpp index acc2df61f..2f980e546 100644 --- a/tests/TestAutoType.cpp +++ b/tests/TestAutoType.cpp @@ -282,3 +282,34 @@ void TestAutoType::testGlobalAutoTypeRegExp() QCOMPARE(m_test->actionChars(), QString("custom_attr_third")); m_test->clearActions(); } + +void TestAutoType::testAutoTypeSyntaxChecks() +{ + // Huge sequence + QCOMPARE(true, AutoType::checkSyntax("{word 23}{F1 23}{~ 23}{% 23}{^}{F12}{(}{) 23}{[}{[}{]}{Delay=23}{+}{SUBTRACT}~+%@fixedstring")); + + QCOMPARE(true, AutoType::checkSyntax("{NUMPAD1 3}")); + + QCOMPARE(true, AutoType::checkSyntax("{BEEP 3 3}")); + QCOMPARE(false, AutoType::checkSyntax("{BEEP 3}")); + + QCOMPARE(true, AutoType::checkSyntax("{VKEY 0x01}")); + QCOMPARE(true, AutoType::checkSyntax("{VKEY VK_LBUTTON}")); + QCOMPARE(true, AutoType::checkSyntax("{VKEY-EX 0x01}")); + // Bad sequence + QCOMPARE(false, AutoType::checkSyntax("{{{}}{}{}}{{}}")); + // Good sequence + QCOMPARE(true, AutoType::checkSyntax("{{}{}}{}}{{}")); + QCOMPARE(true, AutoType::checkSyntax("{]}{[}{[}{]}")); + QCOMPARE(true, AutoType::checkSyntax("{)}{(}{(}{)}")); + // High DelAY / low delay + QCOMPARE(true, AutoType::checkHighDelay("{DelAY 50000}")); + QCOMPARE(false, AutoType::checkHighDelay("{delay 50}")); + // Slow typing + QCOMPARE(true, AutoType::checkSlowKeypress("{DelAY=50000}")); + QCOMPARE(false, AutoType::checkSlowKeypress("{delay=50}")); + // Many repetition / few repetition / delay not repetition + QCOMPARE(true, AutoType::checkHighRepetition("{LEFT 50000000}")); + QCOMPARE(false, AutoType::checkHighRepetition("{SPACE 10}{TAB 3}{RIGHT 50}")); + QCOMPARE(false, AutoType::checkHighRepetition("{delay 5000000000}")); +} \ No newline at end of file diff --git a/tests/TestAutoType.h b/tests/TestAutoType.h index 0cd4a5bdd..b7c33823b 100644 --- a/tests/TestAutoType.h +++ b/tests/TestAutoType.h @@ -47,6 +47,7 @@ private slots: void testGlobalAutoTypeUrlSubdomainMatch(); void testGlobalAutoTypeTitleMatchDisabled(); void testGlobalAutoTypeRegExp(); + void testAutoTypeSyntaxChecks(); private: AutoTypePlatformInterface* m_platform;