/*
  SPDX-FileCopyrightText: 2009 Klaralvdalens Datakonsult AB, a KDAB Group company, info@kdab.net
  SPDX-FileCopyrightText: 2009 Leo Franchi <lfranchi@kde.org>

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

#include "cryptofunctions.h"
using namespace Qt::Literals::StringLiterals;

#include "setupenv.h"

#include <MessageComposer/Util>

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

#include <MimeTreeParser/ObjectTreeParser>
#include <MimeTreeParser/SimpleObjectTreeSource>

#include <QTest>
#include <cstdlib>
#include <gpgme++/keylistresult.h>

using namespace MessageComposer;

void ComposerTestUtil::verify(bool sign,
                              bool encrypt,
                              KMime::Content *content,
                              const QByteArray &origContent,
                              Kleo::CryptoMessageFormat f,
                              KMime::Headers::contentEncoding encoding)
{
    if (sign && encrypt) {
        verifySignatureAndEncryption(content, origContent, f, false);
    } else if (sign) {
        verifySignature(content, origContent, f, encoding);
    } else {
        verifyEncryption(content, origContent, f);
    }
}

void ComposerTestUtil::verifySignature(KMime::Content *content,
                                       const QByteArray &signedContent,
                                       Kleo::CryptoMessageFormat f,
                                       KMime::Headers::contentEncoding encoding)
{
    // store it in a KMime::Message, that's what OTP needs
    auto resultMessage = std::make_unique<KMime::Message>();
    resultMessage->setContent(content->encodedContent());
    resultMessage->parse();

    // parse the result and make sure it is valid in various ways
    MimeTreeParser::SimpleObjectTreeSource testSource;
    auto nh = std::make_unique<MimeTreeParser::NodeHelper>();
    MimeTreeParser::ObjectTreeParser otp(&testSource, nh.get());

    // ensure the signed part exists and is parseable
    if (f & Kleo::OpenPGPMIMEFormat) {
        // process the result..
        otp.parseObjectTree(resultMessage.get());

        KMime::Content *signedPart = Util::findTypeInMessage(resultMessage.get(), "application", "pgp-signature");
        Q_ASSERT(signedPart);
        QCOMPARE(signedPart->contentTransferEncoding()->encoding(), KMime::Headers::CE7Bit);
        Q_UNUSED(signedPart)

        // Q_ASSERT( nh->signatureState( resultMessage ) == MimeTreeParser::KMMsgFullySigned );

        QVERIFY(!resultMessage->contents().isEmpty());
        QCOMPARE(resultMessage->contents().at(0)->contentTransferEncoding()->encoding(), encoding);
    } else if (f & Kleo::InlineOpenPGPFormat) {
        // process the result..
        otp.parseObjectTree(resultMessage.get());
        QCOMPARE(nh->signatureState(resultMessage.get()), MimeTreeParser::KMMsgFullySigned);

        QCOMPARE(resultMessage->contentTransferEncoding()->encoding(), encoding);
    } else if (f & Kleo::AnySMIME) {
        if (f & Kleo::SMIMEFormat) {
            KMime::Content *signedPart = Util::findTypeInMessage(resultMessage.get(), "application", "pkcs7-signature");
            Q_ASSERT(signedPart);
            QCOMPARE(signedPart->contentTransferEncoding()->encoding(), KMime::Headers::CEbase64);
            QCOMPARE(signedPart->contentType()->mimeType(), QByteArray("application/pkcs7-signature"));
            QCOMPARE(signedPart->contentType(KMime::CreatePolicy::DontCreate)->name(), u"smime.p7s"_s);
            QCOMPARE(signedPart->contentDisposition()->disposition(), KMime::Headers::CDattachment);
            QCOMPARE(signedPart->contentDisposition()->filename(), u"smime.p7s"_s);
            Q_UNUSED(signedPart)

            QVERIFY(!resultMessage->contents().isEmpty());
            QCOMPARE(resultMessage->contents().at(0)->contentTransferEncoding()->encoding(), encoding);

            QCOMPARE(resultMessage->contentType()->mimeType(), QByteArray("multipart/signed"));
            QCOMPARE(resultMessage->contentType(KMime::CreatePolicy::DontCreate)->parameter("protocol"), u"application/pkcs7-signature"_s);
            QCOMPARE(resultMessage->contentType(KMime::CreatePolicy::DontCreate)->parameter("micalg"), u"sha1"_s);
        } else if (f & Kleo::SMIMEOpaqueFormat) {
            KMime::Content *signedPart = Util::findTypeInMessage(resultMessage.get(), "application", "pkcs7-mime");
            Q_ASSERT(signedPart);
            QCOMPARE(signedPart->contentTransferEncoding()->encoding(), KMime::Headers::CEbase64);
            QCOMPARE(signedPart->contentType()->mimeType(), QByteArray("application/pkcs7-mime"));
            QCOMPARE(signedPart->contentType(KMime::CreatePolicy::DontCreate)->name(), u"smime.p7m"_s);
            QCOMPARE(signedPart->contentType(KMime::CreatePolicy::DontCreate)->parameter("smime-type"), u"signed-data"_s);
            QCOMPARE(signedPart->contentDisposition()->disposition(), KMime::Headers::CDattachment);
            QCOMPARE(signedPart->contentDisposition(KMime::CreatePolicy::DontCreate)->filename(), u"smime.p7m"_s);
            Q_UNUSED(signedPart)
        }
        // process the result..
        otp.parseObjectTree(resultMessage.get());

        // Q_ASSERT( nh->signatureState( resultMessage ) == MimeTreeParser::KMMsgFullySigned );
    }

    // make sure the good sig is of what we think it is
    QCOMPARE(otp.plainTextContent().trimmed(), QString::fromUtf8(signedContent).trimmed());
    Q_UNUSED(signedContent)
}

void ComposerTestUtil::verifyEncryption(KMime::Content *content, const QByteArray &encrContent, Kleo::CryptoMessageFormat f, bool withAttachment)
{
    // store it in a KMime::Message, that's what OTP needs
    auto resultMessage = std::make_unique<KMime::Message>();
    resultMessage->setContent(content->encodedContent());
    resultMessage->parse();

    // parse the result and make sure it is valid in various ways
    MimeTreeParser::SimpleObjectTreeSource testSource;
    testSource.setDecryptMessage(true);
    auto nh = std::make_unique<MimeTreeParser::NodeHelper>();
    MimeTreeParser::ObjectTreeParser otp(&testSource, nh.get());

    if (f & Kleo::OpenPGPMIMEFormat) {
        // ensure the enc part exists and is parseable
        KMime::Content *encPart = Util::findTypeInMessage(resultMessage.get(), "application", "pgp-encrypted");
        Q_ASSERT(encPart);
        Q_UNUSED(encPart)

        // process the result..
        otp.parseObjectTree(resultMessage.get());
        QCOMPARE(nh->encryptionState(resultMessage.get()), MimeTreeParser::KMMsgFullyEncrypted);
    } else if (f & Kleo::InlineOpenPGPFormat) {
        if (withAttachment) {
            // Only first MimePart is the encrypted Text
            KMime::Content *cContent = resultMessage->contents().at(0);
            resultMessage->setContent(cContent->encodedContent());
            resultMessage->parse();
        }

        otp.parseObjectTree(resultMessage.get());

        QCOMPARE(nh->encryptionState(resultMessage.get()), MimeTreeParser::KMMsgFullyEncrypted);
    } else if (f & Kleo::AnySMIME) {
        // ensure the enc part exists and is parseable
        KMime::Content *encPart = Util::findTypeInMessage(resultMessage.get(), "application", "pkcs7-mime");
        Q_ASSERT(encPart);

        QCOMPARE(encPart->contentType()->mimeType(), QByteArray("application/pkcs7-mime"));
        QCOMPARE(encPart->contentType(KMime::CreatePolicy::DontCreate)->name(), u"smime.p7m"_s);
        QCOMPARE(encPart->contentType(KMime::CreatePolicy::DontCreate)->parameter("smime-type"), u"enveloped-data"_s);
        QCOMPARE(encPart->contentDisposition()->disposition(), KMime::Headers::CDattachment);
        QCOMPARE(encPart->contentDisposition(KMime::CreatePolicy::DontCreate)->filename(), u"smime.p7m"_s);
        Q_UNUSED(encPart)

        otp.parseObjectTree(resultMessage.get());
        QCOMPARE(nh->encryptionState(resultMessage.get()), MimeTreeParser::KMMsgFullyEncrypted);
    }
    QCOMPARE(otp.plainTextContent().trimmed(), QString::fromUtf8(encrContent).trimmed());
    Q_UNUSED(encrContent)
}

void ComposerTestUtil::verifySignatureAndEncryption(KMime::Content *content,
                                                    const QByteArray &origContent,
                                                    Kleo::CryptoMessageFormat f,
                                                    bool withAttachment,
                                                    bool combined)
{
    Q_UNUSED(withAttachment)
    // store it in a KMime::Message, that's what OTP needs
    auto resultMessage = std::make_unique<KMime::Message>();
    resultMessage->setContent(content->encodedContent());
    resultMessage->parse();

    // parse the result and make sure it is valid in various ways
    MimeTreeParser::SimpleObjectTreeSource testSource;
    testSource.setDecryptMessage(true);
    auto nh = std::make_unique<MimeTreeParser::NodeHelper>();
    MimeTreeParser::ObjectTreeParser otp(&testSource, nh.get());

    if (f & Kleo::OpenPGPMIMEFormat) {
        // ensure the enc part exists and is parseable
        KMime::Content *encPart = Util::findTypeInMessage(resultMessage.get(), "application", "pgp-encrypted");
        Q_ASSERT(encPart);
        Q_UNUSED(encPart)
        otp.parseObjectTree(resultMessage.get());
        QCOMPARE(nh->encryptionState(resultMessage.get()), MimeTreeParser::KMMsgFullyEncrypted);

        QList<KMime::Content *> extra = nh->extraContents(resultMessage.get());
        QCOMPARE(extra.size(), 1);
        if (combined) {
            QCOMPARE(nh->signatureState(resultMessage.get()), MimeTreeParser::KMMsgFullySigned);
        } else {
            QCOMPARE(extra.size(), 1);
            QCOMPARE(nh->signatureState(extra[0]), MimeTreeParser::KMMsgFullySigned);
        }
    } else if (f & Kleo::InlineOpenPGPFormat) {
        otp.parseObjectTree(resultMessage.get());

        QCOMPARE(nh->encryptionState(resultMessage.get()), MimeTreeParser::KMMsgFullyEncrypted);
        QCOMPARE(nh->signatureState(resultMessage.get()), MimeTreeParser::KMMsgFullySigned);
    } else if (f & Kleo::AnySMIME) {
        KMime::Content *encPart = Util::findTypeInMessage(resultMessage.get(), "application", "pkcs7-mime");
        Q_ASSERT(encPart);
        QCOMPARE(encPart->contentType()->mimeType(), QByteArray("application/pkcs7-mime"));
        QCOMPARE(encPart->contentType(KMime::CreatePolicy::DontCreate)->name(), u"smime.p7m"_s);
        QCOMPARE(encPart->contentType(KMime::CreatePolicy::DontCreate)->parameter("smime-type"), u"enveloped-data"_s);
        QCOMPARE(encPart->contentDisposition()->disposition(), KMime::Headers::CDattachment);
        QCOMPARE(encPart->contentDisposition(KMime::CreatePolicy::DontCreate)->filename(), u"smime.p7m"_s);
        Q_UNUSED(encPart)

        otp.parseObjectTree(resultMessage.get());
        QCOMPARE(nh->encryptionState(resultMessage.get()), MimeTreeParser::KMMsgFullyEncrypted);

        QList<KMime::Content *> extra = nh->extraContents(resultMessage.get());
        QCOMPARE(extra.size(), 1);
        QCOMPARE(nh->signatureState(extra[0]), MimeTreeParser::KMMsgFullySigned);
    }
    QCOMPARE(otp.plainTextContent().trimmed(), QString::fromUtf8(origContent).trimmed());
    Q_UNUSED(origContent)
}
