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