/*
  SPDX-FileCopyrightText: 2020 Sandro Knauß <sknauss@kde.org>

  SPDX-License-Identifier: LGPL-2.0-or-later
*/

#include "job/protectedheadersjob.h"

#include "contentjobbase_p.h"
#include "job/singlepartjob.h"

#include <KMime/Content>
#include <KMime/Message>

using namespace Qt::Literals::StringLiterals;
using namespace MessageComposer;

class MessageComposer::ProtectedHeadersJobPrivate : public ContentJobBasePrivate
{
public:
    explicit ProtectedHeadersJobPrivate(ProtectedHeadersJob *qq)
        : ContentJobBasePrivate(qq)
    {
    }

    std::unique_ptr<KMime::Content> content;
    KMime::Message *skeletonMessage = nullptr;

    bool obvoscate = false;

    Q_DECLARE_PUBLIC(ProtectedHeadersJob)
};

ProtectedHeadersJob::ProtectedHeadersJob(QObject *parent)
    : ContentJobBase(*new ProtectedHeadersJobPrivate(this), parent)
{
}

ProtectedHeadersJob::~ProtectedHeadersJob() = default;

void ProtectedHeadersJob::setContent(std::unique_ptr<KMime::Content> &&content)
{
    Q_D(ProtectedHeadersJob);

    d->content = std::move(content);
    if (content) {
        d->content->assemble();
    }
}

void ProtectedHeadersJob::setSkeletonMessage(KMime::Message *skeletonMessage)
{
    Q_D(ProtectedHeadersJob);

    d->skeletonMessage = skeletonMessage;
}

void ProtectedHeadersJob::setObvoscate(bool obvoscate)
{
    Q_D(ProtectedHeadersJob);

    d->obvoscate = obvoscate;
}

void ProtectedHeadersJob::doStart()
{
    Q_D(ProtectedHeadersJob);
    Q_ASSERT(d->resultContent == nullptr); // Not processed before.
    Q_ASSERT(d->skeletonMessage); // We need a skeletonMessage to proceed

    const auto subject = d->skeletonMessage->subject(KMime::CreatePolicy::DontCreate);
    if (d->obvoscate && subject) {
        // Create protected header legacy mimepart with replaced headers
        auto cjob = new SinglepartJob;
        auto ct = cjob->contentType();
        ct->setMimeType("text/plain");
        ct->setCharset(subject->rfc2047Charset());
        ct->setParameter(QByteArrayLiteral("protected-headers"), u"v1"_s);
        cjob->contentDisposition()->setDisposition(KMime::Headers::contentDisposition::CDinline);
        cjob->setData(subject->type() + QByteArray(": ") + subject->asUnicodeString().toUtf8());

        QObject::connect(cjob, &SinglepartJob::finished, this, [d, cjob]() {
            auto mixedPart = std::make_unique<KMime::Content>();
            const QByteArray boundary = KMime::multiPartBoundary();
            mixedPart->contentType()->setMimeType("multipart/mixed");
            mixedPart->contentType(KMime::CreatePolicy::DontCreate)->setBoundary(boundary);
            mixedPart->appendContent(cjob->takeContent());

            // if setContent hasn't been called, we assume that a subjob was added
            // and we want to use that
            if (!d->content || !d->content->hasContent()) {
                Q_ASSERT(d->subjobContents.size() == 1);
                d->content = std::move(d->subjobContents.front());
            }

            mixedPart->appendContent(std::move(d->content));
            d->content = std::move(mixedPart);
        });
        appendSubjob(cjob);
    }

    ContentJobBase::doStart();
}

void ProtectedHeadersJob::process()
{
    Q_D(ProtectedHeadersJob);

    // if setContent hasn't been called, we assume that a subjob was added
    // and we want to use that
    if (!d->content || !d->content->hasContent()) {
        Q_ASSERT(d->subjobContents.size() == 1);
        d->content = std::move(d->subjobContents.front());
    }

    auto subject = d->skeletonMessage->subject(KMime::CreatePolicy::DontCreate);
    const auto headers = d->skeletonMessage->headers();
    for (const auto &header : headers) {
        const QByteArrayView headerType(header->type());
        if (headerType.startsWith("X-KMail-")) {
            continue;
        }
        if (headerType == "Bcc") {
            continue;
        }
        if (headerType.startsWith("Content-")) {
            continue;
        }
        // A workaround for #439958
        // KMime strips sometimes the newlines from long headers, if those
        // headers are in the signature block, this breaks the signature.
        // The simplest workaround is not to sign those headers until this
        // get fixed in KMime.
        if (header->as7BitString().length() > 70) {
            continue;
        }
        auto copyHeader = KMime::Headers::createHeader(headerType);
        copyHeader->from7BitString(header->as7BitString());
        d->content->appendHeader(std::move(copyHeader));
    }

    if (d->obvoscate && subject) {
        subject->from7BitString("...");
    }
    auto contentType = d->content->contentType(KMime::CreatePolicy::Create);
    contentType->setParameter(QByteArrayLiteral("protected-headers"), u"v1"_s);

    d->resultContent = std::move(d->content);

    emitResult();
}

#include "moc_protectedheadersjob.cpp"
