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 }