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

0001 // SPDX-FileCopyrightText: 2003 Marc Mutz <mutz@kde.org>
0002 // SPDX-License-Identifier: GPL-2.0-only
0003 
0004 #include "mimetreeparser_debug.h"
0005 
0006 #include "bodypartformatter.h"
0007 
0008 #include "bodypartformatterbasefactory.h"
0009 #include "bodypartformatterbasefactory_p.h"
0010 
0011 #include "messagepart.h"
0012 #include "objecttreeparser.h"
0013 #include "utils.h"
0014 
0015 #include <KMime/Content>
0016 
0017 using namespace MimeTreeParser;
0018 using namespace MimeTreeParser::Interface;
0019 
0020 namespace MimeTreeParser
0021 {
0022 class AnyTypeBodyPartFormatter : public MimeTreeParser::Interface::BodyPartFormatter
0023 {
0024 };
0025 
0026 class MessageRfc822BodyPartFormatter : public MimeTreeParser::Interface::BodyPartFormatter
0027 {
0028 public:
0029     MessagePart::Ptr process(ObjectTreeParser *objectTreeParser, KMime::Content *node) const Q_DECL_OVERRIDE
0030     {
0031         return MessagePart::Ptr(new EncapsulatedRfc822MessagePart(objectTreeParser, node, node->bodyAsMessage()));
0032     }
0033 };
0034 
0035 class HeadersBodyPartFormatter : public MimeTreeParser::Interface::BodyPartFormatter
0036 {
0037 public:
0038     MessagePart::Ptr process(ObjectTreeParser *objectTreeParser, KMime::Content *node) const Q_DECL_OVERRIDE
0039     {
0040         return MessagePart::Ptr(new HeadersPart(objectTreeParser, node));
0041     }
0042 };
0043 
0044 class MultiPartRelatedBodyPartFormatter : public MimeTreeParser::Interface::BodyPartFormatter
0045 {
0046 public:
0047     QVector<MessagePart::Ptr> processList(ObjectTreeParser *objectTreeParser, KMime::Content *node) const Q_DECL_OVERRIDE
0048     {
0049         if (node->contents().isEmpty()) {
0050             return {};
0051         }
0052         // We rely on the order of the parts.
0053         // Theoretically there could also be a Start parameter which would break this..
0054         // https://tools.ietf.org/html/rfc2387#section-4
0055 
0056         // We want to display attachments even if displayed inline.
0057         QVector<MessagePart::Ptr> list;
0058         list.append(MimeMessagePart::Ptr(new MimeMessagePart(objectTreeParser, node->contents().at(0), true)));
0059         for (int i = 1; i < node->contents().size(); i++) {
0060             auto p = node->contents().at(i);
0061             if (KMime::isAttachment(p)) {
0062                 list.append(MimeMessagePart::Ptr(new MimeMessagePart(objectTreeParser, p, true)));
0063             }
0064         }
0065         return list;
0066     }
0067 };
0068 
0069 class MultiPartMixedBodyPartFormatter : public MimeTreeParser::Interface::BodyPartFormatter
0070 {
0071 public:
0072     MessagePart::Ptr process(ObjectTreeParser *objectTreeParser, KMime::Content *node) const Q_DECL_OVERRIDE
0073     {
0074         if (node->contents().isEmpty()) {
0075             return {};
0076         }
0077         // we need the intermediate part to preserve the headers (necessary for with protected headers using multipart mixed)
0078         auto part = MessagePart::Ptr(new MessagePart(objectTreeParser, {}, node));
0079         part->appendSubPart(MimeMessagePart::Ptr(new MimeMessagePart(objectTreeParser, node->contents().at(0), false)));
0080         return part;
0081     }
0082 };
0083 
0084 class ApplicationPGPEncryptedBodyPartFormatter : public MimeTreeParser::Interface::BodyPartFormatter
0085 {
0086 public:
0087     MessagePart::Ptr process(ObjectTreeParser *objectTreeParser, KMime::Content *node) const Q_DECL_OVERRIDE
0088     {
0089         if (node->decodedContent().trimmed() != "Version: 1") {
0090             qCWarning(MIMETREEPARSER_LOG) << "Unknown PGP Version String:" << node->decodedContent().trimmed();
0091         }
0092 
0093         if (!node->parent()) {
0094             return MessagePart::Ptr();
0095         }
0096 
0097         KMime::Content *data = findTypeInDirectChildren(node->parent(), "application/octet-stream");
0098 
0099         if (!data) {
0100             return MessagePart::Ptr(); // new MimeMessagePart(objectTreeParser, node));
0101         }
0102 
0103         EncryptedMessagePart::Ptr mp(new EncryptedMessagePart(objectTreeParser, data->decodedText(), OpenPGP, node, data));
0104         mp->setIsEncrypted(true);
0105         return mp;
0106     }
0107 };
0108 
0109 class ApplicationPkcs7MimeBodyPartFormatter : public MimeTreeParser::Interface::BodyPartFormatter
0110 {
0111 public:
0112     MessagePart::Ptr process(ObjectTreeParser *objectTreeParser, KMime::Content *node) const Q_DECL_OVERRIDE
0113     {
0114         if (node->head().isEmpty()) {
0115             return MessagePart::Ptr();
0116         }
0117 
0118         const QString smimeType = node->contentType()->parameter(QStringLiteral("smime-type")).toLower();
0119 
0120         if (smimeType == QLatin1String("certs-only")) {
0121             return CertMessagePart::Ptr(new CertMessagePart(objectTreeParser, node, CMS));
0122         }
0123 
0124         bool isSigned = (smimeType == QLatin1String("signed-data"));
0125         bool isEncrypted = (smimeType == QLatin1String("enveloped-data"));
0126 
0127         // Analyze "signTestNode" node to find/verify a signature.
0128         // If zero part.objectTreeParser verification was successfully done after
0129         // decrypting via recursion by insertAndParseNewChildNode().
0130         KMime::Content *signTestNode = isEncrypted ? nullptr : node;
0131 
0132         // We try decrypting the content
0133         // if we either *know* that it is an encrypted message part
0134         // or there is neither signed nor encrypted parameter.
0135         MessagePart::Ptr mp;
0136         if (!isSigned) {
0137             if (isEncrypted) {
0138                 qCDebug(MIMETREEPARSER_LOG) << "pkcs7 mime     ==      S/MIME TYPE: enveloped (encrypted) data";
0139             } else {
0140                 qCDebug(MIMETREEPARSER_LOG) << "pkcs7 mime  -  type unknown  -  enveloped (encrypted) data ?";
0141             }
0142 
0143             auto _mp = EncryptedMessagePart::Ptr(new EncryptedMessagePart(objectTreeParser, node->decodedText(), CMS, node));
0144             mp = _mp;
0145             _mp->setIsEncrypted(true);
0146             // PartMetaData *messagePart(_mp->partMetaData());
0147             // if (!part.source()->decryptMessage()) {
0148             isEncrypted = true;
0149             signTestNode = nullptr; // PENDING(marc) to be abs. sure, we'd need to have to look at the content
0150             // } else {
0151             //     _mp->startDecryption();
0152             //     if (messagePart->isDecryptable) {
0153             //         qCDebug(MIMETREEPARSER_LOG) << "pkcs7 mime  -  encryption found  -  enveloped (encrypted) data !";
0154             //         isEncrypted = true;
0155             //         part.nodeHelper()->setEncryptionState(node, KMMsgFullyEncrypted);
0156             //         signTestNode = nullptr;
0157 
0158             //     } else {
0159             //         // decryption failed, which could be because the part was encrypted but
0160             //         // decryption failed, or because we didn't know if it was encrypted, tried,
0161             //         // and failed. If the message was not actually encrypted, we continue
0162             //         // assuming it's signed
0163             //         if (_mp->passphraseError() || (smimeType.isEmpty() && messagePart->isEncrypted)) {
0164             //             isEncrypted = true;
0165             //             signTestNode = nullptr;
0166             //         }
0167 
0168             //         if (isEncrypted) {
0169             //             qCDebug(MIMETREEPARSER_LOG) << "pkcs7 mime  -  ERROR: COULD NOT DECRYPT enveloped data !";
0170             //         } else {
0171             //             qCDebug(MIMETREEPARSER_LOG) << "pkcs7 mime  -  NO encryption found";
0172             //         }
0173             //     }
0174             // }
0175         }
0176 
0177         // We now try signature verification if necessarry.
0178         if (signTestNode) {
0179             if (isSigned) {
0180                 qCDebug(MIMETREEPARSER_LOG) << "pkcs7 mime     ==      S/MIME TYPE: opaque signed data";
0181             } else {
0182                 qCDebug(MIMETREEPARSER_LOG) << "pkcs7 mime  -  type unknown  -  opaque signed data ?";
0183             }
0184 
0185             return SignedMessagePart::Ptr(new SignedMessagePart(objectTreeParser, CMS, nullptr, signTestNode));
0186         }
0187         return mp;
0188     }
0189 };
0190 
0191 class MultiPartAlternativeBodyPartFormatter : public MimeTreeParser::Interface::BodyPartFormatter
0192 {
0193 public:
0194     MessagePart::Ptr process(ObjectTreeParser *objectTreeParser, KMime::Content *node) const Q_DECL_OVERRIDE
0195     {
0196         if (node->contents().isEmpty()) {
0197             return MessagePart::Ptr();
0198         }
0199 
0200         AlternativeMessagePart::Ptr mp(new AlternativeMessagePart(objectTreeParser, node));
0201         if (mp->mChildParts.isEmpty()) {
0202             return MimeMessagePart::Ptr(new MimeMessagePart(objectTreeParser, node->contents().at(0)));
0203         }
0204         return mp;
0205     }
0206 };
0207 
0208 class MultiPartEncryptedBodyPartFormatter : public MimeTreeParser::Interface::BodyPartFormatter
0209 {
0210 public:
0211     MessagePart::Ptr process(ObjectTreeParser *objectTreeParser, KMime::Content *node) const Q_DECL_OVERRIDE
0212     {
0213         if (node->contents().isEmpty()) {
0214             Q_ASSERT(false);
0215             return MessagePart::Ptr();
0216         }
0217 
0218         CryptoProtocol useThisCryptProto = UnknownProtocol;
0219 
0220         /*
0221         ATTENTION: This code is to be replaced by the new 'auto-detect' feature. --------------------------------------
0222         */
0223         KMime::Content *data = findTypeInDirectChildren(node, "application/octet-stream");
0224         if (data) {
0225             useThisCryptProto = OpenPGP;
0226         } else {
0227             data = findTypeInDirectChildren(node, "application/pkcs7-mime");
0228             if (data) {
0229                 useThisCryptProto = CMS;
0230             }
0231         }
0232         /*
0233         ---------------------------------------------------------------------------------------------------------------
0234         */
0235 
0236         if (!data) {
0237             return MessagePart::Ptr(new MimeMessagePart(objectTreeParser, node->contents().at(0)));
0238         }
0239 
0240         EncryptedMessagePart::Ptr mp(new EncryptedMessagePart(objectTreeParser, data->decodedText(), useThisCryptProto, node, data));
0241         mp->setIsEncrypted(true);
0242         return mp;
0243     }
0244 };
0245 
0246 class MultiPartSignedBodyPartFormatter : public MimeTreeParser::Interface::BodyPartFormatter
0247 {
0248 public:
0249     static CryptoProtocol detectProtocol(const QString &protocolContentType_, const QString &signatureContentType)
0250     {
0251         auto protocolContentType = protocolContentType_;
0252         if (protocolContentType.isEmpty()) {
0253             qCWarning(MIMETREEPARSER_LOG) << "Message doesn't set the protocol for the multipart/signed content-type, "
0254                                              "using content-type of the signature:"
0255                                           << signatureContentType;
0256             protocolContentType = signatureContentType;
0257         }
0258 
0259         CryptoProtocol protocol = UnknownProtocol;
0260         if (protocolContentType == QLatin1String("application/pkcs7-signature") || protocolContentType == QLatin1String("application/x-pkcs7-signature")) {
0261             protocol = CMS;
0262         } else if (protocolContentType == QLatin1String("application/pgp-signature") || protocolContentType == QLatin1String("application/x-pgp-signature")) {
0263             protocol = OpenPGP;
0264         }
0265         return protocol;
0266     }
0267 
0268     MessagePart::Ptr process(ObjectTreeParser *objectTreeParser, KMime::Content *node) const Q_DECL_OVERRIDE
0269     {
0270         if (node->contents().size() != 2) {
0271             qCDebug(MIMETREEPARSER_LOG) << "mulitpart/signed must have exactly two child parts!" << Qt::endl << "processing as multipart/mixed";
0272             if (!node->contents().isEmpty()) {
0273                 return MessagePart::Ptr(new MimeMessagePart(objectTreeParser, node->contents().at(0)));
0274             } else {
0275                 return MessagePart::Ptr();
0276             }
0277         }
0278 
0279         KMime::Content *signedData = node->contents().at(0);
0280         KMime::Content *signature = node->contents().at(1);
0281         Q_ASSERT(signedData);
0282         Q_ASSERT(signature);
0283 
0284         auto protocol =
0285             detectProtocol(node->contentType()->parameter(QStringLiteral("protocol")).toLower(), QLatin1String(signature->contentType()->mimeType().toLower()));
0286 
0287         if (protocol == UnknownProtocol) {
0288             return MessagePart::Ptr(new MimeMessagePart(objectTreeParser, signedData));
0289         }
0290 
0291         return SignedMessagePart::Ptr(new SignedMessagePart(objectTreeParser, protocol, signature, signedData));
0292     }
0293 };
0294 
0295 class TextHtmlBodyPartFormatter : public MimeTreeParser::Interface::BodyPartFormatter
0296 {
0297 public:
0298     MessagePart::Ptr process(ObjectTreeParser *objectTreeParser, KMime::Content *node) const Q_DECL_OVERRIDE
0299     {
0300         return HtmlMessagePart::Ptr(new HtmlMessagePart(objectTreeParser, node));
0301     }
0302 };
0303 
0304 class TextPlainBodyPartFormatter : public MimeTreeParser::Interface::BodyPartFormatter
0305 {
0306 public:
0307     MessagePart::Ptr process(ObjectTreeParser *objectTreeParser, KMime::Content *node) const Q_DECL_OVERRIDE
0308     {
0309         if (KMime::isAttachment(node)) {
0310             return AttachmentMessagePart::Ptr(new AttachmentMessagePart(objectTreeParser, node));
0311         }
0312         return TextMessagePart::Ptr(new TextMessagePart(objectTreeParser, node));
0313     }
0314 };
0315 
0316 } // anon namespace
0317 
0318 void BodyPartFormatterBaseFactoryPrivate::messageviewer_create_builtin_bodypart_formatters()
0319 {
0320     auto any = new AnyTypeBodyPartFormatter;
0321     auto textPlain = new TextPlainBodyPartFormatter;
0322     auto pkcs7 = new ApplicationPkcs7MimeBodyPartFormatter;
0323     auto pgp = new ApplicationPGPEncryptedBodyPartFormatter;
0324     auto html = new TextHtmlBodyPartFormatter;
0325     auto headers = new HeadersBodyPartFormatter;
0326     auto multipartAlternative = new MultiPartAlternativeBodyPartFormatter;
0327     auto multipartMixed = new MultiPartMixedBodyPartFormatter;
0328     auto multipartSigned = new MultiPartSignedBodyPartFormatter;
0329     auto multipartEncrypted = new MultiPartEncryptedBodyPartFormatter;
0330     auto message = new MessageRfc822BodyPartFormatter;
0331     auto multipartRelated = new MultiPartRelatedBodyPartFormatter;
0332 
0333     insert("application", "octet-stream", any);
0334     insert("application", "pgp", textPlain);
0335     insert("application", "pkcs7-mime", pkcs7);
0336     insert("application", "x-pkcs7-mime", pkcs7);
0337     insert("application", "pgp-encrypted", pgp);
0338     insert("application", "*", any);
0339 
0340     insert("text", "html", html);
0341     insert("text", "rtf", any);
0342     insert("text", "plain", textPlain);
0343     insert("text", "rfc822-headers", headers);
0344     insert("text", "*", textPlain);
0345 
0346     insert("image", "*", any);
0347 
0348     insert("message", "rfc822", message);
0349     insert("message", "*", any);
0350 
0351     insert("multipart", "alternative", multipartAlternative);
0352     insert("multipart", "encrypted", multipartEncrypted);
0353     insert("multipart", "signed", multipartSigned);
0354     insert("multipart", "related", multipartRelated);
0355     insert("multipart", "*", multipartMixed);
0356     insert("*", "*", any);
0357 }