File indexing completed on 2024-06-16 05:00:15

0001 /*
0002    SPDX-FileCopyrightText: 2015 Sandro Knauß <sknauss@kde.org>
0003 
0004    SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "messagepart.h"
0008 #include "cryptohelper.h"
0009 #include "job/qgpgmejobexecutor.h"
0010 #include "memento/compositememento.h"
0011 #include "memento/cryptobodypartmemento.h"
0012 #include "memento/decryptverifybodypartmemento.h"
0013 #include "memento/keycachememento.h"
0014 #include "memento/verifydetachedbodypartmemento.h"
0015 #include "memento/verifyopaquebodypartmemento.h"
0016 #include "mimetreeparser_debug.h"
0017 #include "objecttreeparser.h"
0018 
0019 #include "bodyformatter/utils.h"
0020 
0021 #include <KMime/Content>
0022 #include <KMime/Types>
0023 #include <Libkleo/Compliance>
0024 #include <Libkleo/KeyCache>
0025 
0026 #include <QGpgME/DN>
0027 #include <QGpgME/ImportJob>
0028 #include <QGpgME/Protocol>
0029 #include <QGpgME/VerifyDetachedJob>
0030 #include <QGpgME/VerifyOpaqueJob>
0031 
0032 #include <gpgme++/key.h>
0033 #include <gpgme++/keylistresult.h>
0034 #include <gpgme.h>
0035 
0036 #include <KLocalizedString>
0037 
0038 #include <QUrl>
0039 
0040 using namespace MimeTreeParser;
0041 
0042 //------MessagePart-----------------------
0043 namespace MimeTreeParser
0044 {
0045 class MessagePartPrivate
0046 {
0047 public:
0048     MessagePart *mParentPart = nullptr;
0049     QList<MessagePart::Ptr> mBlocks;
0050     KMime::Content *mNode = nullptr;
0051     KMime::Content *mAttachmentNode = nullptr;
0052     QString mText;
0053     PartMetaData mMetaData;
0054     bool mRoot = false;
0055     bool mIsImage = false;
0056     bool mNeverDisplayInline = false;
0057 };
0058 }
0059 
0060 MessagePart::MessagePart(ObjectTreeParser *otp, const QString &text)
0061     : mOtp(otp)
0062     , d(new MessagePartPrivate)
0063 {
0064     d->mText = text;
0065 }
0066 
0067 MessagePart::~MessagePart() = default;
0068 
0069 MessagePart *MessagePart::parentPart() const
0070 {
0071     return d->mParentPart;
0072 }
0073 
0074 void MessagePart::setParentPart(MessagePart *parentPart)
0075 {
0076     d->mParentPart = parentPart;
0077 }
0078 
0079 QString MessagePart::htmlContent() const
0080 {
0081     return text();
0082 }
0083 
0084 QString MessagePart::plaintextContent() const
0085 {
0086     return text();
0087 }
0088 
0089 PartMetaData *MessagePart::partMetaData() const
0090 {
0091     return &d->mMetaData;
0092 }
0093 
0094 Interface::BodyPartMemento *MessagePart::memento() const
0095 {
0096     return nodeHelper()->bodyPartMemento(content(), "__plugin__");
0097 }
0098 
0099 void MessagePart::setMemento(Interface::BodyPartMemento *memento)
0100 {
0101     nodeHelper()->setBodyPartMemento(content(), "__plugin__", memento);
0102 }
0103 
0104 KMime::Content *MessagePart::content() const
0105 {
0106     return d->mNode;
0107 }
0108 
0109 void MessagePart::setContent(KMime::Content *node)
0110 {
0111     d->mNode = node;
0112 }
0113 
0114 KMime::Content *MessagePart::attachmentContent() const
0115 {
0116     return d->mAttachmentNode;
0117 }
0118 
0119 void MessagePart::setAttachmentContent(KMime::Content *node)
0120 {
0121     d->mAttachmentNode = node;
0122 }
0123 
0124 bool MessagePart::isAttachment() const
0125 {
0126     return d->mAttachmentNode;
0127 }
0128 
0129 QString MessagePart::attachmentIndex() const
0130 {
0131     return attachmentContent()->index().toString();
0132 }
0133 
0134 QString MessagePart::attachmentLink() const
0135 {
0136     return mOtp->nodeHelper()->asHREF(content(), QStringLiteral("body"));
0137 }
0138 
0139 QString MessagePart::makeLink(const QString &path) const
0140 {
0141     // FIXME: use a PRNG for the first arg, instead of a serial number
0142     static int serial = 0;
0143     if (path.isEmpty()) {
0144         return {};
0145     }
0146     return QStringLiteral("x-kmail:/bodypart/%1/%2/%3")
0147         .arg(serial++)
0148         .arg(mOtp->nodeHelper()->asHREF(content()), QString::fromLatin1(QUrl::toPercentEncoding(path, "/")));
0149 }
0150 
0151 void MessagePart::setIsRoot(bool root)
0152 {
0153     d->mRoot = root;
0154 }
0155 
0156 bool MessagePart::isRoot() const
0157 {
0158     return d->mRoot;
0159 }
0160 
0161 QString MessagePart::text() const
0162 {
0163     return d->mText;
0164 }
0165 
0166 void MessagePart::setText(const QString &text)
0167 {
0168     d->mText = text;
0169 }
0170 
0171 bool MessagePart::isHtml() const
0172 {
0173     return false;
0174 }
0175 
0176 Interface::ObjectTreeSource *MessagePart::source() const
0177 {
0178     Q_ASSERT(mOtp);
0179     return mOtp->mSource;
0180 }
0181 
0182 NodeHelper *MessagePart::nodeHelper() const
0183 {
0184     Q_ASSERT(mOtp);
0185     return mOtp->nodeHelper();
0186 }
0187 
0188 void MessagePart::parseInternal(KMime::Content *node, bool onlyOneMimePart)
0189 {
0190     auto subMessagePart = mOtp->parseObjectTreeInternal(node, onlyOneMimePart);
0191     d->mRoot = subMessagePart->isRoot();
0192     const QList<MessagePart::Ptr> subParts = subMessagePart->subParts();
0193     for (const auto &part : subParts) {
0194         appendSubPart(part);
0195     }
0196 }
0197 
0198 QString MessagePart::renderInternalText() const
0199 {
0200     QString text;
0201     const auto subPartsLst = subParts();
0202     for (const auto &mp : subPartsLst) {
0203         text += mp->text();
0204     }
0205     return text;
0206 }
0207 
0208 void MessagePart::fix() const
0209 {
0210     const auto subPartsLst = subParts();
0211     for (const auto &mp : subPartsLst) {
0212         const auto m = mp.dynamicCast<MessagePart>();
0213         if (m) {
0214             m->fix();
0215         }
0216     }
0217 }
0218 
0219 void MessagePart::appendSubPart(const MessagePart::Ptr &messagePart)
0220 {
0221     messagePart->setParentPart(this);
0222     d->mBlocks.append(messagePart);
0223 }
0224 
0225 const QList<MessagePart::Ptr> &MessagePart::subParts() const
0226 {
0227     return d->mBlocks;
0228 }
0229 
0230 bool MessagePart::hasSubParts() const
0231 {
0232     return !d->mBlocks.isEmpty();
0233 }
0234 
0235 void MessagePart::clearSubParts()
0236 {
0237     d->mBlocks.clear();
0238 }
0239 
0240 bool MessagePart::neverDisplayInline() const
0241 {
0242     return d->mNeverDisplayInline;
0243 }
0244 
0245 void MessagePart::setNeverDisplayInline(bool displayInline)
0246 {
0247     d->mNeverDisplayInline = displayInline;
0248 }
0249 
0250 bool MessagePart::isImage() const
0251 {
0252     return d->mIsImage;
0253 }
0254 
0255 void MessagePart::setIsImage(bool image)
0256 {
0257     d->mIsImage = image;
0258 }
0259 
0260 bool MessagePart::hasHeader(const char *headerType) const
0261 {
0262     Q_UNUSED(headerType)
0263     return false;
0264 }
0265 
0266 const KMime::Headers::Base *MimeTreeParser::MessagePart::header(const char *headerType) const
0267 {
0268     Q_UNUSED(headerType)
0269     return nullptr;
0270 }
0271 
0272 QList<KMime::Headers::Base *> MessagePart::headers(const char *headerType) const
0273 {
0274     Q_UNUSED(headerType)
0275     return {};
0276 }
0277 
0278 //-----MessagePartList----------------------
0279 MessagePartList::MessagePartList(ObjectTreeParser *otp)
0280     : MessagePart(otp, QString())
0281 {
0282 }
0283 
0284 MessagePartList::~MessagePartList() = default;
0285 
0286 QString MessagePartList::text() const
0287 {
0288     return renderInternalText();
0289 }
0290 
0291 QString MessagePartList::plaintextContent() const
0292 {
0293     return {};
0294 }
0295 
0296 QString MessagePartList::htmlContent() const
0297 {
0298     return {};
0299 }
0300 
0301 //-----TextMessageBlock----------------------
0302 
0303 TextMessagePart::TextMessagePart(ObjectTreeParser *otp, KMime::Content *node, bool decryptMessage)
0304     : MessagePartList(otp)
0305     , mDecryptMessage(decryptMessage)
0306 {
0307     if (!node) {
0308         qCWarning(MIMETREEPARSER_LOG) << "not a valid node";
0309         return;
0310     }
0311 
0312     setContent(node);
0313 
0314     parseContent();
0315 }
0316 
0317 TextMessagePart::~TextMessagePart() = default;
0318 
0319 bool TextMessagePart::decryptMessage() const
0320 {
0321     return mDecryptMessage;
0322 }
0323 
0324 void TextMessagePart::parseContent()
0325 {
0326     const auto codecName = mOtp->codecNameFor(content());
0327     QStringDecoder aCodec(codecName.constData());
0328     const QString &fromAddress = mOtp->nodeHelper()->fromAsString(content());
0329     mSignatureState = KMMsgNotSigned;
0330     mEncryptionState = KMMsgNotEncrypted;
0331     const auto blocks = prepareMessageForDecryption(content()->decodedContent());
0332 
0333     const auto cryptProto = QGpgME::openpgp();
0334 
0335     if (!blocks.isEmpty()) {
0336         /* The (overall) signature/encrypted status is broken
0337          * if one unencrypted part is at the beginning or in the middle
0338          * because mailmain adds an unencrypted part at the end this should not break the overall status
0339          *
0340          * That's why we first set the tmp status and if one encrypted/signed block comes afterwards, than
0341          * the status is set to unencrypted
0342          */
0343         bool fullySignedOrEncrypted = true;
0344         bool fullySignedOrEncryptedTmp = true;
0345 
0346         int blockIndex = -1;
0347         for (const auto &block : blocks) {
0348             blockIndex += 1;
0349             if (!fullySignedOrEncryptedTmp) {
0350                 fullySignedOrEncrypted = false;
0351             }
0352 
0353             if (block.type() == NoPgpBlock && !block.text().trimmed().isEmpty()) {
0354                 fullySignedOrEncryptedTmp = false;
0355                 aCodec.resetState();
0356                 appendSubPart(MessagePart::Ptr(new MessagePart(mOtp, aCodec.decode(block.text()))));
0357             } else if (block.type() == PgpMessageBlock) {
0358                 EncryptedMessagePart::Ptr mp(new EncryptedMessagePart(mOtp, QString(), cryptProto, fromAddress, nullptr));
0359                 mp->setDecryptMessage(decryptMessage());
0360                 mp->setIsEncrypted(true);
0361                 mp->setMementoName(mp->mementoName() + "-" + nodeHelper()->asHREF(content(), QString::number(blockIndex)).toLocal8Bit());
0362                 appendSubPart(mp);
0363                 if (!decryptMessage()) {
0364                     continue;
0365                 }
0366                 mp->startDecryption(block.text(), codecName);
0367                 if (mp->partMetaData()->inProgress) {
0368                     continue;
0369                 }
0370             } else if (block.type() == ClearsignedBlock) {
0371                 SignedMessagePart::Ptr mp(new SignedMessagePart(mOtp, QString(), cryptProto, fromAddress, nullptr));
0372                 mp->setMementoName(mp->mementoName() + "-" + nodeHelper()->asHREF(content(), QString::number(blockIndex)).toLocal8Bit());
0373                 appendSubPart(mp);
0374                 mp->startVerification(block.text(), codecName);
0375             } else {
0376                 continue;
0377             }
0378 
0379             const auto mp = subParts().last().staticCast<MessagePart>();
0380             const PartMetaData *messagePart(mp->partMetaData());
0381 
0382             if (!messagePart->isEncrypted && !messagePart->isSigned && !block.text().trimmed().isEmpty()) {
0383                 aCodec.resetState();
0384                 mp->setText(aCodec.decode(block.text()));
0385             }
0386 
0387             if (messagePart->isEncrypted) {
0388                 mEncryptionState = KMMsgPartiallyEncrypted;
0389             }
0390 
0391             if (messagePart->isSigned) {
0392                 mSignatureState = KMMsgPartiallySigned;
0393             }
0394         }
0395 
0396         // Do we have an fully Signed/Encrypted Message?
0397         if (fullySignedOrEncrypted) {
0398             if (mSignatureState == KMMsgPartiallySigned) {
0399                 mSignatureState = KMMsgFullySigned;
0400             }
0401             if (mEncryptionState == KMMsgPartiallyEncrypted) {
0402                 mEncryptionState = KMMsgFullyEncrypted;
0403             }
0404         }
0405     }
0406 }
0407 
0408 KMMsgEncryptionState TextMessagePart::encryptionState() const
0409 {
0410     return mEncryptionState;
0411 }
0412 
0413 KMMsgSignatureState TextMessagePart::signatureState() const
0414 {
0415     return mSignatureState;
0416 }
0417 
0418 bool TextMessagePart::showLink() const
0419 {
0420     return !temporaryFilePath().isEmpty();
0421 }
0422 
0423 bool TextMessagePart::isFirstTextPart() const
0424 {
0425     return content()->topLevel()->textContent() == content();
0426 }
0427 
0428 bool TextMessagePart::hasLabel() const
0429 {
0430     return !NodeHelper::fileName(content()).isEmpty();
0431 }
0432 
0433 QString TextMessagePart::label() const
0434 {
0435     const QString name = content()->contentType()->name();
0436     QString label = name.isEmpty() ? NodeHelper::fileName(content()) : name;
0437     if (label.isEmpty()) {
0438         label = i18nc("display name for an unnamed attachment", "Unnamed");
0439     }
0440     return label;
0441 }
0442 
0443 QString TextMessagePart::comment() const
0444 {
0445     const QString comment = content()->contentDescription()->asUnicodeString();
0446     if (comment == label()) {
0447         return {};
0448     }
0449     return comment;
0450 }
0451 
0452 QString TextMessagePart::temporaryFilePath() const
0453 {
0454     return nodeHelper()->writeNodeToTempFile(content());
0455 }
0456 
0457 //-----AttachmentMessageBlock----------------------
0458 
0459 AttachmentMessagePart::AttachmentMessagePart(ObjectTreeParser *otp, KMime::Content *node, bool decryptMessage)
0460     : TextMessagePart(otp, node, decryptMessage)
0461 {
0462 }
0463 
0464 AttachmentMessagePart::~AttachmentMessagePart() = default;
0465 
0466 //-----HtmlMessageBlock----------------------
0467 
0468 HtmlMessagePart::HtmlMessagePart(ObjectTreeParser *otp, KMime::Content *node, Interface::ObjectTreeSource *source)
0469     : MessagePart(otp, QString())
0470 {
0471     Q_UNUSED(source)
0472     if (!node) {
0473         qCWarning(MIMETREEPARSER_LOG) << "not a valid node";
0474         return;
0475     }
0476     setContent(node);
0477 
0478     const QByteArray partBody(node->decodedContent());
0479     mBodyHTML = QStringDecoder(mOtp->codecNameFor(node).constData()).decode(partBody);
0480     mCharset = NodeHelper::charset(node);
0481 }
0482 
0483 HtmlMessagePart::~HtmlMessagePart() = default;
0484 
0485 void HtmlMessagePart::fix() const
0486 {
0487     mOtp->mHtmlContent += mBodyHTML;
0488     mOtp->mHtmlContentCharset = mCharset;
0489 }
0490 
0491 QString HtmlMessagePart::text() const
0492 {
0493     return mBodyHTML;
0494 }
0495 
0496 QString MimeTreeParser::HtmlMessagePart::plaintextContent() const
0497 {
0498     return {};
0499 }
0500 
0501 bool HtmlMessagePart::isHtml() const
0502 {
0503     return true;
0504 }
0505 
0506 QString HtmlMessagePart::bodyHtml() const
0507 {
0508     return mBodyHTML;
0509 }
0510 
0511 //-----MimeMessageBlock----------------------
0512 
0513 MimeMessagePart::MimeMessagePart(ObjectTreeParser *otp, KMime::Content *node, bool onlyOneMimePart)
0514     : MessagePart(otp, QString())
0515     , mOnlyOneMimePart(onlyOneMimePart)
0516 {
0517     if (!node) {
0518         qCWarning(MIMETREEPARSER_LOG) << "not a valid node";
0519         return;
0520     }
0521     setContent(node);
0522 
0523     parseInternal(node, mOnlyOneMimePart);
0524 }
0525 
0526 MimeMessagePart::~MimeMessagePart() = default;
0527 
0528 QString MimeMessagePart::text() const
0529 {
0530     return renderInternalText();
0531 }
0532 
0533 QString MimeMessagePart::plaintextContent() const
0534 {
0535     return {};
0536 }
0537 
0538 QString MimeMessagePart::htmlContent() const
0539 {
0540     return {};
0541 }
0542 
0543 //-----AlternativeMessagePart----------------------
0544 
0545 AlternativeMessagePart::AlternativeMessagePart(ObjectTreeParser *otp, KMime::Content *node, Util::HtmlMode preferredMode)
0546     : MessagePart(otp, QString())
0547     , mPreferredMode(preferredMode)
0548 {
0549     setContent(node);
0550     KMime::Content *dataIcal = findTypeInDirectChilds(node, "text/calendar");
0551     KMime::Content *dataHtml = findTypeInDirectChilds(node, "text/html");
0552     KMime::Content *dataText = findTypeInDirectChilds(node, "text/plain");
0553 
0554     if (!dataHtml) {
0555         // If we didn't find the HTML part as the first child of the multipart/alternative, it might
0556         // be that this is a HTML message with images, and text/plain and multipart/related are the
0557         // immediate children of this multipart/alternative node.
0558         // In this case, the HTML node is a child of multipart/related.
0559         dataHtml = findTypeInDirectChilds(node, "multipart/related");
0560 
0561         // Still not found? Stupid apple mail actually puts the attachments inside of the
0562         // multipart/alternative, which is wrong. Therefore we also have to look for multipart/mixed
0563         // here.
0564         // Do this only when preferring HTML mail, though, since otherwise the attachments are hidden
0565         // when displaying plain text.
0566         if (!dataHtml) {
0567             dataHtml = findTypeInDirectChilds(node, "multipart/mixed");
0568         }
0569     }
0570 
0571     if (dataIcal) {
0572         mChildNodes[Util::MultipartIcal] = dataIcal;
0573     }
0574 
0575     if (dataText) {
0576         mChildNodes[Util::MultipartPlain] = dataText;
0577     }
0578 
0579     if (dataHtml) {
0580         mChildNodes[Util::MultipartHtml] = dataHtml;
0581     }
0582 
0583     if (mChildNodes.isEmpty()) {
0584         qCWarning(MIMETREEPARSER_LOG) << "no valid nodes";
0585         return;
0586     }
0587 
0588     QMapIterator<Util::HtmlMode, KMime::Content *> i(mChildNodes);
0589     while (i.hasNext()) {
0590         i.next();
0591         mChildParts[i.key()] = MimeMessagePart::Ptr(new MimeMessagePart(mOtp, i.value(), true));
0592     }
0593 }
0594 
0595 AlternativeMessagePart::~AlternativeMessagePart() = default;
0596 
0597 Util::HtmlMode AlternativeMessagePart::preferredMode() const
0598 {
0599     return mPreferredMode;
0600 }
0601 
0602 void AlternativeMessagePart::setPreferredMode(Util::HtmlMode preferredMode)
0603 {
0604     mPreferredMode = preferredMode;
0605 }
0606 
0607 QList<Util::HtmlMode> AlternativeMessagePart::availableModes()
0608 {
0609     return mChildParts.keys();
0610 }
0611 
0612 QString AlternativeMessagePart::text() const
0613 {
0614     if (mChildParts.contains(Util::MultipartPlain)) {
0615         return mChildParts[Util::MultipartPlain]->text();
0616     }
0617     return {};
0618 }
0619 
0620 void AlternativeMessagePart::fix() const
0621 {
0622     if (mChildParts.contains(Util::MultipartPlain)) {
0623         mChildParts[Util::MultipartPlain]->fix();
0624     }
0625 
0626     const auto mode = preferredMode();
0627     if (mode != Util::MultipartPlain && mChildParts.contains(mode)) {
0628         mChildParts[mode]->fix();
0629     }
0630 }
0631 
0632 const QMap<Util::HtmlMode, MimeMessagePart::Ptr> &AlternativeMessagePart::childParts() const
0633 {
0634     return mChildParts;
0635 }
0636 
0637 bool AlternativeMessagePart::isHtml() const
0638 {
0639     return mChildParts.contains(Util::MultipartHtml);
0640 }
0641 
0642 QString AlternativeMessagePart::plaintextContent() const
0643 {
0644     return text();
0645 }
0646 
0647 QString AlternativeMessagePart::htmlContent() const
0648 {
0649     if (mChildParts.contains(Util::MultipartHtml)) {
0650         return mChildParts[Util::MultipartHtml]->text();
0651     } else {
0652         return plaintextContent();
0653     }
0654 }
0655 
0656 //-----CertMessageBlock----------------------
0657 
0658 CertMessagePart::CertMessagePart(ObjectTreeParser *otp, KMime::Content *node, const QGpgME::Protocol *cryptoProto, bool autoImport)
0659     : MessagePart(otp, QString())
0660     , mAutoImport(autoImport)
0661     , mCryptoProto(cryptoProto)
0662 {
0663     if (!node) {
0664         qCWarning(MIMETREEPARSER_LOG) << "not a valid node";
0665         return;
0666     }
0667     setContent(node);
0668 
0669     if (!mAutoImport) {
0670         return;
0671     }
0672 
0673     const QByteArray certData = node->decodedContent();
0674 
0675     QGpgME::ImportJob *import = mCryptoProto->importJob();
0676     QGpgMEJobExecutor executor;
0677     mImportResult = executor.exec(import, certData);
0678 }
0679 
0680 CertMessagePart::~CertMessagePart() = default;
0681 
0682 QString CertMessagePart::text() const
0683 {
0684     return {};
0685 }
0686 
0687 const GpgME::ImportResult &CertMessagePart::importResult() const
0688 {
0689     return mImportResult;
0690 }
0691 
0692 //-----SignedMessageBlock---------------------
0693 SignedMessagePart::SignedMessagePart(ObjectTreeParser *otp,
0694                                      const QString &text,
0695                                      const QGpgME::Protocol *cryptoProto,
0696                                      const QString &fromAddress,
0697                                      KMime::Content *node)
0698     : MessagePart(otp, text)
0699     , mCryptoProto(cryptoProto)
0700     , mFromAddress(fromAddress)
0701     , mMementoName("verification")
0702 {
0703     setContent(node);
0704     partMetaData()->technicalProblem = (mCryptoProto == nullptr);
0705     partMetaData()->isSigned = true;
0706     partMetaData()->isGoodSignature = false;
0707     partMetaData()->keyTrust = GpgME::Signature::Unknown;
0708     partMetaData()->status = i18n("Wrong Crypto Plug-In.");
0709     partMetaData()->status_code = GPGME_SIG_STAT_NONE;
0710 }
0711 
0712 SignedMessagePart::~SignedMessagePart() = default;
0713 
0714 void SignedMessagePart::setIsSigned(bool isSigned)
0715 {
0716     partMetaData()->isSigned = isSigned;
0717 }
0718 
0719 bool SignedMessagePart::isSigned() const
0720 {
0721     return partMetaData()->isSigned;
0722 }
0723 
0724 QByteArray SignedMessagePart::mementoName() const
0725 {
0726     return mMementoName;
0727 }
0728 
0729 void SignedMessagePart::setMementoName(const QByteArray &name)
0730 {
0731     mMementoName = name;
0732 }
0733 
0734 static GpgME::Protocol toGpgMeProtocol(const QGpgME::Protocol *protocol)
0735 {
0736     if (protocol == QGpgME::openpgp()) {
0737         return GpgME::OpenPGP;
0738     }
0739 
0740     if (protocol == QGpgME::smime()) {
0741         return GpgME::CMS;
0742     }
0743 
0744     return GpgME::UnknownProtocol;
0745 }
0746 
0747 bool SignedMessagePart::okVerify(const QByteArray &data, const QByteArray &signature, KMime::Content *textNode)
0748 {
0749     NodeHelper *nodeHelper = mOtp->nodeHelper();
0750 
0751     partMetaData()->isSigned = false;
0752     partMetaData()->technicalProblem = (mCryptoProto == nullptr);
0753     partMetaData()->keyTrust = GpgME::Signature::Unknown;
0754     partMetaData()->status = i18n("Wrong Crypto Plug-In.");
0755     partMetaData()->status_code = GPGME_SIG_STAT_NONE;
0756 
0757     const QByteArray _mementoName = mementoName();
0758 
0759     auto m = dynamic_cast<CompositeMemento *>(nodeHelper->bodyPartMemento(content(), _mementoName));
0760     Q_ASSERT(!m || mCryptoProto); // No CryptoPlugin and having a bodyPartMemento -> there is something completely wrong
0761 
0762     if (!m && mCryptoProto) {
0763         CryptoBodyPartMemento *newM = nullptr;
0764         if (!signature.isEmpty()) {
0765             QGpgME::VerifyDetachedJob *job = mCryptoProto->verifyDetachedJob();
0766             if (job) {
0767                 newM = new VerifyDetachedBodyPartMemento(job, mCryptoProto->keyListJob(), signature, data);
0768             }
0769         } else {
0770             QGpgME::VerifyOpaqueJob *job = mCryptoProto->verifyOpaqueJob();
0771             if (job) {
0772                 newM = new VerifyOpaqueBodyPartMemento(job, mCryptoProto->keyListJob(), data);
0773             }
0774         }
0775 
0776         if (newM) {
0777             m = new CompositeMemento();
0778             m->addMemento(newM);
0779             m->addMemento(new KeyCacheMemento(Kleo::KeyCache::mutableInstance(), toGpgMeProtocol(mCryptoProto)));
0780         }
0781 
0782         if (m) {
0783             if (mOtp->allowAsync()) {
0784                 QObject::connect(m, &CryptoBodyPartMemento::update, nodeHelper, &NodeHelper::update);
0785                 if (m->start()) {
0786                     partMetaData()->inProgress = true;
0787                     mOtp->mHasPendingAsyncJobs = true;
0788                 }
0789             } else {
0790                 m->exec();
0791             }
0792             nodeHelper->setBodyPartMemento(content(), _mementoName, m);
0793         }
0794     } else if (m && m->isRunning()) {
0795         partMetaData()->inProgress = true;
0796         mOtp->mHasPendingAsyncJobs = true;
0797     } else {
0798         partMetaData()->inProgress = false;
0799         mOtp->mHasPendingAsyncJobs = false;
0800     }
0801 
0802     if (m && !partMetaData()->inProgress) {
0803         if (!signature.isEmpty()) {
0804             mVerifiedText = data;
0805         }
0806         setVerificationResult(m, textNode);
0807     }
0808 
0809     if (!m && !partMetaData()->inProgress) {
0810         QString errorMsg;
0811         QString cryptPlugLibName;
0812         QString cryptPlugDisplayName;
0813         if (mCryptoProto) {
0814             cryptPlugLibName = mCryptoProto->name();
0815             cryptPlugDisplayName = mCryptoProto->displayName();
0816         }
0817 
0818         if (!mCryptoProto) {
0819             if (cryptPlugDisplayName.isEmpty()) {
0820                 errorMsg = i18n("No appropriate crypto plug-in was found.");
0821             } else {
0822                 errorMsg = i18nc("%1 is either 'OpenPGP' or 'S/MIME'", "No %1 plug-in was found.", cryptPlugDisplayName);
0823             }
0824         } else {
0825             errorMsg = i18n("Crypto plug-in \"%1\" cannot verify signatures.", cryptPlugLibName);
0826         }
0827         partMetaData()->errorText = i18n(
0828             "The message is signed, but the "
0829             "validity of the signature cannot be "
0830             "verified.<br />"
0831             "Reason: %1",
0832             errorMsg);
0833     }
0834 
0835     return partMetaData()->isSigned;
0836 }
0837 
0838 static int signatureToStatus(const GpgME::Signature &sig)
0839 {
0840     switch (sig.status().code()) {
0841     case GPG_ERR_NO_ERROR:
0842         return GPGME_SIG_STAT_GOOD;
0843     case GPG_ERR_BAD_SIGNATURE:
0844         return GPGME_SIG_STAT_BAD;
0845     case GPG_ERR_NO_PUBKEY:
0846         return GPGME_SIG_STAT_NOKEY;
0847     case GPG_ERR_NO_DATA:
0848         return GPGME_SIG_STAT_NOSIG;
0849     case GPG_ERR_SIG_EXPIRED:
0850         return GPGME_SIG_STAT_GOOD_EXP;
0851     case GPG_ERR_KEY_EXPIRED:
0852         return GPGME_SIG_STAT_GOOD_EXPKEY;
0853     default:
0854         return GPGME_SIG_STAT_ERROR;
0855     }
0856 }
0857 
0858 QString prettifyDN(const char *uid)
0859 {
0860     return QGpgME::DN(uid).prettyDN();
0861 }
0862 
0863 void SignedMessagePart::sigStatusToMetaData()
0864 {
0865     GpgME::Key key;
0866     if (partMetaData()->isSigned) {
0867         GpgME::Signature signature = mSignatures.front();
0868         partMetaData()->status_code = signatureToStatus(signature);
0869         partMetaData()->isGoodSignature = partMetaData()->status_code == GPGME_SIG_STAT_GOOD;
0870         // save extended signature status flags
0871         partMetaData()->sigSummary = signature.summary();
0872 
0873         if (partMetaData()->isGoodSignature && !key.keyID()) {
0874             // Search for the key by its fingerprint so that we can check for
0875             // trust etc.
0876             key = mKeyCache->findByFingerprint(signature.fingerprint());
0877             if (key.isNull() && signature.fingerprint()) {
0878                 // try to find a subkey that was used for signing;
0879                 // assumes that the key ID is the last 16 characters of the fingerprint
0880                 const auto fpr = std::string_view{signature.fingerprint()};
0881                 const auto keyID = std::string{fpr, fpr.size() - 16, 16};
0882                 const auto subkeys = mKeyCache->findSubkeysByKeyID({keyID});
0883                 if (subkeys.size() > 0) {
0884                     key = subkeys[0].parent();
0885                 }
0886             }
0887             if (key.isNull()) {
0888                 qCDebug(MIMETREEPARSER_LOG) << "Found no key or subkey for fingerprint" << signature.fingerprint();
0889             }
0890         }
0891 
0892         if (key.keyID()) {
0893             partMetaData()->keyId = key.keyID();
0894         }
0895         if (partMetaData()->keyId.isEmpty()) {
0896             partMetaData()->keyId = signature.fingerprint();
0897         }
0898         partMetaData()->keyTrust = signature.validity();
0899         if (key.numUserIDs() > 0 && key.userID(0).id()) {
0900             partMetaData()->signer = prettifyDN(key.userID(0).id());
0901         }
0902         for (const auto &uid : key.userIDs()) {
0903             // The following if /should/ always result in TRUE but we
0904             // won't trust implicitly the plugin that gave us these data.
0905             if (uid.email()) {
0906                 KMime::Types::Mailbox mbox;
0907                 mbox.from7BitString(uid.email());
0908                 if (mbox.hasAddress()) {
0909                     partMetaData()->signerMailAddresses.append(mbox.addrSpec().asString());
0910                 }
0911             }
0912         }
0913 
0914         if (signature.creationTime()) {
0915             partMetaData()->creationTime.setSecsSinceEpoch(signature.creationTime());
0916         } else {
0917             partMetaData()->creationTime = QDateTime();
0918         }
0919         if (partMetaData()->signer.isEmpty()) {
0920             if (key.numUserIDs() > 0 && key.userID(0).name()) {
0921                 partMetaData()->signer = prettifyDN(key.userID(0).name());
0922             }
0923             if (!partMetaData()->signerMailAddresses.empty()) {
0924                 if (partMetaData()->signer.isEmpty()) {
0925                     partMetaData()->signer = partMetaData()->signerMailAddresses.front();
0926                 } else {
0927                     partMetaData()->signer += QLatin1StringView(" <") + partMetaData()->signerMailAddresses.front() + QLatin1Char('>');
0928                 }
0929             }
0930         }
0931         if (Kleo::DeVSCompliance::isCompliant()) {
0932             partMetaData()->isCompliant = signature.isDeVs();
0933             partMetaData()->compliance = Kleo::DeVSCompliance::name(signature.isDeVs());
0934         } else {
0935             partMetaData()->isCompliant = true;
0936         }
0937     }
0938 }
0939 
0940 void SignedMessagePart::startVerification(const QByteArray &text, QByteArrayView aCodec)
0941 {
0942     startVerificationDetached(text, nullptr, QByteArray());
0943 
0944     if (!content() && partMetaData()->isSigned) {
0945         QStringDecoder codec(aCodec.constData());
0946         setText(codec.decode(mVerifiedText));
0947     }
0948 }
0949 
0950 void SignedMessagePart::startVerificationDetached(const QByteArray &text, KMime::Content *textNode, const QByteArray &signature)
0951 {
0952     partMetaData()->isEncrypted = false;
0953     partMetaData()->isDecryptable = false;
0954 
0955     if (textNode) {
0956         parseInternal(textNode, false);
0957     }
0958 
0959     if (!okVerify(text, signature, textNode)) {
0960         partMetaData()->creationTime = QDateTime();
0961     }
0962 }
0963 
0964 void SignedMessagePart::setVerificationResult(const CompositeMemento *m, KMime::Content *textNode)
0965 {
0966     {
0967         const auto kc = m->memento<KeyCacheMemento>();
0968         if (kc) {
0969             mKeyCache = kc->keyCache();
0970         }
0971     }
0972     {
0973         const auto vm = m->memento<VerifyDetachedBodyPartMemento>();
0974         if (vm) {
0975             mSignatures = vm->verifyResult().signatures();
0976         }
0977     }
0978     {
0979         const auto vm = m->memento<VerifyOpaqueBodyPartMemento>();
0980         if (vm) {
0981             mVerifiedText = vm->plainText();
0982             mSignatures = vm->verifyResult().signatures();
0983         }
0984     }
0985     {
0986         const auto vm = m->memento<DecryptVerifyBodyPartMemento>();
0987         if (vm) {
0988             mVerifiedText = vm->plainText();
0989             mSignatures = vm->verifyResult().signatures();
0990         }
0991     }
0992     partMetaData()->auditLogError = m->auditLogError();
0993     partMetaData()->auditLog = m->auditLogAsHtml();
0994     partMetaData()->isSigned = !mSignatures.empty();
0995 
0996     if (partMetaData()->isSigned) {
0997         sigStatusToMetaData();
0998         if (content()) {
0999             mOtp->nodeHelper()->setSignatureState(content(), KMMsgFullySigned);
1000             if (!textNode) {
1001                 mOtp->nodeHelper()->setPartMetaData(content(), *partMetaData());
1002 
1003                 if (!mVerifiedText.isEmpty()) {
1004                     auto tempNode = new KMime::Content();
1005                     tempNode->setContent(KMime::CRLFtoLF(mVerifiedText.constData()));
1006                     tempNode->parse();
1007 
1008                     if (!tempNode->head().isEmpty()) {
1009                         tempNode->contentDescription()->from7BitString("signed data");
1010                     }
1011                     mOtp->nodeHelper()->attachExtraContent(content(), tempNode);
1012 
1013                     parseInternal(tempNode, false);
1014                 }
1015             }
1016         }
1017     }
1018 }
1019 
1020 QString SignedMessagePart::plaintextContent() const
1021 {
1022     if (!content()) {
1023         return MessagePart::text();
1024     } else {
1025         return {};
1026     }
1027 }
1028 
1029 QString SignedMessagePart::htmlContent() const
1030 {
1031     if (!content()) {
1032         return MessagePart::text();
1033     } else {
1034         return {};
1035     }
1036 }
1037 
1038 const QGpgME::Protocol *SignedMessagePart::cryptoProto() const
1039 {
1040     return mCryptoProto;
1041 }
1042 
1043 QString SignedMessagePart::fromAddress() const
1044 {
1045     return mFromAddress;
1046 }
1047 
1048 bool SignedMessagePart::hasHeader(const char *headerType) const
1049 {
1050     if (content()) {
1051         return content()->hasHeader(headerType);
1052     }
1053     return false;
1054 }
1055 
1056 const KMime::Headers::Base *MimeTreeParser::SignedMessagePart::header(const char *headerType) const
1057 {
1058     if (content()) {
1059         return content()->headerByType(headerType);
1060     }
1061     return nullptr;
1062 }
1063 
1064 QList<KMime::Headers::Base *> SignedMessagePart::headers(const char *headerType) const
1065 {
1066     if (content()) {
1067         return content()->headersByType(headerType);
1068     }
1069     return {};
1070 }
1071 
1072 //-----CryptMessageBlock---------------------
1073 EncryptedMessagePart::EncryptedMessagePart(ObjectTreeParser *otp,
1074                                            const QString &text,
1075                                            const QGpgME::Protocol *cryptoProto,
1076                                            const QString &fromAddress,
1077                                            KMime::Content *node)
1078     : MessagePart(otp, text)
1079     , mPassphraseError(false)
1080     , mNoSecKey(false)
1081     , mDecryptMessage(false)
1082     , mCryptoProto(cryptoProto)
1083     , mFromAddress(fromAddress)
1084     , mMementoName("decryptverify")
1085 {
1086     setContent(node);
1087     partMetaData()->technicalProblem = (mCryptoProto == nullptr);
1088     partMetaData()->isSigned = false;
1089     partMetaData()->isGoodSignature = false;
1090     partMetaData()->isEncrypted = false;
1091     partMetaData()->isDecryptable = false;
1092     partMetaData()->keyTrust = GpgME::Signature::Unknown;
1093     partMetaData()->status = i18n("Wrong Crypto Plug-In.");
1094     partMetaData()->status_code = GPGME_SIG_STAT_NONE;
1095 }
1096 
1097 EncryptedMessagePart::~EncryptedMessagePart() = default;
1098 
1099 void EncryptedMessagePart::setDecryptMessage(bool decrypt)
1100 {
1101     mDecryptMessage = decrypt;
1102 }
1103 
1104 bool EncryptedMessagePart::decryptMessage() const
1105 {
1106     return mDecryptMessage;
1107 }
1108 
1109 void EncryptedMessagePart::setIsEncrypted(bool encrypted)
1110 {
1111     partMetaData()->isEncrypted = encrypted;
1112 }
1113 
1114 bool EncryptedMessagePart::isEncrypted() const
1115 {
1116     return partMetaData()->isEncrypted;
1117 }
1118 
1119 bool EncryptedMessagePart::isDecryptable() const
1120 {
1121     return partMetaData()->isDecryptable;
1122 }
1123 
1124 bool EncryptedMessagePart::isNoSecKey() const
1125 {
1126     return mNoSecKey;
1127 }
1128 
1129 bool EncryptedMessagePart::passphraseError() const
1130 {
1131     return mPassphraseError;
1132 }
1133 
1134 QByteArray EncryptedMessagePart::mementoName() const
1135 {
1136     return mMementoName;
1137 }
1138 
1139 void EncryptedMessagePart::setMementoName(const QByteArray &name)
1140 {
1141     mMementoName = name;
1142 }
1143 
1144 void EncryptedMessagePart::startDecryption(const QByteArray &text, QByteArrayView aCodec)
1145 {
1146     auto content = new KMime::Content;
1147     content->setBody(text);
1148     content->parse();
1149 
1150     startDecryption(content);
1151 
1152     if (!partMetaData()->inProgress && partMetaData()->isDecryptable) {
1153         QStringDecoder codec(aCodec.constData());
1154         if (hasSubParts()) {
1155             auto _mp = (subParts()[0]).dynamicCast<SignedMessagePart>();
1156             if (_mp) {
1157                 _mp->setText(codec.decode(mDecryptedData));
1158             } else {
1159                 setText(codec.decode(mDecryptedData));
1160             }
1161         } else {
1162             setText(codec.decode(mDecryptedData));
1163         }
1164     }
1165     delete content;
1166 }
1167 
1168 bool EncryptedMessagePart::okDecryptMIME(KMime::Content &data)
1169 {
1170     mPassphraseError = false;
1171     partMetaData()->inProgress = false;
1172     partMetaData()->errorText.clear();
1173     partMetaData()->auditLogError = GpgME::Error();
1174     partMetaData()->auditLog.clear();
1175     bool bDecryptionOk = false;
1176     bool cannotDecrypt = false;
1177     NodeHelper *nodeHelper = mOtp->nodeHelper();
1178 
1179     Q_ASSERT(decryptMessage());
1180 
1181     const QByteArray _mementoName = mementoName();
1182     // Check whether the memento contains a result from last time:
1183     const auto *m = dynamic_cast<CompositeMemento *>(nodeHelper->bodyPartMemento(&data, _mementoName));
1184 
1185     Q_ASSERT(!m || mCryptoProto); // No CryptoPlugin and having a bodyPartMemento -> there is something completely wrong
1186 
1187     if (!m && mCryptoProto) {
1188         QGpgME::DecryptVerifyJob *job = mCryptoProto->decryptVerifyJob();
1189         if (!job) {
1190             cannotDecrypt = true;
1191         } else {
1192             const QByteArray ciphertext = data.decodedContent();
1193             auto newM = new CompositeMemento();
1194             newM->addMemento(new KeyCacheMemento(Kleo::KeyCache::mutableInstance(), toGpgMeProtocol(mCryptoProto)));
1195             newM->addMemento(new DecryptVerifyBodyPartMemento(job, ciphertext));
1196             if (mOtp->allowAsync()) {
1197                 QObject::connect(newM, &CryptoBodyPartMemento::update, nodeHelper, &NodeHelper::update);
1198                 if (newM->start()) {
1199                     partMetaData()->inProgress = true;
1200                     mOtp->mHasPendingAsyncJobs = true;
1201                 } else {
1202                     m = newM;
1203                 }
1204             } else {
1205                 newM->exec();
1206                 m = newM;
1207             }
1208 
1209             nodeHelper->setBodyPartMemento(&data, _mementoName, newM);
1210         }
1211     } else if (m && m->isRunning()) {
1212         partMetaData()->inProgress = true;
1213         mOtp->mHasPendingAsyncJobs = true;
1214         m = nullptr;
1215     }
1216 
1217     if (m) {
1218         {
1219             const auto *kcm = m->memento<KeyCacheMemento>();
1220             if (kcm) {
1221                 mKeyCache = kcm->keyCache();
1222             }
1223         }
1224         auto *decryptMemento = m->memento<DecryptVerifyBodyPartMemento>();
1225         const QByteArray &plainText = decryptMemento->plainText();
1226         const GpgME::DecryptionResult &decryptResult = decryptMemento->decryptResult();
1227         const GpgME::VerificationResult &verifyResult = decryptMemento->verifyResult();
1228         partMetaData()->isSigned = verifyResult.signatures().size() > 0;
1229 
1230         if (partMetaData()->isSigned) {
1231             auto subPart = SignedMessagePart::Ptr(new SignedMessagePart(mOtp, MessagePart::text(), mCryptoProto, mFromAddress, content()));
1232             subPart->setVerificationResult(m, nullptr);
1233             appendSubPart(subPart);
1234         }
1235 
1236         mDecryptRecipients.clear();
1237         bDecryptionOk = !decryptResult.error();
1238 
1239         //         std::stringstream ss;
1240         //         ss << decryptResult << '\n' << verifyResult;
1241         //         qCDebug(MIMETREEPARSER_LOG) << ss.str().c_str();
1242 
1243         for (const auto &recipient : decryptResult.recipients()) {
1244             if (!recipient.status()) {
1245                 bDecryptionOk = true;
1246             }
1247             GpgME::Key key;
1248             key = mKeyCache->findByKeyIDOrFingerprint(recipient.keyID());
1249             if (key.isNull()) {
1250                 auto ret = mKeyCache->findSubkeysByKeyID({recipient.keyID()});
1251                 if (ret.size() == 1) {
1252                     key = ret.front().parent();
1253                 }
1254                 if (key.isNull()) {
1255                     qCDebug(MIMETREEPARSER_LOG) << "Found no Key for KeyID " << recipient.keyID();
1256                 }
1257             }
1258             mDecryptRecipients.emplace_back(recipient, key);
1259         }
1260 
1261         if (!bDecryptionOk && partMetaData()->isSigned) {
1262             // Only a signed part
1263             partMetaData()->isEncrypted = false;
1264             bDecryptionOk = true;
1265             mDecryptedData = plainText;
1266         } else {
1267             mPassphraseError = decryptResult.error().isCanceled() || decryptResult.error().code() == GPG_ERR_NO_SECKEY;
1268             partMetaData()->isEncrypted = bDecryptionOk || decryptResult.error().code() != GPG_ERR_NO_DATA;
1269 
1270             if (decryptResult.error().isCanceled()) {
1271                 setDecryptMessage(false);
1272             }
1273 
1274             partMetaData()->errorText = QString::fromLocal8Bit(decryptResult.error().asString());
1275             if (Kleo::DeVSCompliance::isCompliant()) {
1276                 partMetaData()->isCompliant = decryptResult.isDeVs();
1277                 partMetaData()->compliance = Kleo::DeVSCompliance::name(decryptResult.isDeVs());
1278             } else {
1279                 partMetaData()->isCompliant = true;
1280             }
1281             if (partMetaData()->isEncrypted && decryptResult.numRecipients() > 0) {
1282                 partMetaData()->keyId = decryptResult.recipient(0).keyID();
1283             }
1284 
1285             if (bDecryptionOk) {
1286                 mDecryptedData = plainText;
1287             } else {
1288                 mNoSecKey = true;
1289                 const auto decryRecipients = decryptResult.recipients();
1290                 for (const GpgME::DecryptionResult::Recipient &recipient : decryRecipients) {
1291                     mNoSecKey &= (recipient.status().code() == GPG_ERR_NO_SECKEY);
1292                 }
1293                 if (!mPassphraseError && !mNoSecKey) { // GpgME do not detect passphrase error correctly
1294                     mPassphraseError = true;
1295                 }
1296             }
1297         }
1298     }
1299 
1300     if (!bDecryptionOk) {
1301         QString cryptPlugLibName;
1302         if (mCryptoProto) {
1303             cryptPlugLibName = mCryptoProto->name();
1304         }
1305 
1306         if (!mCryptoProto) {
1307             partMetaData()->errorText = i18n("No appropriate crypto plug-in was found.");
1308         } else if (cannotDecrypt) {
1309             partMetaData()->errorText = i18n("Crypto plug-in \"%1\" cannot decrypt messages.", cryptPlugLibName);
1310         } else if (!passphraseError()) {
1311             partMetaData()->errorText = i18n("Crypto plug-in \"%1\" could not decrypt the data.", cryptPlugLibName) + QLatin1StringView("<br />")
1312                 + i18n("Error: %1", partMetaData()->errorText);
1313         }
1314     }
1315     return bDecryptionOk;
1316 }
1317 
1318 void EncryptedMessagePart::startDecryption(KMime::Content *data)
1319 {
1320     if (!content() && !data) {
1321         return;
1322     }
1323 
1324     if (!data) {
1325         data = content();
1326     }
1327 
1328     partMetaData()->isEncrypted = true;
1329 
1330     bool bOkDecrypt = okDecryptMIME(*data);
1331 
1332     if (partMetaData()->inProgress) {
1333         return;
1334     }
1335     partMetaData()->isDecryptable = bOkDecrypt;
1336 
1337     if (!partMetaData()->isDecryptable) {
1338         setText(QString::fromUtf8(mDecryptedData.constData()));
1339     }
1340 
1341     if (partMetaData()->isEncrypted && !decryptMessage()) {
1342         partMetaData()->isDecryptable = true;
1343     }
1344 
1345     if (content() && !partMetaData()->isSigned) {
1346         mOtp->nodeHelper()->setPartMetaData(content(), *partMetaData());
1347 
1348         if (decryptMessage()) {
1349             auto tempNode = new KMime::Content();
1350             tempNode->setContent(KMime::CRLFtoLF(mDecryptedData.constData()));
1351             tempNode->parse();
1352 
1353             if (!tempNode->head().isEmpty()) {
1354                 tempNode->contentDescription()->from7BitString("encrypted data");
1355             }
1356             mOtp->nodeHelper()->attachExtraContent(content(), tempNode);
1357 
1358             parseInternal(tempNode, false);
1359         }
1360     }
1361 }
1362 
1363 QString EncryptedMessagePart::plaintextContent() const
1364 {
1365     if (!content()) {
1366         return MessagePart::text();
1367     } else {
1368         return {};
1369     }
1370 }
1371 
1372 QString EncryptedMessagePart::htmlContent() const
1373 {
1374     if (!content()) {
1375         return MessagePart::text();
1376     } else {
1377         return {};
1378     }
1379 }
1380 
1381 QString EncryptedMessagePart::text() const
1382 {
1383     if (hasSubParts()) {
1384         auto _mp = (subParts()[0]).dynamicCast<SignedMessagePart>();
1385         if (_mp) {
1386             return _mp->text();
1387         } else {
1388             return MessagePart::text();
1389         }
1390     } else {
1391         return MessagePart::text();
1392     }
1393 }
1394 
1395 const QGpgME::Protocol *EncryptedMessagePart::cryptoProto() const
1396 {
1397     return mCryptoProto;
1398 }
1399 
1400 QString EncryptedMessagePart::fromAddress() const
1401 {
1402     return mFromAddress;
1403 }
1404 
1405 const std::vector<std::pair<GpgME::DecryptionResult::Recipient, GpgME::Key>> &EncryptedMessagePart::decryptRecipients() const
1406 {
1407     return mDecryptRecipients;
1408 }
1409 
1410 bool EncryptedMessagePart::hasHeader(const char *headerType) const
1411 {
1412     const auto extraContent = mOtp->nodeHelper()->decryptedNodeForContent(content());
1413     if (extraContent) {
1414         return nodeHelper()->hasMailHeader(headerType, extraContent);
1415     }
1416     return false;
1417 }
1418 
1419 const KMime::Headers::Base *EncryptedMessagePart::header(const char *headerType) const
1420 {
1421     const auto extraContent = mOtp->nodeHelper()->decryptedNodeForContent(content());
1422     if (extraContent) {
1423         return nodeHelper()->mailHeaderAsBase(headerType, extraContent);
1424     }
1425     return nullptr;
1426 }
1427 
1428 QList<KMime::Headers::Base *> EncryptedMessagePart::headers(const char *headerType) const
1429 {
1430     const auto extraContent = mOtp->nodeHelper()->decryptedNodeForContent(content());
1431     if (extraContent) {
1432         return nodeHelper()->headers(headerType, extraContent);
1433     }
1434     return {};
1435 }
1436 
1437 EncapsulatedRfc822MessagePart::EncapsulatedRfc822MessagePart(ObjectTreeParser *otp, KMime::Content *node, const KMime::Message::Ptr &message)
1438     : MessagePart(otp, QString())
1439     , mMessage(message)
1440 {
1441     setContent(node);
1442     partMetaData()->isEncrypted = false;
1443     partMetaData()->isSigned = false;
1444     partMetaData()->isEncapsulatedRfc822Message = true;
1445 
1446     mOtp->nodeHelper()->setNodeDisplayedEmbedded(node, true);
1447     mOtp->nodeHelper()->setPartMetaData(node, *partMetaData());
1448 
1449     if (!mMessage) {
1450         qCWarning(MIMETREEPARSER_LOG) << "Node is of type message/rfc822 but doesn't have a message!";
1451         return;
1452     }
1453 
1454     // The link to "Encapsulated message" is clickable, therefore the temp file needs to exists,
1455     // since the user can click the link and expect to have normal attachment operations there.
1456     mOtp->nodeHelper()->writeNodeToTempFile(message.data());
1457 
1458     parseInternal(message.data(), false);
1459 }
1460 
1461 EncapsulatedRfc822MessagePart::~EncapsulatedRfc822MessagePart() = default;
1462 
1463 QString EncapsulatedRfc822MessagePart::text() const
1464 {
1465     return renderInternalText();
1466 }
1467 
1468 void EncapsulatedRfc822MessagePart::fix() const
1469 {
1470 }
1471 
1472 const KMime::Message::Ptr EncapsulatedRfc822MessagePart::message() const
1473 {
1474     return mMessage;
1475 }
1476 
1477 #include "moc_messagepart.cpp"