diff --git a/src/format/KeePass1Reader.cpp b/src/format/KeePass1Reader.cpp index 73ea564da..94f237e11 100644 --- a/src/format/KeePass1Reader.cpp +++ b/src/format/KeePass1Reader.cpp @@ -567,7 +567,7 @@ Entry* KeePass1Reader::readEntry(QIODevice* cipherStream) entry->setPassword(QString::fromUtf8(fieldData.constData())); break; case 0x0008: - entry->setNotes(QString::fromUtf8(fieldData.constData())); + parseNotes(QString::fromUtf8(fieldData.constData()), entry.data()); break; case 0x0009: { @@ -636,6 +636,68 @@ Entry* KeePass1Reader::readEntry(QIODevice* cipherStream) return entry.take(); } +void KeePass1Reader::parseNotes(const QString& rawNotes, Entry* entry) +{ + QRegExp sequenceRegexp("Auto-Type(?:-(\\d+))?: (.+)", Qt::CaseInsensitive, QRegExp::RegExp2); + QRegExp windowRegexp("Auto-Type-Window(?:-(\\d+))?: (.+)", Qt::CaseInsensitive, QRegExp::RegExp2); + QHash sequences; + QMap windows; + + QStringList notes; + + bool lastLineAutoType = false; + Q_FOREACH (QString line, rawNotes.split("\n")) { + line.remove("\r"); + + if (sequenceRegexp.exactMatch(line)) { + if (sequenceRegexp.cap(1).isEmpty()) { + entry->setDefaultAutoTypeSequence(sequenceRegexp.cap(2)); + } + else { + sequences[sequenceRegexp.cap(1).toInt()] = sequenceRegexp.cap(2); + } + + lastLineAutoType = true; + } + else if (windowRegexp.exactMatch(line)) { + int nr; + if (windowRegexp.cap(1).isEmpty()) { + nr = -1; // special number that matches no other sequence + } + else { + nr = windowRegexp.cap(1).toInt(); + } + + windows[nr].append(windowRegexp.cap(2)); + + lastLineAutoType = true; + } + else { + // don't add empty lines following a removed auto-type line + if (!lastLineAutoType || !line.isEmpty()) { + notes.append(line); + } + lastLineAutoType = false; + } + } + + entry->setNotes(notes.join("\n")); + + QMapIterator i(windows); + while (i.hasNext()) { + i.next(); + + QString sequence = sequences.value(i.key()); + + Q_FOREACH (const QString& window, i.value()) { + AutoTypeAssociations::Association assoc; + assoc.window = window; + assoc.sequence = sequence; + entry->autoTypeAssociations()->add(assoc); + } + } +} + bool KeePass1Reader::constructGroupTree(const QList groups) { for (int i = 0; i < groups.size(); i++) { diff --git a/src/format/KeePass1Reader.h b/src/format/KeePass1Reader.h index 3375c100e..643587484 100644 --- a/src/format/KeePass1Reader.h +++ b/src/format/KeePass1Reader.h @@ -57,6 +57,7 @@ private: bool verifyKey(SymmetricCipherStream* cipherStream); Group* readGroup(QIODevice* cipherStream); Entry* readEntry(QIODevice* cipherStream); + void parseNotes(const QString& rawNotes, Entry* entry); bool constructGroupTree(const QList groups); void parseMetaStream(const Entry* entry); bool parseGroupTreeState(const QByteArray& data); diff --git a/tests/TestKeePass1Reader.cpp b/tests/TestKeePass1Reader.cpp index b15e8a032..a7e1114fe 100644 --- a/tests/TestKeePass1Reader.cpp +++ b/tests/TestKeePass1Reader.cpp @@ -139,6 +139,36 @@ void TestKeePass1Reader::testGroupExpanded() false); } +void TestKeePass1Reader::testAutoType() +{ + Group* group = m_db->rootGroup()->children().at(0)->children().at(0); + QCOMPARE(group->entries().size(), 2); + + Entry* entry1 = group->entries().at(0); + QCOMPARE(entry1->notes(), QString("last line")); + QCOMPARE(entry1->defaultAutoTypeSequence(), QString("{USERNAME}{ENTER}")); + QCOMPARE(entry1->autoTypeAssociations()->size(), 5); + QCOMPARE(entry1->autoTypeAssociations()->get(0).sequence, QString("")); + QCOMPARE(entry1->autoTypeAssociations()->get(0).window, QString("a window")); + QCOMPARE(entry1->autoTypeAssociations()->get(1).sequence, QString("")); + QCOMPARE(entry1->autoTypeAssociations()->get(1).window, QString("a second window")); + QCOMPARE(entry1->autoTypeAssociations()->get(2).sequence, QString("{PASSWORD}{ENTER}")); + QCOMPARE(entry1->autoTypeAssociations()->get(2).window, QString("Window Nr 1a")); + QCOMPARE(entry1->autoTypeAssociations()->get(3).sequence, QString("{PASSWORD}{ENTER}")); + QCOMPARE(entry1->autoTypeAssociations()->get(3).window, QString("Window Nr 1b")); + QCOMPARE(entry1->autoTypeAssociations()->get(4).sequence, QString("")); + QCOMPARE(entry1->autoTypeAssociations()->get(4).window, QString("Window 2")); + + Entry* entry2 = group->entries().at(1); + QCOMPARE(entry2->notes(), QString("start line\nend line")); + QCOMPARE(entry2->defaultAutoTypeSequence(), QString("")); + QCOMPARE(entry2->autoTypeAssociations()->size(), 2); + QCOMPARE(entry2->autoTypeAssociations()->get(0).sequence, QString("")); + QCOMPARE(entry2->autoTypeAssociations()->get(0).window, QString("Main Window")); + QCOMPARE(entry2->autoTypeAssociations()->get(1).sequence, QString("")); + QCOMPARE(entry2->autoTypeAssociations()->get(1).window, QString("Test Window")); +} + void TestKeePass1Reader::testFileKey() { QFETCH(QString, type); diff --git a/tests/TestKeePass1Reader.h b/tests/TestKeePass1Reader.h index a4fc8a0e5..4088d9f02 100644 --- a/tests/TestKeePass1Reader.h +++ b/tests/TestKeePass1Reader.h @@ -33,6 +33,7 @@ private Q_SLOTS: void testMasterKey(); void testCustomIcons(); void testGroupExpanded(); + void testAutoType(); void testFileKey(); void testFileKey_data(); void testCompositeKey(); diff --git a/tests/data/basic.kdb b/tests/data/basic.kdb index b0daacabc..16968bafa 100644 Binary files a/tests/data/basic.kdb and b/tests/data/basic.kdb differ