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"