From 2d2218157e9c6fbf31dccdb13c9d69df3c804a44 Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Mon, 7 May 2018 13:04:51 +0200 Subject: Extract attachments from multipart/related So we can offer them for download even if displayed inline. This is necessary for some attachments to show up from apple mail. --- .../mime/mimetreeparser/bodypartformatter.cpp | 9 ++++ .../domain/mime/mimetreeparser/bodypartformatter.h | 1 + .../mime/mimetreeparser/bodypartformatter_impl.cpp | 20 +++++--- .../mime/mimetreeparser/objecttreeparser.cpp | 53 +++++++++++++++------- .../domain/mime/mimetreeparser/objecttreeparser.h | 4 +- .../mimetreeparser/tests/mimetreeparsertest.cpp | 2 +- 6 files changed, 64 insertions(+), 25 deletions(-) diff --git a/framework/src/domain/mime/mimetreeparser/bodypartformatter.cpp b/framework/src/domain/mime/mimetreeparser/bodypartformatter.cpp index 232973df..2753bb4b 100644 --- a/framework/src/domain/mime/mimetreeparser/bodypartformatter.cpp +++ b/framework/src/domain/mime/mimetreeparser/bodypartformatter.cpp @@ -46,5 +46,14 @@ MessagePart::Ptr BodyPartFormatter::process(BodyPart &) const { return {}; } + +QVector BodyPartFormatter::processList(BodyPart &part) const +{ + if (auto p = process(part)) { + return {p}; + } + return {}; +} + } } diff --git a/framework/src/domain/mime/mimetreeparser/bodypartformatter.h b/framework/src/domain/mime/mimetreeparser/bodypartformatter.h index 7e1fc74e..18428443 100644 --- a/framework/src/domain/mime/mimetreeparser/bodypartformatter.h +++ b/framework/src/domain/mime/mimetreeparser/bodypartformatter.h @@ -63,6 +63,7 @@ public: enum Result { Ok, NeedContent, AsIcon, Failed }; virtual MessagePart::Ptr process(BodyPart &part) const; + virtual QVector processList(Interface::BodyPart &part) const; }; /** diff --git a/framework/src/domain/mime/mimetreeparser/bodypartformatter_impl.cpp b/framework/src/domain/mime/mimetreeparser/bodypartformatter_impl.cpp index 882dfc05..d4dfd647 100644 --- a/framework/src/domain/mime/mimetreeparser/bodypartformatter_impl.cpp +++ b/framework/src/domain/mime/mimetreeparser/bodypartformatter_impl.cpp @@ -115,14 +115,25 @@ const HeadersBodyPartFormatter *HeadersBodyPartFormatter::self = nullptr; class MultiPartRelatedBodyPartFormatter: public MimeTreeParser::Interface::BodyPartFormatter { static const MultiPartRelatedBodyPartFormatter *self; public: - MessagePart::Ptr process(Interface::BodyPart &part) const Q_DECL_OVERRIDE { + + QVector processList(Interface::BodyPart &part) const Q_DECL_OVERRIDE { if (part.content()->contents().isEmpty()) { - return MessagePart::Ptr(); + return {}; } //We rely on the order of the parts. //Theoretically there could also be a Start parameter which would break this.. //https://tools.ietf.org/html/rfc2387#section-4 - return MimeMessagePart::Ptr(new MimeMessagePart(part.objectTreeParser(), part.content()->contents().at(0), true)); + + //We want to display attachments even if displayed inline. + QVector list; + list.append(MimeMessagePart::Ptr(new MimeMessagePart(part.objectTreeParser(), part.content()->contents().at(0), true))); + for (int i = 1; i < part.content()->contents().size(); i++) { + auto p = part.content()->contents().at(i); + if (KMime::isAttachment(p)) { + list.append(MimeMessagePart::Ptr(new MimeMessagePart(part.objectTreeParser(), p, true))); + } + } + return list; } static const MimeTreeParser::Interface::BodyPartFormatter *create() { @@ -142,9 +153,6 @@ public: if (part.content()->contents().isEmpty()) { return {}; } - //FIXME sometimes we get attachments in here as well - //TODO look for attachment parts anyways - // normal treatment of the parts in the mp/mixed container return MimeMessagePart::Ptr(new MimeMessagePart(part.objectTreeParser(), part.content()->contents().at(0), false)); } diff --git a/framework/src/domain/mime/mimetreeparser/objecttreeparser.cpp b/framework/src/domain/mime/mimetreeparser/objecttreeparser.cpp index fced7997..f44816b0 100644 --- a/framework/src/domain/mime/mimetreeparser/objecttreeparser.cpp +++ b/framework/src/domain/mime/mimetreeparser/objecttreeparser.cpp @@ -399,7 +399,7 @@ MessagePartPtr ObjectTreeParser::parsedPart() const return mParsedPart; } -MessagePartPtr ObjectTreeParser::processType(KMime::Content *node, const QByteArray &mediaType, const QByteArray &subType) +QVector ObjectTreeParser::processType(KMime::Content *node, const QByteArray &mediaType, const QByteArray &subType) { static MimeTreeParser::BodyPartFormatterBaseFactory factory; const auto sub = factory.subtypeRegistry(mediaType.constData()); @@ -410,8 +410,9 @@ MessagePartPtr ObjectTreeParser::processType(KMime::Content *node, const QByteAr continue; } PartNodeBodyPart part(this, mTopLevelContent, node, mNodeHelper); - if (const MessagePart::Ptr result = formatter->process(part)) { - return result; + const auto list = formatter->processList(part); + if (!list.isEmpty()) { + return list; } } return {}; @@ -458,16 +459,35 @@ MessagePart::Ptr ObjectTreeParser::parseObjectTreeInternal(KMime::Content *node, subType = node->contentType()->subType(); } - //Try the specific type handler - if (auto mp = processType(node, mediaType, subType)) { - parsedPart->appendSubPart(mp); - //Fallback to the generic handler - } else if (auto mp = processType(node, mediaType, "*")) { - parsedPart->appendSubPart(mp); - //Fallback to the default handler - } else if (auto mp = defaultHandling(node)) { - parsedPart->appendSubPart(mp); + auto mp = [&] { + //Try the specific type handler + { + auto list = processType(node, mediaType, subType); + if (!list.isEmpty()) { + return list; + } + } + //Fallback to the generic handler + { + auto list = processType(node, mediaType, "*"); + if (!list.isEmpty()) { + return list; + } + } + //Fallback to the default handler + { + auto list = defaultHandling(node); + if (!list.isEmpty()) { + return list; + } + } + return QVector{}; + }(); + + for (const auto p : mp) { + parsedPart->appendSubPart(p); } + mNodeHelper->setNodeProcessed(node, false); if (onlyOneMimePart) { @@ -478,19 +498,20 @@ MessagePart::Ptr ObjectTreeParser::parseObjectTreeInternal(KMime::Content *node, return parsedPart; } -MessagePart::Ptr ObjectTreeParser::defaultHandling(KMime::Content *node) +QVector ObjectTreeParser::defaultHandling(KMime::Content *node) { if (node->contentType()->mimeType() == QByteArrayLiteral("application/octet-stream") && (node->contentType()->name().endsWith(QLatin1String("p7m")) || node->contentType()->name().endsWith(QLatin1String("p7s")) || node->contentType()->name().endsWith(QLatin1String("p7c")) )) { - if (auto mp = processType(node, "application", "pkcs7-mime")) { - return mp; + auto list = processType(node, "application", "pkcs7-mime"); + if (!list.isEmpty()) { + return list; } } - return AttachmentMessagePart::Ptr(new AttachmentMessagePart(this, node)); + return {AttachmentMessagePart::Ptr(new AttachmentMessagePart(this, node))}; } const QTextCodec *ObjectTreeParser::codecFor(KMime::Content *node) const diff --git a/framework/src/domain/mime/mimetreeparser/objecttreeparser.h b/framework/src/domain/mime/mimetreeparser/objecttreeparser.h index 1795c79c..1109d859 100644 --- a/framework/src/domain/mime/mimetreeparser/objecttreeparser.h +++ b/framework/src/domain/mime/mimetreeparser/objecttreeparser.h @@ -282,9 +282,9 @@ private: * top-level content. */ MessagePartPtr parseObjectTreeInternal(KMime::Content *node, bool mOnlyOneMimePart); - MessagePartPtr processType(KMime::Content *node, const QByteArray &mediaType, const QByteArray &subType); + QVector processType(KMime::Content *node, const QByteArray &mediaType, const QByteArray &subType); - MessagePartPtr defaultHandling(KMime::Content *node); + QVector defaultHandling(KMime::Content *node); private: diff --git a/framework/src/domain/mime/mimetreeparser/tests/mimetreeparsertest.cpp b/framework/src/domain/mime/mimetreeparser/tests/mimetreeparsertest.cpp index 38857d58..97c79fe7 100644 --- a/framework/src/domain/mime/mimetreeparser/tests/mimetreeparsertest.cpp +++ b/framework/src/domain/mime/mimetreeparser/tests/mimetreeparsertest.cpp @@ -261,7 +261,7 @@ private slots: QVERIFY(bool(part)); QCOMPARE(part->encryptions().size(), 0); QCOMPARE(part->signatures().size(), 0); - QCOMPARE(otp.collectAttachmentParts().size(), 0); + QCOMPARE(otp.collectAttachmentParts().size(), 1); } void testAttachmentPart() -- cgit v1.2.3