diff --git a/messagecomposer/autotests/signencrypttest.h b/messagecomposer/autotests/signencrypttest.h --- a/messagecomposer/autotests/signencrypttest.h +++ b/messagecomposer/autotests/signencrypttest.h @@ -1,6 +1,5 @@ /* - Copyright (C) 2009 Klaralvdalens Datakonsult AB, a KDAB Group company, info@kdab.net - Copyright (c) 2009 Leo Franchi + Copyright (C) 2020 Sandro Knauß This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by @@ -31,7 +30,9 @@ void initTestCase(); private Q_SLOTS: + void testContent_data(); void testContent(); + void testContentSubjobChained(); void testHeaders(); }; diff --git a/messagecomposer/autotests/signencrypttest.cpp b/messagecomposer/autotests/signencrypttest.cpp --- a/messagecomposer/autotests/signencrypttest.cpp +++ b/messagecomposer/autotests/signencrypttest.cpp @@ -1,6 +1,5 @@ /* - Copyright (C) 2009 Klaralvdalens Datakonsult AB, a KDAB Group company, info@kdab.net - Copyright (c) 2009 Leo Franchi + Copyright (C) 2020 Sandro Knauß This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by @@ -31,17 +30,14 @@ #include #include -#include +#include #include -#include +#include #include #include -#include #include -#include - QTEST_MAIN(SignEncryptTest) using namespace MessageComposer; @@ -51,100 +47,153 @@ Test::setupEnv(); } +void SignEncryptTest::testContent_data() +{ + QTest::addColumn("cryptoMessageFormat"); + QTest::addColumn("error"); + + QTest::newRow("OpenPGPMimeFormat") << (int) Kleo::OpenPGPMIMEFormat << QString(); + QTest::newRow("InlineOpenPGPFormat") << (int) Kleo::InlineOpenPGPFormat << QString(); + QTest::newRow("SMIMEFormat") << (int) Kleo::SMIMEFormat << QStringLiteral("Not implemented"); + QTest::newRow("SMIMEOpaqueFormat") << (int) Kleo::SMIMEOpaqueFormat << QStringLiteral("Not implemented"); +} + void SignEncryptTest::testContent() { + QFETCH(int, cryptoMessageFormat); + QFETCH(QString, error); + std::vector< GpgME::Key > keys = Test::getKeys(); + const QString data(QString::fromLocal8Bit("one flew over the cuckoo's nest")); - Composer *composer = new Composer; - SignJob *sJob = new SignJob(composer); - EncryptJob *eJob = new EncryptJob(composer); + Composer composer; - QVERIFY(composer); - QVERIFY(sJob); - QVERIFY(eJob); + const QVector charsets = {"us-ascii"}; + composer.globalPart()->setCharsets(charsets); - QVector charsets; - charsets << "us-ascii"; - composer->globalPart()->setCharsets(charsets); - TextPart *part = new TextPart(this); - part->setWordWrappingEnabled(false); - part->setCleanPlainText(QStringLiteral("one flew over the cuckoo's nest")); + TextPart part; + part.setWordWrappingEnabled(false); + part.setCleanPlainText(data); - MainTextJob *mainTextJob = new MainTextJob(part, composer); + auto mainTextJob = new MainTextJob(&part, &composer); + auto seJob = new SignEncryptJob(&composer); - QVERIFY(composer); QVERIFY(mainTextJob); VERIFYEXEC(mainTextJob); - QStringList recipients; - recipients << QString::fromLocal8Bit("test@kolab.org"); + const QStringList recipients = {QString::fromLocal8Bit("test@kolab.org")}; + + seJob->setContent(mainTextJob->content()); + seJob->setSigningKeys(keys); + seJob->setCryptoMessageFormat((Kleo::CryptoMessageFormat)cryptoMessageFormat); + seJob->setRecipients(recipients); + seJob->setEncryptionKeys(keys); - sJob->setContent(mainTextJob->content()); - sJob->setSigningKeys(keys); - sJob->setCryptoMessageFormat(Kleo::OpenPGPMIMEFormat); + if (!error.isEmpty()) { + QVERIFY(!seJob->exec()); + QCOMPARE(seJob->errorString(), error); + return; + } - eJob->setCryptoMessageFormat(Kleo::OpenPGPMIMEFormat); - eJob->setRecipients(recipients); - eJob->setEncryptionKeys(keys); + VERIFYEXEC(seJob); + KMime::Content *result = seJob->content(); + QVERIFY(result); + result->assemble(); - eJob->appendSubjob(sJob); + ComposerTestUtil::verifySignatureAndEncryption( + result, + data.toUtf8(), + (Kleo::CryptoMessageFormat)cryptoMessageFormat, + false, + true); - VERIFYEXEC(eJob); + delete result; +} + +void SignEncryptTest::testContentSubjobChained() +{ + std::vector< GpgME::Key > keys = MessageComposer::Test::getKeys(); - KMime::Content *result = eJob->content(); + const QByteArray data(QString::fromLocal8Bit("one flew over the cuckoo's nest").toUtf8()); + KMime::Message skeletonMessage; + + auto content = new KMime::Content; + content->contentType(true)->setMimeType("text/plain"); + content->setBody(data); + + auto tJob = new TransparentJob; + tJob->setContent(content); + + const QStringList recipients = {QString::fromLocal8Bit("test@kolab.org")}; + + Composer composer; + auto seJob = new SignEncryptJob(&composer); + + seJob->setSigningKeys(keys); + seJob->setCryptoMessageFormat(Kleo::OpenPGPMIMEFormat); + seJob->setRecipients(recipients); + seJob->setEncryptionKeys(keys); + seJob->setSkeletonMessage(&skeletonMessage); + seJob->appendSubjob(tJob); + + VERIFYEXEC(seJob); + KMime::Content *result = seJob->content(); QVERIFY(result); result->assemble(); - qDebug() << "result:" << result->encodedContent(); - ComposerTestUtil::verifySignatureAndEncryption( result, - QString::fromLocal8Bit("one flew over the cuckoo's nest").toUtf8(), - Kleo::OpenPGPMIMEFormat); + data, + Kleo::OpenPGPMIMEFormat, + false, + true); + + delete result; } + void SignEncryptTest::testHeaders() { std::vector< GpgME::Key > keys = Test::getKeys(); - Composer *composer = new Composer; - SignJob *sJob = new SignJob(composer); - EncryptJob *eJob = new EncryptJob(composer); + Composer composer; + auto seJob = new SignEncryptJob(&composer); - QVERIFY(composer); - QVERIFY(sJob); - QVERIFY(eJob); + QVERIFY(seJob); - QByteArray data(QString::fromLocal8Bit("one flew over the cuckoo's nest").toUtf8()); - KMime::Content *content = new KMime::Content; + const QByteArray data(QString::fromLocal8Bit("one flew over the cuckoo's nest").toUtf8()); + auto content = new KMime::Content; content->setBody(data); - QStringList recipients; - recipients << QString::fromLocal8Bit("test@kolab.org"); + const QStringList recipients = {QString::fromLocal8Bit("test@kolab.org")}; - sJob->setContent(content); - sJob->setSigningKeys(keys); - sJob->setCryptoMessageFormat(Kleo::OpenPGPMIMEFormat); + seJob->setContent(content); + seJob->setSigningKeys(keys); + seJob->setCryptoMessageFormat(Kleo::OpenPGPMIMEFormat); + seJob->setRecipients(recipients); + seJob->setEncryptionKeys(keys); - eJob->setCryptoMessageFormat(Kleo::OpenPGPMIMEFormat); - eJob->setRecipients(recipients); - eJob->setEncryptionKeys(keys); + VERIFYEXEC(seJob); - eJob->appendSubjob(sJob); - - VERIFYEXEC(eJob); - - KMime::Content *result = eJob->content(); + KMime::Content *result = seJob->content(); QVERIFY(result); result->assemble(); - QByteArray mimeType("multipart/encrypted"); - QByteArray charset("ISO-8859-1"); + QFile f(QStringLiteral("test")); + QVERIFY(f.open(QIODevice::WriteOnly | QIODevice::Truncate)); + const QByteArray encodedContent(result->encodedContent()); + f.write(encodedContent); + if (!encodedContent.endsWith('\n')) { + f.write("\n"); + } + f.close(); QVERIFY(result->contentType(false)); - QCOMPARE(result->contentType()->mimeType(), mimeType); - QCOMPARE(result->contentType()->charset(), charset); + QCOMPARE(result->contentType()->mimeType(), "multipart/encrypted"); + QCOMPARE(result->contentType()->charset(), "ISO-8859-1"); QCOMPARE(result->contentType()->parameter(QString::fromLocal8Bit("protocol")), QString::fromLocal8Bit("application/pgp-encrypted")); QCOMPARE(result->contentTransferEncoding()->encoding(), KMime::Headers::CE7Bit); + + delete result; } diff --git a/messagecomposer/src/composer/composer.cpp b/messagecomposer/src/composer/composer.cpp --- a/messagecomposer/src/composer/composer.cpp +++ b/messagecomposer/src/composer/composer.cpp @@ -274,6 +274,7 @@ seJob->setSigningKeys(signers); seJob->setEncryptionKeys(recipients.second); seJob->setRecipients(recipients.first); + seJob->setSkeletonMessage(skeletonMessage); subJob = seJob; } else { diff --git a/messagecomposer/src/job/signencryptjob.h b/messagecomposer/src/job/signencryptjob.h --- a/messagecomposer/src/job/signencryptjob.h +++ b/messagecomposer/src/job/signencryptjob.h @@ -57,12 +57,19 @@ void setEncryptionKeys(const std::vector &keys) override; void setRecipients(const QStringList &rec) override; + void setSkeletonMessage(KMime::Message *skeletonMessage); + + void setProtectedHeaders(bool protectedHeaders); + void setProtectedHeadersObvoscate(bool protectedHeadersObvoscate); + Q_REQUIRED_RESULT std::vector encryptionKeys() const override; Q_REQUIRED_RESULT QStringList recipients() const override; Q_REQUIRED_RESULT KMime::Content *origContent(); protected Q_SLOTS: + void doStart() override; + void slotResult(KJob *job) override; void process() override; private: diff --git a/messagecomposer/src/job/signencryptjob.cpp b/messagecomposer/src/job/signencryptjob.cpp --- a/messagecomposer/src/job/signencryptjob.cpp +++ b/messagecomposer/src/job/signencryptjob.cpp @@ -21,7 +21,7 @@ #include "job/signencryptjob.h" #include "contentjobbase_p.h" -#include "utils/util.h" +#include "job/protectedheaders.h" #include "utils/util_p.h" #include @@ -54,6 +54,10 @@ QStringList recipients; Kleo::CryptoMessageFormat format; KMime::Content *content = nullptr; + KMime::Message *skeletonMessage = nullptr; + + bool protectedHeaders = true; + bool protectedHeadersObvoscate = false; // copied from messagecomposer.cpp bool binaryHint(Kleo::CryptoMessageFormat f) @@ -130,6 +134,27 @@ d->recipients = recipients; } +void SignEncryptJob::setSkeletonMessage(KMime::Message *skeletonMessage) +{ + Q_D(SignEncryptJob); + + d->skeletonMessage = skeletonMessage; +} + +void SignEncryptJob::setProtectedHeaders(bool protectedHeaders) +{ + Q_D(SignEncryptJob); + + d->protectedHeaders = protectedHeaders; +} + +void SignEncryptJob::setProtectedHeadersObvoscate(bool protectedHeadersObvoscate) +{ + Q_D(SignEncryptJob); + + d->protectedHeadersObvoscate = protectedHeadersObvoscate; +} + QStringList SignEncryptJob::recipients() const { Q_D(const SignEncryptJob); @@ -144,6 +169,48 @@ return d->encKeys; } +void SignEncryptJob::doStart() +{ + Q_D(SignEncryptJob); + Q_ASSERT(d->resultContent == nullptr); // Not processed before. + + if (d->protectedHeaders && d->skeletonMessage && d->format & Kleo::OpenPGPMIMEFormat) { + ProtectedHeadersJob *pJob = new ProtectedHeadersJob; + pJob->setContent(d->content); + pJob->setSkeletonMessage(d->skeletonMessage); + pJob->setObvoscate(d->protectedHeadersObvoscate); + QObject::connect(pJob, &ProtectedHeadersJob::finished, this, [d, pJob](KJob *job) { + if (job->error()) { + return; + } + d->content = pJob->content(); + }); + appendSubjob(pJob); + } + + ContentJobBase::doStart(); +} + + +void SignEncryptJob::slotResult(KJob *job) +{ + Q_D(SignEncryptJob); + if (error()) { + ContentJobBase::slotResult(job); + return; + } + if (subjobs().size() == 2) { + auto pjob = static_cast(subjobs().last()); + if (pjob) { + auto cjob = dynamic_cast(job); + Q_ASSERT(cjob); + pjob->setContent(cjob->content()); + } + } + + ContentJobBase::slotResult(job); +} + void SignEncryptJob::process() { Q_D(SignEncryptJob);