File indexing completed on 2024-05-26 05:27:51

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