diff --git a/src/autotype/AutoType.cpp b/src/autotype/AutoType.cpp index 696f5e1ba..979fe3ad2 100644 --- a/src/autotype/AutoType.cpp +++ b/src/autotype/AutoType.cpp @@ -356,18 +356,18 @@ bool AutoType::parseActions(const QString& sequence, const Entry* entry, QList AutoType::createActionFromTemplate(const QString& tmpl, const Entry* entry) { - QString tmplName = tmpl.toLower(); + QString tmplName = tmpl; int num = -1; QList list; - QRegExp delayRegEx("delay=(\\d+)", Qt::CaseSensitive, QRegExp::RegExp2); + QRegExp delayRegEx("delay=(\\d+)", Qt::CaseInsensitive, QRegExp::RegExp2); if (delayRegEx.exactMatch(tmplName)) { num = delayRegEx.cap(1).toInt(); m_autoTypeDelay = std::max(0, std::min(num, 10000)); return list; } - QRegExp repeatRegEx("(.+) (\\d+)", Qt::CaseSensitive, QRegExp::RegExp2); + QRegExp repeatRegEx("(.+) (\\d+)", Qt::CaseInsensitive, QRegExp::RegExp2); if (repeatRegEx.exactMatch(tmplName)) { tmplName = repeatRegEx.cap(1); num = repeatRegEx.cap(2).toInt(); @@ -376,7 +376,7 @@ QList AutoType::createActionFromTemplate(const QString& tmpl, c return list; } // some safety checks - else if (tmplName == "delay") { + else if (tmplName.compare("delay",Qt::CaseInsensitive)==0) { if (num > 10000) { return list; } @@ -386,98 +386,103 @@ QList AutoType::createActionFromTemplate(const QString& tmpl, c } } - if (tmplName == "tab") { + if (tmplName.compare("tab",Qt::CaseInsensitive)==0) { list.append(new AutoTypeKey(Qt::Key_Tab)); } - else if (tmplName == "enter") { + else if (tmplName.compare("enter",Qt::CaseInsensitive)==0) { list.append(new AutoTypeKey(Qt::Key_Enter)); } - else if (tmplName == "up") { + else if (tmplName.compare("up",Qt::CaseInsensitive)==0) { list.append(new AutoTypeKey(Qt::Key_Up)); } - else if (tmplName == "down") { + else if (tmplName.compare("down",Qt::CaseInsensitive)==0) { list.append(new AutoTypeKey(Qt::Key_Down)); } - else if (tmplName == "left") { + else if (tmplName.compare("left",Qt::CaseInsensitive)==0) { list.append(new AutoTypeKey(Qt::Key_Left)); } - else if (tmplName == "right") { + else if (tmplName.compare("right",Qt::CaseInsensitive)==0) { list.append(new AutoTypeKey(Qt::Key_Right)); } - else if (tmplName == "insert" || tmplName == "ins") { + else if (tmplName.compare("insert",Qt::CaseInsensitive)==0 || + tmplName.compare("ins",Qt::CaseInsensitive)==0) { list.append(new AutoTypeKey(Qt::Key_Insert)); } - else if (tmplName == "delete" || tmplName == "del") { + else if (tmplName.compare("delete",Qt::CaseInsensitive)==0 || + tmplName.compare("del",Qt::CaseInsensitive)==0) { list.append(new AutoTypeKey(Qt::Key_Delete)); } - else if (tmplName == "home") { + else if (tmplName.compare("home",Qt::CaseInsensitive)==0) { list.append(new AutoTypeKey(Qt::Key_Home)); } - else if (tmplName == "end") { + else if (tmplName.compare("end",Qt::CaseInsensitive)==0) { list.append(new AutoTypeKey(Qt::Key_End)); } - else if (tmplName == "pgup") { + else if (tmplName.compare("pgup",Qt::CaseInsensitive)==0) { list.append(new AutoTypeKey(Qt::Key_PageUp)); } - else if (tmplName == "pgdown") { + else if (tmplName.compare("pgdown",Qt::CaseInsensitive)==0) { list.append(new AutoTypeKey(Qt::Key_PageDown)); } - else if (tmplName == "backspace" || tmplName == "bs" || tmplName == "bksp") { + 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 == "break") { + else if (tmplName.compare("break",Qt::CaseInsensitive)==0) { list.append(new AutoTypeKey(Qt::Key_Pause)); } - else if (tmplName == "capslock") { + else if (tmplName.compare("capslock",Qt::CaseInsensitive)==0) { list.append(new AutoTypeKey(Qt::Key_CapsLock)); } - else if (tmplName == "esc") { + else if (tmplName.compare("esc",Qt::CaseInsensitive)==0) { list.append(new AutoTypeKey(Qt::Key_Escape)); } - else if (tmplName == "help") { + else if (tmplName.compare("help",Qt::CaseInsensitive)==0) { list.append(new AutoTypeKey(Qt::Key_Help)); } - else if (tmplName == "numlock") { + else if (tmplName.compare("numlock",Qt::CaseInsensitive)==0) { list.append(new AutoTypeKey(Qt::Key_NumLock)); } - else if (tmplName == "ptrsc") { + else if (tmplName.compare("ptrsc",Qt::CaseInsensitive)==0) { list.append(new AutoTypeKey(Qt::Key_Print)); } - else if (tmplName == "scrolllock") { + 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 == "add" || tmplName == "+") { + else if (tmplName.compare("add",Qt::CaseInsensitive)==0 || + tmplName.compare("+",Qt::CaseInsensitive)==0) { list.append(new AutoTypeChar('+')); } - else if (tmplName == "subtract") { + else if (tmplName.compare("subtract",Qt::CaseInsensitive)==0) { list.append(new AutoTypeChar('-')); } - else if (tmplName == "multiply") { + else if (tmplName.compare("multiply",Qt::CaseInsensitive)==0) { list.append(new AutoTypeChar('*')); } - else if (tmplName == "divide") { + else if (tmplName.compare("divide",Qt::CaseInsensitive)==0) { list.append(new AutoTypeChar('/')); } - else if (tmplName == "^") { + else if (tmplName.compare("^",Qt::CaseInsensitive)==0) { list.append(new AutoTypeChar('^')); } - else if (tmplName == "%") { + else if (tmplName.compare("%",Qt::CaseInsensitive)==0) { list.append(new AutoTypeChar('%')); } - else if (tmplName == "~") { + else if (tmplName.compare("~",Qt::CaseInsensitive)==0) { list.append(new AutoTypeChar('~')); } - else if (tmplName == "(") { + else if (tmplName.compare("(",Qt::CaseInsensitive)==0) { list.append(new AutoTypeChar('(')); } - else if (tmplName == ")") { + else if (tmplName.compare(")",Qt::CaseInsensitive)==0) { list.append(new AutoTypeChar(')')); } - else if (tmplName == "{") { + else if (tmplName.compare("{",Qt::CaseInsensitive)==0) { list.append(new AutoTypeChar('{')); } - else if (tmplName == "}") { + else if (tmplName.compare("}",Qt::CaseInsensitive)==0) { list.append(new AutoTypeChar('}')); } else { @@ -499,10 +504,10 @@ QList AutoType::createActionFromTemplate(const QString& tmpl, c } - if (tmplName == "delay" && num > 0) { + if (tmplName.compare("delay",Qt::CaseInsensitive)==0 && num > 0) { list.append(new AutoTypeDelay(num)); } - else if (tmplName == "clearfield") { + else if (tmplName.compare("clearfield",Qt::CaseInsensitive)==0) { list.append(new AutoTypeClearField()); } @@ -510,9 +515,8 @@ QList AutoType::createActionFromTemplate(const QString& tmpl, c return list; } - const QString placeholder = QString("{%1}").arg(tmplName); - const QString resolved = entry->resolvePlaceholders(placeholder); + const QString resolved = entry->resolvePlaceholder(placeholder); if (placeholder != resolved) { for (const QChar& ch : resolved) { if (ch == '\n') { diff --git a/src/core/Entry.cpp b/src/core/Entry.cpp index 6862fc9d8..629f5141c 100644 --- a/src/core/Entry.cpp +++ b/src/core/Entry.cpp @@ -616,17 +616,40 @@ const Database* Entry::database() const } } -QString Entry::resolvePlaceholders(const QString& str) const +QString Entry::resolveMultiplePlaceholders(const QString& str) const { QString result = str; + QRegExp tmplRegEx("({.*})", Qt::CaseInsensitive, QRegExp::RegExp2); + QStringList tmplList; + int pos = 0; - result.replace("{TITLE}", title(), Qt::CaseInsensitive); - result.replace("{USERNAME}", username(), Qt::CaseInsensitive); - result.replace("{URL}", url(), Qt::CaseInsensitive); - result.replace("{PASSWORD}", password(), Qt::CaseInsensitive); - result.replace("{NOTES}", notes(), Qt::CaseInsensitive); - - // TODO: lots of other placeholders missing + while ((pos = tmplRegEx.indexIn(str, pos)) != -1) { + QString found = tmplRegEx.cap(1); + result.replace(found,resolvePlaceholder(found)); + pos += tmplRegEx.matchedLength(); + } + + return result; +} + +QString Entry::resolvePlaceholder(const QString& str) const +{ + QString result = str; + + const QList keyList = attributes()->keys(); + for (const QString& key : keyList) { + Qt::CaseSensitivity cs = Qt::CaseInsensitive; + if (!EntryAttributes::isDefaultAttribute(key)) { + cs = Qt::CaseSensitive; + } + + QString k = key; + k.prepend("{").append("}"); + if (result.compare(k,cs)==0) { + result.replace(result,attributes()->value(key)); + break; + } + } return result; } diff --git a/src/core/Entry.h b/src/core/Entry.h index 910146968..66b9362a6 100644 --- a/src/core/Entry.h +++ b/src/core/Entry.h @@ -127,7 +127,8 @@ public: */ Entry* clone(CloneFlags flags) const; void copyDataFrom(const Entry* other); - QString resolvePlaceholders(const QString& str) const; + QString resolveMultiplePlaceholders(const QString& str) const; + QString resolvePlaceholder(const QString& str) const; /** * Call before and after set*() methods to create a history item diff --git a/src/core/Group.cpp b/src/core/Group.cpp index 2028c7828..8c96bb074 100644 --- a/src/core/Group.cpp +++ b/src/core/Group.cpp @@ -201,6 +201,10 @@ QString Group::effectiveAutoTypeSequence() const group = group->parentGroup(); } while (group && sequence.isEmpty()); + if (sequence.isEmpty()) { + sequence = "{USERNAME}{TAB}{PASSWORD}{ENTER}"; + } + return sequence; } diff --git a/src/gui/DatabaseWidget.cpp b/src/gui/DatabaseWidget.cpp index 89e7bf689..30924d67e 100644 --- a/src/gui/DatabaseWidget.cpp +++ b/src/gui/DatabaseWidget.cpp @@ -485,7 +485,7 @@ void DatabaseWidget::openUrl() void DatabaseWidget::openUrlForEntry(Entry* entry) { - QString urlString = entry->resolvePlaceholders(entry->url()); + QString urlString = entry->resolveMultiplePlaceholders(entry->url()); if (urlString.isEmpty()) { return; } diff --git a/tests/TestAutoType.cpp b/tests/TestAutoType.cpp index cbd927fe6..c5c1a5933 100644 --- a/tests/TestAutoType.cpp +++ b/tests/TestAutoType.cpp @@ -90,6 +90,20 @@ void TestAutoType::init() association.window = "//^REGEX3-([rd]\\d){2}$//"; association.sequence = "regex3"; m_entry3->autoTypeAssociations()->add(association); + + m_entry4 = new Entry(); + m_entry4->setGroup(m_group); + m_entry4->setPassword("custom_attr"); + m_entry4->attributes()->set("CUSTOM","Attribute",false); + association.window = "//^CustomAttr1$//"; + association.sequence = "{PASSWORD}:{CUSTOM}"; + m_entry4->autoTypeAssociations()->add(association); + association.window = "//^CustomAttr2$//"; + association.sequence = "{CuStOm}"; + m_entry4->autoTypeAssociations()->add(association); + association.window = "//^CustomAttr3$//"; + association.sequence = "{PaSSworD}"; + m_entry4->autoTypeAssociations()->add(association); } void TestAutoType::cleanup() @@ -192,4 +206,22 @@ void TestAutoType::testGlobalAutoTypeRegExp() m_autoType->performGlobalAutoType(m_dbList); QCOMPARE(m_test->actionChars(), QString("regex3")); m_test->clearActions(); + + // with custom attributes + m_test->setActiveWindowTitle("CustomAttr1"); + m_autoType->performGlobalAutoType(m_dbList); + QCOMPARE(m_test->actionChars(), QString("custom_attr:Attribute")); + m_test->clearActions(); + + // with (non uppercase) undefined custom attributes + m_test->setActiveWindowTitle("CustomAttr2"); + m_autoType->performGlobalAutoType(m_dbList); + QCOMPARE(m_test->actionChars(), QString("")); + m_test->clearActions(); + + // with mixedcase default attributes + m_test->setActiveWindowTitle("CustomAttr3"); + m_autoType->performGlobalAutoType(m_dbList); + QCOMPARE(m_test->actionChars(), QString("custom_attr")); + m_test->clearActions(); } diff --git a/tests/TestAutoType.h b/tests/TestAutoType.h index c12817b1d..c585fec25 100644 --- a/tests/TestAutoType.h +++ b/tests/TestAutoType.h @@ -55,6 +55,7 @@ private: Entry* m_entry1; Entry* m_entry2; Entry* m_entry3; + Entry* m_entry4; }; #endif // KEEPASSX_TESTAUTOTYPE_H