/*
  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 "signjobtest.h"
using namespace Qt::Literals::StringLiterals;

#include "cryptofunctions.h"
#include "qtest_messagecomposer.h"
#include <QTest>

#include <KMime/Content>

#include <Libkleo/Enum>

#include <MessageComposer/ComposerJob>
#include <MessageComposer/SignJob>
#include <MessageComposer/TransparentJob>

#include "setupenv.h"

#include <gpgme.h>

#include <cstdlib>

QTEST_MAIN(SignJobTest)

using namespace MessageComposer;

void SignJobTest::initMain()
{
    gpgme_check_version(nullptr);
}

void SignJobTest::initTestCase()
{
    Test::setupEnv();
}

void SignJobTest::testContentDirect()
{
    const std::vector<GpgME::Key> &keys = Test::getKeys();

    ComposerJob composerJob;
    auto sJob = new SignJob(&composerJob);

    QVERIFY(sJob);

    const QByteArray data(QString::fromLocal8Bit("one flew over the cuckoo's nest").toUtf8());
    auto content = std::make_unique<KMime::Content>();
    content->setBody(data);

    sJob->setContent(std::move(content));
    sJob->setCryptoMessageFormat(Kleo::OpenPGPMIMEFormat);
    sJob->setSigningKeys(keys);

    checkSignJob(sJob);
}

void SignJobTest::testContentChained()
{
    const std::vector<GpgME::Key> &keys = Test::getKeys();

    const QByteArray data(QString::fromLocal8Bit("one flew over the cuckoo's nest").toUtf8());
    auto content = std::make_unique<KMime::Content>();
    content->setBody(data);

    auto tJob = new TransparentJob;
    tJob->setContent(std::move(content));

    ComposerJob composerJob;
    auto sJob = new SignJob(&composerJob);

    sJob->setCryptoMessageFormat(Kleo::OpenPGPMIMEFormat);
    sJob->setSigningKeys(keys);

    sJob->appendSubjob(tJob);

    checkSignJob(sJob);
}

void SignJobTest::testHeaders()
{
    const std::vector<GpgME::Key> &keys = Test::getKeys();

    ComposerJob composerJob;
    auto sJob = new SignJob(&composerJob);

    QVERIFY(sJob);

    const QByteArray data(QString::fromLocal8Bit("one flew over the cuckoo's nest").toUtf8());
    auto content = std::make_unique<KMime::Content>();
    content->setBody(data);

    sJob->setContent(std::move(content));
    sJob->setCryptoMessageFormat(Kleo::OpenPGPMIMEFormat);
    sJob->setSigningKeys(keys);

    VERIFYEXEC(sJob);

    auto result = sJob->takeContent();
    result->assemble();

    QVERIFY(result->contentType(KMime::CreatePolicy::DontCreate));
    QCOMPARE(result->contentType()->mimeType(), "multipart/signed");
    QCOMPARE(result->contentType()->charset(), "UTF-8");
    QVERIFY(result->contentType()->parameter("micalg").startsWith(QLatin1StringView("pgp-sha"))); // sha1 or sha256, depending on GnuPG version
    QCOMPARE(result->contentType()->parameter("protocol"), QString::fromLocal8Bit("application/pgp-signature"));
    QCOMPARE(result->contentTransferEncoding()->encoding(), KMime::Headers::CE7Bit);
}

void SignJobTest::testRecommentationRFC3156()
{
    const std::vector<GpgME::Key> &keys = Test::getKeys();

    const QString data = u"=2D Magic foo\nFrom test\n\n-- quaak\nOhno"_s;
    KMime::Headers::contentEncoding cte = KMime::Headers::CEquPr;

    ComposerJob composerJob;
    auto sJob = new SignJob(&composerJob);

    QVERIFY(sJob);

    auto content = std::make_unique<KMime::Content>();
    content->setBody(data.toUtf8());

    sJob->setContent(std::move(content));
    sJob->setCryptoMessageFormat(Kleo::OpenPGPMIMEFormat);
    sJob->setSigningKeys(keys);

    VERIFYEXEC(sJob);

    auto result = sJob->takeContent();
    result->assemble();

    QVERIFY(!result->contents().isEmpty());
    const QByteArray body = result->contents().at(0)->body();
    QCOMPARE(QString::fromUtf8(body), u"=3D2D Magic foo\n=46rom test\n\n=2D- quaak\nOhno"_s);

    ComposerTestUtil::verify(true, false, result.get(), data.toUtf8(), Kleo::OpenPGPMIMEFormat, cte);
}

void SignJobTest::testMixedContent()
{
    const std::vector<GpgME::Key> &keys = Test::getKeys();

    const QString data = u"=2D Magic foo\nFrom test\n\n-- quaak\nOhno"_s;

    ComposerJob composerJob;
    auto sJob = new SignJob(&composerJob);

    QVERIFY(sJob);

    auto content = std::make_unique<KMime::Content>();
    content->contentType()->setMimeType(QByteArrayLiteral("multipart/mixed"));
    content->contentType()->setBoundary(KMime::multiPartBoundary());
    auto subcontent = std::make_unique<KMime::Content>();
    subcontent->contentType()->setMimeType(QByteArrayLiteral("text/plain"));
    subcontent->setBody(data.toUtf8());
    auto attachment = std::make_unique<KMime::Content>();
    attachment->contentType()->setMimeType(QByteArrayLiteral("text/plain"));
    const QByteArray attachmentData("an attachment");
    attachment->setBody(attachmentData);

    content->appendContent(std::move(subcontent));
    content->appendContent(std::move(attachment));
    content->assemble();

    sJob->setContent(std::move(content));
    sJob->setCryptoMessageFormat(Kleo::OpenPGPMIMEFormat);
    sJob->setSigningKeys(keys);

    VERIFYEXEC(sJob);

    auto result = sJob->takeContent();
    result->assemble();

    QCOMPARE(result->contents().count(), 2);
    KMime::Content *firstChild = result->contents().at(0);
    QCOMPARE(firstChild->contents().count(), 2);
    QCOMPARE(firstChild->body(), QByteArray());
    QCOMPARE(firstChild->contentType()->mimeType(), QByteArrayLiteral("multipart/mixed"));
    QCOMPARE(firstChild->contents()[0]->body(), data.toUtf8());
    QCOMPARE(firstChild->contents()[1]->body(), attachmentData);

    ComposerTestUtil::verify(true, false, result.get(), data.toUtf8(), Kleo::OpenPGPMIMEFormat, KMime::Headers::CE7Bit);
}

void SignJobTest::checkSignJob(SignJob *sJob)
{
    VERIFYEXEC(sJob);

    auto result = sJob->takeContent();
    Q_ASSERT(result);
    result->assemble();

    ComposerTestUtil::verifySignature(result.get(),
                                      QString::fromLocal8Bit("one flew over the cuckoo's nest").toUtf8(),
                                      Kleo::OpenPGPMIMEFormat,
                                      KMime::Headers::CE7Bit);
}

void SignJobTest::testProtectedHeaders_data()
{
    QTest::addColumn<bool>("protectedHeaders");
    QTest::addColumn<QString>("referenceFile");

    QTest::newRow("simple-non-obvoscate") << true << u"protected_headers-non-obvoscate.mbox"_s;
    QTest::newRow("non-protected_headers") << false << u"non-protected_headers.mbox"_s;
}

void SignJobTest::testProtectedHeaders()
{
    QFETCH(bool, protectedHeaders);
    QFETCH(QString, referenceFile);

    const std::vector<GpgME::Key> &keys = Test::getKeys();

    ComposerJob composerJob;
    auto sJob = new SignJob(&composerJob);

    QVERIFY(sJob);

    const QByteArray data(QString::fromLocal8Bit("one flew over the cuckoo's nest").toUtf8());
    const QString subject(u"asdfghjklö"_s);

    auto content = std::make_unique<KMime::Content>();
    content->contentType(KMime::CreatePolicy::Create)->setMimeType("text/plain");
    content->setBody(data);

    KMime::Message skeletonMessage;
    skeletonMessage.contentType(KMime::CreatePolicy::Create)->setMimeType("foo/bla");
    skeletonMessage.to(KMime::CreatePolicy::Create)->from7BitString("to@test.de, to2@test.de");
    skeletonMessage.cc(KMime::CreatePolicy::Create)->from7BitString("cc@test.de, cc2@test.de");
    skeletonMessage.bcc(KMime::CreatePolicy::Create)->from7BitString("bcc@test.de, bcc2@test.de");
    skeletonMessage.subject(KMime::CreatePolicy::Create)->fromUnicodeString(subject);

    sJob->setContent(std::move(content));
    sJob->setCryptoMessageFormat(Kleo::OpenPGPMIMEFormat);
    sJob->setSigningKeys(keys);
    sJob->setSkeletonMessage(&skeletonMessage);
    sJob->setProtectedHeaders(protectedHeaders);

    VERIFYEXEC(sJob);

    QCOMPARE(skeletonMessage.subject()->asUnicodeString(), subject);

    auto result = sJob->takeContent();
    result->assemble();

    Test::compareFile(result->contents().at(0), QStringLiteral(MAIL_DATA_DIR "/") + referenceFile);
}

void SignJobTest::testProtectedHeadersOverwrite()
{
    const std::vector<GpgME::Key> &keys = Test::getKeys();
    const auto referenceFile = u"protected_headers-non-obvoscate.mbox"_s;

    ComposerJob composerJob;
    auto sJob = new SignJob(&composerJob);

    QVERIFY(sJob);

    const QByteArray data(QString::fromLocal8Bit("one flew over the cuckoo's nest").toUtf8());
    const QString subject(u"asdfghjklö"_s);

    auto content = std::make_unique<KMime::Content>();
    content->contentType(KMime::CreatePolicy::Create)->setMimeType("text/plain");
    content->setBody(data);

    KMime::Message skeletonMessage;
    skeletonMessage.contentType(KMime::CreatePolicy::Create)->setMimeType("foo/bla");
    skeletonMessage.to(KMime::CreatePolicy::Create)->from7BitString("to@test.de, to2@test.de");
    skeletonMessage.cc(KMime::CreatePolicy::Create)->from7BitString("cc@test.de, cc2@test.de");
    skeletonMessage.bcc(KMime::CreatePolicy::Create)->from7BitString("bcc@test.de, bcc2@test.de");
    skeletonMessage.subject(KMime::CreatePolicy::Create)->fromUnicodeString(subject);

    sJob->setContent(std::move(content));
    sJob->setCryptoMessageFormat(Kleo::OpenPGPMIMEFormat);
    sJob->setSigningKeys(keys);
    sJob->setSkeletonMessage(&skeletonMessage);
    sJob->setProtectedHeaders(true);

    VERIFYEXEC(sJob);

    QCOMPARE(skeletonMessage.subject()->asUnicodeString(), subject);
    skeletonMessage.to()->from7BitString("overwrite@example.org");
    skeletonMessage.cc()->from7BitString("cc_overwrite@example.org");
    skeletonMessage.bcc()->from7BitString("bcc_overwrite@example.org");
    skeletonMessage.subject()->fromUnicodeString(subject + u"_owerwrite"_s);

    auto result = sJob->takeContent();
    result->assemble();

    Test::compareFile(result->contents().at(0), QStringLiteral(MAIL_DATA_DIR "/") + referenceFile);
}

void SignJobTest::testProtectedHeadersSkipLong()
{
    const std::vector<GpgME::Key> &keys = Test::getKeys();
    const auto referenceFile = u"protected_headers-non-obvoscate.mbox"_s;

    ComposerJob composerJob;
    auto sJob = new SignJob(&composerJob);

    QVERIFY(sJob);

    const QByteArray data(QString::fromLocal8Bit("one flew over the cuckoo's nest").toUtf8());
    const QString subject(u"asdfghjklö"_s);

    auto content = std::make_unique<KMime::Content>();
    content->contentType(KMime::CreatePolicy::Create)->setMimeType("text/plain");
    content->setBody(data);

    KMime::Message skeletonMessage;
    auto face = std::make_unique<KMime::Headers::Generic>("Face");
    face->from7BitString(
        "iVBORw0KGgoAAAANSUhEUgAAADAAAAAwBAMAAAClLOS0AAAAElBMVEX9/ftIS5IsNImXjKgc\n"
        " I3Dh29sdnA7JAAAB60lEQVR4nI2US5ajMAxFXeF4HtlhXlZ7AdCCBWCcORTx/rfSkgyEyqg14uii\n"
        " pw8SpnzaRmrm/8yWD0dZqn80G5WlUOVEnsCPNKp6D2w98JsWHcJuiOiCPnXGNPDIU/U7BuKPGe7G\n"
        " fAW/rNPhl+A4bqkC6IZDiwOwXb0CnKBtpjMJhgZoB96bXl5Gtftfr+AHJ4LlhRm8VOvmJlCvwM3U\n"
        " d41f9maX1dOADG4cQe1lDD3RAzVH+DXMhhUTfjOIkWexvAHRkCSiyZmuYBWlGpGfV0CihDyrW8yh\n"
        " bOYDaFU5d+8AU8YXqtQNEz5P92KskWFq54h/ONWs4OtpbDoiGCzmNatc05rmBE6AtQpKsdsOfoLD\n"
        " UBgci6JAqgqI8bI2F+AwjleAew6OwFYSWA8CVgFdrQqfCnIW8MKAdexscwWTbOML8Q0myV4y8218\n"
        " 7FIKomwvf5bO0ph+gZFbZCD7q2DZAc7nyQxv4FSLTcHjkLo5KS/Ox41lPHIExyEZRb4lP5zgR7c8\n"
        " BV822d6UTiAjiQnGVfyyIZfpYmZg+gpiTvHsg4EzHOIVsMC9lsvP0He2B+qHmHOO6fsAAehpqSc+\n"
        " yBNIF1wUgOXzCJDVBIB0Icfd2vKAN2jAubRfOvt3INsO+3XvdgGBgAH/OepfoIJ/RyuNKjhmbCAA\n"
        " AAAASUVORK5CYII=");
    skeletonMessage.contentType(KMime::CreatePolicy::Create)->setMimeType("foo/bla");
    skeletonMessage.to(KMime::CreatePolicy::Create)->from7BitString("to@test.de, to2@test.de");
    skeletonMessage.cc(KMime::CreatePolicy::Create)->from7BitString("cc@test.de, cc2@test.de");
    skeletonMessage.bcc(KMime::CreatePolicy::Create)->from7BitString("bcc@test.de, bcc2@test.de");
    skeletonMessage.appendHeader(std::move(face));
    skeletonMessage.subject(KMime::CreatePolicy::Create)->fromUnicodeString(subject);

    sJob->setContent(std::move(content));
    sJob->setCryptoMessageFormat(Kleo::OpenPGPMIMEFormat);
    sJob->setSigningKeys(keys);
    sJob->setSkeletonMessage(&skeletonMessage);
    sJob->setProtectedHeaders(true);

    VERIFYEXEC(sJob);

    auto result = sJob->takeContent();
    result->assemble();

    Test::compareFile(result->contents().at(0), QStringLiteral(MAIL_DATA_DIR "/") + referenceFile);
}

#include "moc_signjobtest.cpp"
