File indexing completed on 2024-12-22 05:05:20

0001 // SPDX-FileCopyrightText: 2015 Sandro Knauß <knauss@kolabsys.com>
0002 // SPDX-FileCopyrightText: 2017 Christian Mollekopf <mollekopf@kolabsys.com>
0003 // SPDX-License-Identifier: LGPL-2.0-or-later
0004 
0005 #include "messagepart.h"
0006 #include "cryptohelper.h"
0007 #include "mimetreeparser_core_debug.h"
0008 #include "objecttreeparser.h"
0009 
0010 #include "utils.h"
0011 
0012 #include <KLocalizedString>
0013 #include <KMime/Content>
0014 #include <Libkleo/Compliance>
0015 #include <Libkleo/KeyCache>
0016 
0017 #include <QGpgME/DN>
0018 #include <QGpgME/DecryptVerifyJob>
0019 #include <QGpgME/Protocol>
0020 #include <QGpgME/VerifyDetachedJob>
0021 #include <QGpgME/VerifyOpaqueJob>
0022 
0023 #include <QStringDecoder>
0024 
0025 #include <gpgme++/key.h>
0026 #include <gpgme++/keylistresult.h>
0027 #include <gpgme.h>
0028 
0029 using namespace MimeTreeParser;
0030 
0031 //------MessagePart-----------------------
0032 MessagePart::MessagePart(ObjectTreeParser *otp, const QString &text, KMime::Content *node)
0033     : mText(text)
0034     , mOtp(otp)
0035     , mParentPart(nullptr)
0036     , mNode(node) // only null for messagepartlist
0037     , mError(NoError)
0038     , mRoot(false)
0039 {
0040 }
0041 
0042 MessagePart::~MessagePart()
0043 {
0044     for (auto n : std::as_const(mNodesToDelete)) {
0045         delete n;
0046     }
0047 }
0048 
0049 MessagePart::Disposition MessagePart::disposition() const
0050 {
0051     if (!mNode) {
0052         return Invalid;
0053     }
0054     const auto cd = mNode->contentDisposition(false);
0055     if (!cd) {
0056         return Invalid;
0057     }
0058     switch (cd->disposition()) {
0059     case KMime::Headers::CDinline:
0060         return Inline;
0061     case KMime::Headers::CDattachment:
0062         return Attachment;
0063     default:
0064         return Invalid;
0065     }
0066 }
0067 
0068 QString MessagePart::filename() const
0069 {
0070     if (!mNode) {
0071         return {};
0072     }
0073 
0074     if (const auto cd = mNode->contentDisposition(false)) {
0075         const auto name = cd->filename();
0076         // Allow for a fallback for mails that have a ContentDisposition header, but don't set the filename anyways.
0077         // Not the recommended way, but exists.
0078         if (!name.isEmpty()) {
0079             return name;
0080         }
0081     }
0082     if (const auto ct = mNode->contentType(false)) {
0083         return ct->name();
0084     }
0085     return {};
0086 }
0087 
0088 static KMime::Headers::ContentType *contentType(KMime::Content *node)
0089 {
0090     if (node) {
0091         return node->contentType(false);
0092     }
0093     return nullptr;
0094 }
0095 
0096 QByteArray MessagePart::charset() const
0097 {
0098     if (!mNode) {
0099         return "us-ascii";
0100     }
0101     if (auto ct = contentType(mNode)) {
0102         return ct->charset();
0103     }
0104     // Per rfc2045 us-ascii is the default
0105     return "us-ascii";
0106 }
0107 
0108 QByteArray MessagePart::mimeType() const
0109 {
0110     if (auto ct = contentType(mNode)) {
0111         return ct->mimeType();
0112     }
0113     return {};
0114 }
0115 
0116 bool MessagePart::isText() const
0117 {
0118     if (auto ct = contentType(mNode)) {
0119         return ct->isText();
0120     }
0121     return false;
0122 }
0123 
0124 MessagePart::Error MessagePart::error() const
0125 {
0126     return mError;
0127 }
0128 
0129 QString MessagePart::errorString() const
0130 {
0131     return mMetaData.errorText;
0132 }
0133 
0134 PartMetaData *MessagePart::partMetaData()
0135 {
0136     return &mMetaData;
0137 }
0138 
0139 bool MessagePart::isAttachment() const
0140 {
0141     if (mNode) {
0142         return KMime::isAttachment(mNode);
0143     }
0144     return false;
0145 }
0146 
0147 KMime::Content *MessagePart::node() const
0148 {
0149     return mNode;
0150 }
0151 
0152 void MessagePart::setIsRoot(bool root)
0153 {
0154     mRoot = root;
0155 }
0156 
0157 bool MessagePart::isRoot() const
0158 {
0159     return mRoot;
0160 }
0161 
0162 QString MessagePart::text() const
0163 {
0164     return mText;
0165 }
0166 
0167 void MessagePart::setText(const QString &text)
0168 {
0169     mText = text;
0170 }
0171 
0172 bool MessagePart::isHtml() const
0173 {
0174     return false;
0175 }
0176 
0177 MessagePart *MessagePart::parentPart() const
0178 {
0179     return mParentPart;
0180 }
0181 
0182 void MessagePart::setParentPart(MessagePart *parentPart)
0183 {
0184     mParentPart = parentPart;
0185 }
0186 
0187 QString MessagePart::htmlContent() const
0188 {
0189     return text();
0190 }
0191 
0192 QString MessagePart::plaintextContent() const
0193 {
0194     return text();
0195 }
0196 
0197 void MessagePart::parseInternal(KMime::Content *node, bool onlyOneMimePart)
0198 {
0199     auto subMessagePart = mOtp->parseObjectTreeInternal(node, onlyOneMimePart);
0200     mRoot = subMessagePart->isRoot();
0201     for (const auto &part : std::as_const(subMessagePart->subParts())) {
0202         appendSubPart(part);
0203     }
0204 }
0205 
0206 void MessagePart::parseInternal(const QByteArray &data)
0207 {
0208     auto tempNode = new KMime::Content();
0209 
0210     const auto lfData = KMime::CRLFtoLF(data);
0211     // We have to deal with both bodies and full parts. In inline encrypted/signed parts we can have nested parts,
0212     // or just plain-text, and both ends up here. setContent defaults to setting only the header, so we have to avoid this.
0213     if (lfData.contains("\n\n")) {
0214         tempNode->setContent(lfData);
0215     } else {
0216         tempNode->setBody(lfData);
0217     }
0218     tempNode->parse();
0219     tempNode->contentType()->setCharset(charset());
0220     bindLifetime(tempNode);
0221 
0222     if (!tempNode->head().isEmpty()) {
0223         tempNode->contentDescription()->from7BitString("temporary node");
0224     }
0225 
0226     parseInternal(tempNode);
0227 }
0228 
0229 QString MessagePart::renderInternalText() const
0230 {
0231     QString text;
0232     for (const auto &mp : subParts()) {
0233         text += mp->text();
0234     }
0235     return text;
0236 }
0237 
0238 void MessagePart::appendSubPart(const MessagePart::Ptr &messagePart)
0239 {
0240     messagePart->setParentPart(this);
0241     mBlocks.append(messagePart);
0242 }
0243 
0244 const QList<MessagePart::Ptr> &MessagePart::subParts() const
0245 {
0246     return mBlocks;
0247 }
0248 
0249 bool MessagePart::hasSubParts() const
0250 {
0251     return !mBlocks.isEmpty();
0252 }
0253 
0254 QList<SignedMessagePart *> MessagePart::signatures() const
0255 {
0256     QList<SignedMessagePart *> list;
0257     if (auto sig = dynamic_cast<SignedMessagePart *>(const_cast<MessagePart *>(this))) {
0258         list << sig;
0259     }
0260     auto parent = parentPart();
0261     while (parent) {
0262         if (auto sig = dynamic_cast<SignedMessagePart *>(parent)) {
0263             list << sig;
0264         }
0265         parent = parent->parentPart();
0266     }
0267     return list;
0268 }
0269 
0270 QList<EncryptedMessagePart *> MessagePart::encryptions() const
0271 {
0272     QList<EncryptedMessagePart *> list;
0273     if (auto sig = dynamic_cast<EncryptedMessagePart *>(const_cast<MessagePart *>(this))) {
0274         list << sig;
0275     }
0276     auto parent = parentPart();
0277     while (parent) {
0278         if (auto sig = dynamic_cast<EncryptedMessagePart *>(parent)) {
0279             list << sig;
0280         }
0281         parent = parent->parentPart();
0282     }
0283     return list;
0284 }
0285 
0286 KMMsgEncryptionState MessagePart::encryptionState() const
0287 {
0288     if (!encryptions().isEmpty()) {
0289         return KMMsgFullyEncrypted;
0290     }
0291     return KMMsgNotEncrypted;
0292 }
0293 
0294 KMMsgSignatureState MessagePart::signatureState() const
0295 {
0296     if (!signatures().isEmpty()) {
0297         return KMMsgFullySigned;
0298     }
0299     return KMMsgNotSigned;
0300 }
0301 
0302 void MessagePart::bindLifetime(KMime::Content *node)
0303 {
0304     mNodesToDelete << node;
0305 }
0306 
0307 KMime::Headers::Base *MimeTreeParser::MessagePart::header(const char *header) const
0308 {
0309     if (node() && node()->hasHeader(header)) {
0310         return node()->headerByType(header);
0311     }
0312     if (auto parent = parentPart()) {
0313         return parent->header(header);
0314     }
0315 
0316     return nullptr;
0317 }
0318 
0319 //-----MessagePartList----------------------
0320 MessagePartList::MessagePartList(ObjectTreeParser *otp, KMime::Content *node)
0321     : MessagePart(otp, QString(), node)
0322 {
0323 }
0324 
0325 QString MessagePartList::text() const
0326 {
0327     return renderInternalText();
0328 }
0329 
0330 QString MessagePartList::plaintextContent() const
0331 {
0332     return QString();
0333 }
0334 
0335 QString MessagePartList::htmlContent() const
0336 {
0337     return QString();
0338 }
0339 
0340 //-----TextMessageBlock----------------------
0341 
0342 TextMessagePart::TextMessagePart(ObjectTreeParser *otp, KMime::Content *node)
0343     : MessagePartList(otp, node)
0344     , mSignatureState(KMMsgSignatureStateUnknown)
0345     , mEncryptionState(KMMsgEncryptionStateUnknown)
0346 {
0347     if (!mNode) {
0348         qCWarning(MIMETREEPARSER_CORE_LOG) << "not a valid node";
0349         return;
0350     }
0351 
0352     parseContent();
0353 }
0354 
0355 void TextMessagePart::parseContent()
0356 {
0357     mSignatureState = KMMsgNotSigned;
0358     mEncryptionState = KMMsgNotEncrypted;
0359     const auto blocks = prepareMessageForDecryption(mNode->decodedContent());
0360     // We also get blocks for unencrypted messages
0361     if (!blocks.isEmpty()) {
0362         auto aCodec = QStringDecoder(mOtp->codecNameFor(mNode).constData());
0363         const auto cryptProto = QGpgME::openpgp();
0364 
0365         /* The (overall) signature/encrypted status is broken
0366          * if one unencrypted part is at the beginning or in the middle
0367          * because mailmain adds an unencrypted part at the end this should not break the overall status
0368          *
0369          * That's why we first set the tmp status and if one crypted/signed block comes afterwards, than
0370          * the status is set to unencryped
0371          */
0372         bool fullySignedOrEncrypted = true;
0373         bool fullySignedOrEncryptedTmp = true;
0374 
0375         for (const auto &block : blocks) {
0376             if (!fullySignedOrEncryptedTmp) {
0377                 fullySignedOrEncrypted = false;
0378             }
0379 
0380             if (block.type() == NoPgpBlock && !block.text().trimmed().isEmpty()) {
0381                 fullySignedOrEncryptedTmp = false;
0382                 appendSubPart(MessagePart::Ptr(new MessagePart(mOtp, aCodec.decode(KMime::CRLFtoLF(block.text())))));
0383             } else if (block.type() == PgpMessageBlock) {
0384                 auto content = new KMime::Content;
0385                 content->setBody(block.text());
0386                 content->parse();
0387                 content->contentType()->setCharset(charset());
0388                 EncryptedMessagePart::Ptr mp(new EncryptedMessagePart(mOtp, QString(), cryptProto, content, content, false));
0389                 mp->bindLifetime(content);
0390                 mp->setIsEncrypted(true);
0391                 appendSubPart(mp);
0392             } else if (block.type() == ClearsignedBlock) {
0393                 auto content = new KMime::Content;
0394                 content->setBody(block.text());
0395                 content->parse();
0396                 content->contentType()->setCharset(charset());
0397                 SignedMessagePart::Ptr mp(new SignedMessagePart(mOtp, cryptProto, nullptr, content, false));
0398                 mp->bindLifetime(content);
0399                 mp->setIsSigned(true);
0400                 appendSubPart(mp);
0401             } else {
0402                 continue;
0403             }
0404 
0405             const auto mp = subParts().last().staticCast<MessagePart>();
0406             const PartMetaData *messagePart(mp->partMetaData());
0407 
0408             if (!messagePart->isEncrypted && !messagePart->isSigned && !block.text().trimmed().isEmpty()) {
0409                 mp->setText(aCodec.decode(KMime::CRLFtoLF(block.text())));
0410             }
0411 
0412             if (messagePart->isEncrypted) {
0413                 mEncryptionState = KMMsgPartiallyEncrypted;
0414             }
0415 
0416             if (messagePart->isSigned) {
0417                 mSignatureState = KMMsgPartiallySigned;
0418             }
0419         }
0420 
0421         // Do we have an fully Signed/Encrypted Message?
0422         if (fullySignedOrEncrypted) {
0423             if (mSignatureState == KMMsgPartiallySigned) {
0424                 mSignatureState = KMMsgFullySigned;
0425             }
0426             if (mEncryptionState == KMMsgPartiallyEncrypted) {
0427                 mEncryptionState = KMMsgFullyEncrypted;
0428             }
0429         }
0430     }
0431 }
0432 
0433 KMMsgEncryptionState TextMessagePart::encryptionState() const
0434 {
0435     if (mEncryptionState == KMMsgNotEncrypted) {
0436         return MessagePart::encryptionState();
0437     }
0438     return mEncryptionState;
0439 }
0440 
0441 KMMsgSignatureState TextMessagePart::signatureState() const
0442 {
0443     if (mSignatureState == KMMsgNotSigned) {
0444         return MessagePart::signatureState();
0445     }
0446     return mSignatureState;
0447 }
0448 
0449 //-----AttachmentMessageBlock----------------------
0450 
0451 AttachmentMessagePart::AttachmentMessagePart(ObjectTreeParser *otp, KMime::Content *node)
0452     : TextMessagePart(otp, node)
0453 {
0454 }
0455 
0456 //-----HtmlMessageBlock----------------------
0457 
0458 HtmlMessagePart::HtmlMessagePart(ObjectTreeParser *otp, KMime::Content *node)
0459     : MessagePart(otp, QString(), node)
0460 {
0461     if (!mNode) {
0462         qCWarning(MIMETREEPARSER_CORE_LOG) << "not a valid node";
0463         return;
0464     }
0465 
0466     setText(QStringDecoder(mOtp->codecNameFor(mNode).constData()).decode(KMime::CRLFtoLF(mNode->decodedContent())));
0467 }
0468 
0469 //-----MimeMessageBlock----------------------
0470 
0471 MimeMessagePart::MimeMessagePart(ObjectTreeParser *otp, KMime::Content *node, bool onlyOneMimePart)
0472     : MessagePart(otp, QString(), node)
0473 {
0474     if (!mNode) {
0475         qCWarning(MIMETREEPARSER_CORE_LOG) << "not a valid node";
0476         return;
0477     }
0478 
0479     parseInternal(mNode, onlyOneMimePart);
0480 }
0481 
0482 MimeMessagePart::~MimeMessagePart()
0483 {
0484 }
0485 
0486 QString MimeMessagePart::text() const
0487 {
0488     return renderInternalText();
0489 }
0490 
0491 QString MimeMessagePart::plaintextContent() const
0492 {
0493     return QString();
0494 }
0495 
0496 QString MimeMessagePart::htmlContent() const
0497 {
0498     return QString();
0499 }
0500 
0501 //-----AlternativeMessagePart----------------------
0502 
0503 AlternativeMessagePart::AlternativeMessagePart(ObjectTreeParser *otp, KMime::Content *node)
0504     : MessagePart(otp, QString(), node)
0505 {
0506     if (auto dataIcal = findTypeInDirectChildren(mNode, "text/calendar")) {
0507         mChildParts[MultipartIcal] = MimeMessagePart::Ptr(new MimeMessagePart(mOtp, dataIcal, true));
0508     }
0509 
0510     if (auto dataText = findTypeInDirectChildren(mNode, "text/plain")) {
0511         mChildParts[MultipartPlain] = MimeMessagePart::Ptr(new MimeMessagePart(mOtp, dataText, true));
0512     }
0513 
0514     if (auto dataHtml = findTypeInDirectChildren(mNode, "text/html")) {
0515         mChildParts[MultipartHtml] = MimeMessagePart::Ptr(new MimeMessagePart(mOtp, dataHtml, true));
0516     } else {
0517         // If we didn't find the HTML part as the first child of the multipart/alternative, it might
0518         // be that this is a HTML message with images, and text/plain and multipart/related are the
0519         // immediate children of this multipart/alternative node.
0520         // In this case, the HTML node is a child of multipart/related.
0521         // In the case of multipart/related we don't expect multiple html parts, it is usually used to group attachments
0522         // with html content.
0523         //
0524         // In any case, this is not a complete implementation of MIME, but an approximation for the kind of mails we actually see in the wild.
0525         auto data = [&] {
0526             if (auto d = findTypeInDirectChildren(mNode, "multipart/related")) {
0527                 return d;
0528             }
0529             return findTypeInDirectChildren(mNode, "multipart/mixed");
0530         }();
0531         if (data) {
0532             QString htmlContent;
0533             const auto parts = data->contents();
0534             for (auto p : parts) {
0535                 if ((!p->contentType()->isEmpty()) && (p->contentType()->mimeType() == "text/html")) {
0536                     htmlContent += MimeMessagePart(mOtp, p, true).text();
0537                 } else if (KMime::isAttachment(p)) {
0538                     appendSubPart(MimeMessagePart::Ptr(new MimeMessagePart(otp, p, true)));
0539                 }
0540             }
0541             mChildParts[MultipartHtml] = MessagePart::Ptr(new MessagePart(mOtp, htmlContent, nullptr));
0542         }
0543     }
0544 }
0545 
0546 AlternativeMessagePart::~AlternativeMessagePart()
0547 {
0548 }
0549 
0550 QList<AlternativeMessagePart::HtmlMode> AlternativeMessagePart::availableModes()
0551 {
0552     return mChildParts.keys();
0553 }
0554 
0555 QString AlternativeMessagePart::text() const
0556 {
0557     if (mChildParts.contains(MultipartPlain)) {
0558         return mChildParts[MultipartPlain]->text();
0559     }
0560     return QString();
0561 }
0562 
0563 bool AlternativeMessagePart::isHtml() const
0564 {
0565     return mChildParts.contains(MultipartHtml);
0566 }
0567 
0568 QString AlternativeMessagePart::plaintextContent() const
0569 {
0570     return text();
0571 }
0572 
0573 QString AlternativeMessagePart::htmlContent() const
0574 {
0575     if (mChildParts.contains(MultipartHtml)) {
0576         return mChildParts[MultipartHtml]->text();
0577     } else {
0578         return plaintextContent();
0579     }
0580 }
0581 
0582 QString AlternativeMessagePart::icalContent() const
0583 {
0584     if (mChildParts.contains(MultipartIcal)) {
0585         return mChildParts[MultipartIcal]->text();
0586     }
0587     return {};
0588 }
0589 
0590 //-----CertMessageBlock----------------------
0591 
0592 CertMessagePart::CertMessagePart(ObjectTreeParser *otp, KMime::Content *node, QGpgME::Protocol *cryptoProto)
0593     : MessagePart(otp, QString(), node)
0594     , mCryptoProto(cryptoProto)
0595 {
0596     if (!mNode) {
0597         qCWarning(MIMETREEPARSER_CORE_LOG) << "not a valid node";
0598         return;
0599     }
0600 }
0601 
0602 CertMessagePart::~CertMessagePart()
0603 {
0604 }
0605 
0606 QString CertMessagePart::text() const
0607 {
0608     return QString();
0609 }
0610 
0611 //-----SignedMessageBlock---------------------
0612 SignedMessagePart::SignedMessagePart(ObjectTreeParser *otp,
0613                                      const QGpgME::Protocol *cryptoProto,
0614                                      KMime::Content *node,
0615                                      KMime::Content *signedData,
0616                                      bool parseAfterDecryption)
0617     : MessagePart(otp, {}, node)
0618     , mParseAfterDecryption(parseAfterDecryption)
0619     , mCryptoProto(cryptoProto)
0620     , mSignedData(signedData)
0621 {
0622     mMetaData.isSigned = true;
0623     mMetaData.isGoodSignature = false;
0624     mMetaData.status = i18ndc("mimetreeparser", "@info:status", "Wrong Crypto Plug-In.");
0625 }
0626 
0627 SignedMessagePart::~SignedMessagePart()
0628 {
0629 }
0630 
0631 void SignedMessagePart::setIsSigned(bool isSigned)
0632 {
0633     mMetaData.isSigned = isSigned;
0634 }
0635 
0636 bool SignedMessagePart::isSigned() const
0637 {
0638     return mMetaData.isSigned;
0639 }
0640 
0641 static QString prettifyDN(const char *uid)
0642 {
0643     // We used to use QGpgME::DN::prettyDN here. But I'm not sure what we actually need it for.
0644     return QString::fromUtf8(uid);
0645 }
0646 
0647 const QGpgME::Protocol *SignedMessagePart::cryptoProto() const
0648 {
0649     return mCryptoProto;
0650 }
0651 
0652 void SignedMessagePart::startVerification()
0653 {
0654     if (!mSignedData) {
0655         return;
0656     }
0657 
0658     mMetaData.isSigned = false;
0659     mMetaData.status = i18ndc("mimetreeparser", "@info:status", "Wrong Crypto Plug-In.");
0660     mMetaData.isEncrypted = false;
0661     mMetaData.isDecryptable = false;
0662 
0663     auto codec = QStringDecoder(mOtp->codecNameFor(mSignedData).constData());
0664 
0665     // If we have a mNode, this is a detached signature
0666     if (mNode) {
0667         const auto signature = mNode->decodedContent();
0668 
0669         // This is necessary in case the original data contained CRLF's. Otherwise the signature will not match the data (since KMIME normalizes to LF)
0670         const QByteArray signedData = KMime::LFtoCRLF(mSignedData->encodedContent());
0671 
0672         const auto job = mCryptoProto->verifyDetachedJob();
0673         setVerificationResult(job->exec(signature, signedData), signedData);
0674         job->deleteLater();
0675         setText(codec.decode(KMime::CRLFtoLF(signedData)));
0676     } else {
0677         QByteArray outdata;
0678         const auto job = mCryptoProto->verifyOpaqueJob();
0679         setVerificationResult(job->exec(mSignedData->decodedContent(), outdata), outdata);
0680         job->deleteLater();
0681         setText(codec.decode(KMime::CRLFtoLF(outdata)));
0682     }
0683 
0684     if (!mMetaData.isSigned) {
0685         mMetaData.creationTime = QDateTime();
0686     }
0687 }
0688 
0689 static int signatureToStatus(const GpgME::Signature &sig)
0690 {
0691     switch (sig.status().code()) {
0692     case GPG_ERR_NO_ERROR:
0693         return GPGME_SIG_STAT_GOOD;
0694     case GPG_ERR_BAD_SIGNATURE:
0695         return GPGME_SIG_STAT_BAD;
0696     case GPG_ERR_NO_PUBKEY:
0697         return GPGME_SIG_STAT_NOKEY;
0698     case GPG_ERR_NO_DATA:
0699         return GPGME_SIG_STAT_NOSIG;
0700     case GPG_ERR_SIG_EXPIRED:
0701         return GPGME_SIG_STAT_GOOD_EXP;
0702     case GPG_ERR_KEY_EXPIRED:
0703         return GPGME_SIG_STAT_GOOD_EXPKEY;
0704     default:
0705         return GPGME_SIG_STAT_ERROR;
0706     }
0707 }
0708 
0709 void SignedMessagePart::sigStatusToMetaData()
0710 {
0711     GpgME::Key key;
0712     if (partMetaData()->isSigned) {
0713         GpgME::Signature signature = mSignatures.front();
0714         mMetaData.status_code = signatureToStatus(signature);
0715         mMetaData.isGoodSignature = partMetaData()->status_code == GPGME_SIG_STAT_GOOD;
0716         // save extended signature status flags
0717         mMetaData.sigSummary = signature.summary();
0718 
0719         if (partMetaData()->isGoodSignature && !key.keyID()) {
0720             // Search for the key by its fingerprint so that we can check for
0721             // trust etc.
0722             key = Kleo::KeyCache::instance()->findByFingerprint(signature.fingerprint());
0723             if (key.isNull() && signature.fingerprint()) {
0724                 // try to find a subkey that was used for signing;
0725                 // assumes that the key ID is the last 16 characters of the fingerprint
0726                 const auto fpr = std::string_view{signature.fingerprint()};
0727                 const auto keyID = std::string{fpr, fpr.size() - 16, 16};
0728                 const auto subkeys = Kleo::KeyCache::instance()->findSubkeysByKeyID({keyID});
0729                 if (subkeys.size() > 0) {
0730                     key = subkeys[0].parent();
0731                 }
0732             }
0733             if (key.isNull()) {
0734                 qCDebug(MIMETREEPARSER_CORE_LOG) << "Found no key or subkey for fingerprint" << signature.fingerprint();
0735             }
0736         }
0737 
0738         if (key.keyID()) {
0739             partMetaData()->keyId = key.keyID();
0740         }
0741         if (partMetaData()->keyId.isEmpty()) {
0742             partMetaData()->keyId = signature.fingerprint();
0743         }
0744         partMetaData()->keyTrust = signature.validity();
0745         if (key.numUserIDs() > 0 && key.userID(0).id()) {
0746             partMetaData()->signer = prettifyDN(key.userID(0).id());
0747         }
0748         for (const auto &uid : key.userIDs()) {
0749             // The following if /should/ always result in TRUE but we
0750             // won't trust implicitly the plugin that gave us these data.
0751             if (uid.email()) {
0752                 QString email = QString::fromUtf8(uid.email());
0753                 if (!email.isEmpty()) {
0754                     partMetaData()->signerMailAddresses.append(email);
0755                 }
0756             }
0757         }
0758 
0759         if (signature.creationTime()) {
0760             partMetaData()->creationTime.setSecsSinceEpoch(signature.creationTime());
0761         } else {
0762             partMetaData()->creationTime = QDateTime();
0763         }
0764         if (partMetaData()->signer.isEmpty()) {
0765             if (key.numUserIDs() > 0 && key.userID(0).name()) {
0766                 partMetaData()->signer = prettifyDN(key.userID(0).name());
0767             }
0768             if (!partMetaData()->signerMailAddresses.empty()) {
0769                 if (partMetaData()->signer.isEmpty()) {
0770                     partMetaData()->signer = partMetaData()->signerMailAddresses.front();
0771                 } else {
0772                     partMetaData()->signer += QLatin1StringView(" <") + partMetaData()->signerMailAddresses.front() + QLatin1Char('>');
0773                 }
0774             }
0775         }
0776         if (Kleo::DeVSCompliance::isCompliant()) {
0777             partMetaData()->isCompliant = signature.isDeVs();
0778             partMetaData()->compliance = Kleo::DeVSCompliance::name(signature.isDeVs());
0779         } else {
0780             partMetaData()->isCompliant = true;
0781         }
0782     }
0783 }
0784 
0785 void SignedMessagePart::setVerificationResult(const GpgME::VerificationResult &result, const QByteArray &signedData)
0786 {
0787     mSignatures = result.signatures();
0788     // FIXME
0789     // mMetaData.auditLogError = result.error;
0790     mMetaData.isSigned = !mSignatures.empty();
0791     if (mMetaData.isSigned) {
0792         sigStatusToMetaData();
0793         if (!signedData.isEmpty() && mParseAfterDecryption) {
0794             parseInternal(signedData);
0795         }
0796     }
0797 }
0798 
0799 QString SignedMessagePart::plaintextContent() const
0800 {
0801     if (!mNode) {
0802         return MessagePart::text();
0803     } else {
0804         return QString();
0805     }
0806 }
0807 
0808 QString SignedMessagePart::htmlContent() const
0809 {
0810     if (!mNode) {
0811         return MessagePart::text();
0812     } else {
0813         return QString();
0814     }
0815 }
0816 
0817 //-----CryptMessageBlock---------------------
0818 EncryptedMessagePart::EncryptedMessagePart(ObjectTreeParser *otp,
0819                                            const QString &text,
0820                                            const QGpgME::Protocol *cryptoProto,
0821                                            KMime::Content *node,
0822                                            KMime::Content *encryptedNode,
0823                                            bool parseAfterDecryption)
0824     : MessagePart(otp, text, node)
0825     , mParseAfterDecryption(parseAfterDecryption)
0826     , mPassphraseError(false)
0827     , mNoSecKey(false)
0828     , mDecryptMessage(false)
0829     , mCryptoProto(cryptoProto)
0830     , mEncryptedNode(encryptedNode)
0831 {
0832     mMetaData.isSigned = false;
0833     mMetaData.isGoodSignature = false;
0834     mMetaData.isEncrypted = false;
0835     mMetaData.isDecryptable = false;
0836     mMetaData.status = i18ndc("mimetreeparser", "@info:status", "Wrong Crypto Plug-In.");
0837 }
0838 
0839 void EncryptedMessagePart::setIsEncrypted(bool encrypted)
0840 {
0841     mMetaData.isEncrypted = encrypted;
0842 }
0843 
0844 bool EncryptedMessagePart::isEncrypted() const
0845 {
0846     return mMetaData.isEncrypted;
0847 }
0848 
0849 const QGpgME::Protocol *EncryptedMessagePart::cryptoProto() const
0850 {
0851     return mCryptoProto;
0852 }
0853 
0854 void EncryptedMessagePart::setDecryptMessage(bool decrypt)
0855 {
0856     mDecryptMessage = decrypt;
0857 }
0858 
0859 bool EncryptedMessagePart::decryptMessage() const
0860 {
0861     return mDecryptMessage;
0862 }
0863 
0864 bool EncryptedMessagePart::isDecryptable() const
0865 {
0866     return mMetaData.isDecryptable;
0867 }
0868 
0869 bool EncryptedMessagePart::isNoSecKey() const
0870 {
0871     return mNoSecKey;
0872 }
0873 
0874 bool EncryptedMessagePart::passphraseError() const
0875 {
0876     return mPassphraseError;
0877 }
0878 
0879 bool EncryptedMessagePart::decrypt(KMime::Content &data)
0880 {
0881     mError = NoError;
0882     mMetaData.errorText.clear();
0883     // FIXME
0884     //  mMetaData.auditLogError = GpgME::Error();
0885     mMetaData.auditLog.clear();
0886 
0887     const QByteArray ciphertext = data.decodedContent();
0888     QByteArray plainText;
0889     auto job = mCryptoProto->decryptVerifyJob();
0890     const std::pair<GpgME::DecryptionResult, GpgME::VerificationResult> p = job->exec(ciphertext, plainText);
0891     job->deleteLater();
0892     auto decryptResult = p.first;
0893     auto verifyResult = p.second;
0894     mMetaData.isSigned = verifyResult.signatures().size() > 0;
0895 
0896     // Normalize CRLF's
0897     plainText = KMime::CRLFtoLF(plainText);
0898     auto codec = QStringDecoder(mOtp->codecNameFor(&data).constData());
0899     const auto decoded = codec.decode(plainText);
0900 
0901     partMetaData()->isSigned = verifyResult.signatures().size() > 0;
0902 
0903     if (partMetaData()->isSigned) {
0904         // We simply attach a signed message part to indicate that this content is also signed
0905         // We're forwarding mNode to not loose the encoding information
0906         auto subPart = SignedMessagePart::Ptr(new SignedMessagePart(mOtp, mCryptoProto, mNode, nullptr));
0907         subPart->setText(decoded);
0908         subPart->setVerificationResult(verifyResult, plainText);
0909         appendSubPart(subPart);
0910     }
0911 
0912     mDecryptRecipients.clear();
0913     bool cannotDecrypt = false;
0914     bool bDecryptionOk = !decryptResult.error();
0915 
0916     for (const auto &recipient : decryptResult.recipients()) {
0917         if (!recipient.status()) {
0918             bDecryptionOk = true;
0919         }
0920         GpgME::Key key;
0921         key = Kleo::KeyCache::instance()->findByKeyIDOrFingerprint(recipient.keyID());
0922         if (key.isNull()) {
0923             auto ret = Kleo::KeyCache::instance()->findSubkeysByKeyID({recipient.keyID()});
0924             if (ret.size() == 1) {
0925                 key = ret.front().parent();
0926             }
0927             if (key.isNull()) {
0928                 qCDebug(MIMETREEPARSER_CORE_LOG) << "Found no Key for KeyID " << recipient.keyID();
0929             }
0930         }
0931         mDecryptRecipients.emplace_back(recipient, key);
0932     }
0933 
0934     if (!bDecryptionOk && partMetaData()->isSigned) {
0935         // Only a signed part
0936         partMetaData()->isEncrypted = false;
0937         bDecryptionOk = true;
0938         mDecryptedData = plainText;
0939     } else {
0940         mPassphraseError = decryptResult.error().isCanceled() || decryptResult.error().code() == GPG_ERR_BAD_PASSPHRASE;
0941         mMetaData.isEncrypted = bDecryptionOk || decryptResult.error().code() != GPG_ERR_NO_DATA;
0942 
0943         if (decryptResult.error().isCanceled()) {
0944             setDecryptMessage(false);
0945         }
0946 
0947         partMetaData()->errorText = QString::fromLocal8Bit(decryptResult.error().asString());
0948         if (Kleo::DeVSCompliance::isCompliant()) {
0949             partMetaData()->isCompliant = decryptResult.isDeVs();
0950             partMetaData()->compliance = Kleo::DeVSCompliance::name(decryptResult.isDeVs());
0951         } else {
0952             partMetaData()->isCompliant = true;
0953         }
0954         if (partMetaData()->isEncrypted && decryptResult.numRecipients() > 0) {
0955             partMetaData()->keyId = decryptResult.recipient(0).keyID();
0956         }
0957 
0958         if (bDecryptionOk) {
0959             mDecryptedData = plainText;
0960         } else {
0961             mNoSecKey = true;
0962             const auto decryRecipients = decryptResult.recipients();
0963             for (const GpgME::DecryptionResult::Recipient &recipient : decryRecipients) {
0964                 mNoSecKey &= (recipient.status().code() == GPG_ERR_NO_SECKEY);
0965             }
0966             if (!mPassphraseError && !mNoSecKey) { // GpgME do not detect passphrase error correctly
0967                 mPassphraseError = true;
0968             }
0969         }
0970     }
0971 
0972     if (!bDecryptionOk) {
0973         QString cryptPlugLibName;
0974         mError = UnknownError;
0975         if (mCryptoProto) {
0976             cryptPlugLibName = mCryptoProto->name();
0977         }
0978 
0979         if (mNoSecKey) {
0980             mError = NoKeyError;
0981         }
0982 
0983         if (mPassphraseError) {
0984             mError = PassphraseError;
0985         }
0986 
0987         if (!mCryptoProto) {
0988             partMetaData()->errorText = i18n("No appropriate crypto plug-in was found.");
0989         } else if (cannotDecrypt) {
0990             partMetaData()->errorText = i18n("Crypto plug-in \"%1\" cannot decrypt messages.", cryptPlugLibName);
0991         } else if (!passphraseError()) {
0992             partMetaData()->errorText = i18n("Crypto plug-in \"%1\" could not decrypt the data.", cryptPlugLibName) + QLatin1StringView("<br />")
0993                 + i18n("Error: %1", partMetaData()->errorText);
0994         }
0995     }
0996     return bDecryptionOk;
0997 }
0998 
0999 void EncryptedMessagePart::startDecryption(KMime::Content *data)
1000 {
1001     mMetaData.isEncrypted = true;
1002     mMetaData.isDecryptable = decrypt(*data);
1003 
1004     if (mParseAfterDecryption && !mMetaData.isSigned) {
1005         parseInternal(mDecryptedData);
1006     } else {
1007         setText(QString::fromUtf8(mDecryptedData.constData()));
1008     }
1009 }
1010 
1011 void EncryptedMessagePart::startDecryption()
1012 {
1013     if (mEncryptedNode) {
1014         startDecryption(mEncryptedNode);
1015     } else {
1016         startDecryption(mNode);
1017     }
1018 }
1019 
1020 std::vector<std::pair<GpgME::DecryptionResult::Recipient, GpgME::Key>> EncryptedMessagePart::decryptRecipients() const
1021 {
1022     return mDecryptRecipients;
1023 }
1024 
1025 QString EncryptedMessagePart::plaintextContent() const
1026 {
1027     if (!mNode) {
1028         return MessagePart::text();
1029     } else {
1030         return QString();
1031     }
1032 }
1033 
1034 QString EncryptedMessagePart::htmlContent() const
1035 {
1036     if (!mNode) {
1037         return MessagePart::text();
1038     } else {
1039         return QString();
1040     }
1041 }
1042 
1043 QString EncryptedMessagePart::text() const
1044 {
1045     if (hasSubParts()) {
1046         auto _mp = (subParts()[0]).dynamicCast<SignedMessagePart>();
1047         if (_mp) {
1048             return _mp->text();
1049         }
1050     }
1051 
1052     return MessagePart::text();
1053 }
1054 
1055 EncapsulatedRfc822MessagePart::EncapsulatedRfc822MessagePart(ObjectTreeParser *otp, KMime::Content *node, const KMime::Message::Ptr &message)
1056     : MessagePart(otp, QString(), node)
1057     , mMessage(message)
1058 {
1059     mMetaData.isEncrypted = false;
1060     mMetaData.isSigned = false;
1061     mMetaData.isEncapsulatedRfc822Message = true;
1062 
1063     if (!mMessage) {
1064         qCWarning(MIMETREEPARSER_CORE_LOG) << "Node is of type message/rfc822 but doesn't have a message!";
1065         return;
1066     }
1067 
1068     parseInternal(message.data());
1069 }
1070 
1071 QString EncapsulatedRfc822MessagePart::text() const
1072 {
1073     return renderInternalText();
1074 }
1075 
1076 QString EncapsulatedRfc822MessagePart::from() const
1077 {
1078     if (auto from = mMessage->from(false)) {
1079         return from->asUnicodeString();
1080     }
1081     return {};
1082 }
1083 
1084 QDateTime EncapsulatedRfc822MessagePart::date() const
1085 {
1086     if (auto date = mMessage->date(false)) {
1087         return date->dateTime();
1088     }
1089     return {};
1090 }
1091 
1092 HeadersPart::HeadersPart(ObjectTreeParser *otp, KMime::Content *node)
1093     : MessagePart(otp, QString(), node)
1094 {
1095 }
1096 
1097 #include "moc_messagepart.cpp"