File indexing completed on 2024-06-23 05:18:33
0001 /* 0002 SPDX-FileCopyrightText: 2020 Sandro Knauß <sknauss@kde.org> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "job/protectedheadersjob.h" 0008 0009 #include "contentjobbase_p.h" 0010 #include "job/singlepartjob.h" 0011 0012 #include <KMime/Content> 0013 #include <KMime/KMimeMessage> 0014 0015 using namespace MessageComposer; 0016 0017 class MessageComposer::ProtectedHeadersJobPrivate : public ContentJobBasePrivate 0018 { 0019 public: 0020 ProtectedHeadersJobPrivate(ProtectedHeadersJob *qq) 0021 : ContentJobBasePrivate(qq) 0022 { 0023 } 0024 0025 KMime::Content *content = nullptr; 0026 KMime::Message *skeletonMessage = nullptr; 0027 0028 bool obvoscate = false; 0029 0030 Q_DECLARE_PUBLIC(ProtectedHeadersJob) 0031 }; 0032 0033 ProtectedHeadersJob::ProtectedHeadersJob(QObject *parent) 0034 : ContentJobBase(*new ProtectedHeadersJobPrivate(this), parent) 0035 { 0036 } 0037 0038 ProtectedHeadersJob::~ProtectedHeadersJob() = default; 0039 0040 void ProtectedHeadersJob::setContent(KMime::Content *content) 0041 { 0042 Q_D(ProtectedHeadersJob); 0043 0044 d->content = content; 0045 if (content) { 0046 d->content->assemble(); 0047 } 0048 } 0049 0050 void ProtectedHeadersJob::setSkeletonMessage(KMime::Message *skeletonMessage) 0051 { 0052 Q_D(ProtectedHeadersJob); 0053 0054 d->skeletonMessage = skeletonMessage; 0055 } 0056 0057 void ProtectedHeadersJob::setObvoscate(bool obvoscate) 0058 { 0059 Q_D(ProtectedHeadersJob); 0060 0061 d->obvoscate = obvoscate; 0062 } 0063 0064 void ProtectedHeadersJob::doStart() 0065 { 0066 Q_D(ProtectedHeadersJob); 0067 Q_ASSERT(d->resultContent == nullptr); // Not processed before. 0068 Q_ASSERT(d->skeletonMessage); // We need a skeletonMessage to proceed 0069 0070 auto subject = d->skeletonMessage->header<KMime::Headers::Subject>(); 0071 if (d->obvoscate && subject) { 0072 // Create protected header lagacy mimepart with replaced headers 0073 auto cjob = new SinglepartJob; 0074 auto ct = cjob->contentType(); 0075 ct->setMimeType("text/plain"); 0076 ct->setCharset(subject->rfc2047Charset()); 0077 ct->setParameter(QStringLiteral("protected-headers"), QStringLiteral("v1")); 0078 cjob->contentDisposition()->setDisposition(KMime::Headers::contentDisposition::CDinline); 0079 cjob->setData(subject->type() + QByteArray(": ") + subject->asUnicodeString().toUtf8()); 0080 0081 QObject::connect(cjob, &SinglepartJob::finished, this, [d, cjob]() { 0082 auto mixedPart = new KMime::Content(); 0083 const QByteArray boundary = KMime::multiPartBoundary(); 0084 mixedPart->contentType()->setMimeType("multipart/mixed"); 0085 mixedPart->contentType(false)->setBoundary(boundary); 0086 mixedPart->appendContent(cjob->content()); 0087 0088 // if setContent hasn't been called, we assume that a subjob was added 0089 // and we want to use that 0090 if (!d->content || !d->content->hasContent()) { 0091 Q_ASSERT(d->subjobContents.size() == 1); 0092 d->content = d->subjobContents.constFirst(); 0093 } 0094 0095 mixedPart->appendContent(d->content); 0096 d->content = mixedPart; 0097 }); 0098 appendSubjob(cjob); 0099 } 0100 0101 ContentJobBase::doStart(); 0102 } 0103 0104 void ProtectedHeadersJob::process() 0105 { 0106 Q_D(ProtectedHeadersJob); 0107 0108 // if setContent hasn't been called, we assume that a subjob was added 0109 // and we want to use that 0110 if (!d->content || !d->content->hasContent()) { 0111 Q_ASSERT(d->subjobContents.size() == 1); 0112 d->content = d->subjobContents.constFirst(); 0113 } 0114 0115 auto subject = d->skeletonMessage->header<KMime::Headers::Subject>(); 0116 const auto headers = d->skeletonMessage->headers(); 0117 for (const auto &header : headers) { 0118 const QByteArray headerType(header->type()); 0119 if (headerType.startsWith("X-KMail-")) { 0120 continue; 0121 } 0122 if (headerType == "Bcc") { 0123 continue; 0124 } 0125 if (headerType.startsWith("Content-")) { 0126 continue; 0127 } 0128 // A workaround for #439958 0129 // KMime strips sometimes the newlines from long headers, if those 0130 // headers are in the signature block, this breaks the signature. 0131 // The simplest workaround is not to sign those headers until this 0132 // get fixed in KMime. 0133 if (header->as7BitString().length() > 70) { 0134 continue; 0135 } 0136 auto copyHeader = KMime::Headers::createHeader(headerType); 0137 if (!copyHeader) { 0138 copyHeader = new KMime::Headers::Generic(headerType.constData(), headerType.size()); 0139 } 0140 copyHeader->from7BitString(header->as7BitString(false)); 0141 d->content->appendHeader(copyHeader); 0142 } 0143 0144 if (d->obvoscate && subject) { 0145 subject->clear(); 0146 subject->from7BitString("..."); 0147 } 0148 auto contentType = d->content->header<KMime::Headers::ContentType>(); 0149 contentType->setParameter(QStringLiteral("protected-headers"), QStringLiteral("v1")); 0150 0151 d->resultContent = d->content; 0152 0153 emitResult(); 0154 } 0155 0156 #include "moc_protectedheadersjob.cpp"