diff --git a/mimetreeparser/autotests/CMakeLists.txt b/mimetreeparser/autotests/CMakeLists.txt index 71372bbe..be3c835c 100644 --- a/mimetreeparser/autotests/CMakeLists.txt +++ b/mimetreeparser/autotests/CMakeLists.txt @@ -1,40 +1,40 @@ set(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR}) add_definitions( -DMAIL_DATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/data" ) include(${CMAKE_SOURCE_DIR}/cmake/modules/kdepim_add_gpg_crypto_test.cmake) # convenience macro to add qtest unit tests macro(add_mimetreeparser_unittest _source) get_filename_component(_name ${_source} NAME_WE) ecm_add_test(${_source} util.cpp setupenv.cpp TEST_NAME ${_name} NAME_PREFIX "mimetreeparser-" LINK_LIBRARIES KF5::MimeTreeParser Qt5::Test KF5::Mime ) endmacro () macro(add_mimetreeparser_class_unittest _source _additionalSource) get_filename_component(_name ${_source} NAME_WE) ecm_add_test(${_source} ${_additionalSource} TEST_NAME ${_name} NAME_PREFIX "mimetreeparser-" LINK_LIBRARIES KF5::MimeTreeParser Qt5::Test KF5::Mime ) endmacro () macro(add_mimetreeparser_crypto_unittest _source) set(_test ${_source} util.cpp) get_filename_component(_name ${_source} NAME_WE) add_executable( ${_name} ${_test} setupenv.cpp) ecm_mark_as_test(mimetreeparser-${_name}) target_link_libraries( ${_name} KF5::MimeTreeParser Qt5::Test KF5::Mime ) add_gpg_crypto_test(${_name} mimetreeparser-${_name}) endmacro () 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/data/smime-opaque-signed-encrypted-attachment.mbox b/mimetreeparser/autotests/data/smime-opaque-signed-encrypted-attachment.mbox new file mode 100644 index 00000000..2b1a0761 --- /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" + +MIAGCSqGSIb3DQEHA6CAMIACAQAxgfwwgfkCAQAwYjBVMQswCQYDVQQGEwJVUzEN +MAsGA1UECgwES0RBQjEWMBQGA1UEAwwNdW5pdHRlc3QgY2VydDEfMB0GCSqGSIb3 +DQEJARYQdGVzdEBleGFtcGxlLmNvbQIJANNFIDoYY4XJMA0GCSqGSIb3DQEBAQUA +BIGAalG2EoXQOhvVPCef5ru1+++vAfIED/abw8gFPuqWmh1nK2x2Q13U+7I7bv6a +uK2msunHmNwgvNetJ1j4PPMePCU5I0F0jGw5PB8A6lgF8IGzEzU5W9gz1PazKGl4 +PTwxAoJgCeflZwtddGEJvQ86f4OduXEnDmirFg64WUk1jjMwgAYJKoZIhvcNAQcB +MB0GCWCGSAFlAwQBAgQQvthEtJX37uYX68Ri3kURq6CABIIGEOr7cxjOVKzXfAYk +1eBd/HiYcpLlttlNCcwTuQwP0pDpE9YnDA+MfgZn05hxODZDACsOschWFZXBXVY1 +OY/ZTpVYRxAdYXgPymK8+r9fym0A+YiQ5/yKbWjezDmHdOOv6JF03Z+VzBmZtFcL +q/LPr0+EcjiPA9r/EQTA7P1pj+tOAm3krk8s4P+9yDAIQLCQt9yUdbpMsgn1OyJv +Njl7Mq5qcQXdnYYsTF6ODZ9araHOYDS64yP69h+Lh6nsBNWD7W6NvNsS6Hmgkzvg +FK3TNxU+X5x1F7TvKyCSRyWicfV66F/sBXIEo6K8h/rSi978jALahJPZZzNoyQiQ +eaMCjXwuBbeobcChwkRRzU12h07AXhGgZA9AkHIsFAAE4gwnu7yoogLrQqslm/MF +NGlbO68zyw0znK3eqzsOaXDyeLWy1zJcTffOENPhqzbPAPYn4ctyOLucCgSJkRAb +jiKuzgrugxu+J83CBnj5QgOhO++u5gl28UT/hC9eiEbbRZrYt9XCnSOrJiUhH8Gq +i70l/ZQzRGEenc5Oox8gEPT712pBezX4zj1Ow9RibhaU50TPaP+HoCrb3hxX4AMZ ++I9KZucVsgFlezf4IKjtAS/ro4jJLB/u0HhsT5Ury7T5/cobVhK1j2q+q6juKOac +Z7ro/572cTonFqR9zZNOawZTeRpK3f+Dl+Q1S6wid626btg3Li1M1jQAdyGOaRDN +JNcKMFB1XwuE9He4Xs4wvFlNIz4xvoBRwf8EybFmSEyaS3qLbl322Un/z9sCpeZM +fsyUED+YaTHqJhi+XTjWAxy5VfycFu2Ev6EKNItnkkjXOoAXl0Fg7nrnVijKgo+a +4C4RO3nu4IouJlel3Lt9YyFW6CqOb2sARjJHOZtirMHUORm2aAlCnmvcPlBT8s1/ +GaG6e5heeoCMRwD37+rWauAjCvMyMc5JsFF7EUECvQB/7nGQb4JZoPsTW1cQRXDE +mY+horsIpVrXsnsdvYco7itilJAvQUz6YGsyGirMwdHktA8YClVrNArP/HfyLUu1 +uHAhDa0TG6/bouuReHQjrI0CL1k6J7dEfxXgQbAy1FH17/8JgvNT6R+TkL+KcgW6 +VV6tPsmivsZI7mCz1np/uXZX4+t4/6Ei5+kJCLsF1TmEd0mfBioJw7Mqd0Asr+bw +BasZKQG4gVHRjg6EXdSjQ9RoGhR8Q+R2hsb+Pj/z6GVtJTg4dVYRRjRP52tOb3Qx +W4XlzJR/lGjExe4h0D/x2vZnWlE5JvDPPq2Ni2yBeoX2+wgtFYqKGH1f319OMRXs +/BSk/bF7wdeeGn9FDSiQHlvfKJpToC86Yt25ZjGmGH0gbvrFLAd+a5y1046iHauz +mf9cQVM6NJJKngSDUK0JgDLQgdAvZCcqPp/vCfdKC0fzMTDXkkV6eqKTexHQ1oTu +ryWYHdGA+qzQO3OKDwlXTaCLnPN0Ke8BaAB7CJw9hR5t0cdw5e2nSzY96BK97tZy +qOlRKGbuSzv9GGp5RS6qFj9o8GrqCnZZTuDz2+D++yjT4Cg1QfL7Dp/YzpCeZ9vA +v5DMnjM6NUePYX145NgNtVm6y+ThAx4hBm42+B8nZ94GmCXf2MZModpcsnpTZlPe +4F7Hd/rBJG8MkEFPXgxuYF0B5HTlbr/33IsGtXYBEu1ucO19TBUi4ZDil3vl9/+1 +bYX+jn/wnOjtdM+kBj4TV9aCytdBV0my+mkv1nwTK0fiKFHsUG52mbGqq88A9Mmd +Z3grDaR2Rsb5AgLaABFCMoooFDVQtmt7xl1U3t4UZtDqny17wcXRolxXY5+tfI3Y +jWMqfO0QsBKHjfT4At5ToSDX5yjt4Q7UyhRKKprUyyVRYZv4EQZDqi2Hdx0wNDGr +yOQkK/LvXep0r5AEYcMkLO1x4hReaKdnSEPFRdXF/x7daAlRMTkUe5i4zLeYYhvI +Qsl3aErcSP/DWVUyQ2XbHkrG9suPbmLBou7BHNRWXdnFib0+jASQnVKuhVLGykUr +wzTNpGrn7Axna1P3uMwSnlJgA0vSrkR2dONzyq0hzoMmAjfC3Eh1D7tYbb6Cswx7 +5/Emq2cEEGtbyTJ5Q6+omALrsoybx4YAAAAAAAAAAAAA diff --git a/mimetreeparser/autotests/nodehelpertest.cpp b/mimetreeparser/autotests/nodehelpertest.cpp index b46c74f5..ed931ff3 100644 --- a/mimetreeparser/autotests/nodehelpertest.cpp +++ b/mimetreeparser/autotests/nodehelpertest.cpp @@ -1,190 +1,214 @@ /* Copyright 2015 Sandro Knauß This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "nodehelpertest.h" #include "viewer/nodehelper.h" #include +#include using namespace MimeTreeParser; NodeHelperTest::NodeHelperTest() : QObject() { } void NodeHelperTest::testPersistentIndex() { 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(); KMime::Content *subsubExtraNode = new KMime::Content(); subNode->addContent(subsubNode); subNode->addContent(subsubNode2); node->addContent(subNode); subsubExtra->addContent(subsubExtraNode); helper.attachExtraContent(node, extra); helper.attachExtraContent(node, extra2); 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("")); QCOMPARE(helper.contentFromIndex(node, QStringLiteral("")), node); QCOMPARE(helper.persistentIndex(node->contents()[0]), QStringLiteral("1")); QCOMPARE(helper.contentFromIndex(node, QStringLiteral("1")), node->contents()[0]); QCOMPARE(helper.persistentIndex(subNode), QStringLiteral("2")); QCOMPARE(helper.contentFromIndex(node, QStringLiteral("2")), subNode); QCOMPARE(helper.persistentIndex(subsubNode), QStringLiteral("2.2")); QCOMPARE(helper.contentFromIndex(node, QStringLiteral("2.2")), subsubNode); 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; } void NodeHelperTest::testHREF() { NodeHelper helper; KMime::Message::Ptr msg(new KMime::Message); QUrl url; KMime::Content *node = msg->topLevel(); KMime::Content *subNode = new KMime::Content(); KMime::Content *subsubNode = new KMime::Content(), *subsubNode2 = 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(); KMime::Content *subsubExtraNode = new KMime::Content(); subNode->addContent(subsubNode); subNode->addContent(subsubNode2); node->addContent(subNode); subsubExtra->addContent(subsubExtraNode); helper.attachExtraContent(node, extra); helper.attachExtraContent(node, extra2); helper.attachExtraContent(subNode, subExtra); helper.attachExtraContent(subsubNode2, subsubExtra); 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() { NodeHelper helper; KMime::Message::Ptr msg(new KMime::Message); KMime::Content *node = msg->topLevel(); KMime::Content *subNode = new KMime::Content(); KMime::Content *subsubNode = new KMime::Content(), *subsubNode2 = 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(); KMime::Content *subsubExtraNode = new KMime::Content(); subNode->addContent(subsubNode); subNode->addContent(subsubNode2); node->addContent(subNode); subsubExtra->addContent(subsubExtraNode); helper.attachExtraContent(node, extra); helper.attachExtraContent(node, extra2); helper.attachExtraContent(subNode, subExtra); helper.attachExtraContent(subsubNode2, subsubExtra); helper.writeNodeToTempFile(node); QCOMPARE(helper.fromHREF(msg, helper.tempFileUrlFromNode(node)), node); helper.writeNodeToTempFile(subNode); QCOMPARE(helper.fromHREF(msg, helper.tempFileUrlFromNode(subNode)), subNode); helper.writeNodeToTempFile(subsubNode); QCOMPARE(helper.fromHREF(msg, helper.tempFileUrlFromNode(subsubNode)), subsubNode); helper.writeNodeToTempFile(subsubNode2); QCOMPARE(helper.fromHREF(msg, helper.tempFileUrlFromNode(subsubNode2)), subsubNode2); helper.writeNodeToTempFile(extra); QCOMPARE(helper.fromHREF(msg, helper.tempFileUrlFromNode(extra)), extra); helper.writeNodeToTempFile(subExtra); QCOMPARE(helper.fromHREF(msg, helper.tempFileUrlFromNode(subExtra)), subExtra); helper.writeNodeToTempFile(subsubExtra); QCOMPARE(helper.fromHREF(msg, helper.tempFileUrlFromNode(subsubExtra)), subsubExtra); helper.writeNodeToTempFile(subsubExtraNode); QCOMPARE(helper.fromHREF(msg, helper.tempFileUrlFromNode(subsubExtraNode)), subsubExtraNode); helper.forceCleanTempFiles(); } QTEST_GUILESS_MAIN(NodeHelperTest) diff --git a/mimetreeparser/src/viewer/nodehelper.cpp b/mimetreeparser/src/viewer/nodehelper.cpp index 8091c18b..f9a0bc86 100644 --- a/mimetreeparser/src/viewer/nodehelper.cpp +++ b/mimetreeparser/src/viewer/nodehelper.cpp @@ -1,1035 +1,1045 @@ /* Copyright (C) 2009 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net Copyright (c) 2009 Andras Mantia This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "nodehelper.h" #include "mimetreeparser_debug.h" #include "partmetadata.h" #include "interfaces/bodypart.h" #include "temporaryfile/attachmenttemporaryfilesdirs.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace MimeTreeParser { QStringList replySubjPrefixes(QStringList() << QStringLiteral("Re\\s*:") << QStringLiteral("Re\\[\\d+\\]:") << QStringLiteral("Re\\d+:")); QStringList forwardSubjPrefixes(QStringList() << QStringLiteral("Fwd:") << QStringLiteral("FW:")); NodeHelper::NodeHelper() : mAttachmentFilesDir(new AttachmentTemporaryFilesDirs()) { //TODO(Andras) add methods to modify these prefixes mLocalCodec = QTextCodec::codecForLocale(); // In the case of Japan. Japanese locale name is "eucjp" but // The Japanese mail systems normally used "iso-2022-jp" of locale name. // We want to change locale name from eucjp to iso-2022-jp at KMail only. // (Introduction to i18n, 6.6 Limit of Locale technology): // EUC-JP is the de-facto standard for UNIX systems, ISO 2022-JP // is the standard for Internet, and Shift-JIS is the encoding // for Windows and Macintosh. if (mLocalCodec) { if (mLocalCodec->name().toLower() == "eucjp" #if defined Q_OS_WIN || defined Q_OS_MACX || mLocalCodec->name().toLower() == "shift-jis" // OK? #endif ) { mLocalCodec = QTextCodec::codecForName("jis7"); // QTextCodec *cdc = QTextCodec::codecForName("jis7"); // QTextCodec::setCodecForLocale(cdc); // KLocale::global()->setEncoding(cdc->mibEnum()); } } } NodeHelper::~NodeHelper() { //Don't delete it it will delete in class with a deleteLater; if (mAttachmentFilesDir) { mAttachmentFilesDir->removeTempFiles(); mAttachmentFilesDir = 0; } } void NodeHelper::setNodeProcessed(KMime::Content *node, bool recurse) { if (!node) { return; } mProcessedNodes.append(node); qCDebug(MIMETREEPARSER_LOG) << "Node processed: " << node->index().toString() << node->contentType()->as7BitString(); //<< " decodedContent" << node->decodedContent(); if (recurse) { auto contents = node->contents(); Q_FOREACH (KMime::Content *c, contents) { setNodeProcessed(c, true); } } } void NodeHelper::setNodeUnprocessed(KMime::Content *node, bool recurse) { if (!node) { return; } mProcessedNodes.removeAll(node); //avoid double addition of extra nodes, eg. encrypted attachments const QMap >::iterator it = mExtraContents.find(node); if (it != mExtraContents.end()) { Q_FOREACH (KMime::Content *c, it.value()) { KMime::Content *p = c->parent(); if (p) { p->removeContent(c); } } qDeleteAll(it.value()); qCDebug(MIMETREEPARSER_LOG) << "mExtraContents deleted for" << it.key(); mExtraContents.erase(it); } qCDebug(MIMETREEPARSER_LOG) << "Node UNprocessed: " << node; if (recurse) { auto contents = node->contents(); Q_FOREACH (KMime::Content *c, contents) { setNodeUnprocessed(c, true); } } } bool NodeHelper::nodeProcessed(KMime::Content *node) const { if (!node) { return true; } return mProcessedNodes.contains(node); } static void clearBodyPartMemento(QMap &bodyPartMementoMap) { for (QMap::iterator it = bodyPartMementoMap.begin(), end = bodyPartMementoMap.end(); it != end; ++it) { Interface::BodyPartMemento *memento = it.value(); memento->detach(); delete memento; } bodyPartMementoMap.clear(); } void NodeHelper::clear() { mProcessedNodes.clear(); mEncryptionState.clear(); mSignatureState.clear(); mOverrideCodecs.clear(); std::for_each(mBodyPartMementoMap.begin(), mBodyPartMementoMap.end(), &clearBodyPartMemento); mBodyPartMementoMap.clear(); QMap >::ConstIterator end(mExtraContents.constEnd()); for (QMap >::ConstIterator it = mExtraContents.constBegin(); it != end; ++it) { Q_FOREACH (KMime::Content *c, it.value()) { KMime::Content *p = c->parent(); if (p) { p->removeContent(c); } } qDeleteAll(it.value()); qCDebug(MIMETREEPARSER_LOG) << "mExtraContents deleted for" << it.key(); } mExtraContents.clear(); mDisplayEmbeddedNodes.clear(); mDisplayHiddenNodes.clear(); } void NodeHelper::setEncryptionState(const KMime::Content *node, const KMMsgEncryptionState state) { mEncryptionState[node] = state; } KMMsgEncryptionState NodeHelper::encryptionState(const KMime::Content *node) const { return mEncryptionState.value(node, KMMsgNotEncrypted); } void NodeHelper::setSignatureState(const KMime::Content *node, const KMMsgSignatureState state) { mSignatureState[node] = state; } KMMsgSignatureState NodeHelper::signatureState(const KMime::Content *node) const { return mSignatureState.value(node, KMMsgNotSigned); } PartMetaData NodeHelper::partMetaData(KMime::Content *node) { return mPartMetaDatas.value(node, PartMetaData()); } void NodeHelper::setPartMetaData(KMime::Content *node, const PartMetaData &metaData) { mPartMetaDatas.insert(node, metaData); } QString NodeHelper::writeNodeToTempFile(KMime::Content *node) { // If the message part is already written to a file, no point in doing it again. // This function is called twice actually, once from the rendering of the attachment // in the body and once for the header. QUrl existingFileName = tempFileUrlFromNode(node); if (!existingFileName.isEmpty()) { return existingFileName.toLocalFile(); } QString fname = createTempDir(persistentIndex(node)); if (fname.isEmpty()) { return QString(); } QString fileName = NodeHelper::fileName(node); // strip off a leading path int slashPos = fileName.lastIndexOf(QLatin1Char('/')); if (-1 != slashPos) { fileName = fileName.mid(slashPos + 1); } if (fileName.isEmpty()) { fileName = QStringLiteral("unnamed"); } fname += QLatin1Char('/') + fileName; qCDebug(MIMETREEPARSER_LOG) << "Create temp file: " << fname; QByteArray data = node->decodedContent(); if (node->contentType()->isText() && data.size() > 0) { // convert CRLF to LF before writing text attachments to disk data = KMime::CRLFtoLF(data); } QFile f(fname); if (!f.open(QIODevice::ReadWrite)) { qCWarning(MIMETREEPARSER_LOG) << "Failed to write note to file:" << f.errorString(); return QString(); } f.write(data); mAttachmentFilesDir->addTempFile(fname); // make file read-only so that nobody gets the impression that he might // edit attached files (cf. bug #52813) f.setPermissions(QFileDevice::ReadUser); f.close(); return fname; } QUrl NodeHelper::tempFileUrlFromNode(const KMime::Content *node) { if (!node) { return QUrl(); } const QString index = persistentIndex(node); foreach (const QString &path, mAttachmentFilesDir->temporaryFiles()) { const int right = path.lastIndexOf(QLatin1Char('/')); int left = path.lastIndexOf(QLatin1String(".index."), right); if (left != -1) { left += 7; } QStringRef storedIndex(&path, left, right - left); if (left != -1 && storedIndex == index) { return QUrl::fromLocalFile(path); } } return QUrl(); } QString NodeHelper::createTempDir(const QString ¶m) { QTemporaryFile *tempFile = new QTemporaryFile(QDir::tempPath() + QLatin1String("/messageviewer_XXXXXX") + QLatin1String(".index.") + param); tempFile->open(); const QString fname = tempFile->fileName(); delete tempFile; QFile fFile(fname); if (!(fFile.permissions() & QFileDevice::WriteUser)) { // Not there or not writable if (!QDir().mkpath(fname) || !fFile.setPermissions(QFileDevice::WriteUser | QFileDevice::ReadUser | QFileDevice::ExeUser)) { return QString(); //failed create } } Q_ASSERT(!fname.isNull()); mAttachmentFilesDir->addTempDir(fname); return fname; } void NodeHelper::forceCleanTempFiles() { mAttachmentFilesDir->forceCleanTempFiles(); delete mAttachmentFilesDir; mAttachmentFilesDir = 0; } void NodeHelper::removeTempFiles() { //Don't delete it it will delete in class mAttachmentFilesDir->removeTempFiles(); mAttachmentFilesDir = new AttachmentTemporaryFilesDirs(); } void NodeHelper::addTempFile(const QString &file) { mAttachmentFilesDir->addTempFile(file); } bool NodeHelper::isInEncapsulatedMessage(KMime::Content *node) { const KMime::Content *const topLevel = node->topLevel(); const KMime::Content *cur = node; while (cur && cur != topLevel) { const bool parentIsMessage = cur->parent() && cur->parent()->contentType(false) && cur->parent()->contentType()->mimeType().toLower() == "message/rfc822"; if (parentIsMessage && cur->parent() != topLevel) { return true; } cur = cur->parent(); } return false; } QByteArray NodeHelper::charset(KMime::Content *node) { if (node->contentType(false)) { return node->contentType(false)->charset(); } else { return node->defaultCharset(); } } KMMsgEncryptionState NodeHelper::overallEncryptionState(KMime::Content *node) const { KMMsgEncryptionState myState = KMMsgEncryptionStateUnknown; if (!node) { return myState; } KMime::Content *parent = node->parent(); auto contents = parent ? parent->contents() : KMime::Content::List(); if (contents.isEmpty()) { contents.append(node); } int i = contents.indexOf(const_cast(node)); for (; i < contents.size(); ++i) { auto next = contents.at(i); KMMsgEncryptionState otherState = encryptionState(next); // NOTE: children are tested ONLY when parent is not encrypted if (otherState == KMMsgNotEncrypted && !next->contents().isEmpty()) { otherState = overallEncryptionState(next->contents().at(0)); } if (otherState == KMMsgNotEncrypted && !extraContents(next).isEmpty()) { otherState = overallEncryptionState(extraContents(next).at(0)); } if (next == node) { myState = otherState; } switch (otherState) { case KMMsgEncryptionStateUnknown: break; case KMMsgNotEncrypted: if (myState == KMMsgFullyEncrypted) { myState = KMMsgPartiallyEncrypted; } else if (myState != KMMsgPartiallyEncrypted) { myState = KMMsgNotEncrypted; } break; case KMMsgPartiallyEncrypted: myState = KMMsgPartiallyEncrypted; break; case KMMsgFullyEncrypted: if (myState != KMMsgFullyEncrypted) { myState = KMMsgPartiallyEncrypted; } break; case KMMsgEncryptionProblematic: break; } } qCDebug(MIMETREEPARSER_LOG) << "\n\n KMMsgEncryptionState:" << myState; return myState; } KMMsgSignatureState NodeHelper::overallSignatureState(KMime::Content *node) const { KMMsgSignatureState myState = KMMsgSignatureStateUnknown; if (!node) { return myState; } KMime::Content *parent = node->parent(); auto contents = parent ? parent->contents() : KMime::Content::List(); if (contents.isEmpty()) { contents.append(node); } int i = contents.indexOf(const_cast(node)); for (; i < contents.size(); ++i) { auto next = contents.at(i); KMMsgSignatureState otherState = signatureState(next); // NOTE: children are tested ONLY when parent is not encrypted if (otherState == KMMsgNotSigned && !next->contents().isEmpty()) { otherState = overallSignatureState(next->contents().at(0)); } if (otherState == KMMsgNotSigned && !extraContents(next).isEmpty()) { otherState = overallSignatureState(extraContents(next).at(0)); } if (next == node) { myState = otherState; } switch (otherState) { case KMMsgSignatureStateUnknown: break; case KMMsgNotSigned: if (myState == KMMsgFullySigned) { myState = KMMsgPartiallySigned; } else if (myState != KMMsgPartiallySigned) { myState = KMMsgNotSigned; } break; case KMMsgPartiallySigned: myState = KMMsgPartiallySigned; break; case KMMsgFullySigned: if (myState != KMMsgFullySigned) { myState = KMMsgPartiallySigned; } break; case KMMsgSignatureProblematic: break; } } qCDebug(MIMETREEPARSER_LOG) << "\n\n KMMsgSignatureState:" << myState; return myState; } void NodeHelper::magicSetType(KMime::Content *node, bool aAutoDecode) { const QByteArray body = (aAutoDecode) ? node->decodedContent() : node->body(); QMimeDatabase db; QMimeType mime = db.mimeTypeForData(body); QString mimetype = mime.name(); node->contentType()->setMimeType(mimetype.toLatin1()); } // static QString NodeHelper::replacePrefixes(const QString &str, const QStringList &prefixRegExps, bool replace, const QString &newPrefix) { bool recognized = false; // construct a big regexp that // 1. is anchored to the beginning of str (sans whitespace) // 2. matches at least one of the part regexps in prefixRegExps QString bigRegExp = QStringLiteral("^(?:\\s+|(?:%1))+\\s*") .arg(prefixRegExps.join(QStringLiteral(")|(?:"))); QRegExp rx(bigRegExp, Qt::CaseInsensitive); if (!rx.isValid()) { qCWarning(MIMETREEPARSER_LOG) << "bigRegExp = \"" << bigRegExp << "\"\n" << "prefix regexp is invalid!"; // try good ole Re/Fwd: recognized = str.startsWith(newPrefix); } else { // valid rx QString tmp = str; if (rx.indexIn(tmp) == 0) { recognized = true; if (replace) { return tmp.replace(0, rx.matchedLength(), newPrefix + QLatin1Char(' ')); } } } if (!recognized) { return newPrefix + QLatin1Char(' ') + str; } else { return str; } } QString NodeHelper::cleanSubject(KMime::Message *message) { return cleanSubject(message, replySubjPrefixes + forwardSubjPrefixes, true, QString()).trimmed(); } QString NodeHelper::cleanSubject(KMime::Message *message, const QStringList &prefixRegExps, bool replace, const QString &newPrefix) { QString cleanStr; if (message) { cleanStr = NodeHelper::replacePrefixes( message->subject()->asUnicodeString(), prefixRegExps, replace, newPrefix); } return cleanStr; } void NodeHelper::setOverrideCodec(KMime::Content *node, const QTextCodec *codec) { if (!node) { return; } mOverrideCodecs[node] = codec; } const QTextCodec *NodeHelper::codec(KMime::Content *node) { if (! node) { return mLocalCodec; } const QTextCodec *c = mOverrideCodecs.value(node, 0); if (!c) { // no override-codec set for this message, try the CT charset parameter: c = codecForName(node->contentType()->charset()); } if (!c) { // no charset means us-ascii (RFC 2045), so using local encoding should // be okay c = mLocalCodec; } return c; } const QTextCodec *NodeHelper::codecForName(const QByteArray &_str) { if (_str.isEmpty()) { return 0; } QByteArray codec = _str.toLower(); return KCharsets::charsets()->codecForName(QLatin1String(codec)); } QString NodeHelper::fileName(const KMime::Content *node) { QString name = const_cast(node)->contentDisposition()->filename(); if (name.isEmpty()) { name = const_cast(node)->contentType()->name(); } name = name.trimmed(); return name; } //FIXME(Andras) review it (by Marc?) to see if I got it right. This is supposed to be the partNode::internalBodyPartMemento replacement Interface::BodyPartMemento *NodeHelper::bodyPartMemento(KMime::Content *node, const QByteArray &which) const { const QMap< QString, QMap >::const_iterator nit = mBodyPartMementoMap.find(persistentIndex(node)); if (nit == mBodyPartMementoMap.end()) { return 0; } const QMap::const_iterator it = nit->find(which.toLower()); return it != nit->end() ? it.value() : 0; } //FIXME(Andras) review it (by Marc?) to see if I got it right. This is supposed to be the partNode::internalSetBodyPartMemento replacement void NodeHelper::setBodyPartMemento(KMime::Content *node, const QByteArray &which, Interface::BodyPartMemento *memento) { QMap &mementos = mBodyPartMementoMap[persistentIndex(node)]; const QMap::iterator it = mementos.lowerBound(which.toLower()); if (it != mementos.end() && it.key() == which.toLower()) { delete it.value(); if (memento) { it.value() = memento; } else { mementos.erase(it); } } else { mementos.insert(which.toLower(), memento); } } bool NodeHelper::isNodeDisplayedEmbedded(KMime::Content *node) const { qCDebug(MIMETREEPARSER_LOG) << "IS NODE: " << mDisplayEmbeddedNodes.contains(node); return mDisplayEmbeddedNodes.contains(node); } void NodeHelper::setNodeDisplayedEmbedded(KMime::Content *node, bool displayedEmbedded) { qCDebug(MIMETREEPARSER_LOG) << "SET NODE: " << node << displayedEmbedded; if (displayedEmbedded) { mDisplayEmbeddedNodes.insert(node); } else { mDisplayEmbeddedNodes.remove(node); } } bool NodeHelper::isNodeDisplayedHidden(KMime::Content *node) const { return mDisplayHiddenNodes.contains(node); } void NodeHelper::setNodeDisplayedHidden(KMime::Content *node, bool displayedHidden) { if (displayedHidden) { mDisplayHiddenNodes.insert(node); } else { mDisplayEmbeddedNodes.remove(node); } } /*! Creates a persistent index string that bridges the gap between the permanent nodes and the temporary ones. Used internally for robust indexing. */ QString NodeHelper::persistentIndex(const KMime::Content *node) const { if (!node) { return QString(); } 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 { return QStringLiteral("attachment:%1?place=%2").arg(persistentIndex(node), place); } KMime::Content *NodeHelper::fromHREF(const KMime::Message::Ptr &mMessage, const QUrl &url) const { if (url.isEmpty()) { return mMessage.data(); } if (!url.isLocalFile()) { return contentFromIndex(mMessage.data(), url.adjusted(QUrl::StripTrailingSlash).path()); } else { const QString path = url.toLocalFile(); // extract from //qttestn28554.index.2.3:0:2/unnamed -> "2.3:0:2" // 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) { return contentFromIndex(mMessage.data(), rIndex.cap(1)); } return mMessage.data(); } } QString NodeHelper::fixEncoding(const QString &encoding) { QString returnEncoding = encoding; // According to http://www.iana.org/assignments/character-sets, uppercase is // preferred in MIME headers if (returnEncoding.toUpper().contains(QStringLiteral("ISO "))) { returnEncoding = returnEncoding.toUpper(); returnEncoding.replace(QLatin1String("ISO "), QStringLiteral("ISO-")); } return returnEncoding; } //----------------------------------------------------------------------------- QString NodeHelper::encodingForName(const QString &descriptiveName) { QString encoding = KCharsets::charsets()->encodingForName(descriptiveName); return NodeHelper::fixEncoding(encoding); } QStringList NodeHelper::supportedEncodings(bool usAscii) { QStringList encodingNames = KCharsets::charsets()->availableEncodingNames(); QStringList encodings; QMap mimeNames; QStringList::ConstIterator constEnd(encodingNames.constEnd()); for (QStringList::ConstIterator it = encodingNames.constBegin(); it != constEnd; ++it) { QTextCodec *codec = KCharsets::charsets()->codecForName(*it); QString mimeName = (codec) ? QString::fromLatin1(codec->name()).toLower() : (*it); if (!mimeNames.contains(mimeName)) { encodings.append(KCharsets::charsets()->descriptionForEncoding(*it)); mimeNames.insert(mimeName, true); } } encodings.sort(); if (usAscii) { encodings.prepend(KCharsets::charsets()->descriptionForEncoding(QStringLiteral("us-ascii"))); } return encodings; } QString NodeHelper::fromAsString(KMime::Content *node) { KMime::Message *topLevel = dynamic_cast(node->topLevel()); if (topLevel) { return topLevel->from()->asUnicodeString(); } return QString(); } void NodeHelper::attachExtraContent(KMime::Content *topLevelNode, KMime::Content *content) { qCDebug(MIMETREEPARSER_LOG) << "mExtraContents added for" << topLevelNode << " extra content: " << content; mExtraContents[topLevelNode].append(content); } QList< KMime::Content * > NodeHelper::extraContents(KMime::Content *topLevelnode) const { return mExtraContents.value(topLevelnode); } void NodeHelper::mergeExtraNodes(KMime::Content *node) { if (!node) { return; } QList extraNodes = extraContents(node); Q_FOREACH (KMime::Content *extra, extraNodes) { if (node->bodyIsMessage()) { qCWarning(MIMETREEPARSER_LOG) << "Asked to attach extra content to a kmime::message, this does not make sense. Attaching to:" << node << node->encodedContent() << "\n====== with =======\n" << extra << extra->encodedContent(); continue; } KMime::Content *c = new KMime::Content(node); c->setContent(extra->encodedContent()); c->parse(); node->addContent(c); } Q_FOREACH (KMime::Content *child, node->contents()) { mergeExtraNodes(child); } } void NodeHelper::cleanFromExtraNodes(KMime::Content *node) { if (!node) { return; } QList extraNodes = extraContents(node); Q_FOREACH (KMime::Content *extra, extraNodes) { QByteArray s = extra->encodedContent(); auto children = node->contents(); Q_FOREACH (KMime::Content *c, children) { if (c->encodedContent() == s) { node->removeContent(c); } } } Q_FOREACH (KMime::Content *child, node->contents()) { cleanFromExtraNodes(child); } } KMime::Message *NodeHelper::messageWithExtraContent(KMime::Content *topLevelNode) { /*The merge is done in several steps: 1) merge the extra nodes into topLevelNode 2) copy the modified (merged) node tree into a new node tree 3) restore the original node tree in topLevelNode by removing the extra nodes from it The reason is that extra nodes are assigned by pointer value to the nodes in the original tree. */ if (!topLevelNode) { return 0; } mergeExtraNodes(topLevelNode); KMime::Message *m = new KMime::Message; m->setContent(topLevelNode->encodedContent()); m->parse(); cleanFromExtraNodes(topLevelNode); // qCDebug(MIMETREEPARSER_LOG) << "MESSAGE WITH EXTRA: " << m->encodedContent(); // qCDebug(MIMETREEPARSER_LOG) << "MESSAGE WITHOUT EXTRA: " << topLevelNode->encodedContent(); return m; } KMime::Content *NodeHelper::decryptedNodeForContent(KMime::Content *content) const { const QList xc = extraContents(content); if (!xc.empty()) { if (xc.size() == 1) { return xc.front(); } else { qCWarning(MIMETREEPARSER_LOG) << "WTF, encrypted node has multiple extra contents?"; } } return 0; } bool NodeHelper::unencryptedMessage_helper(KMime::Content *node, QByteArray &resultingData, bool addHeaders, int recursionLevel) { bool returnValue = false; if (node) { KMime::Content *curNode = node; KMime::Content *decryptedNode = 0; const QByteArray type = node->contentType(false) ? QByteArray(node->contentType()->mediaType()).toLower() : "text"; const QByteArray subType = node->contentType(false) ? node->contentType()->subType().toLower() : "plain"; const bool isMultipart = node->contentType(false) && node->contentType()->isMultipart(); bool isSignature = false; qCDebug(MIMETREEPARSER_LOG) << "(" << recursionLevel << ") Looking at" << type << "/" << subType; if (isMultipart) { if (subType == "signed") { isSignature = true; } else if (subType == "encrypted") { decryptedNode = decryptedNodeForContent(curNode); } } else if (type == "application") { if (subType == "octet-stream") { decryptedNode = decryptedNodeForContent(curNode); } else if (subType == "pkcs7-signature") { isSignature = true; } else if (subType == "pkcs7-mime") { // note: subtype pkcs7-mime can also be signed // and we do NOT want to remove the signature! if (encryptionState(curNode) != KMMsgNotEncrypted) { decryptedNode = decryptedNodeForContent(curNode); } } } if (decryptedNode) { qCDebug(MIMETREEPARSER_LOG) << "Current node has an associated decrypted node, adding a modified header " "and then processing the children."; Q_ASSERT(addHeaders); KMime::Content headers; headers.setHead(curNode->head()); headers.parse(); if (decryptedNode->contentType(false)) { headers.contentType()->from7BitString(decryptedNode->contentType()->as7BitString(false)); } else { headers.removeHeader(); } if (decryptedNode->contentTransferEncoding(false)) { headers.contentTransferEncoding()->from7BitString(decryptedNode->contentTransferEncoding()->as7BitString(false)); } else { headers.removeHeader(); } if (decryptedNode->contentDisposition(false)) { headers.contentDisposition()->from7BitString(decryptedNode->contentDisposition()->as7BitString(false)); } else { headers.removeHeader(); } if (decryptedNode->contentDescription(false)) { headers.contentDescription()->from7BitString(decryptedNode->contentDescription()->as7BitString(false)); } else { headers.removeHeader(); } headers.assemble(); resultingData += headers.head() + '\n'; unencryptedMessage_helper(decryptedNode, resultingData, false, recursionLevel + 1); returnValue = true; } else if (isSignature) { qCDebug(MIMETREEPARSER_LOG) << "Current node is a signature, adding it as-is."; // We can't change the nodes under the signature, as that would invalidate it. Add the signature // and its child as-is if (addHeaders) { resultingData += curNode->head() + '\n'; } resultingData += curNode->encodedBody(); returnValue = false; } else if (isMultipart) { qCDebug(MIMETREEPARSER_LOG) << "Current node is a multipart node, adding its header and then processing all children."; // Normal multipart node, add the header and all of its children bool somethingChanged = false; if (addHeaders) { resultingData += curNode->head() + '\n'; } const QByteArray boundary = curNode->contentType()->boundary(); foreach (KMime::Content *child, curNode->contents()) { resultingData += "\n--" + boundary + '\n'; const bool changed = unencryptedMessage_helper(child, resultingData, true, recursionLevel + 1); if (changed) { somethingChanged = true; } } resultingData += "\n--" + boundary + "--\n\n"; returnValue = somethingChanged; } else if (curNode->bodyIsMessage()) { qCDebug(MIMETREEPARSER_LOG) << "Current node is a message, adding the header and then processing the child."; if (addHeaders) { resultingData += curNode->head() + '\n'; } returnValue = unencryptedMessage_helper(curNode->bodyAsMessage().data(), resultingData, true, recursionLevel + 1); } else { qCDebug(MIMETREEPARSER_LOG) << "Current node is an ordinary leaf node, adding it as-is."; if (addHeaders) { resultingData += curNode->head() + '\n'; } resultingData += curNode->body(); returnValue = false; } } qCDebug(MIMETREEPARSER_LOG) << "(" << recursionLevel << ") done."; return returnValue; } KMime::Message::Ptr NodeHelper::unencryptedMessage(const KMime::Message::Ptr &originalMessage) { QByteArray resultingData; const bool messageChanged = unencryptedMessage_helper(originalMessage.data(), resultingData, true); if (messageChanged) { #if 0 qCDebug(MIMETREEPARSER_LOG) << "Resulting data is:" << resultingData; QFile bla("stripped.mbox"); bla.open(QIODevice::WriteOnly); bla.write(resultingData); bla.close(); #endif KMime::Message::Ptr newMessage(new KMime::Message); newMessage->setContent(resultingData); newMessage->parse(); return newMessage; } else { return KMime::Message::Ptr(); } } QVector NodeHelper::attachmentsOfExtraContents() const { QVector result; for (auto it = mExtraContents.begin(); it != mExtraContents.end(); ++it) { foreach (auto content, it.value()) { if (KMime::isAttachment(content)) { result.push_back(content); } else { result += content->attachments(); } } } return result; } }