From 4b1798f0cdf87361869e7cf2b341acacd056c410 Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Wed, 5 Apr 2017 15:04:00 +0200 Subject: Moved cpp code into src directory --- framework/src/domain/mimetreeparser/interface.cpp | 1179 +++++++++++++++++++++ 1 file changed, 1179 insertions(+) create mode 100644 framework/src/domain/mimetreeparser/interface.cpp (limited to 'framework/src/domain/mimetreeparser/interface.cpp') diff --git a/framework/src/domain/mimetreeparser/interface.cpp b/framework/src/domain/mimetreeparser/interface.cpp new file mode 100644 index 00000000..663a2013 --- /dev/null +++ b/framework/src/domain/mimetreeparser/interface.cpp @@ -0,0 +1,1179 @@ +/* + Copyright (c) 2016 Sandro Knauß + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library 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 Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#include "interface.h" +#include "interface_p.h" + +#include "stringhtmlwriter.h" +#include "objecttreesource.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +class MailMimePrivate +{ +public: + MailMimePrivate(MailMime *p); + + MailMime *q; + KMime::Content *mNode; + std::shared_ptr parent; +}; + +MailMimePrivate::MailMimePrivate(MailMime* p) + : q(p) + , mNode(nullptr) + , parent(nullptr) +{ +} + + +MailMime::MailMime() + : d(std::unique_ptr(new MailMimePrivate(this))) +{ +} + +QByteArray MailMime::cid() const +{ + if (!d->mNode || !d->mNode->contentID()) { + return QByteArray(); + } + return d->mNode->contentID()->identifier(); +} + +QByteArray MailMime::charset() const +{ + if(!d->mNode || !d->mNode->contentType(false)) { + return QByteArray(); + } + if (d->mNode->contentType(false)) { + return d->mNode->contentType(false)->charset(); + } + return d->mNode->defaultCharset(); +} + +bool MailMime::isFirstTextPart() const +{ + if (!d->mNode || !d->mNode->topLevel()) { + return false; + } + return (d->mNode->topLevel()->textContent() == d->mNode); +} + +bool MailMime::isFirstPart() const +{ + if (!d->mNode || !d->mNode->parent()) { + return false; + } + return (d->mNode->parent()->contents().first() == d->mNode); +} + +bool MailMime::isTopLevelPart() const +{ + if (!d->mNode) { + return false; + } + return (d->mNode->topLevel() == d->mNode); +} + +MailMime::Disposition MailMime::disposition() const +{ + if (!d->mNode) { + return Invalid; + } + const auto cd = d->mNode->contentDisposition(false); + if (!cd) { + return Invalid; + } + switch (cd->disposition()){ + case KMime::Headers::CDinline: + return Inline; + case KMime::Headers::CDattachment: + return Attachment; + default: + return Invalid; + } +} + +QString MailMime::filename() const +{ + if (!d->mNode) { + return QString(); + } + const auto cd = d->mNode->contentDisposition(false); + if (!cd) { + return QString(); + } + return cd->filename(); +} + +QMimeType MailMime::mimetype() const +{ + if (!d->mNode) { + return QMimeType(); + } + + const auto ct = d->mNode->contentType(false); + if (!ct) { + return QMimeType(); + } + + QMimeDatabase mimeDb; + return mimeDb.mimeTypeForName(ct->mimeType()); +} + +MailMime::Ptr MailMime::parent() const +{ + if (!d->parent) { + d->parent = std::shared_ptr(new MailMime()); + d->parent->d->mNode = d->mNode->parent(); + } + return d->parent; +} + +QByteArray MailMime::decodedContent() const +{ + if (!d->mNode) { + return QByteArray(); + } + return d->mNode->decodedContent(); +} + +class KeyPrivate +{ +public: + Key *q; + GpgME::Key mKey; + QByteArray mKeyID; +}; + +Key::Key() + :d(std::unique_ptr(new KeyPrivate)) +{ + d->q = this; +} + + +Key::Key(KeyPrivate *d_ptr) + :d(std::unique_ptr(d_ptr)) +{ + d->q = this; +} + +Key::~Key() +{ + +} + +QString Key::keyid() const +{ + if (!d->mKey.isNull()) { + return d->mKey.keyID(); + } + + return d->mKeyID; +} + +QString Key::name() const +{ + //FIXME: is this the correct way to get the primary UID? + if (!d->mKey.isNull()) { + return d->mKey.userID(0).name(); + } + + return QString(); +} + +QString Key::email() const +{ + if (!d->mKey.isNull()) { + return d->mKey.userID(0).email(); + } + return QString(); +} + +QString Key::comment() const +{ + if (!d->mKey.isNull()) { + return d->mKey.userID(0).comment(); + } + return QString(); +} + +class SignaturePrivate +{ +public: + Signature *q; + GpgME::Signature mSignature; + Key::Ptr mKey; +}; + +Signature::Signature() + :d(std::unique_ptr(new SignaturePrivate)) +{ + d->q = this; +} + + +Signature::Signature(SignaturePrivate *d_ptr) + :d(std::unique_ptr(d_ptr)) +{ + d->q = this; +} + +Signature::~Signature() +{ + +} + +QDateTime Signature::creationDateTime() const +{ + QDateTime dt; + dt.setTime_t(d->mSignature.creationTime()); + return dt; +} + +QDateTime Signature::expirationDateTime() const +{ + QDateTime dt; + dt.setTime_t(d->mSignature.expirationTime()); + return dt; +} + +bool Signature::neverExpires() const +{ + return d->mSignature.neverExpires(); +} + +Key::Ptr Signature::key() const +{ + return d->mKey; +} + +class EncryptionPrivate +{ +public: + Encryption *q; + std::vector mRecipients; + Encryption::ErrorType mErrorType; + QString mErrorString; +}; + +Encryption::Encryption(EncryptionPrivate *d_ptr) + :d(std::unique_ptr(d_ptr)) +{ + d->q = this; +} + +Encryption::Encryption() + :d(std::unique_ptr(new EncryptionPrivate)) +{ + d->q = this; + d->mErrorType = Encryption::NoError; +} + +Encryption::~Encryption() +{ + +} + +std::vector Encryption::recipients() const +{ + return d->mRecipients; +} + +QString Encryption::errorString() +{ + return d->mErrorString; +} + +Encryption::ErrorType Encryption::errorType() +{ + return d->mErrorType; +} + + +class PartPrivate +{ +public: + PartPrivate(Part *part); + void appendSubPart(Part::Ptr subpart); + + QVector subParts(); + + Part *parent() const; + + const MailMime::Ptr &mailMime() const; + void createMailMime(const MimeTreeParser::MimeMessagePart::Ptr &part); + void createMailMime(const MimeTreeParser::TextMessagePart::Ptr &part); + void createMailMime(const MimeTreeParser::AlternativeMessagePart::Ptr &part); + void createMailMime(const MimeTreeParser::HtmlMessagePart::Ptr &part); + void createMailMime(const MimeTreeParser::EncryptedMessagePart::Ptr &part); + + static Encryption::Ptr createEncryption(const MimeTreeParser::EncryptedMessagePart::Ptr& part); + void appendEncryption(const MimeTreeParser::EncryptedMessagePart::Ptr &part); + static QVector createSignature(const MimeTreeParser::SignedMessagePart::Ptr& part); + void appendSignature(const MimeTreeParser::SignedMessagePart::Ptr &part); + + void setSignatures(const QVector &sigs); + void setEncryptions(const QVector &encs); + + const QVector &encryptions() const; + const QVector &signatures() const; +private: + Part *q; + Part *mParent; + QVector mSubParts; + QVector mEncryptions; + QVector mSignatures; + MailMime::Ptr mMailMime; +}; + +PartPrivate::PartPrivate(Part* part) + : q(part) + , mParent(Q_NULLPTR) +{ + +} + +void PartPrivate::createMailMime(const MimeTreeParser::HtmlMessagePart::Ptr& part) +{ + mMailMime = MailMime::Ptr(new MailMime); + mMailMime->d->mNode = part->mNode; +} + +void PartPrivate::createMailMime(const MimeTreeParser::AlternativeMessagePart::Ptr& part) +{ + mMailMime = MailMime::Ptr(new MailMime); + mMailMime->d->mNode = part->mNode; +} + +void PartPrivate::createMailMime(const MimeTreeParser::TextMessagePart::Ptr& part) +{ + mMailMime = MailMime::Ptr(new MailMime); + mMailMime->d->mNode = part->mNode; +} + +void PartPrivate::createMailMime(const MimeTreeParser::MimeMessagePart::Ptr& part) +{ + mMailMime = MailMime::Ptr(new MailMime); + mMailMime->d->mNode = part->mNode; +} + +void PartPrivate::createMailMime(const MimeTreeParser::EncryptedMessagePart::Ptr& part) +{ + mMailMime = MailMime::Ptr(new MailMime); + mMailMime->d->mNode = part->mNode; +} + +void PartPrivate::appendSubPart(Part::Ptr subpart) +{ + subpart->d->mParent = q; + mSubParts.append(subpart); +} + +Encryption::Ptr PartPrivate::createEncryption(const MimeTreeParser::EncryptedMessagePart::Ptr& part) +{ + QGpgME::KeyListJob *job = part->mCryptoProto->keyListJob(false); // local, no sigs + if (!job) { + qWarning() << "The Crypto backend does not support listing keys. "; + return Encryption::Ptr(); + } + + auto encpriv = new EncryptionPrivate(); + if (part->passphraseError()) { + encpriv->mErrorType = Encryption::PassphraseError; + encpriv->mErrorString = part->mMetaData.errorText; + } else if (part->isEncrypted() && !part->isDecryptable()) { + encpriv->mErrorType = Encryption::KeyMissing; + encpriv->mErrorString = part->mMetaData.errorText; + } else if (!part->isEncrypted() && !part->isDecryptable()) { + encpriv->mErrorType = Encryption::UnknownError; + encpriv->mErrorString = part->mMetaData.errorText; + } else { + encpriv->mErrorType = Encryption::NoError; + } + + foreach(const auto &recipient, part->mDecryptRecipients) { + std::vector found_keys; + const auto &keyid = recipient.keyID(); + GpgME::KeyListResult res = job->exec(QStringList(QLatin1String(keyid)), false, found_keys); + if (res.error()) { + qWarning() << "Error while searching key for Fingerprint: " << keyid; + continue; + } + if (found_keys.size() > 1) { + // Should not Happen + qWarning() << "Oops: Found more then one Key for Fingerprint: " << keyid; + } + if (found_keys.size() != 1) { + // Should not Happen at this point + qWarning() << "Oops: Found no Key for Fingerprint: " << keyid; + auto keypriv = new KeyPrivate; + keypriv->mKeyID = keyid; + encpriv->mRecipients.push_back(Key::Ptr(new Key(keypriv))); + } else { + auto key = found_keys[0]; + auto keypriv = new KeyPrivate; + keypriv->mKey = key; + encpriv->mRecipients.push_back(Key::Ptr(new Key(keypriv))); + } + } + return Encryption::Ptr(new Encryption(encpriv)); +} + +void PartPrivate::appendEncryption(const MimeTreeParser::EncryptedMessagePart::Ptr& part) +{ + mEncryptions.append(createEncryption(part)); +} + +void PartPrivate::setEncryptions(const QVector< Encryption::Ptr >& encs) +{ + mEncryptions = encs; +} + +QVector PartPrivate::createSignature(const MimeTreeParser::SignedMessagePart::Ptr& part) +{ + QVector sigs; + QGpgME::KeyListJob *job = part->mCryptoProto->keyListJob(false); // local, no sigs + if (!job) { + qWarning() << "The Crypto backend does not support listing keys. "; + return sigs; + } + + foreach(const auto &sig, part->mSignatures) { + auto sigpriv = new SignaturePrivate(); + sigpriv->mSignature = sig; + auto signature = std::make_shared(sigpriv); + sigs.append(signature); + + std::vector found_keys; + const auto &keyid = sig.fingerprint(); + GpgME::KeyListResult res = job->exec(QStringList(QLatin1String(keyid)), false, found_keys); + if (res.error()) { + qWarning() << "Error while searching key for Fingerprint: " << keyid; + continue; + } + if (found_keys.size() > 1) { + // Should not Happen + qWarning() << "Oops: Found more then one Key for Fingerprint: " << keyid; + continue; + } + if (found_keys.size() != 1) { + // Should not Happen at this point + qWarning() << "Oops: Found no Key for Fingerprint: " << keyid; + continue; + } else { + auto key = found_keys[0]; + auto keypriv = new KeyPrivate; + keypriv->mKey = key; + sigpriv->mKey = Key::Ptr(new Key(keypriv)); + } + } + return sigs; +} + +void PartPrivate::appendSignature(const MimeTreeParser::SignedMessagePart::Ptr& part) +{ + mSignatures.append(createSignature(part)); +} + + +void PartPrivate::setSignatures(const QVector< Signature::Ptr >& sigs) +{ + mSignatures = sigs; +} + +Part *PartPrivate::parent() const +{ + return mParent; +} + +QVector< Part::Ptr > PartPrivate::subParts() +{ + return mSubParts; +} + +const MailMime::Ptr& PartPrivate::mailMime() const +{ + return mMailMime; +} + +const QVector< Encryption::Ptr >& PartPrivate::encryptions() const +{ + return mEncryptions; +} + +const QVector< Signature::Ptr >& PartPrivate::signatures() const +{ + return mSignatures; +} + +Part::Part() + : d(std::unique_ptr(new PartPrivate(this))) +{ + +} + +bool Part::hasSubParts() const +{ + return !subParts().isEmpty(); +} + +QVector Part::subParts() const +{ + return d->subParts(); +} + +QByteArray Part::type() const +{ + return "Part"; +} + +QVector Part::availableContents() const +{ + return QVector(); +} + +QVector Part::content() const +{ + return content(availableContents().first()); +} + +QVector Part::content(const QByteArray& ct) const +{ + return QVector(); +} + +QVector Part::encryptions() const +{ + auto ret = d->encryptions(); + auto parent = d->parent(); + if (parent) { + ret.append(parent->encryptions()); + } + return ret; +} + +QVector Part::signatures() const +{ + auto ret = d->signatures(); + auto parent = d->parent(); + if (parent) { + ret.append(parent->signatures()); + } + return ret; +} + +MailMime::Ptr Part::mailMime() const +{ + return d->mailMime(); +} + +Part *Part::parent() const +{ + return d->parent(); +} + +class ContentPrivate +{ +public: + QByteArray mContent; + QByteArray mCodec; + Part *mParent; + Content *q; + MailMime::Ptr mMailMime; + QVector mEncryptions; + QVector mSignatures; + void appendSignature(const MimeTreeParser::SignedMessagePart::Ptr &sig); + void appendEncryption(const MimeTreeParser::EncryptedMessagePart::Ptr &enc); +}; + +void ContentPrivate::appendEncryption(const MimeTreeParser::EncryptedMessagePart::Ptr& enc) +{ + mEncryptions.append(PartPrivate::createEncryption(enc)); +} + +void ContentPrivate::appendSignature(const MimeTreeParser::SignedMessagePart::Ptr& sig) +{ + mSignatures.append(PartPrivate::createSignature(sig)); +} + + +Content::Content(const QByteArray& content, Part *parent) + : d(std::unique_ptr(new ContentPrivate)) +{ + d->q = this; + d->mContent = content; + d->mCodec = "utf-8"; + d->mParent = parent; +} + +Content::Content(ContentPrivate* d_ptr) + : d(std::unique_ptr(d_ptr)) +{ + d->q = this; +} + +Content::~Content() +{ +} + +QVector Content::encryptions() const +{ + auto ret = d->mEncryptions; + if (d->mParent) { + ret.append(d->mParent->encryptions()); + } + return ret; +} + +QVector Content::signatures() const +{ + auto ret = d->mSignatures; + if (d->mParent) { + ret.append(d->mParent->signatures()); + } + return ret; +} + +QByteArray Content::content() const +{ + return d->mContent; +} + +QByteArray Content::charset() const +{ + return d->mCodec; +} + +QString Content::encodedContent() const +{ + return QString::fromUtf8(content()); + // TODO: should set "raw" content not the already utf8 encoded content + // return encodedContent(charset()); +} + +QString Content::encodedContent(const QByteArray &charset) const +{ + QTextCodec *codec = QTextCodec::codecForName(charset); + return codec->toUnicode(content()); +} + +QByteArray Content::type() const +{ + return "Content"; +} + +MailMime::Ptr Content::mailMime() const +{ + if (d->mMailMime) { + return d->mMailMime; + } else { + return d->mParent->mailMime(); + } +} + +Part *Content::parent() const +{ + return d->mParent; +} + +HtmlContent::HtmlContent(const QByteArray& content, Part* parent) + : Content(content, parent) +{ + +} + +QByteArray HtmlContent::type() const +{ + return "HtmlContent"; +} + +PlainTextContent::PlainTextContent(const QByteArray& content, Part* parent) + : Content(content, parent) +{ + +} + +PlainTextContent::PlainTextContent(ContentPrivate* d_ptr) + : Content(d_ptr) +{ + +} + +HtmlContent::HtmlContent(ContentPrivate* d_ptr) + : Content(d_ptr) +{ + +} + + +QByteArray PlainTextContent::type() const +{ + return "PlainTextContent"; +} + +class AlternativePartPrivate +{ +public: + void fillFrom(MimeTreeParser::AlternativeMessagePart::Ptr part); + + QVector content(const QByteArray &ct) const; + + AlternativePart *q; + + QVector types() const; + +private: + QMap> mContent; + QVector mTypes; +}; + +void AlternativePartPrivate::fillFrom(MimeTreeParser::AlternativeMessagePart::Ptr part) +{ + mTypes = QVector() << "html" << "plaintext"; + + Content::Ptr content = std::make_shared(part->htmlContent().toLocal8Bit(), q); + mContent["html"].append(content); + content = std::make_shared(part->plaintextContent().toLocal8Bit(), q); + mContent["plaintext"].append(content); + q->reachParentD()->createMailMime(part); +} + +QVector AlternativePartPrivate::types() const +{ + return mTypes; +} + +QVector AlternativePartPrivate::content(const QByteArray& ct) const +{ + return mContent[ct]; +} + +AlternativePart::AlternativePart() + : d(std::unique_ptr(new AlternativePartPrivate)) +{ + d->q = this; +} + +AlternativePart::~AlternativePart() +{ + +} + +QByteArray AlternativePart::type() const +{ + return "AlternativePart"; +} + +QVector AlternativePart::availableContents() const +{ + return d->types(); +} + +QVector AlternativePart::content(const QByteArray& ct) const +{ + return d->content(ct); +} + +PartPrivate* AlternativePart::reachParentD() const +{ + return Part::d.get(); +} + +class SinglePartPrivate +{ +public: + void fillFrom(const MimeTreeParser::TextMessagePart::Ptr &part); + void fillFrom(const MimeTreeParser::HtmlMessagePart::Ptr &part); + void fillFrom(const MimeTreeParser::AttachmentMessagePart::Ptr &part); + void createEncryptionFailBlock(const MimeTreeParser::EncryptedMessagePart::Ptr &part); + SinglePart *q; + + QVector mContent; + QByteArray mType; +}; + +void SinglePartPrivate::fillFrom(const MimeTreeParser::TextMessagePart::Ptr &part) +{ + mType = "plaintext"; + mContent.clear(); + foreach (const auto &mp, part->subParts()) { + auto d_ptr = new ContentPrivate; + d_ptr->mContent = mp->text().toUtf8(); // TODO: should set "raw" content not the already utf8 encoded content + d_ptr->mParent = q; + const auto enc = mp.dynamicCast(); + auto sig = mp.dynamicCast(); + if (enc) { + d_ptr->appendEncryption(enc); + if (!enc->isDecryptable()) { + d_ptr->mContent = QByteArray(); + } + const auto s = enc->subParts(); + if (s.size() == 1) { + sig = s[0].dynamicCast(); + } + } + if (sig) { + d_ptr->appendSignature(sig); + } + mContent.append(std::make_shared(d_ptr)); + q->reachParentD()->createMailMime(part); + d_ptr->mCodec = q->mailMime()->charset(); + } +} + +void SinglePartPrivate::fillFrom(const MimeTreeParser::HtmlMessagePart::Ptr &part) +{ + mType = "html"; + mContent.clear(); + mContent.append(std::make_shared(part->text().toUtf8(), q)); + q->reachParentD()->createMailMime(part); +} + +void SinglePartPrivate::fillFrom(const MimeTreeParser::AttachmentMessagePart::Ptr &part) +{ + QMimeDatabase mimeDb; + q->reachParentD()->createMailMime(part.staticCast()); + const auto mimetype = q->mailMime()->mimetype(); + const auto content = q->mailMime()->decodedContent(); + mContent.clear(); + if (mimetype == mimeDb.mimeTypeForName("text/plain")) { + mType = "plaintext"; + mContent.append(std::make_shared(content, q)); + } else if (mimetype == mimeDb.mimeTypeForName("text/html")) { + mType = "html"; + mContent.append(std::make_shared(content, q)); + } else { + mType = mimetype.name().toUtf8(); + mContent.append(std::make_shared(content, q)); + } +} + +void SinglePartPrivate::createEncryptionFailBlock(const MimeTreeParser::EncryptedMessagePart::Ptr &part) +{ + mType = "plaintext"; + mContent.clear(); + mContent.append(std::make_shared(QByteArray(), q)); + q->reachParentD()->createMailMime(part); +} + +SinglePart::SinglePart() + : d(std::unique_ptr(new SinglePartPrivate)) +{ + d->q = this; +} + +SinglePart::~SinglePart() +{ + +} + +QVector SinglePart::availableContents() const +{ + return QVector() << d->mType; +} + +QVector< Content::Ptr > SinglePart::content(const QByteArray &ct) const +{ + if (ct == d->mType) { + return d->mContent; + } + return QVector(); +} + +QByteArray SinglePart::type() const +{ + return "SinglePart"; +} + +PartPrivate* SinglePart::reachParentD() const +{ + return Part::d.get(); +} + +ParserPrivate::ParserPrivate(Parser* parser) + : q(parser) + , mNodeHelper(std::make_shared()) +{ + +} + +void ParserPrivate::setMessage(const QByteArray& mimeMessage) +{ + const auto mailData = KMime::CRLFtoLF(mimeMessage); + mMsg = KMime::Message::Ptr(new KMime::Message); + mMsg->setContent(mailData); + mMsg->parse(); + + // render the mail + StringHtmlWriter htmlWriter; + ObjectTreeSource source(&htmlWriter); + MimeTreeParser::ObjectTreeParser otp(&source, mNodeHelper.get()); + + otp.parseObjectTree(mMsg.data()); + mPartTree = otp.parsedPart().dynamicCast(); + + mEmbeddedPartMap = htmlWriter.embeddedParts(); + mHtml = htmlWriter.html(); + + mTree = std::make_shared(); + createTree(mPartTree, mTree); +} + + +void ParserPrivate::createTree(const MimeTreeParser::MessagePart::Ptr &start, const Part::Ptr &tree) +{ + foreach (const auto &mp, start->subParts()) { + const auto m = mp.dynamicCast(); + const auto text = mp.dynamicCast(); + const auto alternative = mp.dynamicCast(); + const auto html = mp.dynamicCast(); + const auto attachment = mp.dynamicCast(); + if (attachment) { + auto part = std::make_shared(); + part->d->fillFrom(attachment); + tree->d->appendSubPart(part); + } else if (text) { + auto part = std::make_shared(); + part->d->fillFrom(text); + tree->d->appendSubPart(part); + } else if (alternative) { + auto part = std::make_shared(); + part->d->fillFrom(alternative); + tree->d->appendSubPart(part); + } else if (html) { + auto part = std::make_shared(); + part->d->fillFrom(html); + tree->d->appendSubPart(part); + } else { + const auto enc = mp.dynamicCast(); + const auto sig = mp.dynamicCast(); + if (enc || sig) { + auto subTree = std::make_shared(); + if (enc) { + subTree->d->appendEncryption(enc); + if (!enc->isDecryptable()) { + auto part = std::make_shared(); + part->d->createEncryptionFailBlock(enc); + part->reachParentD()->setEncryptions(subTree->d->encryptions()); + tree->d->appendSubPart(part); + return; + } + } + if (sig) { + subTree->d->appendSignature(sig); + } + createTree(m, subTree); + foreach(const auto &p, subTree->subParts()) { + tree->d->appendSubPart(p); + if (enc) { + p->d->setEncryptions(subTree->d->encryptions()); + } + if (sig) { + p->d->setSignatures(subTree->d->signatures()); + } + } + } else { + createTree(m, tree); + } + } + } +} + +Parser::Parser(const QByteArray& mimeMessage) + :d(std::unique_ptr(new ParserPrivate(this))) +{ + d->setMessage(mimeMessage); +} + +Parser::~Parser() +{ +} + +Part::Ptr Parser::getPart(const QUrl &url) +{ + if (url.scheme() == QStringLiteral("cid") && !url.path().isEmpty()) { + const auto cid = url.path(); + return find(d->mTree, [&cid](const Part::Ptr &p){ + const auto mime = p->mailMime(); + return mime->cid() == cid; + }); + } + return Part::Ptr(); +} + +QVector Parser::collectContentParts() const +{ + return collect(d->mTree, [](const Part::Ptr &p){return p->type() != "EncapsulatedPart";}, + [](const Content::Ptr &content){ + const auto mime = content->mailMime(); + + if (!mime) { + return true; + } + + if (mime->isFirstTextPart()) { + return true; + } + + { + auto _mime = content->parent()->mailMime(); + while (_mime) { + if (_mime && (_mime->isTopLevelPart() || _mime->isFirstTextPart())) { + return true; + } + if (_mime->isFirstPart()) { + _mime = _mime->parent(); + } else { + break; + } + } + } + + const auto ctname = mime->mimetype().name().trimmed().toLower(); + bool mightContent = (content->type() != "Content"); //Content we understand + + const auto cd = mime->disposition(); + if (cd && cd == MailMime::Inline) { + return mightContent; + } + + if (cd && cd == MailMime::Attachment) { + return false; + } + + if ((ctname.startsWith("text/") || ctname.isEmpty()) && + (!mime || mime->filename().trimmed().isEmpty())) { + // text/* w/o filename parameter: + return true; + } + return false; + }); +} + + +QVector Parser::collectAttachmentParts() const +{ + return collect(d->mTree, [](const Part::Ptr &p){return p->type() != "EncapsulatedPart";}, + [](const Content::Ptr &content){ + const auto mime = content->mailMime(); + + if (!mime) { + return false; + } + + if (mime->isFirstTextPart()) { + return false; + } + + { + QMimeDatabase mimeDb; + auto _mime = content->parent()->mailMime(); + const auto parent = _mime->parent(); + if (parent) { + const auto mimetype = parent->mimetype(); + if (mimetype == mimeDb.mimeTypeForName("multipart/related")) { + return false; + } + } + while (_mime) { + if (_mime && (_mime->isTopLevelPart() || _mime->isFirstTextPart())) { + return false; + } + if (_mime->isFirstPart()) { + _mime = _mime->parent(); + } else { + break; + } + } + } + + const auto ctname = mime->mimetype().name().trimmed().toLower(); + bool mightContent = (content->type() != "Content"); //Content we understand + + const auto cd = mime->disposition(); + if (cd && cd == MailMime::Inline) { + // explict "inline" disposition: + return !mightContent; + } + if (cd && cd == MailMime::Attachment) { + // explicit "attachment" disposition: + return true; + } + + const auto ct = mime->mimetype(); + if ((ctname.startsWith("text/") || ctname.isEmpty()) && + (!mime || mime->filename().trimmed().isEmpty())) { + // text/* w/o filename parameter: + return false; + } + return true; + }); +} + +QVector Parser::collect(const Part::Ptr &start, std::function select, std::function filter) const +{ + QVector ret; + foreach (const auto &part, start->subParts()) { + QVector contents; + foreach(const auto &ct, part->availableContents()) { + foreach(const auto &content, part->content(ct)) { + if (filter(content)) { + contents.append(ct); + break; + } + } + } + if (!contents.isEmpty()) { + ret.append(part); + } + if (select(part)){ + ret += collect(part, select, filter); + } + } + return ret; +} + +Part::Ptr Parser::find(const Part::Ptr &start, std::function select) const +{ + foreach (const auto &part, start->subParts()) { + if (select(part)) { + return part; + } + const auto ret = find(part, select); + if (ret) { + return ret; + } + } + return Part::Ptr(); +} -- cgit v1.2.3