diff --git a/messageviewer/src/header/autotests/grantleeheaderformattertest.cpp b/messageviewer/src/header/autotests/grantleeheaderformattertest.cpp --- a/messageviewer/src/header/autotests/grantleeheaderformattertest.cpp +++ b/messageviewer/src/header/autotests/grantleeheaderformattertest.cpp @@ -20,6 +20,7 @@ #include "grantleeheaderformattertest.h" #include +#include #include "../grantleeheaderformatter.h" #include @@ -102,6 +103,8 @@ { auto style = GrantleeHeaderStyle(); auto formatter = GrantleeHeaderFormatter(); + MimeTreeParser::NodeHelper nodeHelper; + style.setNodeHelper(&nodeHelper); auto aMsg = readAndParseMail(QStringLiteral("allheaders.mbox")); QString filename = QStringLiteral("invalid"); @@ -117,6 +120,8 @@ auto style = GrantleeHeaderStyle(); auto formatter = GrantleeHeaderFormatter(); + MimeTreeParser::NodeHelper nodeHelper; + style.setNodeHelper(&nodeHelper); KMime::Message::Ptr aMsg(new KMime::Message); const QString &absolutePath = QStringLiteral(HEADER_DATA_DIR) + QLatin1Char('/') + tmplName; @@ -138,6 +143,8 @@ auto style = GrantleeHeaderStyle(); auto formatter = GrantleeHeaderFormatter(); + MimeTreeParser::NodeHelper nodeHelper; + style.setNodeHelper(&nodeHelper); KMime::Message::Ptr msg(new KMime::Message); { @@ -177,6 +184,8 @@ auto style = GrantleeHeaderStyle(); auto formatter = GrantleeHeaderFormatter(); + MimeTreeParser::NodeHelper nodeHelper; + style.setNodeHelper(&nodeHelper); auto aMsg = readAndParseMail(QStringLiteral("headertest.mbox")); QString absolutePath = QStringLiteral(HEADER_DATA_DIR) + QLatin1Char('/') + tmplName; diff --git a/messageviewer/src/header/autotests/grantleeheaderstyletest.cpp b/messageviewer/src/header/autotests/grantleeheaderstyletest.cpp --- a/messageviewer/src/header/autotests/grantleeheaderstyletest.cpp +++ b/messageviewer/src/header/autotests/grantleeheaderstyletest.cpp @@ -20,6 +20,7 @@ #include "grantleeheaderstyletest.h" #include +#include #include @@ -208,6 +209,8 @@ QFETCH(QString, mailFileName); auto style = GrantleeHeaderStyle(); + MimeTreeParser::NodeHelper nodeHelper; + style.setNodeHelper(&nodeHelper); auto aMsg = readAndParseMail(mailFileName); style.setTheme(defaultTheme()); diff --git a/mimetreeparser/autotests/basicobjecttreeparsertest.h b/mimetreeparser/autotests/basicobjecttreeparsertest.h --- a/mimetreeparser/autotests/basicobjecttreeparsertest.h +++ b/mimetreeparser/autotests/basicobjecttreeparsertest.h @@ -40,6 +40,7 @@ void testAsync(); void testHtmlContent_data(); void testHtmlContent(); + void testMemoryHole(); void testRenderedTree(); void testRenderedTree_data(); }; diff --git a/mimetreeparser/autotests/basicobjecttreeparsertest.cpp b/mimetreeparser/autotests/basicobjecttreeparsertest.cpp --- a/mimetreeparser/autotests/basicobjecttreeparsertest.cpp +++ b/mimetreeparser/autotests/basicobjecttreeparsertest.cpp @@ -352,6 +352,26 @@ QCOMPARE(otp.htmlContent(), output); } +void ObjectTreeParserTest::testMemoryHole() +{ + const QString fileName = QStringLiteral("openpgp-encrypted-memoryhole.mbox"); + KMime::Message::Ptr originalMessage = readAndParseMail(fileName); + NodeHelper nodeHelper; + SimpleObjectTreeSource testSource; + ObjectTreeParser otp(&testSource, &nodeHelper); + testSource.setDecryptMessage(true); + otp.parseObjectTree(originalMessage.data()); + + QCOMPARE(nodeHelper.mailHeaderAsBase("from", originalMessage.data())->asUnicodeString(), QStringLiteral("you@example.com")); + QCOMPARE(nodeHelper.mailHeaderAsBase("to", originalMessage.data())->asUnicodeString(), QStringLiteral("me@example.com")); + QCOMPARE(nodeHelper.mailHeaderAsBase("subject", originalMessage.data())->asUnicodeString(), QStringLiteral("hidden subject")); + QCOMPARE(nodeHelper.mailHeaderAsBase("cc", originalMessage.data())->asUnicodeString(), QStringLiteral("cc@example.com")); + QCOMPARE(nodeHelper.mailHeaderAsBase("message-id", originalMessage.data())->asUnicodeString(), QStringLiteral("")); + QCOMPARE(nodeHelper.mailHeaderAsBase("references", originalMessage.data())->asUnicodeString(), QStringLiteral("")); + QCOMPARE(nodeHelper.mailHeaderAsBase("in-reply-to", originalMessage.data())->asUnicodeString(), QStringLiteral("")); + QCOMPARE(nodeHelper.dateHeader(originalMessage.data()), QDateTime(QDate(2018, 1, 2), QTime(3,4,5))); +} + void ObjectTreeParserTest::testRenderedTree_data() { QTest::addColumn("mailFileName"); diff --git a/mimetreeparser/src/bodyformatter/applicationpgpencrypted.cpp b/mimetreeparser/src/bodyformatter/applicationpgpencrypted.cpp --- a/mimetreeparser/src/bodyformatter/applicationpgpencrypted.cpp +++ b/mimetreeparser/src/bodyformatter/applicationpgpencrypted.cpp @@ -71,11 +71,13 @@ if (!part.source()->decryptMessage()) { part.nodeHelper()->setNodeProcessed(data, false); // Set the data node to done to prevent it from being processed } else if (KMime::Content *newNode = part.nodeHelper()->decryptedNodeForContent(data)) { + part.nodeHelper()->registerOverrideHeader(data->parent(), mp); // if we already have a decrypted node for this encrypted node, don't do the decryption again return MessagePart::Ptr(new MimeMessagePart(part.objectTreeParser(), newNode, false)); } else { mp->startDecryption(data); if (!messagePart->inProgress) { + part.nodeHelper()->registerOverrideHeader(data->parent(), mp); part.nodeHelper()->setNodeProcessed(data, false); // Set the data node to done to prevent it from being processed if (messagePart->isDecryptable && messagePart->isSigned) { part.nodeHelper()->setSignatureState(node, KMMsgFullySigned); diff --git a/mimetreeparser/src/bodyformatter/multipartencrypted.cpp b/mimetreeparser/src/bodyformatter/multipartencrypted.cpp --- a/mimetreeparser/src/bodyformatter/multipartencrypted.cpp +++ b/mimetreeparser/src/bodyformatter/multipartencrypted.cpp @@ -82,17 +82,20 @@ mp->setIsEncrypted(true); mp->setDecryptMessage(part.source()->decryptMessage()); PartMetaData *messagePart(mp->partMetaData()); + if (!part.source()->decryptMessage()) { part.nodeHelper()->setNodeProcessed(data, false); // Set the data node to done to prevent it from being processed } else if (KMime::Content *newNode = part.nodeHelper()->decryptedNodeForContent(data)) { + part.nodeHelper()->registerOverrideHeader(data->parent(), mp); + // if we already have a decrypted node for part.objectTreeParser() encrypted node, don't do the decryption again return MessagePart::Ptr(new MimeMessagePart(part.objectTreeParser(), newNode, true)); } else { mp->startDecryption(data); - qCDebug(MIMETREEPARSER_LOG) << "decrypted, signed?:" << messagePart->isSigned; if (!messagePart->inProgress) { + part.nodeHelper()->registerOverrideHeader(data->parent(), mp); part.nodeHelper()->setNodeProcessed(data, false); // Set the data node to done to prevent it from being processed } } diff --git a/mimetreeparser/src/messagepart.h b/mimetreeparser/src/messagepart.h --- a/mimetreeparser/src/messagepart.h +++ b/mimetreeparser/src/messagepart.h @@ -135,6 +135,9 @@ Interface::ObjectTreeSource *source() const; NodeHelper *nodeHelper() const; + virtual bool hasHeader(const char *header) const; + virtual KMime::Headers::Base *header(const char *header) const; + protected: void parseInternal(KMime::Content *node, bool onlyOneMimePart); QString renderInternalText() const; @@ -376,6 +379,8 @@ const std::vector > &decryptRecipients() const; + bool hasHeader(const char *header) const override; + KMime::Headers::Base* header(const char* header) const override; private: /** Handles the decryption of a given content * returns true if the decryption was successful @@ -426,6 +431,8 @@ const QGpgME::Protocol *cryptoProto() const; QString fromAddress() const; + bool hasHeader(const char *header) const override; + KMime::Headers::Base *header(const char *header) const override; private: /** Handles the verification of data * If signature is empty it is handled as inline signature otherwise as detached signature mode. diff --git a/mimetreeparser/src/messagepart.cpp b/mimetreeparser/src/messagepart.cpp --- a/mimetreeparser/src/messagepart.cpp +++ b/mimetreeparser/src/messagepart.cpp @@ -263,6 +263,18 @@ d->mIsImage = image; } +bool MessagePart::hasHeader(const char *header) const +{ + Q_UNUSED(header); + return false; +} + +KMime::Headers::Base * MimeTreeParser::MessagePart::header(const char* header) const +{ + Q_UNUSED(header); + return nullptr; +} + //-----MessagePartList---------------------- MessagePartList::MessagePartList(ObjectTreeParser *otp) : MessagePart(otp, QString()) @@ -1025,6 +1037,24 @@ return mFromAddress; } +bool SignedMessagePart::hasHeader(const char* header) const +{ + const auto extraContent = mOtp->nodeHelper()->decryptedNodeForContent(content()); + if (extraContent) { + return extraContent->hasHeader(header); + } + return false; +} + +KMime::Headers::Base * SignedMessagePart::header(const char* header) const +{ + const auto extraContent = mOtp->nodeHelper()->decryptedNodeForContent(content()); + if (extraContent) { + return extraContent->headerByType(header); + } + return nullptr; +} + //-----CryptMessageBlock--------------------- EncryptedMessagePart::EncryptedMessagePart(ObjectTreeParser *otp, const QString &text, const QGpgME::Protocol *cryptoProto, const QString &fromAddress, KMime::Content *node) : MessagePart(otp, text) @@ -1358,6 +1388,24 @@ return mDecryptRecipients; } +bool EncryptedMessagePart::hasHeader(const char* header) const +{ + const auto extraContent = mOtp->nodeHelper()->decryptedNodeForContent(content()); + if (extraContent) { + return extraContent->hasHeader(header); + } + return false; +} + +KMime::Headers::Base * EncryptedMessagePart::header(const char* header) const +{ + const auto extraContent = mOtp->nodeHelper()->decryptedNodeForContent(content()); + if (extraContent) { + return extraContent->headerByType(header); + } + return nullptr; +} + EncapsulatedRfc822MessagePart::EncapsulatedRfc822MessagePart(ObjectTreeParser *otp, KMime::Content *node, const KMime::Message::Ptr &message) : MessagePart(otp, QString()) , mMessage(message) diff --git a/mimetreeparser/src/nodehelper.h b/mimetreeparser/src/nodehelper.h --- a/mimetreeparser/src/nodehelper.h +++ b/mimetreeparser/src/nodehelper.h @@ -26,6 +26,7 @@ #include "mimetreeparser/enums.h" #include +#include #include #include @@ -36,6 +37,8 @@ namespace MimeTreeParser { class AttachmentTemporaryFilesDirs; +class MessagePart; +typedef QSharedPointer MessagePartPtr; namespace Interface { class BodyPartMemento; } @@ -78,10 +81,12 @@ */ static void magicSetType(KMime::Content *node, bool autoDecode = true); - bool hasMailHeader(const char *header, const KMime::Message *message) const; - KMime::Headers::Base *mailHeaderAsBase(const char *header, const KMime::Message *message) const; - KMime::Headers::Generics::AddressList *mailHeaderAsAddressList(const char *header, KMime::Message *message) const; - QDateTime dateHeader(KMime::Message *message) const; + void clearOverrideHeaders(); + void registerOverrideHeader(KMime::Content *message, MessagePartPtr); + bool hasMailHeader(const char *header, const KMime::Content *message) const; + KMime::Headers::Base const *mailHeaderAsBase(const char *header, const KMime::Content *message) const; + KMime::Headers::Generics::AddressList const *mailHeaderAsAddressList(const char *header, const KMime::Content *message) const; + QDateTime dateHeader(KMime::Content *message) const; /** Attach an extra node to an existing node */ void attachExtraContent(KMime::Content *topLevelNode, KMime::Content *content); @@ -259,6 +264,7 @@ QMap mPartMetaDatas; QMap > mExtraContents; AttachmentTemporaryFilesDirs *mAttachmentFilesDir = nullptr; + QMap> mHeaderOverwrite; friend class NodeHelperTest; }; diff --git a/mimetreeparser/src/nodehelper.cpp b/mimetreeparser/src/nodehelper.cpp --- a/mimetreeparser/src/nodehelper.cpp +++ b/mimetreeparser/src/nodehelper.cpp @@ -20,6 +20,7 @@ #include "nodehelper.h" #include "mimetreeparser_debug.h" #include "partmetadata.h" +#include "messagepart.h" #include "interfaces/bodypart.h" #include "temporaryfile/attachmenttemporaryfilesdirs.h" @@ -498,17 +499,24 @@ node->contentType()->setMimeType(mimetype.toLatin1()); } -bool NodeHelper::hasMailHeader(const char *header, const KMime::Message *message) const +bool NodeHelper::hasMailHeader(const char *header, const KMime::Content *message) const { return message->hasHeader(header); } -KMime::Headers::Base * NodeHelper::mailHeaderAsBase(const char *header, const KMime::Message *message) const +KMime::Headers::Base const * NodeHelper::mailHeaderAsBase(const char *header, const KMime::Content *message) const { + if (mHeaderOverwrite.contains(message)) { + foreach (const auto messagePart, mHeaderOverwrite.value(message)) { + if (messagePart->hasHeader(header)) { + return messagePart->header(header); // Found. + } + } + } return message->headerByType(header); } -KMime::Headers::Generics::AddressList * NodeHelper::mailHeaderAsAddressList(const char *header, KMime::Message *message) const +KMime::Headers::Generics::AddressList const * NodeHelper::mailHeaderAsAddressList(const char *header, const KMime::Content *message) const { /* works without this is maybe faster ? if(strcmp(header, "to") == 0) { @@ -521,15 +529,32 @@ return message->cc(); } */ auto addressList = new KMime::Headers::Generics::AddressList(); - const auto hrd = message->headerByType(header); + const auto hrd = mailHeaderAsBase(header, message); const QByteArray &data = hrd->as7BitString(false); addressList->from7BitString(data); return addressList; } -QDateTime NodeHelper::dateHeader(KMime::Message *message) const +void NodeHelper::clearOverrideHeaders() +{ + mHeaderOverwrite.clear(); +} + +void NodeHelper::registerOverrideHeader(KMime::Content *message, MessagePart::Ptr part) { - return message->date()->dateTime(); + if (!mHeaderOverwrite.contains(message)) { + mHeaderOverwrite[message] = QVector(); + } + mHeaderOverwrite[message].append(part); +} + +QDateTime NodeHelper::dateHeader(KMime::Content *message) const +{ + const auto dateHeader = mailHeaderAsBase("date", message); + if (dateHeader != nullptr) { + return static_cast(dateHeader)->dateTime(); + } + return QDateTime(); } void NodeHelper::setOverrideCodec(KMime::Content *node, const QTextCodec *codec) diff --git a/mimetreeparser/src/objecttreeparser.cpp b/mimetreeparser/src/objecttreeparser.cpp --- a/mimetreeparser/src/objecttreeparser.cpp +++ b/mimetreeparser/src/objecttreeparser.cpp @@ -194,6 +194,8 @@ // reset pending async jobs state (we'll rediscover pending jobs as we go) mHasPendingAsyncJobs = false; + mNodeHelper->clearOverrideHeaders(); + // reset "processed" flags for... if (onlyOneMimePart) { // ... this node and all descendants