File indexing completed on 2024-06-16 05:00:15
0001 /* 0002 SPDX-FileCopyrightText: 2015 Sandro Knauß <sknauss@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "messagepart.h" 0008 #include "cryptohelper.h" 0009 #include "job/qgpgmejobexecutor.h" 0010 #include "memento/compositememento.h" 0011 #include "memento/cryptobodypartmemento.h" 0012 #include "memento/decryptverifybodypartmemento.h" 0013 #include "memento/keycachememento.h" 0014 #include "memento/verifydetachedbodypartmemento.h" 0015 #include "memento/verifyopaquebodypartmemento.h" 0016 #include "mimetreeparser_debug.h" 0017 #include "objecttreeparser.h" 0018 0019 #include "bodyformatter/utils.h" 0020 0021 #include <KMime/Content> 0022 #include <KMime/Types> 0023 #include <Libkleo/Compliance> 0024 #include <Libkleo/KeyCache> 0025 0026 #include <QGpgME/DN> 0027 #include <QGpgME/ImportJob> 0028 #include <QGpgME/Protocol> 0029 #include <QGpgME/VerifyDetachedJob> 0030 #include <QGpgME/VerifyOpaqueJob> 0031 0032 #include <gpgme++/key.h> 0033 #include <gpgme++/keylistresult.h> 0034 #include <gpgme.h> 0035 0036 #include <KLocalizedString> 0037 0038 #include <QUrl> 0039 0040 using namespace MimeTreeParser; 0041 0042 //------MessagePart----------------------- 0043 namespace MimeTreeParser 0044 { 0045 class MessagePartPrivate 0046 { 0047 public: 0048 MessagePart *mParentPart = nullptr; 0049 QList<MessagePart::Ptr> mBlocks; 0050 KMime::Content *mNode = nullptr; 0051 KMime::Content *mAttachmentNode = nullptr; 0052 QString mText; 0053 PartMetaData mMetaData; 0054 bool mRoot = false; 0055 bool mIsImage = false; 0056 bool mNeverDisplayInline = false; 0057 }; 0058 } 0059 0060 MessagePart::MessagePart(ObjectTreeParser *otp, const QString &text) 0061 : mOtp(otp) 0062 , d(new MessagePartPrivate) 0063 { 0064 d->mText = text; 0065 } 0066 0067 MessagePart::~MessagePart() = default; 0068 0069 MessagePart *MessagePart::parentPart() const 0070 { 0071 return d->mParentPart; 0072 } 0073 0074 void MessagePart::setParentPart(MessagePart *parentPart) 0075 { 0076 d->mParentPart = parentPart; 0077 } 0078 0079 QString MessagePart::htmlContent() const 0080 { 0081 return text(); 0082 } 0083 0084 QString MessagePart::plaintextContent() const 0085 { 0086 return text(); 0087 } 0088 0089 PartMetaData *MessagePart::partMetaData() const 0090 { 0091 return &d->mMetaData; 0092 } 0093 0094 Interface::BodyPartMemento *MessagePart::memento() const 0095 { 0096 return nodeHelper()->bodyPartMemento(content(), "__plugin__"); 0097 } 0098 0099 void MessagePart::setMemento(Interface::BodyPartMemento *memento) 0100 { 0101 nodeHelper()->setBodyPartMemento(content(), "__plugin__", memento); 0102 } 0103 0104 KMime::Content *MessagePart::content() const 0105 { 0106 return d->mNode; 0107 } 0108 0109 void MessagePart::setContent(KMime::Content *node) 0110 { 0111 d->mNode = node; 0112 } 0113 0114 KMime::Content *MessagePart::attachmentContent() const 0115 { 0116 return d->mAttachmentNode; 0117 } 0118 0119 void MessagePart::setAttachmentContent(KMime::Content *node) 0120 { 0121 d->mAttachmentNode = node; 0122 } 0123 0124 bool MessagePart::isAttachment() const 0125 { 0126 return d->mAttachmentNode; 0127 } 0128 0129 QString MessagePart::attachmentIndex() const 0130 { 0131 return attachmentContent()->index().toString(); 0132 } 0133 0134 QString MessagePart::attachmentLink() const 0135 { 0136 return mOtp->nodeHelper()->asHREF(content(), QStringLiteral("body")); 0137 } 0138 0139 QString MessagePart::makeLink(const QString &path) const 0140 { 0141 // FIXME: use a PRNG for the first arg, instead of a serial number 0142 static int serial = 0; 0143 if (path.isEmpty()) { 0144 return {}; 0145 } 0146 return QStringLiteral("x-kmail:/bodypart/%1/%2/%3") 0147 .arg(serial++) 0148 .arg(mOtp->nodeHelper()->asHREF(content()), QString::fromLatin1(QUrl::toPercentEncoding(path, "/"))); 0149 } 0150 0151 void MessagePart::setIsRoot(bool root) 0152 { 0153 d->mRoot = root; 0154 } 0155 0156 bool MessagePart::isRoot() const 0157 { 0158 return d->mRoot; 0159 } 0160 0161 QString MessagePart::text() const 0162 { 0163 return d->mText; 0164 } 0165 0166 void MessagePart::setText(const QString &text) 0167 { 0168 d->mText = text; 0169 } 0170 0171 bool MessagePart::isHtml() const 0172 { 0173 return false; 0174 } 0175 0176 Interface::ObjectTreeSource *MessagePart::source() const 0177 { 0178 Q_ASSERT(mOtp); 0179 return mOtp->mSource; 0180 } 0181 0182 NodeHelper *MessagePart::nodeHelper() const 0183 { 0184 Q_ASSERT(mOtp); 0185 return mOtp->nodeHelper(); 0186 } 0187 0188 void MessagePart::parseInternal(KMime::Content *node, bool onlyOneMimePart) 0189 { 0190 auto subMessagePart = mOtp->parseObjectTreeInternal(node, onlyOneMimePart); 0191 d->mRoot = subMessagePart->isRoot(); 0192 const QList<MessagePart::Ptr> subParts = subMessagePart->subParts(); 0193 for (const auto &part : subParts) { 0194 appendSubPart(part); 0195 } 0196 } 0197 0198 QString MessagePart::renderInternalText() const 0199 { 0200 QString text; 0201 const auto subPartsLst = subParts(); 0202 for (const auto &mp : subPartsLst) { 0203 text += mp->text(); 0204 } 0205 return text; 0206 } 0207 0208 void MessagePart::fix() const 0209 { 0210 const auto subPartsLst = subParts(); 0211 for (const auto &mp : subPartsLst) { 0212 const auto m = mp.dynamicCast<MessagePart>(); 0213 if (m) { 0214 m->fix(); 0215 } 0216 } 0217 } 0218 0219 void MessagePart::appendSubPart(const MessagePart::Ptr &messagePart) 0220 { 0221 messagePart->setParentPart(this); 0222 d->mBlocks.append(messagePart); 0223 } 0224 0225 const QList<MessagePart::Ptr> &MessagePart::subParts() const 0226 { 0227 return d->mBlocks; 0228 } 0229 0230 bool MessagePart::hasSubParts() const 0231 { 0232 return !d->mBlocks.isEmpty(); 0233 } 0234 0235 void MessagePart::clearSubParts() 0236 { 0237 d->mBlocks.clear(); 0238 } 0239 0240 bool MessagePart::neverDisplayInline() const 0241 { 0242 return d->mNeverDisplayInline; 0243 } 0244 0245 void MessagePart::setNeverDisplayInline(bool displayInline) 0246 { 0247 d->mNeverDisplayInline = displayInline; 0248 } 0249 0250 bool MessagePart::isImage() const 0251 { 0252 return d->mIsImage; 0253 } 0254 0255 void MessagePart::setIsImage(bool image) 0256 { 0257 d->mIsImage = image; 0258 } 0259 0260 bool MessagePart::hasHeader(const char *headerType) const 0261 { 0262 Q_UNUSED(headerType) 0263 return false; 0264 } 0265 0266 const KMime::Headers::Base *MimeTreeParser::MessagePart::header(const char *headerType) const 0267 { 0268 Q_UNUSED(headerType) 0269 return nullptr; 0270 } 0271 0272 QList<KMime::Headers::Base *> MessagePart::headers(const char *headerType) const 0273 { 0274 Q_UNUSED(headerType) 0275 return {}; 0276 } 0277 0278 //-----MessagePartList---------------------- 0279 MessagePartList::MessagePartList(ObjectTreeParser *otp) 0280 : MessagePart(otp, QString()) 0281 { 0282 } 0283 0284 MessagePartList::~MessagePartList() = default; 0285 0286 QString MessagePartList::text() const 0287 { 0288 return renderInternalText(); 0289 } 0290 0291 QString MessagePartList::plaintextContent() const 0292 { 0293 return {}; 0294 } 0295 0296 QString MessagePartList::htmlContent() const 0297 { 0298 return {}; 0299 } 0300 0301 //-----TextMessageBlock---------------------- 0302 0303 TextMessagePart::TextMessagePart(ObjectTreeParser *otp, KMime::Content *node, bool decryptMessage) 0304 : MessagePartList(otp) 0305 , mDecryptMessage(decryptMessage) 0306 { 0307 if (!node) { 0308 qCWarning(MIMETREEPARSER_LOG) << "not a valid node"; 0309 return; 0310 } 0311 0312 setContent(node); 0313 0314 parseContent(); 0315 } 0316 0317 TextMessagePart::~TextMessagePart() = default; 0318 0319 bool TextMessagePart::decryptMessage() const 0320 { 0321 return mDecryptMessage; 0322 } 0323 0324 void TextMessagePart::parseContent() 0325 { 0326 const auto codecName = mOtp->codecNameFor(content()); 0327 QStringDecoder aCodec(codecName.constData()); 0328 const QString &fromAddress = mOtp->nodeHelper()->fromAsString(content()); 0329 mSignatureState = KMMsgNotSigned; 0330 mEncryptionState = KMMsgNotEncrypted; 0331 const auto blocks = prepareMessageForDecryption(content()->decodedContent()); 0332 0333 const auto cryptProto = QGpgME::openpgp(); 0334 0335 if (!blocks.isEmpty()) { 0336 /* The (overall) signature/encrypted status is broken 0337 * if one unencrypted part is at the beginning or in the middle 0338 * because mailmain adds an unencrypted part at the end this should not break the overall status 0339 * 0340 * That's why we first set the tmp status and if one encrypted/signed block comes afterwards, than 0341 * the status is set to unencrypted 0342 */ 0343 bool fullySignedOrEncrypted = true; 0344 bool fullySignedOrEncryptedTmp = true; 0345 0346 int blockIndex = -1; 0347 for (const auto &block : blocks) { 0348 blockIndex += 1; 0349 if (!fullySignedOrEncryptedTmp) { 0350 fullySignedOrEncrypted = false; 0351 } 0352 0353 if (block.type() == NoPgpBlock && !block.text().trimmed().isEmpty()) { 0354 fullySignedOrEncryptedTmp = false; 0355 aCodec.resetState(); 0356 appendSubPart(MessagePart::Ptr(new MessagePart(mOtp, aCodec.decode(block.text())))); 0357 } else if (block.type() == PgpMessageBlock) { 0358 EncryptedMessagePart::Ptr mp(new EncryptedMessagePart(mOtp, QString(), cryptProto, fromAddress, nullptr)); 0359 mp->setDecryptMessage(decryptMessage()); 0360 mp->setIsEncrypted(true); 0361 mp->setMementoName(mp->mementoName() + "-" + nodeHelper()->asHREF(content(), QString::number(blockIndex)).toLocal8Bit()); 0362 appendSubPart(mp); 0363 if (!decryptMessage()) { 0364 continue; 0365 } 0366 mp->startDecryption(block.text(), codecName); 0367 if (mp->partMetaData()->inProgress) { 0368 continue; 0369 } 0370 } else if (block.type() == ClearsignedBlock) { 0371 SignedMessagePart::Ptr mp(new SignedMessagePart(mOtp, QString(), cryptProto, fromAddress, nullptr)); 0372 mp->setMementoName(mp->mementoName() + "-" + nodeHelper()->asHREF(content(), QString::number(blockIndex)).toLocal8Bit()); 0373 appendSubPart(mp); 0374 mp->startVerification(block.text(), codecName); 0375 } else { 0376 continue; 0377 } 0378 0379 const auto mp = subParts().last().staticCast<MessagePart>(); 0380 const PartMetaData *messagePart(mp->partMetaData()); 0381 0382 if (!messagePart->isEncrypted && !messagePart->isSigned && !block.text().trimmed().isEmpty()) { 0383 aCodec.resetState(); 0384 mp->setText(aCodec.decode(block.text())); 0385 } 0386 0387 if (messagePart->isEncrypted) { 0388 mEncryptionState = KMMsgPartiallyEncrypted; 0389 } 0390 0391 if (messagePart->isSigned) { 0392 mSignatureState = KMMsgPartiallySigned; 0393 } 0394 } 0395 0396 // Do we have an fully Signed/Encrypted Message? 0397 if (fullySignedOrEncrypted) { 0398 if (mSignatureState == KMMsgPartiallySigned) { 0399 mSignatureState = KMMsgFullySigned; 0400 } 0401 if (mEncryptionState == KMMsgPartiallyEncrypted) { 0402 mEncryptionState = KMMsgFullyEncrypted; 0403 } 0404 } 0405 } 0406 } 0407 0408 KMMsgEncryptionState TextMessagePart::encryptionState() const 0409 { 0410 return mEncryptionState; 0411 } 0412 0413 KMMsgSignatureState TextMessagePart::signatureState() const 0414 { 0415 return mSignatureState; 0416 } 0417 0418 bool TextMessagePart::showLink() const 0419 { 0420 return !temporaryFilePath().isEmpty(); 0421 } 0422 0423 bool TextMessagePart::isFirstTextPart() const 0424 { 0425 return content()->topLevel()->textContent() == content(); 0426 } 0427 0428 bool TextMessagePart::hasLabel() const 0429 { 0430 return !NodeHelper::fileName(content()).isEmpty(); 0431 } 0432 0433 QString TextMessagePart::label() const 0434 { 0435 const QString name = content()->contentType()->name(); 0436 QString label = name.isEmpty() ? NodeHelper::fileName(content()) : name; 0437 if (label.isEmpty()) { 0438 label = i18nc("display name for an unnamed attachment", "Unnamed"); 0439 } 0440 return label; 0441 } 0442 0443 QString TextMessagePart::comment() const 0444 { 0445 const QString comment = content()->contentDescription()->asUnicodeString(); 0446 if (comment == label()) { 0447 return {}; 0448 } 0449 return comment; 0450 } 0451 0452 QString TextMessagePart::temporaryFilePath() const 0453 { 0454 return nodeHelper()->writeNodeToTempFile(content()); 0455 } 0456 0457 //-----AttachmentMessageBlock---------------------- 0458 0459 AttachmentMessagePart::AttachmentMessagePart(ObjectTreeParser *otp, KMime::Content *node, bool decryptMessage) 0460 : TextMessagePart(otp, node, decryptMessage) 0461 { 0462 } 0463 0464 AttachmentMessagePart::~AttachmentMessagePart() = default; 0465 0466 //-----HtmlMessageBlock---------------------- 0467 0468 HtmlMessagePart::HtmlMessagePart(ObjectTreeParser *otp, KMime::Content *node, Interface::ObjectTreeSource *source) 0469 : MessagePart(otp, QString()) 0470 { 0471 Q_UNUSED(source) 0472 if (!node) { 0473 qCWarning(MIMETREEPARSER_LOG) << "not a valid node"; 0474 return; 0475 } 0476 setContent(node); 0477 0478 const QByteArray partBody(node->decodedContent()); 0479 mBodyHTML = QStringDecoder(mOtp->codecNameFor(node).constData()).decode(partBody); 0480 mCharset = NodeHelper::charset(node); 0481 } 0482 0483 HtmlMessagePart::~HtmlMessagePart() = default; 0484 0485 void HtmlMessagePart::fix() const 0486 { 0487 mOtp->mHtmlContent += mBodyHTML; 0488 mOtp->mHtmlContentCharset = mCharset; 0489 } 0490 0491 QString HtmlMessagePart::text() const 0492 { 0493 return mBodyHTML; 0494 } 0495 0496 QString MimeTreeParser::HtmlMessagePart::plaintextContent() const 0497 { 0498 return {}; 0499 } 0500 0501 bool HtmlMessagePart::isHtml() const 0502 { 0503 return true; 0504 } 0505 0506 QString HtmlMessagePart::bodyHtml() const 0507 { 0508 return mBodyHTML; 0509 } 0510 0511 //-----MimeMessageBlock---------------------- 0512 0513 MimeMessagePart::MimeMessagePart(ObjectTreeParser *otp, KMime::Content *node, bool onlyOneMimePart) 0514 : MessagePart(otp, QString()) 0515 , mOnlyOneMimePart(onlyOneMimePart) 0516 { 0517 if (!node) { 0518 qCWarning(MIMETREEPARSER_LOG) << "not a valid node"; 0519 return; 0520 } 0521 setContent(node); 0522 0523 parseInternal(node, mOnlyOneMimePart); 0524 } 0525 0526 MimeMessagePart::~MimeMessagePart() = default; 0527 0528 QString MimeMessagePart::text() const 0529 { 0530 return renderInternalText(); 0531 } 0532 0533 QString MimeMessagePart::plaintextContent() const 0534 { 0535 return {}; 0536 } 0537 0538 QString MimeMessagePart::htmlContent() const 0539 { 0540 return {}; 0541 } 0542 0543 //-----AlternativeMessagePart---------------------- 0544 0545 AlternativeMessagePart::AlternativeMessagePart(ObjectTreeParser *otp, KMime::Content *node, Util::HtmlMode preferredMode) 0546 : MessagePart(otp, QString()) 0547 , mPreferredMode(preferredMode) 0548 { 0549 setContent(node); 0550 KMime::Content *dataIcal = findTypeInDirectChilds(node, "text/calendar"); 0551 KMime::Content *dataHtml = findTypeInDirectChilds(node, "text/html"); 0552 KMime::Content *dataText = findTypeInDirectChilds(node, "text/plain"); 0553 0554 if (!dataHtml) { 0555 // If we didn't find the HTML part as the first child of the multipart/alternative, it might 0556 // be that this is a HTML message with images, and text/plain and multipart/related are the 0557 // immediate children of this multipart/alternative node. 0558 // In this case, the HTML node is a child of multipart/related. 0559 dataHtml = findTypeInDirectChilds(node, "multipart/related"); 0560 0561 // Still not found? Stupid apple mail actually puts the attachments inside of the 0562 // multipart/alternative, which is wrong. Therefore we also have to look for multipart/mixed 0563 // here. 0564 // Do this only when preferring HTML mail, though, since otherwise the attachments are hidden 0565 // when displaying plain text. 0566 if (!dataHtml) { 0567 dataHtml = findTypeInDirectChilds(node, "multipart/mixed"); 0568 } 0569 } 0570 0571 if (dataIcal) { 0572 mChildNodes[Util::MultipartIcal] = dataIcal; 0573 } 0574 0575 if (dataText) { 0576 mChildNodes[Util::MultipartPlain] = dataText; 0577 } 0578 0579 if (dataHtml) { 0580 mChildNodes[Util::MultipartHtml] = dataHtml; 0581 } 0582 0583 if (mChildNodes.isEmpty()) { 0584 qCWarning(MIMETREEPARSER_LOG) << "no valid nodes"; 0585 return; 0586 } 0587 0588 QMapIterator<Util::HtmlMode, KMime::Content *> i(mChildNodes); 0589 while (i.hasNext()) { 0590 i.next(); 0591 mChildParts[i.key()] = MimeMessagePart::Ptr(new MimeMessagePart(mOtp, i.value(), true)); 0592 } 0593 } 0594 0595 AlternativeMessagePart::~AlternativeMessagePart() = default; 0596 0597 Util::HtmlMode AlternativeMessagePart::preferredMode() const 0598 { 0599 return mPreferredMode; 0600 } 0601 0602 void AlternativeMessagePart::setPreferredMode(Util::HtmlMode preferredMode) 0603 { 0604 mPreferredMode = preferredMode; 0605 } 0606 0607 QList<Util::HtmlMode> AlternativeMessagePart::availableModes() 0608 { 0609 return mChildParts.keys(); 0610 } 0611 0612 QString AlternativeMessagePart::text() const 0613 { 0614 if (mChildParts.contains(Util::MultipartPlain)) { 0615 return mChildParts[Util::MultipartPlain]->text(); 0616 } 0617 return {}; 0618 } 0619 0620 void AlternativeMessagePart::fix() const 0621 { 0622 if (mChildParts.contains(Util::MultipartPlain)) { 0623 mChildParts[Util::MultipartPlain]->fix(); 0624 } 0625 0626 const auto mode = preferredMode(); 0627 if (mode != Util::MultipartPlain && mChildParts.contains(mode)) { 0628 mChildParts[mode]->fix(); 0629 } 0630 } 0631 0632 const QMap<Util::HtmlMode, MimeMessagePart::Ptr> &AlternativeMessagePart::childParts() const 0633 { 0634 return mChildParts; 0635 } 0636 0637 bool AlternativeMessagePart::isHtml() const 0638 { 0639 return mChildParts.contains(Util::MultipartHtml); 0640 } 0641 0642 QString AlternativeMessagePart::plaintextContent() const 0643 { 0644 return text(); 0645 } 0646 0647 QString AlternativeMessagePart::htmlContent() const 0648 { 0649 if (mChildParts.contains(Util::MultipartHtml)) { 0650 return mChildParts[Util::MultipartHtml]->text(); 0651 } else { 0652 return plaintextContent(); 0653 } 0654 } 0655 0656 //-----CertMessageBlock---------------------- 0657 0658 CertMessagePart::CertMessagePart(ObjectTreeParser *otp, KMime::Content *node, const QGpgME::Protocol *cryptoProto, bool autoImport) 0659 : MessagePart(otp, QString()) 0660 , mAutoImport(autoImport) 0661 , mCryptoProto(cryptoProto) 0662 { 0663 if (!node) { 0664 qCWarning(MIMETREEPARSER_LOG) << "not a valid node"; 0665 return; 0666 } 0667 setContent(node); 0668 0669 if (!mAutoImport) { 0670 return; 0671 } 0672 0673 const QByteArray certData = node->decodedContent(); 0674 0675 QGpgME::ImportJob *import = mCryptoProto->importJob(); 0676 QGpgMEJobExecutor executor; 0677 mImportResult = executor.exec(import, certData); 0678 } 0679 0680 CertMessagePart::~CertMessagePart() = default; 0681 0682 QString CertMessagePart::text() const 0683 { 0684 return {}; 0685 } 0686 0687 const GpgME::ImportResult &CertMessagePart::importResult() const 0688 { 0689 return mImportResult; 0690 } 0691 0692 //-----SignedMessageBlock--------------------- 0693 SignedMessagePart::SignedMessagePart(ObjectTreeParser *otp, 0694 const QString &text, 0695 const QGpgME::Protocol *cryptoProto, 0696 const QString &fromAddress, 0697 KMime::Content *node) 0698 : MessagePart(otp, text) 0699 , mCryptoProto(cryptoProto) 0700 , mFromAddress(fromAddress) 0701 , mMementoName("verification") 0702 { 0703 setContent(node); 0704 partMetaData()->technicalProblem = (mCryptoProto == nullptr); 0705 partMetaData()->isSigned = true; 0706 partMetaData()->isGoodSignature = false; 0707 partMetaData()->keyTrust = GpgME::Signature::Unknown; 0708 partMetaData()->status = i18n("Wrong Crypto Plug-In."); 0709 partMetaData()->status_code = GPGME_SIG_STAT_NONE; 0710 } 0711 0712 SignedMessagePart::~SignedMessagePart() = default; 0713 0714 void SignedMessagePart::setIsSigned(bool isSigned) 0715 { 0716 partMetaData()->isSigned = isSigned; 0717 } 0718 0719 bool SignedMessagePart::isSigned() const 0720 { 0721 return partMetaData()->isSigned; 0722 } 0723 0724 QByteArray SignedMessagePart::mementoName() const 0725 { 0726 return mMementoName; 0727 } 0728 0729 void SignedMessagePart::setMementoName(const QByteArray &name) 0730 { 0731 mMementoName = name; 0732 } 0733 0734 static GpgME::Protocol toGpgMeProtocol(const QGpgME::Protocol *protocol) 0735 { 0736 if (protocol == QGpgME::openpgp()) { 0737 return GpgME::OpenPGP; 0738 } 0739 0740 if (protocol == QGpgME::smime()) { 0741 return GpgME::CMS; 0742 } 0743 0744 return GpgME::UnknownProtocol; 0745 } 0746 0747 bool SignedMessagePart::okVerify(const QByteArray &data, const QByteArray &signature, KMime::Content *textNode) 0748 { 0749 NodeHelper *nodeHelper = mOtp->nodeHelper(); 0750 0751 partMetaData()->isSigned = false; 0752 partMetaData()->technicalProblem = (mCryptoProto == nullptr); 0753 partMetaData()->keyTrust = GpgME::Signature::Unknown; 0754 partMetaData()->status = i18n("Wrong Crypto Plug-In."); 0755 partMetaData()->status_code = GPGME_SIG_STAT_NONE; 0756 0757 const QByteArray _mementoName = mementoName(); 0758 0759 auto m = dynamic_cast<CompositeMemento *>(nodeHelper->bodyPartMemento(content(), _mementoName)); 0760 Q_ASSERT(!m || mCryptoProto); // No CryptoPlugin and having a bodyPartMemento -> there is something completely wrong 0761 0762 if (!m && mCryptoProto) { 0763 CryptoBodyPartMemento *newM = nullptr; 0764 if (!signature.isEmpty()) { 0765 QGpgME::VerifyDetachedJob *job = mCryptoProto->verifyDetachedJob(); 0766 if (job) { 0767 newM = new VerifyDetachedBodyPartMemento(job, mCryptoProto->keyListJob(), signature, data); 0768 } 0769 } else { 0770 QGpgME::VerifyOpaqueJob *job = mCryptoProto->verifyOpaqueJob(); 0771 if (job) { 0772 newM = new VerifyOpaqueBodyPartMemento(job, mCryptoProto->keyListJob(), data); 0773 } 0774 } 0775 0776 if (newM) { 0777 m = new CompositeMemento(); 0778 m->addMemento(newM); 0779 m->addMemento(new KeyCacheMemento(Kleo::KeyCache::mutableInstance(), toGpgMeProtocol(mCryptoProto))); 0780 } 0781 0782 if (m) { 0783 if (mOtp->allowAsync()) { 0784 QObject::connect(m, &CryptoBodyPartMemento::update, nodeHelper, &NodeHelper::update); 0785 if (m->start()) { 0786 partMetaData()->inProgress = true; 0787 mOtp->mHasPendingAsyncJobs = true; 0788 } 0789 } else { 0790 m->exec(); 0791 } 0792 nodeHelper->setBodyPartMemento(content(), _mementoName, m); 0793 } 0794 } else if (m && m->isRunning()) { 0795 partMetaData()->inProgress = true; 0796 mOtp->mHasPendingAsyncJobs = true; 0797 } else { 0798 partMetaData()->inProgress = false; 0799 mOtp->mHasPendingAsyncJobs = false; 0800 } 0801 0802 if (m && !partMetaData()->inProgress) { 0803 if (!signature.isEmpty()) { 0804 mVerifiedText = data; 0805 } 0806 setVerificationResult(m, textNode); 0807 } 0808 0809 if (!m && !partMetaData()->inProgress) { 0810 QString errorMsg; 0811 QString cryptPlugLibName; 0812 QString cryptPlugDisplayName; 0813 if (mCryptoProto) { 0814 cryptPlugLibName = mCryptoProto->name(); 0815 cryptPlugDisplayName = mCryptoProto->displayName(); 0816 } 0817 0818 if (!mCryptoProto) { 0819 if (cryptPlugDisplayName.isEmpty()) { 0820 errorMsg = i18n("No appropriate crypto plug-in was found."); 0821 } else { 0822 errorMsg = i18nc("%1 is either 'OpenPGP' or 'S/MIME'", "No %1 plug-in was found.", cryptPlugDisplayName); 0823 } 0824 } else { 0825 errorMsg = i18n("Crypto plug-in \"%1\" cannot verify signatures.", cryptPlugLibName); 0826 } 0827 partMetaData()->errorText = i18n( 0828 "The message is signed, but the " 0829 "validity of the signature cannot be " 0830 "verified.<br />" 0831 "Reason: %1", 0832 errorMsg); 0833 } 0834 0835 return partMetaData()->isSigned; 0836 } 0837 0838 static int signatureToStatus(const GpgME::Signature &sig) 0839 { 0840 switch (sig.status().code()) { 0841 case GPG_ERR_NO_ERROR: 0842 return GPGME_SIG_STAT_GOOD; 0843 case GPG_ERR_BAD_SIGNATURE: 0844 return GPGME_SIG_STAT_BAD; 0845 case GPG_ERR_NO_PUBKEY: 0846 return GPGME_SIG_STAT_NOKEY; 0847 case GPG_ERR_NO_DATA: 0848 return GPGME_SIG_STAT_NOSIG; 0849 case GPG_ERR_SIG_EXPIRED: 0850 return GPGME_SIG_STAT_GOOD_EXP; 0851 case GPG_ERR_KEY_EXPIRED: 0852 return GPGME_SIG_STAT_GOOD_EXPKEY; 0853 default: 0854 return GPGME_SIG_STAT_ERROR; 0855 } 0856 } 0857 0858 QString prettifyDN(const char *uid) 0859 { 0860 return QGpgME::DN(uid).prettyDN(); 0861 } 0862 0863 void SignedMessagePart::sigStatusToMetaData() 0864 { 0865 GpgME::Key key; 0866 if (partMetaData()->isSigned) { 0867 GpgME::Signature signature = mSignatures.front(); 0868 partMetaData()->status_code = signatureToStatus(signature); 0869 partMetaData()->isGoodSignature = partMetaData()->status_code == GPGME_SIG_STAT_GOOD; 0870 // save extended signature status flags 0871 partMetaData()->sigSummary = signature.summary(); 0872 0873 if (partMetaData()->isGoodSignature && !key.keyID()) { 0874 // Search for the key by its fingerprint so that we can check for 0875 // trust etc. 0876 key = mKeyCache->findByFingerprint(signature.fingerprint()); 0877 if (key.isNull() && signature.fingerprint()) { 0878 // try to find a subkey that was used for signing; 0879 // assumes that the key ID is the last 16 characters of the fingerprint 0880 const auto fpr = std::string_view{signature.fingerprint()}; 0881 const auto keyID = std::string{fpr, fpr.size() - 16, 16}; 0882 const auto subkeys = mKeyCache->findSubkeysByKeyID({keyID}); 0883 if (subkeys.size() > 0) { 0884 key = subkeys[0].parent(); 0885 } 0886 } 0887 if (key.isNull()) { 0888 qCDebug(MIMETREEPARSER_LOG) << "Found no key or subkey for fingerprint" << signature.fingerprint(); 0889 } 0890 } 0891 0892 if (key.keyID()) { 0893 partMetaData()->keyId = key.keyID(); 0894 } 0895 if (partMetaData()->keyId.isEmpty()) { 0896 partMetaData()->keyId = signature.fingerprint(); 0897 } 0898 partMetaData()->keyTrust = signature.validity(); 0899 if (key.numUserIDs() > 0 && key.userID(0).id()) { 0900 partMetaData()->signer = prettifyDN(key.userID(0).id()); 0901 } 0902 for (const auto &uid : key.userIDs()) { 0903 // The following if /should/ always result in TRUE but we 0904 // won't trust implicitly the plugin that gave us these data. 0905 if (uid.email()) { 0906 KMime::Types::Mailbox mbox; 0907 mbox.from7BitString(uid.email()); 0908 if (mbox.hasAddress()) { 0909 partMetaData()->signerMailAddresses.append(mbox.addrSpec().asString()); 0910 } 0911 } 0912 } 0913 0914 if (signature.creationTime()) { 0915 partMetaData()->creationTime.setSecsSinceEpoch(signature.creationTime()); 0916 } else { 0917 partMetaData()->creationTime = QDateTime(); 0918 } 0919 if (partMetaData()->signer.isEmpty()) { 0920 if (key.numUserIDs() > 0 && key.userID(0).name()) { 0921 partMetaData()->signer = prettifyDN(key.userID(0).name()); 0922 } 0923 if (!partMetaData()->signerMailAddresses.empty()) { 0924 if (partMetaData()->signer.isEmpty()) { 0925 partMetaData()->signer = partMetaData()->signerMailAddresses.front(); 0926 } else { 0927 partMetaData()->signer += QLatin1StringView(" <") + partMetaData()->signerMailAddresses.front() + QLatin1Char('>'); 0928 } 0929 } 0930 } 0931 if (Kleo::DeVSCompliance::isCompliant()) { 0932 partMetaData()->isCompliant = signature.isDeVs(); 0933 partMetaData()->compliance = Kleo::DeVSCompliance::name(signature.isDeVs()); 0934 } else { 0935 partMetaData()->isCompliant = true; 0936 } 0937 } 0938 } 0939 0940 void SignedMessagePart::startVerification(const QByteArray &text, QByteArrayView aCodec) 0941 { 0942 startVerificationDetached(text, nullptr, QByteArray()); 0943 0944 if (!content() && partMetaData()->isSigned) { 0945 QStringDecoder codec(aCodec.constData()); 0946 setText(codec.decode(mVerifiedText)); 0947 } 0948 } 0949 0950 void SignedMessagePart::startVerificationDetached(const QByteArray &text, KMime::Content *textNode, const QByteArray &signature) 0951 { 0952 partMetaData()->isEncrypted = false; 0953 partMetaData()->isDecryptable = false; 0954 0955 if (textNode) { 0956 parseInternal(textNode, false); 0957 } 0958 0959 if (!okVerify(text, signature, textNode)) { 0960 partMetaData()->creationTime = QDateTime(); 0961 } 0962 } 0963 0964 void SignedMessagePart::setVerificationResult(const CompositeMemento *m, KMime::Content *textNode) 0965 { 0966 { 0967 const auto kc = m->memento<KeyCacheMemento>(); 0968 if (kc) { 0969 mKeyCache = kc->keyCache(); 0970 } 0971 } 0972 { 0973 const auto vm = m->memento<VerifyDetachedBodyPartMemento>(); 0974 if (vm) { 0975 mSignatures = vm->verifyResult().signatures(); 0976 } 0977 } 0978 { 0979 const auto vm = m->memento<VerifyOpaqueBodyPartMemento>(); 0980 if (vm) { 0981 mVerifiedText = vm->plainText(); 0982 mSignatures = vm->verifyResult().signatures(); 0983 } 0984 } 0985 { 0986 const auto vm = m->memento<DecryptVerifyBodyPartMemento>(); 0987 if (vm) { 0988 mVerifiedText = vm->plainText(); 0989 mSignatures = vm->verifyResult().signatures(); 0990 } 0991 } 0992 partMetaData()->auditLogError = m->auditLogError(); 0993 partMetaData()->auditLog = m->auditLogAsHtml(); 0994 partMetaData()->isSigned = !mSignatures.empty(); 0995 0996 if (partMetaData()->isSigned) { 0997 sigStatusToMetaData(); 0998 if (content()) { 0999 mOtp->nodeHelper()->setSignatureState(content(), KMMsgFullySigned); 1000 if (!textNode) { 1001 mOtp->nodeHelper()->setPartMetaData(content(), *partMetaData()); 1002 1003 if (!mVerifiedText.isEmpty()) { 1004 auto tempNode = new KMime::Content(); 1005 tempNode->setContent(KMime::CRLFtoLF(mVerifiedText.constData())); 1006 tempNode->parse(); 1007 1008 if (!tempNode->head().isEmpty()) { 1009 tempNode->contentDescription()->from7BitString("signed data"); 1010 } 1011 mOtp->nodeHelper()->attachExtraContent(content(), tempNode); 1012 1013 parseInternal(tempNode, false); 1014 } 1015 } 1016 } 1017 } 1018 } 1019 1020 QString SignedMessagePart::plaintextContent() const 1021 { 1022 if (!content()) { 1023 return MessagePart::text(); 1024 } else { 1025 return {}; 1026 } 1027 } 1028 1029 QString SignedMessagePart::htmlContent() const 1030 { 1031 if (!content()) { 1032 return MessagePart::text(); 1033 } else { 1034 return {}; 1035 } 1036 } 1037 1038 const QGpgME::Protocol *SignedMessagePart::cryptoProto() const 1039 { 1040 return mCryptoProto; 1041 } 1042 1043 QString SignedMessagePart::fromAddress() const 1044 { 1045 return mFromAddress; 1046 } 1047 1048 bool SignedMessagePart::hasHeader(const char *headerType) const 1049 { 1050 if (content()) { 1051 return content()->hasHeader(headerType); 1052 } 1053 return false; 1054 } 1055 1056 const KMime::Headers::Base *MimeTreeParser::SignedMessagePart::header(const char *headerType) const 1057 { 1058 if (content()) { 1059 return content()->headerByType(headerType); 1060 } 1061 return nullptr; 1062 } 1063 1064 QList<KMime::Headers::Base *> SignedMessagePart::headers(const char *headerType) const 1065 { 1066 if (content()) { 1067 return content()->headersByType(headerType); 1068 } 1069 return {}; 1070 } 1071 1072 //-----CryptMessageBlock--------------------- 1073 EncryptedMessagePart::EncryptedMessagePart(ObjectTreeParser *otp, 1074 const QString &text, 1075 const QGpgME::Protocol *cryptoProto, 1076 const QString &fromAddress, 1077 KMime::Content *node) 1078 : MessagePart(otp, text) 1079 , mPassphraseError(false) 1080 , mNoSecKey(false) 1081 , mDecryptMessage(false) 1082 , mCryptoProto(cryptoProto) 1083 , mFromAddress(fromAddress) 1084 , mMementoName("decryptverify") 1085 { 1086 setContent(node); 1087 partMetaData()->technicalProblem = (mCryptoProto == nullptr); 1088 partMetaData()->isSigned = false; 1089 partMetaData()->isGoodSignature = false; 1090 partMetaData()->isEncrypted = false; 1091 partMetaData()->isDecryptable = false; 1092 partMetaData()->keyTrust = GpgME::Signature::Unknown; 1093 partMetaData()->status = i18n("Wrong Crypto Plug-In."); 1094 partMetaData()->status_code = GPGME_SIG_STAT_NONE; 1095 } 1096 1097 EncryptedMessagePart::~EncryptedMessagePart() = default; 1098 1099 void EncryptedMessagePart::setDecryptMessage(bool decrypt) 1100 { 1101 mDecryptMessage = decrypt; 1102 } 1103 1104 bool EncryptedMessagePart::decryptMessage() const 1105 { 1106 return mDecryptMessage; 1107 } 1108 1109 void EncryptedMessagePart::setIsEncrypted(bool encrypted) 1110 { 1111 partMetaData()->isEncrypted = encrypted; 1112 } 1113 1114 bool EncryptedMessagePart::isEncrypted() const 1115 { 1116 return partMetaData()->isEncrypted; 1117 } 1118 1119 bool EncryptedMessagePart::isDecryptable() const 1120 { 1121 return partMetaData()->isDecryptable; 1122 } 1123 1124 bool EncryptedMessagePart::isNoSecKey() const 1125 { 1126 return mNoSecKey; 1127 } 1128 1129 bool EncryptedMessagePart::passphraseError() const 1130 { 1131 return mPassphraseError; 1132 } 1133 1134 QByteArray EncryptedMessagePart::mementoName() const 1135 { 1136 return mMementoName; 1137 } 1138 1139 void EncryptedMessagePart::setMementoName(const QByteArray &name) 1140 { 1141 mMementoName = name; 1142 } 1143 1144 void EncryptedMessagePart::startDecryption(const QByteArray &text, QByteArrayView aCodec) 1145 { 1146 auto content = new KMime::Content; 1147 content->setBody(text); 1148 content->parse(); 1149 1150 startDecryption(content); 1151 1152 if (!partMetaData()->inProgress && partMetaData()->isDecryptable) { 1153 QStringDecoder codec(aCodec.constData()); 1154 if (hasSubParts()) { 1155 auto _mp = (subParts()[0]).dynamicCast<SignedMessagePart>(); 1156 if (_mp) { 1157 _mp->setText(codec.decode(mDecryptedData)); 1158 } else { 1159 setText(codec.decode(mDecryptedData)); 1160 } 1161 } else { 1162 setText(codec.decode(mDecryptedData)); 1163 } 1164 } 1165 delete content; 1166 } 1167 1168 bool EncryptedMessagePart::okDecryptMIME(KMime::Content &data) 1169 { 1170 mPassphraseError = false; 1171 partMetaData()->inProgress = false; 1172 partMetaData()->errorText.clear(); 1173 partMetaData()->auditLogError = GpgME::Error(); 1174 partMetaData()->auditLog.clear(); 1175 bool bDecryptionOk = false; 1176 bool cannotDecrypt = false; 1177 NodeHelper *nodeHelper = mOtp->nodeHelper(); 1178 1179 Q_ASSERT(decryptMessage()); 1180 1181 const QByteArray _mementoName = mementoName(); 1182 // Check whether the memento contains a result from last time: 1183 const auto *m = dynamic_cast<CompositeMemento *>(nodeHelper->bodyPartMemento(&data, _mementoName)); 1184 1185 Q_ASSERT(!m || mCryptoProto); // No CryptoPlugin and having a bodyPartMemento -> there is something completely wrong 1186 1187 if (!m && mCryptoProto) { 1188 QGpgME::DecryptVerifyJob *job = mCryptoProto->decryptVerifyJob(); 1189 if (!job) { 1190 cannotDecrypt = true; 1191 } else { 1192 const QByteArray ciphertext = data.decodedContent(); 1193 auto newM = new CompositeMemento(); 1194 newM->addMemento(new KeyCacheMemento(Kleo::KeyCache::mutableInstance(), toGpgMeProtocol(mCryptoProto))); 1195 newM->addMemento(new DecryptVerifyBodyPartMemento(job, ciphertext)); 1196 if (mOtp->allowAsync()) { 1197 QObject::connect(newM, &CryptoBodyPartMemento::update, nodeHelper, &NodeHelper::update); 1198 if (newM->start()) { 1199 partMetaData()->inProgress = true; 1200 mOtp->mHasPendingAsyncJobs = true; 1201 } else { 1202 m = newM; 1203 } 1204 } else { 1205 newM->exec(); 1206 m = newM; 1207 } 1208 1209 nodeHelper->setBodyPartMemento(&data, _mementoName, newM); 1210 } 1211 } else if (m && m->isRunning()) { 1212 partMetaData()->inProgress = true; 1213 mOtp->mHasPendingAsyncJobs = true; 1214 m = nullptr; 1215 } 1216 1217 if (m) { 1218 { 1219 const auto *kcm = m->memento<KeyCacheMemento>(); 1220 if (kcm) { 1221 mKeyCache = kcm->keyCache(); 1222 } 1223 } 1224 auto *decryptMemento = m->memento<DecryptVerifyBodyPartMemento>(); 1225 const QByteArray &plainText = decryptMemento->plainText(); 1226 const GpgME::DecryptionResult &decryptResult = decryptMemento->decryptResult(); 1227 const GpgME::VerificationResult &verifyResult = decryptMemento->verifyResult(); 1228 partMetaData()->isSigned = verifyResult.signatures().size() > 0; 1229 1230 if (partMetaData()->isSigned) { 1231 auto subPart = SignedMessagePart::Ptr(new SignedMessagePart(mOtp, MessagePart::text(), mCryptoProto, mFromAddress, content())); 1232 subPart->setVerificationResult(m, nullptr); 1233 appendSubPart(subPart); 1234 } 1235 1236 mDecryptRecipients.clear(); 1237 bDecryptionOk = !decryptResult.error(); 1238 1239 // std::stringstream ss; 1240 // ss << decryptResult << '\n' << verifyResult; 1241 // qCDebug(MIMETREEPARSER_LOG) << ss.str().c_str(); 1242 1243 for (const auto &recipient : decryptResult.recipients()) { 1244 if (!recipient.status()) { 1245 bDecryptionOk = true; 1246 } 1247 GpgME::Key key; 1248 key = mKeyCache->findByKeyIDOrFingerprint(recipient.keyID()); 1249 if (key.isNull()) { 1250 auto ret = mKeyCache->findSubkeysByKeyID({recipient.keyID()}); 1251 if (ret.size() == 1) { 1252 key = ret.front().parent(); 1253 } 1254 if (key.isNull()) { 1255 qCDebug(MIMETREEPARSER_LOG) << "Found no Key for KeyID " << recipient.keyID(); 1256 } 1257 } 1258 mDecryptRecipients.emplace_back(recipient, key); 1259 } 1260 1261 if (!bDecryptionOk && partMetaData()->isSigned) { 1262 // Only a signed part 1263 partMetaData()->isEncrypted = false; 1264 bDecryptionOk = true; 1265 mDecryptedData = plainText; 1266 } else { 1267 mPassphraseError = decryptResult.error().isCanceled() || decryptResult.error().code() == GPG_ERR_NO_SECKEY; 1268 partMetaData()->isEncrypted = bDecryptionOk || decryptResult.error().code() != GPG_ERR_NO_DATA; 1269 1270 if (decryptResult.error().isCanceled()) { 1271 setDecryptMessage(false); 1272 } 1273 1274 partMetaData()->errorText = QString::fromLocal8Bit(decryptResult.error().asString()); 1275 if (Kleo::DeVSCompliance::isCompliant()) { 1276 partMetaData()->isCompliant = decryptResult.isDeVs(); 1277 partMetaData()->compliance = Kleo::DeVSCompliance::name(decryptResult.isDeVs()); 1278 } else { 1279 partMetaData()->isCompliant = true; 1280 } 1281 if (partMetaData()->isEncrypted && decryptResult.numRecipients() > 0) { 1282 partMetaData()->keyId = decryptResult.recipient(0).keyID(); 1283 } 1284 1285 if (bDecryptionOk) { 1286 mDecryptedData = plainText; 1287 } else { 1288 mNoSecKey = true; 1289 const auto decryRecipients = decryptResult.recipients(); 1290 for (const GpgME::DecryptionResult::Recipient &recipient : decryRecipients) { 1291 mNoSecKey &= (recipient.status().code() == GPG_ERR_NO_SECKEY); 1292 } 1293 if (!mPassphraseError && !mNoSecKey) { // GpgME do not detect passphrase error correctly 1294 mPassphraseError = true; 1295 } 1296 } 1297 } 1298 } 1299 1300 if (!bDecryptionOk) { 1301 QString cryptPlugLibName; 1302 if (mCryptoProto) { 1303 cryptPlugLibName = mCryptoProto->name(); 1304 } 1305 1306 if (!mCryptoProto) { 1307 partMetaData()->errorText = i18n("No appropriate crypto plug-in was found."); 1308 } else if (cannotDecrypt) { 1309 partMetaData()->errorText = i18n("Crypto plug-in \"%1\" cannot decrypt messages.", cryptPlugLibName); 1310 } else if (!passphraseError()) { 1311 partMetaData()->errorText = i18n("Crypto plug-in \"%1\" could not decrypt the data.", cryptPlugLibName) + QLatin1StringView("<br />") 1312 + i18n("Error: %1", partMetaData()->errorText); 1313 } 1314 } 1315 return bDecryptionOk; 1316 } 1317 1318 void EncryptedMessagePart::startDecryption(KMime::Content *data) 1319 { 1320 if (!content() && !data) { 1321 return; 1322 } 1323 1324 if (!data) { 1325 data = content(); 1326 } 1327 1328 partMetaData()->isEncrypted = true; 1329 1330 bool bOkDecrypt = okDecryptMIME(*data); 1331 1332 if (partMetaData()->inProgress) { 1333 return; 1334 } 1335 partMetaData()->isDecryptable = bOkDecrypt; 1336 1337 if (!partMetaData()->isDecryptable) { 1338 setText(QString::fromUtf8(mDecryptedData.constData())); 1339 } 1340 1341 if (partMetaData()->isEncrypted && !decryptMessage()) { 1342 partMetaData()->isDecryptable = true; 1343 } 1344 1345 if (content() && !partMetaData()->isSigned) { 1346 mOtp->nodeHelper()->setPartMetaData(content(), *partMetaData()); 1347 1348 if (decryptMessage()) { 1349 auto tempNode = new KMime::Content(); 1350 tempNode->setContent(KMime::CRLFtoLF(mDecryptedData.constData())); 1351 tempNode->parse(); 1352 1353 if (!tempNode->head().isEmpty()) { 1354 tempNode->contentDescription()->from7BitString("encrypted data"); 1355 } 1356 mOtp->nodeHelper()->attachExtraContent(content(), tempNode); 1357 1358 parseInternal(tempNode, false); 1359 } 1360 } 1361 } 1362 1363 QString EncryptedMessagePart::plaintextContent() const 1364 { 1365 if (!content()) { 1366 return MessagePart::text(); 1367 } else { 1368 return {}; 1369 } 1370 } 1371 1372 QString EncryptedMessagePart::htmlContent() const 1373 { 1374 if (!content()) { 1375 return MessagePart::text(); 1376 } else { 1377 return {}; 1378 } 1379 } 1380 1381 QString EncryptedMessagePart::text() const 1382 { 1383 if (hasSubParts()) { 1384 auto _mp = (subParts()[0]).dynamicCast<SignedMessagePart>(); 1385 if (_mp) { 1386 return _mp->text(); 1387 } else { 1388 return MessagePart::text(); 1389 } 1390 } else { 1391 return MessagePart::text(); 1392 } 1393 } 1394 1395 const QGpgME::Protocol *EncryptedMessagePart::cryptoProto() const 1396 { 1397 return mCryptoProto; 1398 } 1399 1400 QString EncryptedMessagePart::fromAddress() const 1401 { 1402 return mFromAddress; 1403 } 1404 1405 const std::vector<std::pair<GpgME::DecryptionResult::Recipient, GpgME::Key>> &EncryptedMessagePart::decryptRecipients() const 1406 { 1407 return mDecryptRecipients; 1408 } 1409 1410 bool EncryptedMessagePart::hasHeader(const char *headerType) const 1411 { 1412 const auto extraContent = mOtp->nodeHelper()->decryptedNodeForContent(content()); 1413 if (extraContent) { 1414 return nodeHelper()->hasMailHeader(headerType, extraContent); 1415 } 1416 return false; 1417 } 1418 1419 const KMime::Headers::Base *EncryptedMessagePart::header(const char *headerType) const 1420 { 1421 const auto extraContent = mOtp->nodeHelper()->decryptedNodeForContent(content()); 1422 if (extraContent) { 1423 return nodeHelper()->mailHeaderAsBase(headerType, extraContent); 1424 } 1425 return nullptr; 1426 } 1427 1428 QList<KMime::Headers::Base *> EncryptedMessagePart::headers(const char *headerType) const 1429 { 1430 const auto extraContent = mOtp->nodeHelper()->decryptedNodeForContent(content()); 1431 if (extraContent) { 1432 return nodeHelper()->headers(headerType, extraContent); 1433 } 1434 return {}; 1435 } 1436 1437 EncapsulatedRfc822MessagePart::EncapsulatedRfc822MessagePart(ObjectTreeParser *otp, KMime::Content *node, const KMime::Message::Ptr &message) 1438 : MessagePart(otp, QString()) 1439 , mMessage(message) 1440 { 1441 setContent(node); 1442 partMetaData()->isEncrypted = false; 1443 partMetaData()->isSigned = false; 1444 partMetaData()->isEncapsulatedRfc822Message = true; 1445 1446 mOtp->nodeHelper()->setNodeDisplayedEmbedded(node, true); 1447 mOtp->nodeHelper()->setPartMetaData(node, *partMetaData()); 1448 1449 if (!mMessage) { 1450 qCWarning(MIMETREEPARSER_LOG) << "Node is of type message/rfc822 but doesn't have a message!"; 1451 return; 1452 } 1453 1454 // The link to "Encapsulated message" is clickable, therefore the temp file needs to exists, 1455 // since the user can click the link and expect to have normal attachment operations there. 1456 mOtp->nodeHelper()->writeNodeToTempFile(message.data()); 1457 1458 parseInternal(message.data(), false); 1459 } 1460 1461 EncapsulatedRfc822MessagePart::~EncapsulatedRfc822MessagePart() = default; 1462 1463 QString EncapsulatedRfc822MessagePart::text() const 1464 { 1465 return renderInternalText(); 1466 } 1467 1468 void EncapsulatedRfc822MessagePart::fix() const 1469 { 1470 } 1471 1472 const KMime::Message::Ptr EncapsulatedRfc822MessagePart::message() const 1473 { 1474 return mMessage; 1475 } 1476 1477 #include "moc_messagepart.cpp"