File indexing completed on 2024-05-12 05:28:19

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_debug.h"
0008 #include "objecttreeparser.h"
0009 
0010 #include "utils.h"
0011 
0012 #include <KLocalizedString>
0013 #include <KMime/Content>
0014 
0015 #include <QTextCodec>
0016 
0017 using namespace MimeTreeParser;
0018 using namespace Crypto;
0019 
0020 //------MessagePart-----------------------
0021 MessagePart::MessagePart(ObjectTreeParser *otp, const QString &text, KMime::Content *node)
0022     : mText(text)
0023     , mOtp(otp)
0024     , mParentPart(nullptr)
0025     , mNode(node) // only null for messagepartlist
0026     , mError(NoError)
0027     , mRoot(false)
0028 {
0029 }
0030 
0031 MessagePart::~MessagePart()
0032 {
0033     for (auto n : mNodesToDelete) {
0034         delete n;
0035     }
0036 }
0037 
0038 MessagePart::Disposition MessagePart::disposition() const
0039 {
0040     if (!mNode) {
0041         return Invalid;
0042     }
0043     const auto cd = mNode->contentDisposition(false);
0044     if (!cd) {
0045         return Invalid;
0046     }
0047     switch (cd->disposition()) {
0048     case KMime::Headers::CDinline:
0049         return Inline;
0050     case KMime::Headers::CDattachment:
0051         return Attachment;
0052     default:
0053         return Invalid;
0054     }
0055 }
0056 
0057 QString MessagePart::filename() const
0058 {
0059     if (!mNode) {
0060         return {};
0061     }
0062 
0063     if (const auto cd = mNode->contentDisposition(false)) {
0064         const auto name = cd->filename();
0065         // Allow for a fallback for mails that have a ContentDisposition header, but don't set the filename anyways.
0066         // Not the recommended way, but exists.
0067         if (!name.isEmpty()) {
0068             return name;
0069         }
0070     }
0071     if (const auto ct = mNode->contentType(false)) {
0072         return ct->name();
0073     }
0074     return {};
0075 }
0076 
0077 static KMime::Headers::ContentType *contentType(KMime::Content *node)
0078 {
0079     if (node) {
0080         return node->contentType(false);
0081     }
0082     return nullptr;
0083 }
0084 
0085 QByteArray MessagePart::charset() const
0086 {
0087     if (!mNode) {
0088         return "us-ascii";
0089     }
0090     if (auto ct = contentType(mNode)) {
0091         return ct->charset();
0092     }
0093     // Per rfc2045 us-ascii is the default
0094     return "us-ascii";
0095 }
0096 
0097 QByteArray MessagePart::mimeType() const
0098 {
0099     if (auto ct = contentType(mNode)) {
0100         return ct->mimeType();
0101     }
0102     return {};
0103 }
0104 
0105 bool MessagePart::isText() const
0106 {
0107     if (auto ct = contentType(mNode)) {
0108         return ct->isText();
0109     }
0110     return false;
0111 }
0112 
0113 MessagePart::Error MessagePart::error() const
0114 {
0115     return mError;
0116 }
0117 
0118 QString MessagePart::errorString() const
0119 {
0120     return mMetaData.errorText;
0121 }
0122 
0123 PartMetaData *MessagePart::partMetaData()
0124 {
0125     return &mMetaData;
0126 }
0127 
0128 bool MessagePart::isAttachment() const
0129 {
0130     if (mNode) {
0131         return KMime::isAttachment(mNode);
0132     }
0133     return false;
0134 }
0135 
0136 KMime::Content *MessagePart::node() const
0137 {
0138     return mNode;
0139 }
0140 
0141 void MessagePart::setIsRoot(bool root)
0142 {
0143     mRoot = root;
0144 }
0145 
0146 bool MessagePart::isRoot() const
0147 {
0148     return mRoot;
0149 }
0150 
0151 QString MessagePart::text() const
0152 {
0153     return mText;
0154 }
0155 
0156 void MessagePart::setText(const QString &text)
0157 {
0158     mText = text;
0159 }
0160 
0161 bool MessagePart::isHtml() const
0162 {
0163     return false;
0164 }
0165 
0166 MessagePart *MessagePart::parentPart() const
0167 {
0168     return mParentPart;
0169 }
0170 
0171 void MessagePart::setParentPart(MessagePart *parentPart)
0172 {
0173     mParentPart = parentPart;
0174 }
0175 
0176 QString MessagePart::htmlContent() const
0177 {
0178     return text();
0179 }
0180 
0181 QString MessagePart::plaintextContent() const
0182 {
0183     return text();
0184 }
0185 
0186 void MessagePart::parseInternal(KMime::Content *node, bool onlyOneMimePart)
0187 {
0188     auto subMessagePart = mOtp->parseObjectTreeInternal(node, onlyOneMimePart);
0189     mRoot = subMessagePart->isRoot();
0190     for (const auto &part : std::as_const(subMessagePart->subParts())) {
0191         appendSubPart(part);
0192     }
0193 }
0194 
0195 void MessagePart::parseInternal(const QByteArray &data)
0196 {
0197     auto tempNode = new KMime::Content();
0198 
0199     const auto lfData = KMime::CRLFtoLF(data);
0200     // We have to deal with both bodies and full parts. In inline encrypted/signed parts we can have nested parts,
0201     // or just plain-text, and both ends up here. setContent defaults to setting only the header, so we have to avoid this.
0202     if (lfData.contains("\n\n")) {
0203         tempNode->setContent(lfData);
0204     } else {
0205         tempNode->setBody(lfData);
0206     }
0207     tempNode->parse();
0208     tempNode->contentType()->setCharset(charset());
0209     bindLifetime(tempNode);
0210 
0211     if (!tempNode->head().isEmpty()) {
0212         tempNode->contentDescription()->from7BitString("temporary node");
0213     }
0214 
0215     parseInternal(tempNode);
0216 }
0217 
0218 QString MessagePart::renderInternalText() const
0219 {
0220     QString text;
0221     for (const auto &mp : subParts()) {
0222         text += mp->text();
0223     }
0224     return text;
0225 }
0226 
0227 void MessagePart::appendSubPart(const MessagePart::Ptr &messagePart)
0228 {
0229     messagePart->setParentPart(this);
0230     mBlocks.append(messagePart);
0231 }
0232 
0233 const QVector<MessagePart::Ptr> &MessagePart::subParts() const
0234 {
0235     return mBlocks;
0236 }
0237 
0238 bool MessagePart::hasSubParts() const
0239 {
0240     return !mBlocks.isEmpty();
0241 }
0242 
0243 QVector<SignedMessagePart *> MessagePart::signatures() const
0244 {
0245     QVector<SignedMessagePart *> list;
0246     if (auto sig = dynamic_cast<SignedMessagePart *>(const_cast<MessagePart *>(this))) {
0247         list << sig;
0248     }
0249     auto parent = parentPart();
0250     while (parent) {
0251         if (auto sig = dynamic_cast<SignedMessagePart *>(parent)) {
0252             list << sig;
0253         }
0254         parent = parent->parentPart();
0255     }
0256     return list;
0257 }
0258 
0259 QVector<EncryptedMessagePart *> MessagePart::encryptions() const
0260 {
0261     QVector<EncryptedMessagePart *> list;
0262     if (auto sig = dynamic_cast<EncryptedMessagePart *>(const_cast<MessagePart *>(this))) {
0263         list << sig;
0264     }
0265     auto parent = parentPart();
0266     while (parent) {
0267         if (auto sig = dynamic_cast<EncryptedMessagePart *>(parent)) {
0268             list << sig;
0269         }
0270         parent = parent->parentPart();
0271     }
0272     return list;
0273 }
0274 
0275 KMMsgEncryptionState MessagePart::encryptionState() const
0276 {
0277     if (!encryptions().isEmpty()) {
0278         return KMMsgFullyEncrypted;
0279     }
0280     return KMMsgNotEncrypted;
0281 }
0282 
0283 KMMsgSignatureState MessagePart::signatureState() const
0284 {
0285     if (!signatures().isEmpty()) {
0286         return KMMsgFullySigned;
0287     }
0288     return KMMsgNotSigned;
0289 }
0290 
0291 void MessagePart::bindLifetime(KMime::Content *node)
0292 {
0293     mNodesToDelete << node;
0294 }
0295 
0296 KMime::Headers::Base *MimeTreeParser::MessagePart::header(const char *header) const
0297 {
0298     if (node() && node()->hasHeader(header)) {
0299         return node()->headerByType(header);
0300     }
0301     if (auto parent = parentPart()) {
0302         return parent->header(header);
0303     }
0304 
0305     return nullptr;
0306 }
0307 
0308 //-----MessagePartList----------------------
0309 MessagePartList::MessagePartList(ObjectTreeParser *otp, KMime::Content *node)
0310     : MessagePart(otp, QString(), node)
0311 {
0312 }
0313 
0314 QString MessagePartList::text() const
0315 {
0316     return renderInternalText();
0317 }
0318 
0319 QString MessagePartList::plaintextContent() const
0320 {
0321     return QString();
0322 }
0323 
0324 QString MessagePartList::htmlContent() const
0325 {
0326     return QString();
0327 }
0328 
0329 //-----TextMessageBlock----------------------
0330 
0331 TextMessagePart::TextMessagePart(ObjectTreeParser *otp, KMime::Content *node)
0332     : MessagePartList(otp, node)
0333     , mSignatureState(KMMsgSignatureStateUnknown)
0334     , mEncryptionState(KMMsgEncryptionStateUnknown)
0335 {
0336     if (!mNode) {
0337         qCWarning(MIMETREEPARSER_LOG) << "not a valid node";
0338         return;
0339     }
0340 
0341     parseContent();
0342 }
0343 
0344 void TextMessagePart::parseContent()
0345 {
0346     mSignatureState = KMMsgNotSigned;
0347     mEncryptionState = KMMsgNotEncrypted;
0348     const auto blocks = prepareMessageForDecryption(mNode->decodedContent());
0349     // We also get blocks for unencrypted messages
0350     if (!blocks.isEmpty()) {
0351         const auto aCodec = mOtp->codecFor(mNode);
0352         const auto cryptProto = OpenPGP;
0353 
0354         /* The (overall) signature/encrypted status is broken
0355          * if one unencrypted part is at the beginning or in the middle
0356          * because mailmain adds an unencrypted part at the end this should not break the overall status
0357          *
0358          * That's why we first set the tmp status and if one crypted/signed block comes afterwards, than
0359          * the status is set to unencryped
0360          */
0361         bool fullySignedOrEncrypted = true;
0362         bool fullySignedOrEncryptedTmp = true;
0363 
0364         for (const auto &block : blocks) {
0365             if (!fullySignedOrEncryptedTmp) {
0366                 fullySignedOrEncrypted = false;
0367             }
0368 
0369             if (block.type() == NoPgpBlock && !block.text().trimmed().isEmpty()) {
0370                 fullySignedOrEncryptedTmp = false;
0371                 appendSubPart(MessagePart::Ptr(new MessagePart(mOtp, aCodec->toUnicode(KMime::CRLFtoLF(block.text())))));
0372             } else if (block.type() == PgpMessageBlock) {
0373                 KMime::Content *content = new KMime::Content;
0374                 content->setBody(block.text());
0375                 content->parse();
0376                 content->contentType()->setCharset(charset());
0377                 EncryptedMessagePart::Ptr mp(new EncryptedMessagePart(mOtp, QString(), cryptProto, content, content, false));
0378                 mp->bindLifetime(content);
0379                 mp->setIsEncrypted(true);
0380                 appendSubPart(mp);
0381             } else if (block.type() == ClearsignedBlock) {
0382                 KMime::Content *content = new KMime::Content;
0383                 content->setBody(block.text());
0384                 content->parse();
0385                 content->contentType()->setCharset(charset());
0386                 SignedMessagePart::Ptr mp(new SignedMessagePart(mOtp, cryptProto, nullptr, content, false));
0387                 mp->bindLifetime(content);
0388                 mp->setIsSigned(true);
0389                 appendSubPart(mp);
0390             } else {
0391                 continue;
0392             }
0393 
0394             const auto mp = subParts().last().staticCast<MessagePart>();
0395             const PartMetaData *messagePart(mp->partMetaData());
0396 
0397             if (!messagePart->isEncrypted && !messagePart->isSigned && !block.text().trimmed().isEmpty()) {
0398                 mp->setText(aCodec->toUnicode(KMime::CRLFtoLF(block.text())));
0399             }
0400 
0401             if (messagePart->isEncrypted) {
0402                 mEncryptionState = KMMsgPartiallyEncrypted;
0403             }
0404 
0405             if (messagePart->isSigned) {
0406                 mSignatureState = KMMsgPartiallySigned;
0407             }
0408         }
0409 
0410         // Do we have an fully Signed/Encrypted Message?
0411         if (fullySignedOrEncrypted) {
0412             if (mSignatureState == KMMsgPartiallySigned) {
0413                 mSignatureState = KMMsgFullySigned;
0414             }
0415             if (mEncryptionState == KMMsgPartiallyEncrypted) {
0416                 mEncryptionState = KMMsgFullyEncrypted;
0417             }
0418         }
0419     }
0420 }
0421 
0422 KMMsgEncryptionState TextMessagePart::encryptionState() const
0423 {
0424     if (mEncryptionState == KMMsgNotEncrypted) {
0425         return MessagePart::encryptionState();
0426     }
0427     return mEncryptionState;
0428 }
0429 
0430 KMMsgSignatureState TextMessagePart::signatureState() const
0431 {
0432     if (mSignatureState == KMMsgNotSigned) {
0433         return MessagePart::signatureState();
0434     }
0435     return mSignatureState;
0436 }
0437 
0438 //-----AttachmentMessageBlock----------------------
0439 
0440 AttachmentMessagePart::AttachmentMessagePart(ObjectTreeParser *otp, KMime::Content *node)
0441     : TextMessagePart(otp, node)
0442 {
0443 }
0444 
0445 //-----HtmlMessageBlock----------------------
0446 
0447 HtmlMessagePart::HtmlMessagePart(ObjectTreeParser *otp, KMime::Content *node)
0448     : MessagePart(otp, QString(), node)
0449 {
0450     if (!mNode) {
0451         qCWarning(MIMETREEPARSER_LOG) << "not a valid node";
0452         return;
0453     }
0454 
0455     setText(mOtp->codecFor(mNode)->toUnicode(KMime::CRLFtoLF(mNode->decodedContent())));
0456 }
0457 
0458 //-----MimeMessageBlock----------------------
0459 
0460 MimeMessagePart::MimeMessagePart(ObjectTreeParser *otp, KMime::Content *node, bool onlyOneMimePart)
0461     : MessagePart(otp, QString(), node)
0462 {
0463     if (!mNode) {
0464         qCWarning(MIMETREEPARSER_LOG) << "not a valid node";
0465         return;
0466     }
0467 
0468     parseInternal(mNode, onlyOneMimePart);
0469 }
0470 
0471 MimeMessagePart::~MimeMessagePart()
0472 {
0473 }
0474 
0475 QString MimeMessagePart::text() const
0476 {
0477     return renderInternalText();
0478 }
0479 
0480 QString MimeMessagePart::plaintextContent() const
0481 {
0482     return QString();
0483 }
0484 
0485 QString MimeMessagePart::htmlContent() const
0486 {
0487     return QString();
0488 }
0489 
0490 //-----AlternativeMessagePart----------------------
0491 
0492 AlternativeMessagePart::AlternativeMessagePart(ObjectTreeParser *otp, KMime::Content *node)
0493     : MessagePart(otp, QString(), node)
0494 {
0495     if (auto dataIcal = findTypeInDirectChildren(mNode, "text/calendar")) {
0496         mChildParts[MultipartIcal] = MimeMessagePart::Ptr(new MimeMessagePart(mOtp, dataIcal, true));
0497     }
0498 
0499     if (auto dataText = findTypeInDirectChildren(mNode, "text/plain")) {
0500         mChildParts[MultipartPlain] = MimeMessagePart::Ptr(new MimeMessagePart(mOtp, dataText, true));
0501     }
0502 
0503     if (auto dataHtml = findTypeInDirectChildren(mNode, "text/html")) {
0504         mChildParts[MultipartHtml] = MimeMessagePart::Ptr(new MimeMessagePart(mOtp, dataHtml, true));
0505     } else {
0506         // If we didn't find the HTML part as the first child of the multipart/alternative, it might
0507         // be that this is a HTML message with images, and text/plain and multipart/related are the
0508         // immediate children of this multipart/alternative node.
0509         // In this case, the HTML node is a child of multipart/related.
0510         // In the case of multipart/related we don't expect multiple html parts, it is usually used to group attachments
0511         // with html content.
0512         //
0513         // 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.
0514         auto data = [&] {
0515             if (auto d = findTypeInDirectChildren(mNode, "multipart/related")) {
0516                 return d;
0517             }
0518             return findTypeInDirectChildren(mNode, "multipart/mixed");
0519         }();
0520         if (data) {
0521             QString htmlContent;
0522             const auto parts = data->contents();
0523             for (auto p : data->contents()) {
0524                 if ((!p->contentType()->isEmpty()) && (p->contentType()->mimeType() == "text/html")) {
0525                     htmlContent += MimeMessagePart(mOtp, p, true).text();
0526                 } else if (KMime::isAttachment(p)) {
0527                     appendSubPart(MimeMessagePart::Ptr(new MimeMessagePart(otp, p, true)));
0528                 }
0529             }
0530             mChildParts[MultipartHtml] = MessagePart::Ptr(new MessagePart(mOtp, htmlContent, nullptr));
0531         }
0532     }
0533 }
0534 
0535 AlternativeMessagePart::~AlternativeMessagePart()
0536 {
0537 }
0538 
0539 QList<AlternativeMessagePart::HtmlMode> AlternativeMessagePart::availableModes()
0540 {
0541     return mChildParts.keys();
0542 }
0543 
0544 QString AlternativeMessagePart::text() const
0545 {
0546     if (mChildParts.contains(MultipartPlain)) {
0547         return mChildParts[MultipartPlain]->text();
0548     }
0549     return QString();
0550 }
0551 
0552 bool AlternativeMessagePart::isHtml() const
0553 {
0554     return mChildParts.contains(MultipartHtml);
0555 }
0556 
0557 QString AlternativeMessagePart::plaintextContent() const
0558 {
0559     return text();
0560 }
0561 
0562 QString AlternativeMessagePart::htmlContent() const
0563 {
0564     if (mChildParts.contains(MultipartHtml)) {
0565         return mChildParts[MultipartHtml]->text();
0566     } else {
0567         return plaintextContent();
0568     }
0569 }
0570 
0571 QString AlternativeMessagePart::icalContent() const
0572 {
0573     if (mChildParts.contains(MultipartIcal)) {
0574         return mChildParts[MultipartIcal]->text();
0575     }
0576     return {};
0577 }
0578 
0579 //-----CertMessageBlock----------------------
0580 
0581 CertMessagePart::CertMessagePart(ObjectTreeParser *otp, KMime::Content *node, const CryptoProtocol cryptoProto)
0582     : MessagePart(otp, QString(), node)
0583     , mProtocol(cryptoProto)
0584 {
0585     if (!mNode) {
0586         qCWarning(MIMETREEPARSER_LOG) << "not a valid node";
0587         return;
0588     }
0589 }
0590 
0591 CertMessagePart::~CertMessagePart()
0592 {
0593 }
0594 
0595 void CertMessagePart::import()
0596 {
0597     importKey(mProtocol, mNode->decodedContent());
0598 }
0599 
0600 QString CertMessagePart::text() const
0601 {
0602     return QString();
0603 }
0604 
0605 //-----SignedMessageBlock---------------------
0606 SignedMessagePart::SignedMessagePart(ObjectTreeParser *otp,
0607                                      const CryptoProtocol cryptoProto,
0608                                      KMime::Content *node,
0609                                      KMime::Content *signedData,
0610                                      bool parseAfterDecryption)
0611     : MessagePart(otp, {}, node)
0612     , mParseAfterDecryption(parseAfterDecryption)
0613     , mProtocol(cryptoProto)
0614     , mSignedData(signedData)
0615 {
0616     mMetaData.isSigned = true;
0617     mMetaData.isGoodSignature = false;
0618     mMetaData.status = i18n("Wrong Crypto Plug-In.");
0619 }
0620 
0621 SignedMessagePart::~SignedMessagePart()
0622 {
0623 }
0624 
0625 void SignedMessagePart::setIsSigned(bool isSigned)
0626 {
0627     mMetaData.isSigned = isSigned;
0628 }
0629 
0630 bool SignedMessagePart::isSigned() const
0631 {
0632     return mMetaData.isSigned;
0633 }
0634 
0635 static QString prettifyDN(const char *uid)
0636 {
0637     // We used to use QGpgME::DN::prettyDN here. But I'm not sure what we actually need it for.
0638     return QString::fromUtf8(uid);
0639 }
0640 
0641 static void sigStatusToMetaData(PartMetaData &mMetaData, const Signature &signature)
0642 {
0643     mMetaData.isGoodSignature = !signature.status;
0644     if (!mMetaData.isGoodSignature) {
0645         qWarning() << "The signature is bad." << signature.status;
0646     }
0647     // save extended signature status flags
0648     mMetaData.keyMissing = signature.result == Crypto::Signature::KeyNotFound;
0649     mMetaData.keyExpired = signature.result == Crypto::Signature::Expired;
0650 
0651     Key key;
0652     if (mMetaData.isGoodSignature) {
0653         // Search for the key by its fingerprint so that we can check for trust etc.
0654         const auto keys = findKeys({QString::fromUtf8(signature.fingerprint)});
0655         if (keys.size() > 1) {
0656             // Should not happen
0657             qCDebug(MIMETREEPARSER_LOG) << "Oops: Found more then one Key for Fingerprint: " << signature.fingerprint;
0658         }
0659         if (keys.empty()) {
0660             // Should not happen at this point
0661             qCWarning(MIMETREEPARSER_LOG) << "Oops: Found no Key for Fingerprint: " << signature.fingerprint;
0662         } else {
0663             key = keys[0];
0664         }
0665     }
0666 
0667     mMetaData.keyId = key.keyId;
0668     if (mMetaData.keyId.isEmpty()) {
0669         mMetaData.keyId = signature.fingerprint;
0670     }
0671     mMetaData.keyIsTrusted = signature.isTrusted;
0672     if (!key.userIds.empty()) {
0673         mMetaData.signer = prettifyDN(key.userIds[0].id.data());
0674     }
0675     for (const auto &userId : key.userIds) {
0676         QString email = QString::fromUtf8(userId.email);
0677         // Work around cryptplug bug where email addresses are specified as angle-addr ( <person@example.org> ),
0678         // not addr-spec ( person@example.org ):
0679         if (email.startsWith(QLatin1Char('<')) && email.endsWith(QLatin1Char('>'))) {
0680             email = email.mid(1, email.length() - 2);
0681         }
0682         if (!email.isEmpty()) {
0683             mMetaData.signerMailAddresses.append(email);
0684         }
0685     }
0686 
0687     mMetaData.creationTime = signature.creationTime;
0688     if (mMetaData.signer.isEmpty()) {
0689         if (!key.userIds.empty()) {
0690             mMetaData.signer = prettifyDN(key.userIds[0].name.data());
0691         }
0692         if (!mMetaData.signerMailAddresses.empty()) {
0693             if (mMetaData.signer.isEmpty()) {
0694                 mMetaData.signer = mMetaData.signerMailAddresses.front();
0695             } else {
0696                 mMetaData.signer += QLatin1String(" <") + mMetaData.signerMailAddresses.front() + QLatin1Char('>');
0697             }
0698         }
0699     }
0700 }
0701 
0702 void SignedMessagePart::startVerification()
0703 {
0704     if (!mSignedData) {
0705         return;
0706     }
0707 
0708     mMetaData.isSigned = false;
0709     mMetaData.status = i18n("Wrong Crypto Plug-In.");
0710     mMetaData.isEncrypted = false;
0711     mMetaData.isDecryptable = false;
0712 
0713     const auto codec = mOtp->codecFor(mSignedData);
0714 
0715     // If we have a mNode, this is a detached signature
0716     if (mNode) {
0717         const auto signature = mNode->decodedContent();
0718 
0719         // This is necessary in case the original data contained CRLF's. Otherwise the signature will not match the data (since KMIME normalizes to LF)
0720         const QByteArray signedData = KMime::LFtoCRLF(mSignedData->encodedContent());
0721 
0722         setVerificationResult(Crypto::verifyDetachedSignature(mProtocol, signature, signedData), signedData);
0723         setText(codec->toUnicode(KMime::CRLFtoLF(signedData)));
0724     } else {
0725         QByteArray outdata;
0726         setVerificationResult(Crypto::verifyOpaqueSignature(mProtocol, mSignedData->decodedContent(), outdata), outdata);
0727         setText(codec->toUnicode(KMime::CRLFtoLF(outdata)));
0728     }
0729 
0730     if (!mMetaData.isSigned) {
0731         mMetaData.creationTime = QDateTime();
0732     }
0733 }
0734 
0735 void SignedMessagePart::setVerificationResult(const VerificationResult &result, const QByteArray &signedData)
0736 {
0737     const auto signatures = result.signatures;
0738     // FIXME
0739     // mMetaData.auditLogError = result.error;
0740     if (!signatures.empty()) {
0741         mMetaData.isSigned = true;
0742         sigStatusToMetaData(mMetaData, signatures.front());
0743         if (!signedData.isEmpty() && mParseAfterDecryption) {
0744             parseInternal(signedData);
0745         }
0746     }
0747 }
0748 
0749 QString SignedMessagePart::plaintextContent() const
0750 {
0751     if (!mNode) {
0752         return MessagePart::text();
0753     } else {
0754         return QString();
0755     }
0756 }
0757 
0758 QString SignedMessagePart::htmlContent() const
0759 {
0760     if (!mNode) {
0761         return MessagePart::text();
0762     } else {
0763         return QString();
0764     }
0765 }
0766 
0767 //-----CryptMessageBlock---------------------
0768 EncryptedMessagePart::EncryptedMessagePart(ObjectTreeParser *otp,
0769                                            const QString &text,
0770                                            const CryptoProtocol cryptoProto,
0771                                            KMime::Content *node,
0772                                            KMime::Content *encryptedNode,
0773                                            bool parseAfterDecryption)
0774     : MessagePart(otp, text, node)
0775     , mParseAfterDecryption(parseAfterDecryption)
0776     , mProtocol(cryptoProto)
0777     , mEncryptedNode(encryptedNode)
0778 {
0779     mMetaData.isSigned = false;
0780     mMetaData.isGoodSignature = false;
0781     mMetaData.isEncrypted = false;
0782     mMetaData.isDecryptable = false;
0783     mMetaData.status = i18n("Wrong Crypto Plug-In.");
0784 }
0785 
0786 void EncryptedMessagePart::setIsEncrypted(bool encrypted)
0787 {
0788     mMetaData.isEncrypted = encrypted;
0789 }
0790 
0791 bool EncryptedMessagePart::isEncrypted() const
0792 {
0793     return mMetaData.isEncrypted;
0794 }
0795 
0796 bool EncryptedMessagePart::isDecryptable() const
0797 {
0798     return mMetaData.isDecryptable;
0799 }
0800 
0801 bool EncryptedMessagePart::decrypt(KMime::Content &data)
0802 {
0803     mError = NoError;
0804     mMetaData.errorText.clear();
0805     // FIXME
0806     //  mMetaData.auditLogError = GpgME::Error();
0807     mMetaData.auditLog.clear();
0808 
0809     const QByteArray ciphertext = data.decodedContent();
0810     QByteArray plainText;
0811     DecryptionResult decryptResult;
0812     VerificationResult verifyResult;
0813     std::tie(decryptResult, verifyResult) = Crypto::decryptAndVerify(mProtocol, ciphertext, plainText);
0814     mMetaData.isSigned = verifyResult.signatures.size() > 0;
0815 
0816     // Normalize CRLF's
0817     plainText = KMime::CRLFtoLF(plainText);
0818     const auto codec = mOtp->codecFor(&data);
0819     const auto decoded = codec->toUnicode(plainText);
0820 
0821     if (verifyResult.signatures.size() > 0) {
0822         // We simply attach a signed message part to indicate that this content is also signed
0823         // We're forwarding mNode to not loose the encoding information
0824         auto subPart = SignedMessagePart::Ptr(new SignedMessagePart(mOtp, mProtocol, mNode, nullptr));
0825         subPart->setText(decoded);
0826         subPart->setVerificationResult(verifyResult, plainText);
0827         appendSubPart(subPart);
0828     }
0829 
0830     if (decryptResult.error && mMetaData.isSigned) {
0831         // Only a signed part
0832         mMetaData.isEncrypted = false;
0833         mDecryptedData = plainText;
0834         return true;
0835     }
0836 
0837     if (mMetaData.isEncrypted) {
0838         mMetaData.keyId = [&] {
0839             for (const auto &recipient : std::as_const(decryptResult.recipients)) {
0840                 if (recipient.secretKeyAvailable) {
0841                     return recipient.keyId;
0842                 }
0843             }
0844             return QByteArray{};
0845         }();
0846     }
0847 
0848     if (!decryptResult.error) {
0849         mDecryptedData = plainText;
0850         setText(decoded);
0851     } else {
0852         mMetaData.isEncrypted = decryptResult.result != Crypto::DecryptionResult::NotEncrypted;
0853         qWarning() << "Failed to decrypt : " << decryptResult.error;
0854 
0855         if (decryptResult.result == Crypto::DecryptionResult::NoSecretKeyError) {
0856             mError = NoKeyError;
0857             mMetaData.errorText = i18n("Could not decrypt the data: no key found for recipients.");
0858         } else if (decryptResult.result == Crypto::DecryptionResult::PassphraseError) {
0859             mError = PassphraseError;
0860         } else {
0861             mError = UnknownError;
0862             mMetaData.errorText = i18n("Could not decrypt the data.");
0863         }
0864         setText(QString::fromUtf8(mDecryptedData.constData()));
0865         return false;
0866     }
0867 
0868     return true;
0869 }
0870 
0871 void EncryptedMessagePart::startDecryption(KMime::Content *data)
0872 {
0873     mMetaData.isEncrypted = true;
0874     mMetaData.isDecryptable = decrypt(*data);
0875 
0876     if (mParseAfterDecryption && !mMetaData.isSigned) {
0877         parseInternal(mDecryptedData);
0878     }
0879 }
0880 
0881 void EncryptedMessagePart::startDecryption()
0882 {
0883     if (mEncryptedNode) {
0884         startDecryption(mEncryptedNode);
0885     } else {
0886         startDecryption(mNode);
0887     }
0888 }
0889 
0890 QString EncryptedMessagePart::plaintextContent() const
0891 {
0892     if (!mNode) {
0893         return MessagePart::text();
0894     } else {
0895         return QString();
0896     }
0897 }
0898 
0899 QString EncryptedMessagePart::htmlContent() const
0900 {
0901     if (!mNode) {
0902         return MessagePart::text();
0903     } else {
0904         return QString();
0905     }
0906 }
0907 
0908 QString EncryptedMessagePart::text() const
0909 {
0910     if (hasSubParts()) {
0911         auto _mp = (subParts()[0]).dynamicCast<SignedMessagePart>();
0912         if (_mp) {
0913             return _mp->text();
0914         } else {
0915             return MessagePart::text();
0916         }
0917     } else {
0918         return MessagePart::text();
0919     }
0920 }
0921 
0922 EncapsulatedRfc822MessagePart::EncapsulatedRfc822MessagePart(ObjectTreeParser *otp, KMime::Content *node, const KMime::Message::Ptr &message)
0923     : MessagePart(otp, QString(), node)
0924     , mMessage(message)
0925 {
0926     mMetaData.isEncrypted = false;
0927     mMetaData.isSigned = false;
0928     mMetaData.isEncapsulatedRfc822Message = true;
0929 
0930     if (!mMessage) {
0931         qCWarning(MIMETREEPARSER_LOG) << "Node is of type message/rfc822 but doesn't have a message!";
0932         return;
0933     }
0934 
0935     parseInternal(message.data());
0936 }
0937 
0938 QString EncapsulatedRfc822MessagePart::text() const
0939 {
0940     return renderInternalText();
0941 }
0942 
0943 QString EncapsulatedRfc822MessagePart::from() const
0944 {
0945     if (auto from = mMessage->from(false)) {
0946         return from->asUnicodeString();
0947     }
0948     return {};
0949 }
0950 
0951 QDateTime EncapsulatedRfc822MessagePart::date() const
0952 {
0953     if (auto date = mMessage->date(false)) {
0954         return date->dateTime();
0955     }
0956     return {};
0957 }
0958 
0959 HeadersPart::HeadersPart(ObjectTreeParser *otp, KMime::Content *node)
0960     : MessagePart(otp, QString(), node)
0961 {
0962 }