diff --git a/mimetreeparser/autotests/CMakeLists.txt b/mimetreeparser/autotests/CMakeLists.txt --- a/mimetreeparser/autotests/CMakeLists.txt +++ b/mimetreeparser/autotests/CMakeLists.txt @@ -37,4 +37,4 @@ add_mimetreeparser_crypto_unittest(attachmenttest.cpp) add_mimetreeparser_unittest(nodehelpertest.cpp) -add_mimetreeparser_class_unittest( cryptohelpertest.cpp "../src/viewer/cryptohelper.cpp") \ No newline at end of file +add_mimetreeparser_class_unittest( cryptohelpertest.cpp "../src/viewer/cryptohelper.cpp") diff --git a/mimetreeparser/autotests/attachmenttest.cpp b/mimetreeparser/autotests/attachmenttest.cpp --- a/mimetreeparser/autotests/attachmenttest.cpp +++ b/mimetreeparser/autotests/attachmenttest.cpp @@ -48,6 +48,7 @@ QTest::newRow("signed") << "openpgp-signed-two-attachments.mbox"; QTest::newRow("signed+encrypted") << "openpgp-signed-encrypted-two-attachments.mbox"; QTest::newRow("encrypted+partial signed") << "openpgp-encrypted-partially-signed-attachments.mbox"; + QTest::newRow("opaque smime signed + encrypted") << "smime-opaque-signed-encrypted-attachment.mbox"; } void AttachmentTest::testEncryptedAttachment() diff --git a/mimetreeparser/autotests/data/smime-opaque-signed-encrypted-attachment.mbox b/mimetreeparser/autotests/data/smime-opaque-signed-encrypted-attachment.mbox new file mode 100644 --- /dev/null +++ b/mimetreeparser/autotests/data/smime-opaque-signed-encrypted-attachment.mbox @@ -0,0 +1,50 @@ +From test@example.com Thu Jun 09 12:52:44 2016 +From: test@example.com +To: test@example.com +Subject: Opaque S/MIME signed and encrypted message with attachment +Date: Thu, 09 Jun 2016 14:52:44 +0200 +MIME-Version: 1.0 +Content-Type: application/pkcs7-mime; name="smime.p7m"; smime-type="enveloped-data" +Content-Transfer-Encoding: base64 +Content-Disposition: attachment; filename="smime.p7m" + +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 diff --git a/mimetreeparser/autotests/nodehelpertest.cpp b/mimetreeparser/autotests/nodehelpertest.cpp --- a/mimetreeparser/autotests/nodehelpertest.cpp +++ b/mimetreeparser/autotests/nodehelpertest.cpp @@ -21,6 +21,7 @@ #include "viewer/nodehelper.h" #include +#include using namespace MimeTreeParser; @@ -35,8 +36,13 @@ NodeHelper helper; KMime::Content *node = new KMime::Content(); + KMime::Content *node2 = new KMime::Content(); + KMime::Content *node2Extra = new KMime::Content(); KMime::Content *subNode = new KMime::Content(); KMime::Content *subsubNode = new KMime::Content(), *subsubNode2 = new KMime::Content(); + KMime::Content *node2ExtraSubNode = new KMime::Content(); + KMime::Content *node2ExtraSubsubNode = new KMime::Content(); + KMime::Content *node2ExtraSubsubNode2 = new KMime::Content(); KMime::Content *extra = new KMime::Content(), *extra2 = new KMime::Content(); KMime::Content *subExtra = new KMime::Content(); KMime::Content *subsubExtra = new KMime::Content(); @@ -51,23 +57,38 @@ helper.attachExtraContent(subNode, subExtra); helper.attachExtraContent(subsubNode2, subsubExtra); + // This simulates Opaque S/MIME signed and encrypted message with attachment + // (attachment is node2SubsubNode2) + node2Extra->addContent(node2ExtraSubNode); + node2ExtraSubNode->addContent(node2ExtraSubsubNode); + node2ExtraSubNode->addContent(node2ExtraSubsubNode2); + helper.attachExtraContent(node2, node2Extra); + helper.attachExtraContent(node2Extra, node2ExtraSubNode); + /* all content has a internal first child, so indexes starts at 2 * node "" * -> subNode "2" * -> subsubNode "2.2" * -> subsubNode2 "2.3" * * node "" - * -> extra "0:" - * -> extra2 "1:" + * -> extra "e0" + * -> extra2 "e1" * * subNode "2" - * -> subExtra "2:0:" + * -> subExtra "2:e0" * * subsubNode2 "2.3" - * -> subsubExtra "2.3:0:" - * -> subsubExtraNode "2.3:0:2" + * -> subsubExtra "2.3:e0" + * -> subsubExtraNode "2.3:e0:2" + * + * node2 "" * + * node2 "" + * -> node2Extra "e0" + * -> node2ExtraSubNode "e0:2" + * -> node2ExtraSubsubNode "e0:2.2" + * -> node2ExtraSubsubNode2 "e0:2.3" */ QCOMPARE(helper.persistentIndex(node), QStringLiteral("")); @@ -85,20 +106,23 @@ QCOMPARE(helper.persistentIndex(subsubNode2), QStringLiteral("2.3")); QCOMPARE(helper.contentFromIndex(node, QStringLiteral("2.3")), subsubNode2); - QCOMPARE(helper.persistentIndex(extra), QStringLiteral("0:")); - QCOMPARE(helper.contentFromIndex(node, QStringLiteral("0:")), extra); + QCOMPARE(helper.persistentIndex(extra), QStringLiteral("e0")); + QCOMPARE(helper.contentFromIndex(node, QStringLiteral("e0")), extra); + + QCOMPARE(helper.persistentIndex(extra2), QStringLiteral("e1")); + QCOMPARE(helper.contentFromIndex(node, QStringLiteral("e1")), extra2); - QCOMPARE(helper.persistentIndex(extra2), QStringLiteral("1:")); - QCOMPARE(helper.contentFromIndex(node, QStringLiteral("1:")), extra2); + QCOMPARE(helper.persistentIndex(subExtra), QStringLiteral("2:e0")); + QCOMPARE(helper.contentFromIndex(node, QStringLiteral("2:e0")), subExtra); - QCOMPARE(helper.persistentIndex(subExtra), QStringLiteral("2:0:")); - QCOMPARE(helper.contentFromIndex(node, QStringLiteral("2:0:")), subExtra); + QCOMPARE(helper.persistentIndex(subsubExtra), QStringLiteral("2.3:e0")); + QCOMPARE(helper.contentFromIndex(node, QStringLiteral("2.3:e0")), subsubExtra); - QCOMPARE(helper.persistentIndex(subsubExtra), QStringLiteral("2.3:0:")); - QCOMPARE(helper.contentFromIndex(node, QStringLiteral("2.3:0:")), subsubExtra); + QCOMPARE(helper.persistentIndex(subsubExtraNode), QStringLiteral("2.3:e0:2")); + QCOMPARE(helper.contentFromIndex(node, QStringLiteral("2.3:e0:2")), subsubExtraNode); - QCOMPARE(helper.persistentIndex(subsubExtraNode), QStringLiteral("2.3:0:2")); - QCOMPARE(helper.contentFromIndex(node, QStringLiteral("2.3:0:2")), subsubExtraNode); + QCOMPARE(helper.persistentIndex(node2ExtraSubsubNode2), QStringLiteral("e0:2.3")); + QCOMPARE(helper.contentFromIndex(node2, QStringLiteral("e0:2.3")), node2ExtraSubsubNode2); delete node; } @@ -129,19 +153,19 @@ url = QUrl(QStringLiteral("")); QCOMPARE(helper.fromHREF(msg, url), node); - url = QUrl(QStringLiteral("attachment:0:?place=body")); + url = QUrl(QStringLiteral("attachment:e0?place=body")); QCOMPARE(helper.fromHREF(msg, url), extra); url = QUrl(QStringLiteral("attachment:2.2?place=body")); QCOMPARE(helper.fromHREF(msg, url), subsubNode); - url = QUrl(QStringLiteral("attachment:2.3:0:2?place=body")); + url = QUrl(QStringLiteral("attachment:2.3:e0:2?place=body")); QCOMPARE(helper.fromHREF(msg, url), subsubExtraNode); QCOMPARE(helper.asHREF(node, QStringLiteral("body")), QStringLiteral("attachment:?place=body")); - QCOMPARE(helper.asHREF(extra, QStringLiteral("body")), QStringLiteral("attachment:0:?place=body")); + QCOMPARE(helper.asHREF(extra, QStringLiteral("body")), QStringLiteral("attachment:e0?place=body")); QCOMPARE(helper.asHREF(subsubNode, QStringLiteral("body")), QStringLiteral("attachment:2.2?place=body")); - QCOMPARE(helper.asHREF(subsubExtraNode, QStringLiteral("body")), QStringLiteral("attachment:2.3:0:2?place=body")); + QCOMPARE(helper.asHREF(subsubExtraNode, QStringLiteral("body")), QStringLiteral("attachment:2.3:e0:2?place=body")); } void NodeHelperTest::testLocalFiles() diff --git a/mimetreeparser/src/viewer/nodehelper.cpp b/mimetreeparser/src/viewer/nodehelper.cpp --- a/mimetreeparser/src/viewer/nodehelper.cpp +++ b/mimetreeparser/src/viewer/nodehelper.cpp @@ -652,52 +652,62 @@ } QString indexStr = node->index().toString(); - const KMime::Content *const topLevel = node->topLevel(); - //if the node is an extra node, prepend the index of the extra node to the url - Q_FOREACH (KMime::Content *realNode, mExtraContents.keys()) { - const QList &extraNodes = extraContents(realNode); - const int extraNodesSize(extraNodes.size()); - for (int i = 0; i < extraNodesSize; ++i) { - if (topLevel == extraNodes[i]) { - indexStr = indexStr.prepend(QString::fromLatin1("%1:").arg(i)); - const QString outsideIndex = persistentIndex(realNode); - if (!outsideIndex.isEmpty()) { - indexStr = QString::fromLatin1("%1:").arg(outsideIndex) + indexStr; + if (indexStr.isEmpty()) { + Q_FOREACH (KMime::Content *realNode, mExtraContents.keys()) { + const auto &extraNodes = mExtraContents.value(realNode); + for (int i = 0; i < extraNodes.size(); i++) { + if (extraNodes[i] == node) { + indexStr = QString::fromLatin1("e%1").arg(i); + const QString parentIndex = persistentIndex(realNode); + if (!parentIndex.isEmpty()) { + indexStr = QString::fromLatin1("%1:%2").arg(parentIndex, indexStr); + } + return indexStr; + } + } + } + } else { + const KMime::Content *const topLevel = node->topLevel(); + //if the node is an extra node, prepend the index of the extra node to the url + Q_FOREACH (KMime::Content *realNode, mExtraContents.keys()) { + const QList &extraNodes = extraContents(realNode); + for (int i = 0; i < extraNodes.size(); ++i) { + KMime::Content *const extraNode = extraNodes[i]; + if (topLevel == extraNode) { + indexStr.prepend(QString::fromLatin1("e%1:").arg(i)); + const QString parentIndex = persistentIndex(realNode); + if (!parentIndex.isEmpty()) { + indexStr = QString::fromLatin1("%1:%2").arg(parentIndex, indexStr); + } + return indexStr; } } } } + return indexStr; } KMime::Content *NodeHelper::contentFromIndex(KMime::Content *node, const QString &persistentIndex) const { - KMime::Content *topLevel = node->topLevel(); - if (persistentIndex.contains(QLatin1Char(':'))) { - //if the content was not found, it might be in an extra node. Get the index of the extra node (the first part of the url), - //and use the remaining part as a ContentIndex to find the node inside the extra node - QString left = persistentIndex.left(persistentIndex.indexOf(QLatin1Char(':'))); - QString index = persistentIndex.mid(persistentIndex.indexOf(QLatin1Char(':')) + 1); - - QList extras = extraContents(topLevel); - - if (index.contains(QLatin1Char(':'))) { - extras = extraContents(topLevel->content(KMime::ContentIndex(left))); - left = index.left(index.indexOf(QLatin1Char(':'))); - index = index.mid(index.indexOf(QLatin1Char(':')) + 1); - } - const KMime::ContentIndex idx(index); - const int i = left.toInt(); - if (i >= 0 && i < extras.size()) { - const KMime::Content *c = extras[i]; - return c->content(idx); - } - } else { - if (topLevel) { - return topLevel->content(KMime::ContentIndex(persistentIndex)); + KMime::Content *c = node->topLevel(); + if (c) { + const QStringList pathParts = persistentIndex.split(QLatin1Char(':'), QString::SkipEmptyParts); + for (int i = 0; i < pathParts.size(); ++i) { + const QString &path = pathParts[i]; + if (path.startsWith(QLatin1Char('e'))) { + const QList &extraParts = mExtraContents.value(c); + const int idx = path.midRef(1, -1).toInt(); + c = (idx < extraParts.size()) ? extraParts[idx] : Q_NULLPTR; + } else { + c = c->content(KMime::ContentIndex(path)); + } + if (!c) { + break; + } } } - return 0; + return c; } QString NodeHelper::asHREF(const KMime::Content *node, const QString &place) const @@ -719,7 +729,7 @@ // start of the index is something that is not a number followed by a dot: \D. // index is only made of numbers,"." and ":": ([0-9.:]+) // index is the last part of the folder name: / - const QRegExp rIndex(QStringLiteral("\\D\\.([0-9.:]+)/")); + const QRegExp rIndex(QStringLiteral("\\D\\.([e0-9.:]+)/")); //search the occurence at most at the end if (rIndex.lastIndexIn(path) != -1) {