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 }