From 23132b14e9bf3e1ec1f7720bbe28a2051508bd0f Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Wed, 7 Jun 2017 22:17:12 +0200 Subject: Encapsulated parts --- .../src/domain/mime/mimetreeparser/messagepart.cpp | 15 +++++ .../src/domain/mime/mimetreeparser/messagepart.h | 2 + .../mime/mimetreeparser/objecttreeparser.cpp | 38 +++++++---- .../domain/mime/mimetreeparser/objecttreeparser.h | 1 + .../mime/mimetreeparser/tests/interfacetest.cpp | 18 ++++++ .../src/domain/mime/mimetreeparser/textplain.cpp | 7 +- framework/src/domain/mime/partmodel.cpp | 74 +++++++++++++++++++--- framework/src/domain/mime/partmodel.h | 4 +- 8 files changed, 133 insertions(+), 26 deletions(-) (limited to 'framework/src/domain/mime') diff --git a/framework/src/domain/mime/mimetreeparser/messagepart.cpp b/framework/src/domain/mime/mimetreeparser/messagepart.cpp index 00abb003..df4dfeac 100644 --- a/framework/src/domain/mime/mimetreeparser/messagepart.cpp +++ b/framework/src/domain/mime/mimetreeparser/messagepart.cpp @@ -1292,3 +1292,18 @@ QString EncapsulatedRfc822MessagePart::text() const return renderInternalText(); } +QString EncapsulatedRfc822MessagePart::from() const +{ + if (auto from = mMessage->from(false)) { + return from->asUnicodeString(); + } + return {}; +} + +QDateTime EncapsulatedRfc822MessagePart::date() const +{ + if (auto date = mMessage->date(false)) { + return date->dateTime(); + } + return {}; +} diff --git a/framework/src/domain/mime/mimetreeparser/messagepart.h b/framework/src/domain/mime/mimetreeparser/messagepart.h index dd3a842f..7d266ac5 100644 --- a/framework/src/domain/mime/mimetreeparser/messagepart.h +++ b/framework/src/domain/mime/mimetreeparser/messagepart.h @@ -281,6 +281,8 @@ public: virtual ~EncapsulatedRfc822MessagePart(); QString text() const Q_DECL_OVERRIDE; + QString from() const; + QDateTime date() const; private: const KMime::Message::Ptr mMessage; diff --git a/framework/src/domain/mime/mimetreeparser/objecttreeparser.cpp b/framework/src/domain/mime/mimetreeparser/objecttreeparser.cpp index b137be87..8d8ddf12 100644 --- a/framework/src/domain/mime/mimetreeparser/objecttreeparser.cpp +++ b/framework/src/domain/mime/mimetreeparser/objecttreeparser.cpp @@ -190,20 +190,17 @@ KMime::Content *ObjectTreeParser::find(const std::function collect(MessagePart::Ptr start, const std::function &filter, const std::function &select) +static QVector collect(MessagePart::Ptr start, const std::function &evaluateSubtree, const std::function &select) { MessagePartPtr ptr = start.dynamicCast(); Q_ASSERT(ptr); - if (!filter(ptr)) { - return {}; - } - QVector list; - if (ptr) { + if (evaluateSubtree(ptr)) { for (const auto &p: ptr->subParts()) { - list << ::collect(p, filter, select); + list << ::collect(p, evaluateSubtree, select); } } + //Don't consider this part if we already selected a subpart if (list.isEmpty()) { if (select(ptr)) { @@ -213,14 +210,25 @@ static QVector collect(MessagePart::Ptr start, const std::func return list; } -QVector ObjectTreeParser::collectContentParts() +QVector ObjectTreeParser::collectContentParts() { - QVector contentParts = ::collect(mParsedPart, - [] (const MessagePartPtr &part) { - // return p->type() != "EncapsulatedPart"; + return collectContentParts(mParsedPart); +} + +QVector ObjectTreeParser::collectContentParts(MessagePart::Ptr start) +{ + return ::collect(start, + [start] (const MessagePartPtr &part) { + //Ignore the top-level + if (start.data() == part.data()) { + return true; + } + if (auto e = part.dynamicCast()) { + return false; + } return true; }, - [] (const MessagePartPtr &part) { + [start] (const MessagePartPtr &part) { if (dynamic_cast(part.data())) { return false; } else if (const auto text = dynamic_cast(part.data())) { @@ -233,6 +241,11 @@ QVector ObjectTreeParser::collectContentParts() return true; } else if (dynamic_cast(part.data())) { return true; + } else if (dynamic_cast(part.data())) { + if (start.data() == part.data()) { + return false; + } + return true; } else if (const auto enc = dynamic_cast(part.data())) { if (enc->error()) { return true; @@ -247,7 +260,6 @@ QVector ObjectTreeParser::collectContentParts() } return false; }); - return contentParts; } QVector ObjectTreeParser::collectAttachmentParts() diff --git a/framework/src/domain/mime/mimetreeparser/objecttreeparser.h b/framework/src/domain/mime/mimetreeparser/objecttreeparser.h index 398cbcd7..d219a52b 100644 --- a/framework/src/domain/mime/mimetreeparser/objecttreeparser.h +++ b/framework/src/domain/mime/mimetreeparser/objecttreeparser.h @@ -285,6 +285,7 @@ public: MessagePartPtr parsedPart() const; KMime::Content *find(const std::function &select); QVector collectContentParts(); + QVector collectContentParts(MessagePart::Ptr start); QVector collectAttachmentParts(); void decryptParts(); void importCertificates(); diff --git a/framework/src/domain/mime/mimetreeparser/tests/interfacetest.cpp b/framework/src/domain/mime/mimetreeparser/tests/interfacetest.cpp index f9b557c9..b9fdd356 100644 --- a/framework/src/domain/mime/mimetreeparser/tests/interfacetest.cpp +++ b/framework/src/domain/mime/mimetreeparser/tests/interfacetest.cpp @@ -319,6 +319,24 @@ private slots: QVERIFY(bool(part)); QVERIFY(part->error()); } + + void testEncapsulated() + { + MimeTreeParser::ObjectTreeParser otp; + otp.parseObjectTree(readMailFromFile("encapsulated-with-attachment.mbox")); + otp.decryptParts(); + auto partList = otp.collectContentParts(); + QCOMPARE(partList.size(), 2); + auto part = partList[1].dynamicCast(); + QVERIFY(bool(part)); + QCOMPARE(part->from(), QLatin1String("Thomas McGuire ")); + QCOMPARE(part->date().toString(), QLatin1String("Wed Aug 5 10:57:58 2009 GMT+0200")); + auto subPartList = otp.collectContentParts(part); + QCOMPARE(subPartList.size(), 1); + qWarning() << subPartList[0]->metaObject()->className(); + auto subPart = subPartList[0].dynamicCast(); + QVERIFY(bool(subPart)); + } }; QTEST_GUILESS_MAIN(InterfaceTest) diff --git a/framework/src/domain/mime/mimetreeparser/textplain.cpp b/framework/src/domain/mime/mimetreeparser/textplain.cpp index 32201a8a..d62cb2cf 100644 --- a/framework/src/domain/mime/mimetreeparser/textplain.cpp +++ b/framework/src/domain/mime/mimetreeparser/textplain.cpp @@ -41,13 +41,12 @@ const Interface::BodyPartFormatter *TextPlainBodyPartFormatter::create() MessagePart::Ptr TextPlainBodyPartFormatter::process(Interface::BodyPart &part) const { KMime::Content *node = part.content(); - const bool isFirstTextPart = (node->topLevel()->textContent() == node); TextMessagePart::Ptr mp; - if (isFirstTextPart) { - mp = TextMessagePart::Ptr(new TextMessagePart(part.objectTreeParser(), node)); + if (KMime::isAttachment(node)) { + mp = AttachmentMessagePart::Ptr(new AttachmentMessagePart(part.objectTreeParser(), node)); } else { - mp = TextMessagePart::Ptr(new AttachmentMessagePart(part.objectTreeParser(), node)); + mp = TextMessagePart::Ptr(new TextMessagePart(part.objectTreeParser(), node)); } return mp; diff --git a/framework/src/domain/mime/partmodel.cpp b/framework/src/domain/mime/partmodel.cpp index ea3e90e1..6ee3d46f 100644 --- a/framework/src/domain/mime/partmodel.cpp +++ b/framework/src/domain/mime/partmodel.cpp @@ -35,6 +35,8 @@ public: void createTree(); PartModel *q; QVector mParts; + QHash> mEncapsulatedParts; + QHash mParents; std::shared_ptr mParser; }; @@ -44,6 +46,14 @@ PartModelPrivate::PartModelPrivate(PartModel *q_ptr, const std::shared_ptrcollectContentParts(); qWarning() << "Collected content parts: " << mParts.size(); + for (auto p : mParts) { + if (auto e = p.dynamicCast()) { + mEncapsulatedParts[e.data()] = mParser->collectContentParts(e); + for (auto subPart : mEncapsulatedParts[e.data()]) { + mParents[subPart.data()] = e.data(); + } + } + } } PartModelPrivate::~PartModelPrivate() @@ -71,6 +81,8 @@ QHash PartModel::roleNames() const roles[EncryptionErrorType] = "errorType"; roles[EncryptionErrorString] = "errorString"; roles[IsErrorRole] = "error"; + roles[SenderRole] = "sender"; + roles[DateRole] = "date"; return roles; } @@ -79,6 +91,15 @@ QModelIndex PartModel::index(int row, int column, const QModelIndex &parent) con if (row < 0 || column != 0) { return QModelIndex(); } + if (parent.isValid()) { + if (auto e = dynamic_cast(static_cast(parent.internalPointer()))) { + const auto parts = d->mEncapsulatedParts[e]; + if (row < parts.size()) { + return createIndex(row, column, parts.at(row).data()); + } + } + return QModelIndex(); + } if (row < d->mParts.size()) { return createIndex(row, column, d->mParts.at(row).data()); } @@ -88,26 +109,35 @@ QModelIndex PartModel::index(int row, int column, const QModelIndex &parent) con QVariant PartModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) { - switch (role) { - case Qt::DisplayRole: - return QString("root"); - case IsEmbededRole: - return false; - } return QVariant(); } if (index.internalPointer()) { const auto messagePart = static_cast(index.internalPointer()); - qWarning() << "Found message part " << messagePart->metaObject()->className() << messagePart->partMetaData()->status << messagePart->error(); + // qWarning() << "Found message part " << messagePart->metaObject()->className() << messagePart->partMetaData()->status << messagePart->error(); Q_ASSERT(messagePart); switch(role) { case Qt::DisplayRole: return QStringLiteral("Content%1"); + case SenderRole: { + if (auto e = dynamic_cast(messagePart)) { + return e->from(); + } + return {}; + } + case DateRole: { + if (auto e = dynamic_cast(messagePart)) { + return e->date(); + } + return {}; + } case TypeRole: { if (messagePart->error()) { return "error"; } + if (dynamic_cast(messagePart)) { + return "encapsulated"; + } //For simple html we don't need a browser auto complexHtml = [&] { if (messagePart->isHtml()) { @@ -165,11 +195,39 @@ QVariant PartModel::data(const QModelIndex &index, int role) const QModelIndex PartModel::parent(const QModelIndex &index) const { - return QModelIndex(); + if (index.isValid()) { + if (auto e = static_cast(index.internalPointer())) { + for (const auto &p : d->mParts) { + if (p.data() == e) { + return QModelIndex(); + } + } + const auto parentPart = d->mParents[e]; + Q_ASSERT(parentPart); + int row = 0; + const auto parts = d->mEncapsulatedParts[parentPart]; + for (const auto &p : parts) { + if (p.data() == e) { + break; + } + row++; + } + return createIndex(row, 0, parentPart); + } + return {}; + } + return {}; } int PartModel::rowCount(const QModelIndex &parent) const { + if (parent.isValid()) { + if (auto e = dynamic_cast(static_cast(parent.internalPointer()))) { + const auto parts = d->mEncapsulatedParts[e]; + return parts.size(); + } + return 0; + } return d->mParts.count(); } diff --git a/framework/src/domain/mime/partmodel.h b/framework/src/domain/mime/partmodel.h index efbd154b..a0e14b61 100644 --- a/framework/src/domain/mime/partmodel.h +++ b/framework/src/domain/mime/partmodel.h @@ -47,7 +47,9 @@ public: IsErrorRole, SecurityLevelRole, EncryptionErrorType, - EncryptionErrorString + EncryptionErrorString, + SenderRole, + DateRole }; QHash roleNames() const Q_DECL_OVERRIDE; -- cgit v1.2.3