From c71bc1b1fdd4055dfe2e2155961827b8652dd96c Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Thu, 3 Aug 2017 08:44:40 -0600 Subject: A first signed message --- framework/src/domain/mime/mailtemplates.cpp | 34 +++++++++-- framework/src/domain/mime/mailtemplates.h | 3 +- framework/src/domain/mime/tests/CMakeLists.txt | 13 +++-- .../src/domain/mime/tests/mailtemplatetest.cpp | 68 ++++++++++++++++++++++ 4 files changed, 107 insertions(+), 11 deletions(-) diff --git a/framework/src/domain/mime/mailtemplates.cpp b/framework/src/domain/mime/mailtemplates.cpp index 608f7546..dc713ee4 100644 --- a/framework/src/domain/mime/mailtemplates.cpp +++ b/framework/src/domain/mime/mailtemplates.cpp @@ -39,6 +39,8 @@ #include +#include "mailcrypto.h" + namespace KMime { namespace Types { static bool operator==(const KMime::Types::AddrSpec &left, const KMime::Types::AddrSpec &right) @@ -910,7 +912,6 @@ static KMime::Content *createBodyPart(const QByteArray &body) { auto mainMessage = new KMime::Content; mainMessage->setBody(body); mainMessage->contentType(true)->setMimeType("text/plain"); - // return MailCrypto::sign(mainMessage, {}); return mainMessage; } @@ -930,12 +931,13 @@ static void applyAddresses(const QStringList &list, std::function &attachments) +KMime::Message::Ptr MailTemplates::createMessage(KMime::Message::Ptr existingMessage, const QStringList &to, const QStringList &cc, const QStringList &bcc, const KMime::Types::Mailbox &from, const QString &subject, const QString &body, const QList &attachments, const std::vector &signingKeys) { auto mail = existingMessage; if (!mail) { mail = KMime::Message::Ptr::create(); } + mail->to(true)->clear(); applyAddresses(to, [&](const QByteArray &addrSpec, const QByteArray &displayName) { mail->to(true)->addAddress(addrSpec, displayName); @@ -966,6 +968,7 @@ KMime::Message::Ptr MailTemplates::createMessage(KMime::Message::Ptr existingMes mail->date(true)->setDateTime(QDateTime::currentDateTimeUtc()); } + KMime::Content *bodyPart; if (!attachments.isEmpty()) { mail->contentType(true)->setMimeType("multipart/mixed"); mail->contentType()->setBoundary(KMime::multiPartBoundary()); @@ -974,13 +977,32 @@ KMime::Message::Ptr MailTemplates::createMessage(KMime::Message::Ptr existingMes for (const auto &attachment : attachments) { mail->addContent(createAttachmentPart(attachment.data, attachment.filename, attachment.isInline, attachment.mimeType, attachment.name)); } - mail->addContent(createBodyPart(body.toUtf8())); + bodyPart = createBodyPart(body.toUtf8()); } else { //FIXME same implementation as above for attachments - mail->setBody(body.toUtf8()); - mail->contentType(true)->setMimeType("text/plain"); + bodyPart = createBodyPart(body.toUtf8()); } + mail->assemble(); + KMime::Content *signedResult = nullptr; + if (!signingKeys.empty()) { + signedResult = MailCrypto::sign(bodyPart, signingKeys); + if (!signedResult) { + qWarning() << "Signing failed"; + return {}; + } + } else { + if (!mail->contentType(false)) { + mail->contentType(true)->setMimeType("text/plain"); + } + } mail->assemble(); - return mail; + + const QByteArray allData = mail->head() + (signedResult ? signedResult->encodedContent() : bodyPart->encodedContent()); + delete bodyPart; + KMime::Message::Ptr resultMessage(new KMime::Message); + resultMessage->setContent(allData); + resultMessage->parse(); // Not strictly necessary. + + return resultMessage; } diff --git a/framework/src/domain/mime/mailtemplates.h b/framework/src/domain/mime/mailtemplates.h index 773ded05..a8a4b606 100644 --- a/framework/src/domain/mime/mailtemplates.h +++ b/framework/src/domain/mime/mailtemplates.h @@ -22,6 +22,7 @@ #include #include #include +#include struct Attachment { QString name; @@ -35,5 +36,5 @@ namespace MailTemplates { void reply(const KMime::Message::Ptr &origMsg, const std::function &callback); QString plaintextContent(const KMime::Message::Ptr &origMsg); - KMime::Message::Ptr createMessage(KMime::Message::Ptr existingMessage, const QStringList &to, const QStringList &cc, const QStringList &bcc, const KMime::Types::Mailbox &from, const QString &subject, const QString &body, const QList &attachments); + KMime::Message::Ptr createMessage(KMime::Message::Ptr existingMessage, const QStringList &to, const QStringList &cc, const QStringList &bcc, const KMime::Types::Mailbox &from, const QString &subject, const QString &body, const QList &attachments, const std::vector &signingKeys = {}); }; diff --git a/framework/src/domain/mime/tests/CMakeLists.txt b/framework/src/domain/mime/tests/CMakeLists.txt index 4fad00a1..f94447ff 100644 --- a/framework/src/domain/mime/tests/CMakeLists.txt +++ b/framework/src/domain/mime/tests/CMakeLists.txt @@ -8,8 +8,13 @@ include_directories( include(ECMAddTests) find_package(Qt5 REQUIRED NO_MODULE COMPONENTS Core Test WebEngine) -ecm_add_test(mailtemplatetest.cpp - TEST_NAME "mailtemplatetest" - NAME_PREFIX "" - LINK_LIBRARIES Qt5::Core Qt5::Test Qt5::WebEngine frameworkplugin +include( ${CMAKE_SOURCE_DIR}/cmake/modules/add_gpg_crypto_test.cmake ) + +add_executable(mailtemplatetest mailtemplatetest.cpp) +add_gpg_crypto_test(mailtemplatetest mailtemplatetest) +target_link_libraries(mailtemplatetest + Qt5::Core + Qt5::Test + Qt5::WebEngine + frameworkplugin ) diff --git a/framework/src/domain/mime/tests/mailtemplatetest.cpp b/framework/src/domain/mime/tests/mailtemplatetest.cpp index fbe5568d..62b17a6c 100644 --- a/framework/src/domain/mime/tests/mailtemplatetest.cpp +++ b/framework/src/domain/mime/tests/mailtemplatetest.cpp @@ -6,7 +6,52 @@ #include #include +#include +#include +#include +#include + #include "mailtemplates.h" +#include "mailcrypto.h" + +static std::vector< GpgME::Key, std::allocator< GpgME::Key > > getKeys(bool smime = false) +{ + QGpgME::KeyListJob *job = nullptr; + + if (smime) { + const QGpgME::Protocol *const backend = QGpgME::smime(); + Q_ASSERT(backend); + job = backend->keyListJob(false); + } else { + const QGpgME::Protocol *const backend = QGpgME::openpgp(); + Q_ASSERT(backend); + job = backend->keyListJob(false); + } + Q_ASSERT(job); + + std::vector< GpgME::Key > keys; + GpgME::KeyListResult res = job->exec(QStringList(), true, keys); + + if (!smime) { + Q_ASSERT(keys.size() == 3); + } + + Q_ASSERT(!res.error()); + + /* + qDebug() << "got private keys:" << keys.size(); + + for (std::vector< GpgME::Key >::iterator i = keys.begin(); i != keys.end(); ++i) { + qDebug() << "key isnull:" << i->isNull() << "isexpired:" << i->isExpired(); + qDebug() << "key numuserIds:" << i->numUserIDs(); + for (uint k = 0; k < i->numUserIDs(); ++k) { + qDebug() << "userIDs:" << i->userID(k).email(); + } + } + */ + + return keys; +} static QByteArray readMailFromFile(const QString &mailFile) { @@ -147,6 +192,29 @@ private slots: //1 Plain + 2 Attachments QCOMPARE(contents.size(), 3); } + + void testCreatePlainMailSigned() + { + QStringList to = {{"to@example.org"}}; + QStringList cc = {{"cc@example.org"}};; + QStringList bcc = {{"bcc@example.org"}};; + KMime::Types::Mailbox from; + from.fromUnicodeString("from@example.org"); + QString subject = "subject"; + QString body = "body"; + QList attachments; + + std::vector keys = getKeys(); + + auto result = MailTemplates::createMessage({}, to, cc, bcc, from, subject, body, attachments, keys); + + QVERIFY(result); + qWarning() << "---------------------------------"; + qWarning().noquote() << result->encodedContent(); + qWarning() << "---------------------------------"; + QCOMPARE(result->subject()->asUnicodeString(), subject); + QVERIFY(result->contentType()->isMimeType("multipart/signed")); + } }; QTEST_MAIN(MailTemplateTest) -- cgit v1.2.3