7#include "messagepart.h"
8#include "cryptohelper.h"
9#include "job/qgpgmejobexecutor.h"
10#include "memento/compositememento.h"
11#include "memento/cryptobodypartmemento.h"
12#include "memento/decryptverifybodypartmemento.h"
13#include "memento/keycachememento.h"
14#include "memento/verifydetachedbodypartmemento.h"
15#include "memento/verifyopaquebodypartmemento.h"
16#include "mimetreeparser_debug.h"
17#include "objecttreeparser.h"
19#include "bodyformatter/utils.h"
21#include <KMime/Content>
23#include <Libkleo/Compliance>
24#include <Libkleo/KeyCache>
27#include <QGpgME/ImportJob>
28#include <QGpgME/Protocol>
29#include <QGpgME/VerifyDetachedJob>
30#include <QGpgME/VerifyOpaqueJob>
32#include <gpgme++/key.h>
33#include <gpgme++/keylistresult.h>
36#include <KLocalizedString>
40using namespace MimeTreeParser;
43namespace MimeTreeParser
45class MessagePartPrivate
53 PartMetaData mMetaData;
55 bool mIsImage =
false;
56 bool mNeverDisplayInline =
false;
62 , d(new MessagePartPrivate)
67MessagePart::~MessagePart() =
default;
71 return d->mParentPart;
74void MessagePart::setParentPart(
MessagePart *parentPart)
76 d->mParentPart = parentPart;
79QString MessagePart::htmlContent()
const
84QString MessagePart::plaintextContent()
const
89PartMetaData *MessagePart::partMetaData()
const
96 return nodeHelper()->bodyPartMemento(
content(),
"__plugin__");
101 nodeHelper()->setBodyPartMemento(
content(),
"__plugin__", memento);
116 return d->mAttachmentNode;
121 d->mAttachmentNode = node;
124bool MessagePart::isAttachment()
const
126 return d->mAttachmentNode;
136 return mOtp->nodeHelper()->asHREF(
content(), QStringLiteral(
"body"));
142 static int serial = 0;
146 return QStringLiteral(
"x-kmail:/bodypart/%1/%2/%3")
151void MessagePart::setIsRoot(
bool root)
156bool MessagePart::isRoot()
const
161QString MessagePart::text()
const
166void MessagePart::setText(
const QString &text)
171bool MessagePart::isHtml()
const
179 return mOtp->mSource;
185 return mOtp->nodeHelper();
188void MessagePart::parseInternal(
KMime::Content *node,
bool onlyOneMimePart)
193 for (
const auto &part : subParts) {
198QString MessagePart::renderInternalText()
const
208void MessagePart::fix()
const
230bool MessagePart::hasSubParts()
const
232 return !d->mBlocks.isEmpty();
235void MessagePart::clearSubParts()
240bool MessagePart::neverDisplayInline()
const
242 return d->mNeverDisplayInline;
245void MessagePart::setNeverDisplayInline(
bool displayInline)
250bool MessagePart::isImage()
const
255void MessagePart::setIsImage(
bool image)
260bool MessagePart::hasHeader(
const char *headerType)
const
284MessagePartList::~MessagePartList() =
default;
286QString MessagePartList::text()
const
288 return renderInternalText();
291QString MessagePartList::plaintextContent()
const
296QString MessagePartList::htmlContent()
const
305 , mDecryptMessage(decryptMessage)
317TextMessagePart::~TextMessagePart() =
default;
319bool TextMessagePart::decryptMessage()
const
321 return mDecryptMessage;
324void TextMessagePart::parseContent()
326 const auto codecName = mOtp->codecNameFor(
content());
328 const QString &fromAddress = mOtp->nodeHelper()->fromAsString(
content());
329 mSignatureState = KMMsgNotSigned;
330 mEncryptionState = KMMsgNotEncrypted;
331 const auto blocks = prepareMessageForDecryption(
content()->decodedContent());
347 for (
const auto &block :
blocks) {
353 if (block.type() == NoPgpBlock && !block.text().trimmed().isEmpty()) {
357 }
else if (block.type() == PgpMessageBlock) {
359 mp->setDecryptMessage(decryptMessage());
360 mp->setIsEncrypted(
true);
363 if (!decryptMessage()) {
366 mp->startDecryption(block.text(), codecName);
367 if (
mp->partMetaData()->inProgress) {
370 }
else if (block.type() == ClearsignedBlock) {
374 mp->startVerification(block.text(), codecName);
384 mp->setText(
aCodec.decode(block.text()));
388 mEncryptionState = KMMsgPartiallyEncrypted;
392 mSignatureState = KMMsgPartiallySigned;
398 if (mSignatureState == KMMsgPartiallySigned) {
399 mSignatureState = KMMsgFullySigned;
401 if (mEncryptionState == KMMsgPartiallyEncrypted) {
402 mEncryptionState = KMMsgFullyEncrypted;
408KMMsgEncryptionState TextMessagePart::encryptionState()
const
410 return mEncryptionState;
413KMMsgSignatureState TextMessagePart::signatureState()
const
415 return mSignatureState;
418bool TextMessagePart::showLink()
const
423bool TextMessagePart::isFirstTextPart()
const
428bool TextMessagePart::hasLabel()
const
438 label =
i18nc(
"display name for an unnamed attachment",
"Unnamed");
446 if (comment == label()) {
464AttachmentMessagePart::~AttachmentMessagePart() =
default;
483HtmlMessagePart::~HtmlMessagePart() =
default;
485void HtmlMessagePart::fix()
const
487 mOtp->mHtmlContent += mBodyHTML;
488 mOtp->mHtmlContentCharset = mCharset;
491QString HtmlMessagePart::text()
const
496QString MimeTreeParser::HtmlMessagePart::plaintextContent()
const
501bool HtmlMessagePart::isHtml()
const
506QString HtmlMessagePart::bodyHtml()
const
515 , mOnlyOneMimePart(onlyOneMimePart)
523 parseInternal(node, mOnlyOneMimePart);
526MimeMessagePart::~MimeMessagePart() =
default;
528QString MimeMessagePart::text()
const
530 return renderInternalText();
533QString MimeMessagePart::plaintextContent()
const
538QString MimeMessagePart::htmlContent()
const
547 , mPreferredMode(preferredMode)
559 dataHtml = findTypeInDirectChilds(node,
"multipart/related");
567 dataHtml = findTypeInDirectChilds(node,
"multipart/mixed");
583 if (mChildNodes.isEmpty()) {
589 while (
i.hasNext()) {
595AlternativeMessagePart::~AlternativeMessagePart() =
default;
599 return mPreferredMode;
602void AlternativeMessagePart::setPreferredMode(
Util::HtmlMode preferredMode)
604 mPreferredMode = preferredMode;
609 return mChildParts.keys();
612QString AlternativeMessagePart::text()
const
620void AlternativeMessagePart::fix()
const
626 const auto mode = preferredMode();
628 mChildParts[mode]->fix();
637bool AlternativeMessagePart::isHtml()
const
642QString AlternativeMessagePart::plaintextContent()
const
647QString AlternativeMessagePart::htmlContent()
const
652 return plaintextContent();
660 , mAutoImport(autoImport)
661 , mCryptoProto(cryptoProto)
675 QGpgME::ImportJob *
import = mCryptoProto->importJob();
677 mImportResult = executor.exec(
import,
certData);
680CertMessagePart::~CertMessagePart() =
default;
682QString CertMessagePart::text()
const
687const GpgME::ImportResult &CertMessagePart::importResult()
const
689 return mImportResult;
695 const QGpgME::Protocol *cryptoProto,
699 , mCryptoProto(cryptoProto)
700 , mFromAddress(fromAddress)
701 , mMementoName(
"verification")
704 partMetaData()->technicalProblem = (mCryptoProto ==
nullptr);
705 partMetaData()->isSigned =
true;
706 partMetaData()->isGoodSignature =
false;
707 partMetaData()->keyTrust = GpgME::Signature::Unknown;
708 partMetaData()->status =
i18n(
"Wrong Crypto Plug-In.");
712SignedMessagePart::~SignedMessagePart() =
default;
714void SignedMessagePart::setIsSigned(
bool isSigned)
716 partMetaData()->isSigned = isSigned;
719bool SignedMessagePart::isSigned()
const
721 return partMetaData()->isSigned;
724QByteArray SignedMessagePart::mementoName()
const
729void SignedMessagePart::setMementoName(
const QByteArray &name)
734static GpgME::Protocol toGpgMeProtocol(
const QGpgME::Protocol *protocol)
736 if (protocol == QGpgME::openpgp()) {
737 return GpgME::OpenPGP;
740 if (protocol == QGpgME::smime()) {
744 return GpgME::UnknownProtocol;
751 partMetaData()->isSigned =
false;
752 partMetaData()->technicalProblem = (mCryptoProto ==
nullptr);
753 partMetaData()->keyTrust = GpgME::Signature::Unknown;
754 partMetaData()->status =
i18n(
"Wrong Crypto Plug-In.");
759 auto m =
dynamic_cast<CompositeMemento *
>(nodeHelper->bodyPartMemento(
content(),
_mementoName));
762 if (!
m && mCryptoProto) {
763 CryptoBodyPartMemento *
newM =
nullptr;
765 QGpgME::VerifyDetachedJob *job = mCryptoProto->verifyDetachedJob();
767 newM =
new VerifyDetachedBodyPartMemento(job, mCryptoProto->keyListJob(), signature, data);
770 QGpgME::VerifyOpaqueJob *job = mCryptoProto->verifyOpaqueJob();
772 newM =
new VerifyOpaqueBodyPartMemento(job, mCryptoProto->keyListJob(), data);
777 m =
new CompositeMemento();
779 m->addMemento(
new KeyCacheMemento(Kleo::KeyCache::mutableInstance(), toGpgMeProtocol(mCryptoProto)));
783 if (mOtp->allowAsync()) {
784 QObject::connect(
m, &CryptoBodyPartMemento::update, nodeHelper, &NodeHelper::update);
786 partMetaData()->inProgress =
true;
787 mOtp->mHasPendingAsyncJobs =
true;
794 }
else if (
m &&
m->isRunning()) {
795 partMetaData()->inProgress =
true;
796 mOtp->mHasPendingAsyncJobs =
true;
798 partMetaData()->inProgress =
false;
799 mOtp->mHasPendingAsyncJobs =
false;
802 if (
m && !partMetaData()->inProgress) {
804 mVerifiedText = data;
809 if (!
m && !partMetaData()->inProgress) {
820 errorMsg =
i18n(
"No appropriate crypto plug-in was found.");
827 partMetaData()->errorText =
i18n(
828 "The message is signed, but the "
829 "validity of the signature cannot be "
835 return partMetaData()->isSigned;
838static int signatureToStatus(
const GpgME::Signature &sig)
840 switch (sig.status().code()) {
841 case GPG_ERR_NO_ERROR:
842 return GPGME_SIG_STAT_GOOD;
843 case GPG_ERR_BAD_SIGNATURE:
844 return GPGME_SIG_STAT_BAD;
845 case GPG_ERR_NO_PUBKEY:
846 return GPGME_SIG_STAT_NOKEY;
847 case GPG_ERR_NO_DATA:
848 return GPGME_SIG_STAT_NOSIG;
849 case GPG_ERR_SIG_EXPIRED:
850 return GPGME_SIG_STAT_GOOD_EXP;
851 case GPG_ERR_KEY_EXPIRED:
852 return GPGME_SIG_STAT_GOOD_EXPKEY;
854 return GPGME_SIG_STAT_ERROR;
858QString prettifyDN(
const char *uid)
860 return QGpgME::DN(uid).prettyDN();
863void SignedMessagePart::sigStatusToMetaData()
866 if (partMetaData()->isSigned) {
867 GpgME::Signature signature = mSignatures.front();
868 partMetaData()->status_code = signatureToStatus(signature);
871 partMetaData()->sigSummary = signature.summary();
873 if (partMetaData()->isGoodSignature && !key.keyID()) {
876 key = mKeyCache->findByFingerprint(signature.fingerprint());
877 if (key.isNull() && signature.fingerprint()) {
880 const auto fpr = std::string_view{signature.fingerprint()};
881 const auto keyID = std::string{
fpr,
fpr.size() - 16, 16};
882 const auto subkeys = mKeyCache->findSubkeysByKeyID({
keyID});
888 qCDebug(
MIMETREEPARSER_LOG) <<
"Found no key or subkey for fingerprint" << signature.fingerprint();
893 partMetaData()->keyId = key.keyID();
895 if (partMetaData()->keyId.isEmpty()) {
896 partMetaData()->keyId = signature.fingerprint();
898 partMetaData()->keyTrust = signature.validity();
899 if (key.numUserIDs() > 0 && key.userID(0).id()) {
900 partMetaData()->signer = prettifyDN(key.userID(0).id());
902 for (
const auto &uid : key.
userIDs()) {
907 mbox.from7BitString(uid.email());
908 if (
mbox.hasAddress()) {
909 partMetaData()->signerMailAddresses.
append(
mbox.addrSpec().asString());
914 if (signature.creationTime()) {
917 partMetaData()->creationTime =
QDateTime();
919 if (partMetaData()->signer.isEmpty()) {
920 if (key.numUserIDs() > 0 && key.userID(0).name()) {
921 partMetaData()->signer = prettifyDN(key.userID(0).name());
923 if (!partMetaData()->signerMailAddresses.empty()) {
924 if (partMetaData()->signer.isEmpty()) {
925 partMetaData()->signer = partMetaData()->signerMailAddresses.
front();
931 if (Kleo::DeVSCompliance::isCompliant()) {
932 partMetaData()->isCompliant = signature.isDeVs();
933 partMetaData()->compliance = Kleo::DeVSCompliance::name(signature.isDeVs());
935 partMetaData()->isCompliant =
true;
942 startVerificationDetached(text,
nullptr,
QByteArray());
944 if (!
content() && partMetaData()->isSigned) {
946 setText(codec.decode(mVerifiedText));
952 partMetaData()->isEncrypted =
false;
953 partMetaData()->isDecryptable =
false;
959 if (!okVerify(text, signature,
textNode)) {
960 partMetaData()->creationTime =
QDateTime();
964void SignedMessagePart::setVerificationResult(
const CompositeMemento *m,
KMime::Content *textNode)
967 const auto kc =
m->memento<KeyCacheMemento>();
969 mKeyCache =
kc->keyCache();
973 const auto vm =
m->memento<VerifyDetachedBodyPartMemento>();
975 mSignatures =
vm->verifyResult().signatures();
979 const auto vm =
m->memento<VerifyOpaqueBodyPartMemento>();
981 mVerifiedText =
vm->plainText();
982 mSignatures =
vm->verifyResult().signatures();
986 const auto vm =
m->memento<DecryptVerifyBodyPartMemento>();
988 mVerifiedText =
vm->plainText();
989 mSignatures =
vm->verifyResult().signatures();
992 partMetaData()->auditLogError =
m->auditLogError();
993 partMetaData()->auditLog =
m->auditLogAsHtml();
994 partMetaData()->isSigned = !mSignatures.empty();
996 if (partMetaData()->isSigned) {
997 sigStatusToMetaData();
999 mOtp->nodeHelper()->setSignatureState(
content(), KMMsgFullySigned);
1001 mOtp->nodeHelper()->setPartMetaData(
content(), *partMetaData());
1003 if (!mVerifiedText.
isEmpty()) {
1009 tempNode->contentDescription()->from7BitString(
"signed data");
1020QString SignedMessagePart::plaintextContent()
const
1023 return MessagePart::text();
1029QString SignedMessagePart::htmlContent()
const
1032 return MessagePart::text();
1038const QGpgME::Protocol *SignedMessagePart::cryptoProto()
const
1040 return mCryptoProto;
1043QString SignedMessagePart::fromAddress()
const
1045 return mFromAddress;
1048bool SignedMessagePart::hasHeader(
const char *headerType)
const
1056const KMime::Headers::Base *MimeTreeParser::SignedMessagePart::header(
const char *headerType)
const
1059 return content()->headerByType(headerType);
1075 const QGpgME::Protocol *cryptoProto,
1079 , mPassphraseError(false)
1081 , mDecryptMessage(false)
1082 , mCryptoProto(cryptoProto)
1083 , mFromAddress(fromAddress)
1084 , mMementoName(
"decryptverify")
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.");
1097EncryptedMessagePart::~EncryptedMessagePart() =
default;
1099void EncryptedMessagePart::setDecryptMessage(
bool decrypt)
1101 mDecryptMessage = decrypt;
1104bool EncryptedMessagePart::decryptMessage()
const
1106 return mDecryptMessage;
1109void EncryptedMessagePart::setIsEncrypted(
bool encrypted)
1111 partMetaData()->isEncrypted =
encrypted;
1114bool EncryptedMessagePart::isEncrypted()
const
1116 return partMetaData()->isEncrypted;
1119bool EncryptedMessagePart::isDecryptable()
const
1121 return partMetaData()->isDecryptable;
1124bool EncryptedMessagePart::isNoSecKey()
const
1129bool EncryptedMessagePart::passphraseError()
const
1131 return mPassphraseError;
1134QByteArray EncryptedMessagePart::mementoName()
const
1136 return mMementoName;
1139void EncryptedMessagePart::setMementoName(
const QByteArray &name)
1141 mMementoName =
name;
1152 if (!partMetaData()->inProgress && partMetaData()->isDecryptable) {
1154 if (hasSubParts()) {
1157 _mp->setText(codec.decode(mDecryptedData));
1159 setText(codec.decode(mDecryptedData));
1162 setText(codec.decode(mDecryptedData));
1170 mPassphraseError =
false;
1171 partMetaData()->inProgress =
false;
1172 partMetaData()->errorText.
clear();
1173 partMetaData()->auditLogError = GpgME::Error();
1174 partMetaData()->auditLog.
clear();
1183 const auto *
m =
dynamic_cast<CompositeMemento *
>(nodeHelper->bodyPartMemento(&data,
_mementoName));
1187 if (!
m && mCryptoProto) {
1188 QGpgME::DecryptVerifyJob *job = mCryptoProto->decryptVerifyJob();
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()) {
1198 if (
newM->start()) {
1199 partMetaData()->inProgress =
true;
1200 mOtp->mHasPendingAsyncJobs =
true;
1211 }
else if (
m &&
m->isRunning()) {
1212 partMetaData()->inProgress =
true;
1213 mOtp->mHasPendingAsyncJobs =
true;
1219 const auto *
kcm =
m->memento<KeyCacheMemento>();
1221 mKeyCache =
kcm->keyCache();
1226 const GpgME::DecryptionResult &decryptResult =
decryptMemento->decryptResult();
1227 const GpgME::VerificationResult &verifyResult =
decryptMemento->verifyResult();
1228 partMetaData()->isSigned = verifyResult.signatures().size() > 0;
1230 if (partMetaData()->isSigned) {
1232 subPart->setVerificationResult(
m,
nullptr);
1236 mDecryptRecipients.clear();
1243 for (
const auto &recipient : decryptResult.recipients()) {
1244 if (!recipient.status()) {
1248 key = mKeyCache->findByKeyIDOrFingerprint(recipient.keyID());
1250 auto ret = mKeyCache->findSubkeysByKeyID({recipient.keyID()});
1251 if (
ret.size() == 1) {
1252 key =
ret.front().parent();
1258 mDecryptRecipients.emplace_back(recipient, key);
1263 partMetaData()->isEncrypted =
false;
1265 mDecryptedData = plainText;
1267 mPassphraseError = decryptResult.error().isCanceled() || decryptResult.error().code() ==
GPG_ERR_NO_SECKEY;
1270 if (decryptResult.error().isCanceled()) {
1271 setDecryptMessage(
false);
1275 if (Kleo::DeVSCompliance::isCompliant()) {
1276 partMetaData()->isCompliant = decryptResult.isDeVs();
1277 partMetaData()->compliance = Kleo::DeVSCompliance::name(decryptResult.isDeVs());
1279 partMetaData()->isCompliant =
true;
1281 if (partMetaData()->isEncrypted && decryptResult.numRecipients() > 0) {
1282 partMetaData()->keyId = decryptResult.recipient(0).keyID();
1286 mDecryptedData = plainText;
1290 for (
const GpgME::DecryptionResult::Recipient &recipient :
decryRecipients) {
1293 if (!mPassphraseError && !mNoSecKey) {
1294 mPassphraseError =
true;
1306 if (!mCryptoProto) {
1307 partMetaData()->errorText =
i18n(
"No appropriate crypto plug-in was found.");
1309 partMetaData()->errorText =
i18n(
"Crypto plug-in \"%1\" cannot decrypt messages.",
cryptPlugLibName);
1310 }
else if (!passphraseError()) {
1312 +
i18n(
"Error: %1", partMetaData()->errorText);
1328 partMetaData()->isEncrypted =
true;
1332 if (partMetaData()->inProgress) {
1337 if (!partMetaData()->isDecryptable) {
1341 if (partMetaData()->isEncrypted && !decryptMessage()) {
1342 partMetaData()->isDecryptable =
true;
1345 if (
content() && !partMetaData()->isSigned) {
1346 mOtp->nodeHelper()->setPartMetaData(
content(), *partMetaData());
1348 if (decryptMessage()) {
1354 tempNode->contentDescription()->from7BitString(
"encrypted data");
1363QString EncryptedMessagePart::plaintextContent()
const
1366 return MessagePart::text();
1372QString EncryptedMessagePart::htmlContent()
const
1375 return MessagePart::text();
1381QString EncryptedMessagePart::text()
const
1383 if (hasSubParts()) {
1388 return MessagePart::text();
1391 return MessagePart::text();
1395const QGpgME::Protocol *EncryptedMessagePart::cryptoProto()
const
1397 return mCryptoProto;
1400QString EncryptedMessagePart::fromAddress()
const
1402 return mFromAddress;
1405const std::vector<std::pair<GpgME::DecryptionResult::Recipient, GpgME::Key>> &EncryptedMessagePart::decryptRecipients()
const
1407 return mDecryptRecipients;
1410bool EncryptedMessagePart::hasHeader(
const char *headerType)
const
1412 const auto extraContent = mOtp->nodeHelper()->decryptedNodeForContent(
content());
1414 return nodeHelper()->hasMailHeader(headerType, extraContent);
1421 const auto extraContent = mOtp->nodeHelper()->decryptedNodeForContent(
content());
1423 return nodeHelper()->mailHeaderAsBase(headerType, extraContent);
1430 const auto extraContent = mOtp->nodeHelper()->decryptedNodeForContent(
content());
1432 return nodeHelper()->headers(headerType, extraContent);
1442 partMetaData()->isEncrypted =
false;
1443 partMetaData()->isSigned =
false;
1444 partMetaData()->isEncapsulatedRfc822Message =
true;
1446 mOtp->nodeHelper()->setNodeDisplayedEmbedded(node,
true);
1447 mOtp->nodeHelper()->setPartMetaData(node, *partMetaData());
1450 qCWarning(
MIMETREEPARSER_LOG) <<
"Node is of type message/rfc822 but doesn't have a message!";
1458 parseInternal(message.
data(),
false);
1461EncapsulatedRfc822MessagePart::~EncapsulatedRfc822MessagePart() =
default;
1463QString EncapsulatedRfc822MessagePart::text()
const
1465 return renderInternalText();
1468void EncapsulatedRfc822MessagePart::fix()
const
1477#include "moc_messagepart.cpp"
Headers::ContentDescription * contentDescription(bool create=true)
Headers::ContentType * contentType(bool create=true)
Content * topLevel() const
ContentIndex index() const
QByteArray decodedContent()
QList< Headers::Base * > headersByType(const char *type) const
bool hasHeader(const char *type) const
void setBody(const QByteArray &body)
The EncryptedMessagePart class.
interface of classes that implement status for BodyPartFormatters.
Interface for object tree sources.
The MessagePartList class.
KMime::Content * content() const
The KMime::Content* node that's represented by this part.
QString makeLink(const QString &path) const
Returns a string representation of an URL that can be used to invoke a BodyPartURLHandler for this bo...
KMime::Content * attachmentContent() const
The KMime::Content* node that's the source of this part.
QString attachmentLink() const
The MimeMessagePart class.
static QString fileName(const KMime::Content *node)
Returns a usable filename for a node, that can be the filename from the content disposition header,...
void attachExtraContent(KMime::Content *topLevelNode, KMime::Content *content)
Attach an extra node to an existing node.
static QByteArray charset(KMime::Content *node)
Returns the charset for the given node.
QString writeNodeToTempFile(KMime::Content *node)
Writes the given message part to a temporary file and returns the name of this file or QString() if w...
Parses messages and generates HTML display code out of them.
Helper class for synchronous execution of Kleo crypto jobs.
The SignedMessagePart class.
The TextMessagePart class.
QString temporaryFilePath() const
Temporary file containing the part content.
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
QString name(StandardShortcut id)
HtmlMode
Describes the type of the displayed message.
@ MultipartPlain
A multipart/alternative message, the plain text part is currently displayed.
@ MultipartIcal
A multipart/alternative message, the ICal part is currently displayed.
@ MultipartHtml
A multipart/alternative message, the HTML part is currently displayed.
const char * constData() const const
bool isEmpty() const const
void setSecsSinceEpoch(qint64 secs)
void append(QList< T > &&value)
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
T qobject_cast(QObject *object)
QString arg(Args &&... args) const const
QString fromLatin1(QByteArrayView str)
QString fromLocal8Bit(QByteArrayView str)
QString fromUtf8(QByteArrayView str)
bool isEmpty() const const
QString number(double n, char format, int precision)
EncodedData< QByteArrayView > decode(QByteArrayView ba)
QByteArray toPercentEncoding(const QString &input, const QByteArray &exclude, const QByteArray &include)