File indexing completed on 2024-05-12 05:28:19
0001 // SPDX-FileCopyrightText: 2015 Sandro Knauß <knauss@kolabsys.com> 0002 // SPDX-FileCopyrightText: 2017 Christian Mollekopf <mollekopf@kolabsys.com> 0003 // SPDX-License-Identifier: LGPL-2.0-or-later 0004 0005 #include "messagepart.h" 0006 #include "cryptohelper.h" 0007 #include "mimetreeparser_debug.h" 0008 #include "objecttreeparser.h" 0009 0010 #include "utils.h" 0011 0012 #include <KLocalizedString> 0013 #include <KMime/Content> 0014 0015 #include <QTextCodec> 0016 0017 using namespace MimeTreeParser; 0018 using namespace Crypto; 0019 0020 //------MessagePart----------------------- 0021 MessagePart::MessagePart(ObjectTreeParser *otp, const QString &text, KMime::Content *node) 0022 : mText(text) 0023 , mOtp(otp) 0024 , mParentPart(nullptr) 0025 , mNode(node) // only null for messagepartlist 0026 , mError(NoError) 0027 , mRoot(false) 0028 { 0029 } 0030 0031 MessagePart::~MessagePart() 0032 { 0033 for (auto n : mNodesToDelete) { 0034 delete n; 0035 } 0036 } 0037 0038 MessagePart::Disposition MessagePart::disposition() const 0039 { 0040 if (!mNode) { 0041 return Invalid; 0042 } 0043 const auto cd = mNode->contentDisposition(false); 0044 if (!cd) { 0045 return Invalid; 0046 } 0047 switch (cd->disposition()) { 0048 case KMime::Headers::CDinline: 0049 return Inline; 0050 case KMime::Headers::CDattachment: 0051 return Attachment; 0052 default: 0053 return Invalid; 0054 } 0055 } 0056 0057 QString MessagePart::filename() const 0058 { 0059 if (!mNode) { 0060 return {}; 0061 } 0062 0063 if (const auto cd = mNode->contentDisposition(false)) { 0064 const auto name = cd->filename(); 0065 // Allow for a fallback for mails that have a ContentDisposition header, but don't set the filename anyways. 0066 // Not the recommended way, but exists. 0067 if (!name.isEmpty()) { 0068 return name; 0069 } 0070 } 0071 if (const auto ct = mNode->contentType(false)) { 0072 return ct->name(); 0073 } 0074 return {}; 0075 } 0076 0077 static KMime::Headers::ContentType *contentType(KMime::Content *node) 0078 { 0079 if (node) { 0080 return node->contentType(false); 0081 } 0082 return nullptr; 0083 } 0084 0085 QByteArray MessagePart::charset() const 0086 { 0087 if (!mNode) { 0088 return "us-ascii"; 0089 } 0090 if (auto ct = contentType(mNode)) { 0091 return ct->charset(); 0092 } 0093 // Per rfc2045 us-ascii is the default 0094 return "us-ascii"; 0095 } 0096 0097 QByteArray MessagePart::mimeType() const 0098 { 0099 if (auto ct = contentType(mNode)) { 0100 return ct->mimeType(); 0101 } 0102 return {}; 0103 } 0104 0105 bool MessagePart::isText() const 0106 { 0107 if (auto ct = contentType(mNode)) { 0108 return ct->isText(); 0109 } 0110 return false; 0111 } 0112 0113 MessagePart::Error MessagePart::error() const 0114 { 0115 return mError; 0116 } 0117 0118 QString MessagePart::errorString() const 0119 { 0120 return mMetaData.errorText; 0121 } 0122 0123 PartMetaData *MessagePart::partMetaData() 0124 { 0125 return &mMetaData; 0126 } 0127 0128 bool MessagePart::isAttachment() const 0129 { 0130 if (mNode) { 0131 return KMime::isAttachment(mNode); 0132 } 0133 return false; 0134 } 0135 0136 KMime::Content *MessagePart::node() const 0137 { 0138 return mNode; 0139 } 0140 0141 void MessagePart::setIsRoot(bool root) 0142 { 0143 mRoot = root; 0144 } 0145 0146 bool MessagePart::isRoot() const 0147 { 0148 return mRoot; 0149 } 0150 0151 QString MessagePart::text() const 0152 { 0153 return mText; 0154 } 0155 0156 void MessagePart::setText(const QString &text) 0157 { 0158 mText = text; 0159 } 0160 0161 bool MessagePart::isHtml() const 0162 { 0163 return false; 0164 } 0165 0166 MessagePart *MessagePart::parentPart() const 0167 { 0168 return mParentPart; 0169 } 0170 0171 void MessagePart::setParentPart(MessagePart *parentPart) 0172 { 0173 mParentPart = parentPart; 0174 } 0175 0176 QString MessagePart::htmlContent() const 0177 { 0178 return text(); 0179 } 0180 0181 QString MessagePart::plaintextContent() const 0182 { 0183 return text(); 0184 } 0185 0186 void MessagePart::parseInternal(KMime::Content *node, bool onlyOneMimePart) 0187 { 0188 auto subMessagePart = mOtp->parseObjectTreeInternal(node, onlyOneMimePart); 0189 mRoot = subMessagePart->isRoot(); 0190 for (const auto &part : std::as_const(subMessagePart->subParts())) { 0191 appendSubPart(part); 0192 } 0193 } 0194 0195 void MessagePart::parseInternal(const QByteArray &data) 0196 { 0197 auto tempNode = new KMime::Content(); 0198 0199 const auto lfData = KMime::CRLFtoLF(data); 0200 // We have to deal with both bodies and full parts. In inline encrypted/signed parts we can have nested parts, 0201 // or just plain-text, and both ends up here. setContent defaults to setting only the header, so we have to avoid this. 0202 if (lfData.contains("\n\n")) { 0203 tempNode->setContent(lfData); 0204 } else { 0205 tempNode->setBody(lfData); 0206 } 0207 tempNode->parse(); 0208 tempNode->contentType()->setCharset(charset()); 0209 bindLifetime(tempNode); 0210 0211 if (!tempNode->head().isEmpty()) { 0212 tempNode->contentDescription()->from7BitString("temporary node"); 0213 } 0214 0215 parseInternal(tempNode); 0216 } 0217 0218 QString MessagePart::renderInternalText() const 0219 { 0220 QString text; 0221 for (const auto &mp : subParts()) { 0222 text += mp->text(); 0223 } 0224 return text; 0225 } 0226 0227 void MessagePart::appendSubPart(const MessagePart::Ptr &messagePart) 0228 { 0229 messagePart->setParentPart(this); 0230 mBlocks.append(messagePart); 0231 } 0232 0233 const QVector<MessagePart::Ptr> &MessagePart::subParts() const 0234 { 0235 return mBlocks; 0236 } 0237 0238 bool MessagePart::hasSubParts() const 0239 { 0240 return !mBlocks.isEmpty(); 0241 } 0242 0243 QVector<SignedMessagePart *> MessagePart::signatures() const 0244 { 0245 QVector<SignedMessagePart *> list; 0246 if (auto sig = dynamic_cast<SignedMessagePart *>(const_cast<MessagePart *>(this))) { 0247 list << sig; 0248 } 0249 auto parent = parentPart(); 0250 while (parent) { 0251 if (auto sig = dynamic_cast<SignedMessagePart *>(parent)) { 0252 list << sig; 0253 } 0254 parent = parent->parentPart(); 0255 } 0256 return list; 0257 } 0258 0259 QVector<EncryptedMessagePart *> MessagePart::encryptions() const 0260 { 0261 QVector<EncryptedMessagePart *> list; 0262 if (auto sig = dynamic_cast<EncryptedMessagePart *>(const_cast<MessagePart *>(this))) { 0263 list << sig; 0264 } 0265 auto parent = parentPart(); 0266 while (parent) { 0267 if (auto sig = dynamic_cast<EncryptedMessagePart *>(parent)) { 0268 list << sig; 0269 } 0270 parent = parent->parentPart(); 0271 } 0272 return list; 0273 } 0274 0275 KMMsgEncryptionState MessagePart::encryptionState() const 0276 { 0277 if (!encryptions().isEmpty()) { 0278 return KMMsgFullyEncrypted; 0279 } 0280 return KMMsgNotEncrypted; 0281 } 0282 0283 KMMsgSignatureState MessagePart::signatureState() const 0284 { 0285 if (!signatures().isEmpty()) { 0286 return KMMsgFullySigned; 0287 } 0288 return KMMsgNotSigned; 0289 } 0290 0291 void MessagePart::bindLifetime(KMime::Content *node) 0292 { 0293 mNodesToDelete << node; 0294 } 0295 0296 KMime::Headers::Base *MimeTreeParser::MessagePart::header(const char *header) const 0297 { 0298 if (node() && node()->hasHeader(header)) { 0299 return node()->headerByType(header); 0300 } 0301 if (auto parent = parentPart()) { 0302 return parent->header(header); 0303 } 0304 0305 return nullptr; 0306 } 0307 0308 //-----MessagePartList---------------------- 0309 MessagePartList::MessagePartList(ObjectTreeParser *otp, KMime::Content *node) 0310 : MessagePart(otp, QString(), node) 0311 { 0312 } 0313 0314 QString MessagePartList::text() const 0315 { 0316 return renderInternalText(); 0317 } 0318 0319 QString MessagePartList::plaintextContent() const 0320 { 0321 return QString(); 0322 } 0323 0324 QString MessagePartList::htmlContent() const 0325 { 0326 return QString(); 0327 } 0328 0329 //-----TextMessageBlock---------------------- 0330 0331 TextMessagePart::TextMessagePart(ObjectTreeParser *otp, KMime::Content *node) 0332 : MessagePartList(otp, node) 0333 , mSignatureState(KMMsgSignatureStateUnknown) 0334 , mEncryptionState(KMMsgEncryptionStateUnknown) 0335 { 0336 if (!mNode) { 0337 qCWarning(MIMETREEPARSER_LOG) << "not a valid node"; 0338 return; 0339 } 0340 0341 parseContent(); 0342 } 0343 0344 void TextMessagePart::parseContent() 0345 { 0346 mSignatureState = KMMsgNotSigned; 0347 mEncryptionState = KMMsgNotEncrypted; 0348 const auto blocks = prepareMessageForDecryption(mNode->decodedContent()); 0349 // We also get blocks for unencrypted messages 0350 if (!blocks.isEmpty()) { 0351 const auto aCodec = mOtp->codecFor(mNode); 0352 const auto cryptProto = OpenPGP; 0353 0354 /* The (overall) signature/encrypted status is broken 0355 * if one unencrypted part is at the beginning or in the middle 0356 * because mailmain adds an unencrypted part at the end this should not break the overall status 0357 * 0358 * That's why we first set the tmp status and if one crypted/signed block comes afterwards, than 0359 * the status is set to unencryped 0360 */ 0361 bool fullySignedOrEncrypted = true; 0362 bool fullySignedOrEncryptedTmp = true; 0363 0364 for (const auto &block : blocks) { 0365 if (!fullySignedOrEncryptedTmp) { 0366 fullySignedOrEncrypted = false; 0367 } 0368 0369 if (block.type() == NoPgpBlock && !block.text().trimmed().isEmpty()) { 0370 fullySignedOrEncryptedTmp = false; 0371 appendSubPart(MessagePart::Ptr(new MessagePart(mOtp, aCodec->toUnicode(KMime::CRLFtoLF(block.text()))))); 0372 } else if (block.type() == PgpMessageBlock) { 0373 KMime::Content *content = new KMime::Content; 0374 content->setBody(block.text()); 0375 content->parse(); 0376 content->contentType()->setCharset(charset()); 0377 EncryptedMessagePart::Ptr mp(new EncryptedMessagePart(mOtp, QString(), cryptProto, content, content, false)); 0378 mp->bindLifetime(content); 0379 mp->setIsEncrypted(true); 0380 appendSubPart(mp); 0381 } else if (block.type() == ClearsignedBlock) { 0382 KMime::Content *content = new KMime::Content; 0383 content->setBody(block.text()); 0384 content->parse(); 0385 content->contentType()->setCharset(charset()); 0386 SignedMessagePart::Ptr mp(new SignedMessagePart(mOtp, cryptProto, nullptr, content, false)); 0387 mp->bindLifetime(content); 0388 mp->setIsSigned(true); 0389 appendSubPart(mp); 0390 } else { 0391 continue; 0392 } 0393 0394 const auto mp = subParts().last().staticCast<MessagePart>(); 0395 const PartMetaData *messagePart(mp->partMetaData()); 0396 0397 if (!messagePart->isEncrypted && !messagePart->isSigned && !block.text().trimmed().isEmpty()) { 0398 mp->setText(aCodec->toUnicode(KMime::CRLFtoLF(block.text()))); 0399 } 0400 0401 if (messagePart->isEncrypted) { 0402 mEncryptionState = KMMsgPartiallyEncrypted; 0403 } 0404 0405 if (messagePart->isSigned) { 0406 mSignatureState = KMMsgPartiallySigned; 0407 } 0408 } 0409 0410 // Do we have an fully Signed/Encrypted Message? 0411 if (fullySignedOrEncrypted) { 0412 if (mSignatureState == KMMsgPartiallySigned) { 0413 mSignatureState = KMMsgFullySigned; 0414 } 0415 if (mEncryptionState == KMMsgPartiallyEncrypted) { 0416 mEncryptionState = KMMsgFullyEncrypted; 0417 } 0418 } 0419 } 0420 } 0421 0422 KMMsgEncryptionState TextMessagePart::encryptionState() const 0423 { 0424 if (mEncryptionState == KMMsgNotEncrypted) { 0425 return MessagePart::encryptionState(); 0426 } 0427 return mEncryptionState; 0428 } 0429 0430 KMMsgSignatureState TextMessagePart::signatureState() const 0431 { 0432 if (mSignatureState == KMMsgNotSigned) { 0433 return MessagePart::signatureState(); 0434 } 0435 return mSignatureState; 0436 } 0437 0438 //-----AttachmentMessageBlock---------------------- 0439 0440 AttachmentMessagePart::AttachmentMessagePart(ObjectTreeParser *otp, KMime::Content *node) 0441 : TextMessagePart(otp, node) 0442 { 0443 } 0444 0445 //-----HtmlMessageBlock---------------------- 0446 0447 HtmlMessagePart::HtmlMessagePart(ObjectTreeParser *otp, KMime::Content *node) 0448 : MessagePart(otp, QString(), node) 0449 { 0450 if (!mNode) { 0451 qCWarning(MIMETREEPARSER_LOG) << "not a valid node"; 0452 return; 0453 } 0454 0455 setText(mOtp->codecFor(mNode)->toUnicode(KMime::CRLFtoLF(mNode->decodedContent()))); 0456 } 0457 0458 //-----MimeMessageBlock---------------------- 0459 0460 MimeMessagePart::MimeMessagePart(ObjectTreeParser *otp, KMime::Content *node, bool onlyOneMimePart) 0461 : MessagePart(otp, QString(), node) 0462 { 0463 if (!mNode) { 0464 qCWarning(MIMETREEPARSER_LOG) << "not a valid node"; 0465 return; 0466 } 0467 0468 parseInternal(mNode, onlyOneMimePart); 0469 } 0470 0471 MimeMessagePart::~MimeMessagePart() 0472 { 0473 } 0474 0475 QString MimeMessagePart::text() const 0476 { 0477 return renderInternalText(); 0478 } 0479 0480 QString MimeMessagePart::plaintextContent() const 0481 { 0482 return QString(); 0483 } 0484 0485 QString MimeMessagePart::htmlContent() const 0486 { 0487 return QString(); 0488 } 0489 0490 //-----AlternativeMessagePart---------------------- 0491 0492 AlternativeMessagePart::AlternativeMessagePart(ObjectTreeParser *otp, KMime::Content *node) 0493 : MessagePart(otp, QString(), node) 0494 { 0495 if (auto dataIcal = findTypeInDirectChildren(mNode, "text/calendar")) { 0496 mChildParts[MultipartIcal] = MimeMessagePart::Ptr(new MimeMessagePart(mOtp, dataIcal, true)); 0497 } 0498 0499 if (auto dataText = findTypeInDirectChildren(mNode, "text/plain")) { 0500 mChildParts[MultipartPlain] = MimeMessagePart::Ptr(new MimeMessagePart(mOtp, dataText, true)); 0501 } 0502 0503 if (auto dataHtml = findTypeInDirectChildren(mNode, "text/html")) { 0504 mChildParts[MultipartHtml] = MimeMessagePart::Ptr(new MimeMessagePart(mOtp, dataHtml, true)); 0505 } else { 0506 // If we didn't find the HTML part as the first child of the multipart/alternative, it might 0507 // be that this is a HTML message with images, and text/plain and multipart/related are the 0508 // immediate children of this multipart/alternative node. 0509 // In this case, the HTML node is a child of multipart/related. 0510 // In the case of multipart/related we don't expect multiple html parts, it is usually used to group attachments 0511 // with html content. 0512 // 0513 // In any case, this is not a complete implementation of MIME, but an approximation for the kind of mails we actually see in the wild. 0514 auto data = [&] { 0515 if (auto d = findTypeInDirectChildren(mNode, "multipart/related")) { 0516 return d; 0517 } 0518 return findTypeInDirectChildren(mNode, "multipart/mixed"); 0519 }(); 0520 if (data) { 0521 QString htmlContent; 0522 const auto parts = data->contents(); 0523 for (auto p : data->contents()) { 0524 if ((!p->contentType()->isEmpty()) && (p->contentType()->mimeType() == "text/html")) { 0525 htmlContent += MimeMessagePart(mOtp, p, true).text(); 0526 } else if (KMime::isAttachment(p)) { 0527 appendSubPart(MimeMessagePart::Ptr(new MimeMessagePart(otp, p, true))); 0528 } 0529 } 0530 mChildParts[MultipartHtml] = MessagePart::Ptr(new MessagePart(mOtp, htmlContent, nullptr)); 0531 } 0532 } 0533 } 0534 0535 AlternativeMessagePart::~AlternativeMessagePart() 0536 { 0537 } 0538 0539 QList<AlternativeMessagePart::HtmlMode> AlternativeMessagePart::availableModes() 0540 { 0541 return mChildParts.keys(); 0542 } 0543 0544 QString AlternativeMessagePart::text() const 0545 { 0546 if (mChildParts.contains(MultipartPlain)) { 0547 return mChildParts[MultipartPlain]->text(); 0548 } 0549 return QString(); 0550 } 0551 0552 bool AlternativeMessagePart::isHtml() const 0553 { 0554 return mChildParts.contains(MultipartHtml); 0555 } 0556 0557 QString AlternativeMessagePart::plaintextContent() const 0558 { 0559 return text(); 0560 } 0561 0562 QString AlternativeMessagePart::htmlContent() const 0563 { 0564 if (mChildParts.contains(MultipartHtml)) { 0565 return mChildParts[MultipartHtml]->text(); 0566 } else { 0567 return plaintextContent(); 0568 } 0569 } 0570 0571 QString AlternativeMessagePart::icalContent() const 0572 { 0573 if (mChildParts.contains(MultipartIcal)) { 0574 return mChildParts[MultipartIcal]->text(); 0575 } 0576 return {}; 0577 } 0578 0579 //-----CertMessageBlock---------------------- 0580 0581 CertMessagePart::CertMessagePart(ObjectTreeParser *otp, KMime::Content *node, const CryptoProtocol cryptoProto) 0582 : MessagePart(otp, QString(), node) 0583 , mProtocol(cryptoProto) 0584 { 0585 if (!mNode) { 0586 qCWarning(MIMETREEPARSER_LOG) << "not a valid node"; 0587 return; 0588 } 0589 } 0590 0591 CertMessagePart::~CertMessagePart() 0592 { 0593 } 0594 0595 void CertMessagePart::import() 0596 { 0597 importKey(mProtocol, mNode->decodedContent()); 0598 } 0599 0600 QString CertMessagePart::text() const 0601 { 0602 return QString(); 0603 } 0604 0605 //-----SignedMessageBlock--------------------- 0606 SignedMessagePart::SignedMessagePart(ObjectTreeParser *otp, 0607 const CryptoProtocol cryptoProto, 0608 KMime::Content *node, 0609 KMime::Content *signedData, 0610 bool parseAfterDecryption) 0611 : MessagePart(otp, {}, node) 0612 , mParseAfterDecryption(parseAfterDecryption) 0613 , mProtocol(cryptoProto) 0614 , mSignedData(signedData) 0615 { 0616 mMetaData.isSigned = true; 0617 mMetaData.isGoodSignature = false; 0618 mMetaData.status = i18n("Wrong Crypto Plug-In."); 0619 } 0620 0621 SignedMessagePart::~SignedMessagePart() 0622 { 0623 } 0624 0625 void SignedMessagePart::setIsSigned(bool isSigned) 0626 { 0627 mMetaData.isSigned = isSigned; 0628 } 0629 0630 bool SignedMessagePart::isSigned() const 0631 { 0632 return mMetaData.isSigned; 0633 } 0634 0635 static QString prettifyDN(const char *uid) 0636 { 0637 // We used to use QGpgME::DN::prettyDN here. But I'm not sure what we actually need it for. 0638 return QString::fromUtf8(uid); 0639 } 0640 0641 static void sigStatusToMetaData(PartMetaData &mMetaData, const Signature &signature) 0642 { 0643 mMetaData.isGoodSignature = !signature.status; 0644 if (!mMetaData.isGoodSignature) { 0645 qWarning() << "The signature is bad." << signature.status; 0646 } 0647 // save extended signature status flags 0648 mMetaData.keyMissing = signature.result == Crypto::Signature::KeyNotFound; 0649 mMetaData.keyExpired = signature.result == Crypto::Signature::Expired; 0650 0651 Key key; 0652 if (mMetaData.isGoodSignature) { 0653 // Search for the key by its fingerprint so that we can check for trust etc. 0654 const auto keys = findKeys({QString::fromUtf8(signature.fingerprint)}); 0655 if (keys.size() > 1) { 0656 // Should not happen 0657 qCDebug(MIMETREEPARSER_LOG) << "Oops: Found more then one Key for Fingerprint: " << signature.fingerprint; 0658 } 0659 if (keys.empty()) { 0660 // Should not happen at this point 0661 qCWarning(MIMETREEPARSER_LOG) << "Oops: Found no Key for Fingerprint: " << signature.fingerprint; 0662 } else { 0663 key = keys[0]; 0664 } 0665 } 0666 0667 mMetaData.keyId = key.keyId; 0668 if (mMetaData.keyId.isEmpty()) { 0669 mMetaData.keyId = signature.fingerprint; 0670 } 0671 mMetaData.keyIsTrusted = signature.isTrusted; 0672 if (!key.userIds.empty()) { 0673 mMetaData.signer = prettifyDN(key.userIds[0].id.data()); 0674 } 0675 for (const auto &userId : key.userIds) { 0676 QString email = QString::fromUtf8(userId.email); 0677 // Work around cryptplug bug where email addresses are specified as angle-addr ( <person@example.org> ), 0678 // not addr-spec ( person@example.org ): 0679 if (email.startsWith(QLatin1Char('<')) && email.endsWith(QLatin1Char('>'))) { 0680 email = email.mid(1, email.length() - 2); 0681 } 0682 if (!email.isEmpty()) { 0683 mMetaData.signerMailAddresses.append(email); 0684 } 0685 } 0686 0687 mMetaData.creationTime = signature.creationTime; 0688 if (mMetaData.signer.isEmpty()) { 0689 if (!key.userIds.empty()) { 0690 mMetaData.signer = prettifyDN(key.userIds[0].name.data()); 0691 } 0692 if (!mMetaData.signerMailAddresses.empty()) { 0693 if (mMetaData.signer.isEmpty()) { 0694 mMetaData.signer = mMetaData.signerMailAddresses.front(); 0695 } else { 0696 mMetaData.signer += QLatin1String(" <") + mMetaData.signerMailAddresses.front() + QLatin1Char('>'); 0697 } 0698 } 0699 } 0700 } 0701 0702 void SignedMessagePart::startVerification() 0703 { 0704 if (!mSignedData) { 0705 return; 0706 } 0707 0708 mMetaData.isSigned = false; 0709 mMetaData.status = i18n("Wrong Crypto Plug-In."); 0710 mMetaData.isEncrypted = false; 0711 mMetaData.isDecryptable = false; 0712 0713 const auto codec = mOtp->codecFor(mSignedData); 0714 0715 // If we have a mNode, this is a detached signature 0716 if (mNode) { 0717 const auto signature = mNode->decodedContent(); 0718 0719 // This is necessary in case the original data contained CRLF's. Otherwise the signature will not match the data (since KMIME normalizes to LF) 0720 const QByteArray signedData = KMime::LFtoCRLF(mSignedData->encodedContent()); 0721 0722 setVerificationResult(Crypto::verifyDetachedSignature(mProtocol, signature, signedData), signedData); 0723 setText(codec->toUnicode(KMime::CRLFtoLF(signedData))); 0724 } else { 0725 QByteArray outdata; 0726 setVerificationResult(Crypto::verifyOpaqueSignature(mProtocol, mSignedData->decodedContent(), outdata), outdata); 0727 setText(codec->toUnicode(KMime::CRLFtoLF(outdata))); 0728 } 0729 0730 if (!mMetaData.isSigned) { 0731 mMetaData.creationTime = QDateTime(); 0732 } 0733 } 0734 0735 void SignedMessagePart::setVerificationResult(const VerificationResult &result, const QByteArray &signedData) 0736 { 0737 const auto signatures = result.signatures; 0738 // FIXME 0739 // mMetaData.auditLogError = result.error; 0740 if (!signatures.empty()) { 0741 mMetaData.isSigned = true; 0742 sigStatusToMetaData(mMetaData, signatures.front()); 0743 if (!signedData.isEmpty() && mParseAfterDecryption) { 0744 parseInternal(signedData); 0745 } 0746 } 0747 } 0748 0749 QString SignedMessagePart::plaintextContent() const 0750 { 0751 if (!mNode) { 0752 return MessagePart::text(); 0753 } else { 0754 return QString(); 0755 } 0756 } 0757 0758 QString SignedMessagePart::htmlContent() const 0759 { 0760 if (!mNode) { 0761 return MessagePart::text(); 0762 } else { 0763 return QString(); 0764 } 0765 } 0766 0767 //-----CryptMessageBlock--------------------- 0768 EncryptedMessagePart::EncryptedMessagePart(ObjectTreeParser *otp, 0769 const QString &text, 0770 const CryptoProtocol cryptoProto, 0771 KMime::Content *node, 0772 KMime::Content *encryptedNode, 0773 bool parseAfterDecryption) 0774 : MessagePart(otp, text, node) 0775 , mParseAfterDecryption(parseAfterDecryption) 0776 , mProtocol(cryptoProto) 0777 , mEncryptedNode(encryptedNode) 0778 { 0779 mMetaData.isSigned = false; 0780 mMetaData.isGoodSignature = false; 0781 mMetaData.isEncrypted = false; 0782 mMetaData.isDecryptable = false; 0783 mMetaData.status = i18n("Wrong Crypto Plug-In."); 0784 } 0785 0786 void EncryptedMessagePart::setIsEncrypted(bool encrypted) 0787 { 0788 mMetaData.isEncrypted = encrypted; 0789 } 0790 0791 bool EncryptedMessagePart::isEncrypted() const 0792 { 0793 return mMetaData.isEncrypted; 0794 } 0795 0796 bool EncryptedMessagePart::isDecryptable() const 0797 { 0798 return mMetaData.isDecryptable; 0799 } 0800 0801 bool EncryptedMessagePart::decrypt(KMime::Content &data) 0802 { 0803 mError = NoError; 0804 mMetaData.errorText.clear(); 0805 // FIXME 0806 // mMetaData.auditLogError = GpgME::Error(); 0807 mMetaData.auditLog.clear(); 0808 0809 const QByteArray ciphertext = data.decodedContent(); 0810 QByteArray plainText; 0811 DecryptionResult decryptResult; 0812 VerificationResult verifyResult; 0813 std::tie(decryptResult, verifyResult) = Crypto::decryptAndVerify(mProtocol, ciphertext, plainText); 0814 mMetaData.isSigned = verifyResult.signatures.size() > 0; 0815 0816 // Normalize CRLF's 0817 plainText = KMime::CRLFtoLF(plainText); 0818 const auto codec = mOtp->codecFor(&data); 0819 const auto decoded = codec->toUnicode(plainText); 0820 0821 if (verifyResult.signatures.size() > 0) { 0822 // We simply attach a signed message part to indicate that this content is also signed 0823 // We're forwarding mNode to not loose the encoding information 0824 auto subPart = SignedMessagePart::Ptr(new SignedMessagePart(mOtp, mProtocol, mNode, nullptr)); 0825 subPart->setText(decoded); 0826 subPart->setVerificationResult(verifyResult, plainText); 0827 appendSubPart(subPart); 0828 } 0829 0830 if (decryptResult.error && mMetaData.isSigned) { 0831 // Only a signed part 0832 mMetaData.isEncrypted = false; 0833 mDecryptedData = plainText; 0834 return true; 0835 } 0836 0837 if (mMetaData.isEncrypted) { 0838 mMetaData.keyId = [&] { 0839 for (const auto &recipient : std::as_const(decryptResult.recipients)) { 0840 if (recipient.secretKeyAvailable) { 0841 return recipient.keyId; 0842 } 0843 } 0844 return QByteArray{}; 0845 }(); 0846 } 0847 0848 if (!decryptResult.error) { 0849 mDecryptedData = plainText; 0850 setText(decoded); 0851 } else { 0852 mMetaData.isEncrypted = decryptResult.result != Crypto::DecryptionResult::NotEncrypted; 0853 qWarning() << "Failed to decrypt : " << decryptResult.error; 0854 0855 if (decryptResult.result == Crypto::DecryptionResult::NoSecretKeyError) { 0856 mError = NoKeyError; 0857 mMetaData.errorText = i18n("Could not decrypt the data: no key found for recipients."); 0858 } else if (decryptResult.result == Crypto::DecryptionResult::PassphraseError) { 0859 mError = PassphraseError; 0860 } else { 0861 mError = UnknownError; 0862 mMetaData.errorText = i18n("Could not decrypt the data."); 0863 } 0864 setText(QString::fromUtf8(mDecryptedData.constData())); 0865 return false; 0866 } 0867 0868 return true; 0869 } 0870 0871 void EncryptedMessagePart::startDecryption(KMime::Content *data) 0872 { 0873 mMetaData.isEncrypted = true; 0874 mMetaData.isDecryptable = decrypt(*data); 0875 0876 if (mParseAfterDecryption && !mMetaData.isSigned) { 0877 parseInternal(mDecryptedData); 0878 } 0879 } 0880 0881 void EncryptedMessagePart::startDecryption() 0882 { 0883 if (mEncryptedNode) { 0884 startDecryption(mEncryptedNode); 0885 } else { 0886 startDecryption(mNode); 0887 } 0888 } 0889 0890 QString EncryptedMessagePart::plaintextContent() const 0891 { 0892 if (!mNode) { 0893 return MessagePart::text(); 0894 } else { 0895 return QString(); 0896 } 0897 } 0898 0899 QString EncryptedMessagePart::htmlContent() const 0900 { 0901 if (!mNode) { 0902 return MessagePart::text(); 0903 } else { 0904 return QString(); 0905 } 0906 } 0907 0908 QString EncryptedMessagePart::text() const 0909 { 0910 if (hasSubParts()) { 0911 auto _mp = (subParts()[0]).dynamicCast<SignedMessagePart>(); 0912 if (_mp) { 0913 return _mp->text(); 0914 } else { 0915 return MessagePart::text(); 0916 } 0917 } else { 0918 return MessagePart::text(); 0919 } 0920 } 0921 0922 EncapsulatedRfc822MessagePart::EncapsulatedRfc822MessagePart(ObjectTreeParser *otp, KMime::Content *node, const KMime::Message::Ptr &message) 0923 : MessagePart(otp, QString(), node) 0924 , mMessage(message) 0925 { 0926 mMetaData.isEncrypted = false; 0927 mMetaData.isSigned = false; 0928 mMetaData.isEncapsulatedRfc822Message = true; 0929 0930 if (!mMessage) { 0931 qCWarning(MIMETREEPARSER_LOG) << "Node is of type message/rfc822 but doesn't have a message!"; 0932 return; 0933 } 0934 0935 parseInternal(message.data()); 0936 } 0937 0938 QString EncapsulatedRfc822MessagePart::text() const 0939 { 0940 return renderInternalText(); 0941 } 0942 0943 QString EncapsulatedRfc822MessagePart::from() const 0944 { 0945 if (auto from = mMessage->from(false)) { 0946 return from->asUnicodeString(); 0947 } 0948 return {}; 0949 } 0950 0951 QDateTime EncapsulatedRfc822MessagePart::date() const 0952 { 0953 if (auto date = mMessage->date(false)) { 0954 return date->dateTime(); 0955 } 0956 return {}; 0957 } 0958 0959 HeadersPart::HeadersPart(ObjectTreeParser *otp, KMime::Content *node) 0960 : MessagePart(otp, QString(), node) 0961 { 0962 }