From 7ab399ce3b4b33439fe2607cf38e3f27a7d6008e Mon Sep 17 00:00:00 2001 From: Minijackson Date: Wed, 7 Mar 2018 17:30:50 +0100 Subject: Use Expected in crypto + pass attached key + add bool part of Expected + remove old code --- framework/src/domain/composercontroller.cpp | 11 +- framework/src/domain/mime/mailcrypto.cpp | 568 +++++++--------------------- framework/src/domain/mime/mailcrypto.h | 3 +- framework/src/domain/mime/mailtemplates.cpp | 9 +- framework/src/domain/mime/mailtemplates.h | 2 +- framework/src/errors.h | 46 ++- 6 files changed, 192 insertions(+), 447 deletions(-) diff --git a/framework/src/domain/composercontroller.cpp b/framework/src/domain/composercontroller.cpp index 2286a71b..37902164 100644 --- a/framework/src/domain/composercontroller.cpp +++ b/framework/src/domain/composercontroller.cpp @@ -463,18 +463,25 @@ KMime::Message::Ptr ComposerController::assembleMessage() }; }); + GpgME::Key attachedKey; std::vector signingKeys; if (getSign()) { signingKeys = getPersonalKeys().value>(); + Q_ASSERT(!signingKeys.empty()); + attachedKey = signingKeys[0]; } std::vector encryptionKeys; if (getEncrypt()) { //Encrypt to self so we can read the sent message - encryptionKeys += getPersonalKeys().value>(); + auto personalKeys = getPersonalKeys().value>(); + + attachedKey = personalKeys[0]; + + encryptionKeys += personalKeys; encryptionKeys += getRecipientKeys(); } - return MailTemplates::createMessage(mExistingMessage, toAddresses, ccAddresses, bccAddresses, getIdentity(), getSubject(), getBody(), getHtmlBody(), attachments, signingKeys, encryptionKeys); + return MailTemplates::createMessage(mExistingMessage, toAddresses, ccAddresses, bccAddresses, getIdentity(), getSubject(), getBody(), getHtmlBody(), attachments, signingKeys, encryptionKeys, attachedKey); } void ComposerController::send() diff --git a/framework/src/domain/mime/mailcrypto.cpp b/framework/src/domain/mime/mailcrypto.cpp index 4a26829f..f25c75fe 100644 --- a/framework/src/domain/mime/mailcrypto.cpp +++ b/framework/src/domain/mime/mailcrypto.cpp @@ -21,6 +21,8 @@ */ #include "mailcrypto.h" +#include "framework/src/errors.h" + #include #include #include @@ -41,119 +43,6 @@ #include #include -/* - * FIXME: - * - * This code is WIP. - * It currently only implements OpenPGPMIMEFormat for signing. - * All the commented code are intentional leftovers that we can clean-up - * once all necessary signing mechanisms have been implemented. - * - * Creating an ecrypted mail: - * * get keys (email -> fingreprint -> key) - * * Use Kleo::OpenPGPMIMEFormat, - * - */ - -// bool chooseCTE() -// { -// Q_Q(SinglepartJob); - -// auto allowed = KMime::encodingsForData(data); - -// if (!q->globalPart()->is8BitAllowed()) { -// allowed.removeAll(KMime::Headers::CE8Bit); -// } - -// #if 0 //TODO signing -// // In the following cases only QP and Base64 are allowed: -// // - the buffer will be OpenPGP/MIME signed and it contains trailing -// // whitespace (cf. RFC 3156) -// // - a line starts with "From " -// if ((willBeSigned && cf.hasTrailingWhitespace()) || -// cf.hasLeadingFrom()) { -// ret.removeAll(DwMime::kCte8bit); -// ret.removeAll(DwMime::kCte7bit); -// } -// #endif - -// if (contentTransferEncoding) { -// // Specific CTE set. Check that our data fits in it. -// if (!allowed.contains(contentTransferEncoding->encoding())) { -// q->setError(JobBase::BugError); -// q->setErrorText(i18n("%1 Content-Transfer-Encoding cannot correctly encode this message.", -// KMime::nameForEncoding(contentTransferEncoding->encoding()))); -// return false; -// // TODO improve error message in case 8bit is requested but not allowed. -// } -// } else { -// // No specific CTE set. Choose the best one. -// Q_ASSERT(!allowed.isEmpty()); -// contentTransferEncoding = new KMime::Headers::ContentTransferEncoding; -// contentTransferEncoding->setEncoding(allowed.first()); -// } -// qCDebug(MESSAGECOMPOSER_LOG) << "Settled on encoding" << KMime::nameForEncoding(contentTransferEncoding->encoding()); -// return true; -// } - -KMime::Content *createPart(const QByteArray &encodedBody, const QByteArray &mimeType, const QByteArray &charset) -{ - auto resultContent = new KMime::Content; - - auto contentType = new KMime::Headers::ContentType; - contentType->setMimeType(mimeType); - contentType->setCharset(charset); - // if (!chooseCTE()) { - // Q_ASSERT(error()); - // emitResult(); - // return; - // } - - // Set headers. - // if (contentDescription) { - // resultContent->setHeader(contentDescription); - // } - // if (contentDisposition) { - // resultContent->setHeader(contentDisposition); - // } - // if (contentID) { - // resultContent->setHeader(contentID); - // } - // Q_ASSERT(contentTransferEncoding); // chooseCTE() created it if it didn't exist. - auto contentTransferEncoding = new KMime::Headers::ContentTransferEncoding; - auto allowed = KMime::encodingsForData(encodedBody); - Q_ASSERT(!allowed.isEmpty()); - contentTransferEncoding->setEncoding(allowed.first()); - resultContent->setHeader(contentTransferEncoding); - - if (contentType) { - resultContent->setHeader(contentType); - } - - // Set data. - resultContent->setBody(encodedBody); - return resultContent; -} - -KMime::Content *setBodyAndCTE(QByteArray &encodedBody, KMime::Headers::ContentType *contentType, KMime::Content *ret) -{ - // MessageComposer::Composer composer; - // MessageComposer::SinglepartJob cteJob(&composer); - auto part = createPart(encodedBody, contentType->mimeType(), contentType->charset()); - part->assemble(); - - // cteJob.contentType()->setMimeType(contentType->mimeType()); - // cteJob.contentType()->setCharset(contentType->charset()); - // cteJob.setData(encodedBody); - // cteJob.exec(); - // cteJob.content()->assemble(); - - ret->contentTransferEncoding()->setEncoding(part->contentTransferEncoding()->encoding()); - ret->setBody(part->encodedBody()); - - return ret; -} - // replace simple LFs by CRLFs for all MIME supporting CryptPlugs // according to RfC 2633, 3.1.1 Canonicalization static QByteArray canonicalizeContent(KMime::Content *content) @@ -219,45 +108,61 @@ static QByteArray canonicalizeContent(KMime::Content *content) } +/** + * Get the given `key` in the armor format. + */ +Expected exportPublicKey(const GpgME::Key &key) +{ + // Not using the Qt API because it apparently blocks (the `result` signal is never + // triggered) + std::unique_ptr ctx(GpgME::Context::createForProtocol(GpgME::OpenPGP)); + ctx->setArmor(true); + + QGpgME::QByteArrayDataProvider dp; + GpgME::Data data(&dp); + + qDebug() << "Exporting public key:" << key.shortKeyID(); + auto error = ctx->exportPublicKeys(key.keyID(), data); + + if (error.code()) { + return makeUnexpected(error); + } + + return dp.data(); +} + /** * Create an Email with `msg` as a body and `key` as an attachment. * + * Will create the given structure: + * + * + `multipart/mixed` + * - the given `msg` + * - `application/pgp-keys` (the given `key` as attachment) + * * Used by the `createSignedEmail` and `createEncryptedEmail` functions. */ -KMime::Content *setMessageAndPublicKey(KMime::Content *msg, const GpgME::Key &key) +Expected appendPublicKey(KMime::Content *msg, const GpgME::Key &key) { + const auto publicKeyExportResult = exportPublicKey(key); + + if(!publicKeyExportResult) { + // "Could not export public key" + return makeUnexpected(publicKeyExportResult.error()); + } + + const auto publicKeyData = publicKeyExportResult.value(); + auto result = new KMime::Content; result->contentType()->setMimeType("multipart/mixed"); result->contentType()->setBoundary(KMime::multiPartBoundary()); KMime::Content *keyAttachment = new KMime::Content; { - // Not using the Qt API because it apparently blocks (the `result` signal is never - // triggered) - std::unique_ptr ctx(GpgME::Context::createForProtocol(GpgME::OpenPGP)); - ctx->setArmor(true); - - qDebug() << "Setting up data container"; - QGpgME::QByteArrayDataProvider dp; - GpgME::Data data(&dp); - - qDebug() << "Exporting public key"; - auto error = ctx->exportPublicKeys(key.keyID(), data); - - if (error.code()) { - qWarning() << "Could not export public key:" << error.asString(); - // TODO: XXX: handle errors better - // throw std::runtime_error("Export public key failed"); - delete result; - return msg; - } - keyAttachment->contentType()->setMimeType("application/pgp-keys"); keyAttachment->contentDisposition()->setDisposition(KMime::Headers::CDattachment); keyAttachment->contentDisposition()->setFilename(QString("0x") + key.shortKeyID() + ".asc"); - qDebug() << "Getting data"; - qWarning() << "Data:" << dp.data().constData(); - keyAttachment->setBody(dp.data()); + keyAttachment->setBody(publicKeyData); } msg->assemble(); @@ -272,7 +177,7 @@ KMime::Content *setMessageAndPublicKey(KMime::Content *msg, const GpgME::Key &ke return result; } -QByteArray encrypt(const QByteArray &content, const std::vector &encryptionKeys) +Expected encrypt(const QByteArray &content, const std::vector &encryptionKeys) { QByteArray resultData; @@ -282,15 +187,13 @@ QByteArray encrypt(const QByteArray &content, const std::vector &enc if (result.error().code()) { qWarning() << "Encryption failed:" << result.error().asString(); - // TODO: XXX: handle errors better - // throw std::runtime_error("Signing failed"); - return ""; + return makeUnexpected(result.error()); } return resultData; } -QByteArray signAndEncrypt(const QByteArray &content, const std::vector &signingKeys, +Expected signAndEncrypt(const QByteArray &content, const std::vector &signingKeys, const std::vector &encryptionKeys) { QByteArray resultData; @@ -301,26 +204,29 @@ QByteArray signAndEncrypt(const QByteArray &content, const std::vector &encryptionKeys, - bool sign, const std::vector &signingKeys = {}) +/** + * Create an encrypted (optionally signed) email with a public key attached to it. + * + * Will create a message like this: + * + * + `multipart/mixed` + * - `multipart/encrypted` + * + `application/pgp-encrypted + * + `application/octet-stream` (a generated encrypted version of the original message) + * - `application/pgp-keys` (the public key as attachment, which is the first of the + * `signingKeys`) + */ +Expected +createEncryptedEmail(KMime::Content *content, const std::vector &encryptionKeys, + const GpgME::Key &attachedKey, const std::vector &signingKeys = {}) { auto contentToEncrypt = canonicalizeContent(content); - auto encryptedData = sign ? encrypt(contentToEncrypt, encryptionKeys) : + auto encryptionResult = signingKeys.empty() ? + encrypt(contentToEncrypt, encryptionKeys) : signAndEncrypt(contentToEncrypt, signingKeys, encryptionKeys); + + if (!encryptionResult) { + return makeUnexpected(encryptionResult.error()); + } + + auto encryptedData = encryptionResult.value(); + KMime::Content *encryptedPart = createEncryptedPart(encryptedData); - // TODO: check signingKeys not empty - return setMessageAndPublicKey(encryptedPart, signingKeys[0]); + auto publicKeyAppendResult = appendPublicKey(encryptedPart, attachedKey); + + // TODO: this is ugly + if (!publicKeyAppendResult) { + delete encryptedPart; + } + + return publicKeyAppendResult; } -QByteArray sign(const QByteArray &content, const std::vector &signingKeys) +Expected sign(const QByteArray &content, const std::vector &signingKeys) { QByteArray resultData; @@ -381,20 +314,25 @@ QByteArray sign(const QByteArray &content, const std::vector &signin if (result.error().code()) { qWarning() << "Signing failed:" << result.error().asString(); - // TODO: XXX: handle errors better - // throw std::runtime_error("Signing failed"); - return ""; + return makeUnexpected(result.error()); } return resultData; } -// Create a message like this (according to RFC 3156 Section 5): -// -// - multipart/signed -// - whatever the given original message is (should be canonicalized) -// - application/octet-stream (given encrypted data) -KMime::Content *createSignedPart(KMime::Content *message, const QByteArray &signedData, const QString &micAlg) +/** + * Create a message like this (according to RFC 3156 Section 5): + * + * + `multipart/signed` + * - whatever the given original `message` is (should be canonicalized) + * - `application/octet-stream` (the given `signature`) + * + * Should not be used directly since the public key should be attached, hence + * the `createSignedEmail` function. + * + * The signature can be generated by the `sign` function. + */ +KMime::Content *createSignedPart(KMime::Content *message, const QByteArray &signature, const QString &micAlg) { KMime::Content *result = new KMime::Content; @@ -413,7 +351,7 @@ KMime::Content *createSignedPart(KMime::Content *message, const QByteArray &sign signedPartPart->contentDescription()->from7BitString( "This is a digitally signed message part"); - signedPartPart->setBody(signedData); + signedPartPart->setBody(signature); result->addContent(signedPartPart); } @@ -421,283 +359,64 @@ KMime::Content *createSignedPart(KMime::Content *message, const QByteArray &sign return result; } -KMime::Content *createSignedEmail(KMime::Content *content, const std::vector &signingKeys) +/** + * Create a signed email with a public key attached to it. + * + * Will create a message like this: + * + * + `multipart/mixed` + * - `multipart/signed` + * + whatever the given original `content` is (should not be canonalized) + * + `application/octet-stream` (a generated signature of the original message) + * - `application/pgp-keys` (the public key as attachment, which is the first of the + * `signingKeys`) + */ +Expected createSignedEmail(KMime::Content *content, + const std::vector &signingKeys, const GpgME::Key &attachedKey) { - auto contentToSign = canonicalizeContent(content); + Q_ASSERT(!signingKeys.empty()); - auto signedData = sign(contentToSign, signingKeys); - KMime::Content *signedPart = createSignedPart(content, signedData, "TODO: pgp-something"); - - // TODO: check signingKeys not empty - return setMessageAndPublicKey(signedPart, signingKeys[0]); -} - -void makeToplevelContentType(KMime::Content *content, bool encrypt, const QByteArray &hashAlgo) -{ - //Kleo::CryptoMessageFormat format, - // switch (format) { - // default: - // case Kleo::InlineOpenPGPFormat: - // case Kleo::OpenPGPMIMEFormat: - if (encrypt) { - content->contentType()->setMimeType(QByteArrayLiteral("multipart/encrypted")); - content->contentType()->setParameter(QStringLiteral("protocol"), QStringLiteral("application/pgp-encrypted")); - } else { - content->contentType()->setMimeType(QByteArrayLiteral("multipart/signed")); - content->contentType()->setParameter(QStringLiteral("protocol"), QStringLiteral("application/pgp-signature")); - content->contentType()->setParameter(QStringLiteral("micalg"), QString::fromLatin1(QByteArray(QByteArrayLiteral("pgp-") + hashAlgo)).toLower()); - } - return; - // case Kleo::SMIMEFormat: - // if (sign) { - // qCDebug(MESSAGECOMPOSER_LOG) << "setting headers for SMIME"; - // content->contentType()->setMimeType(QByteArrayLiteral("multipart/signed")); - // content->contentType()->setParameter(QStringLiteral("protocol"), QString::fromAscii("application/pkcs7-signature")); - // content->contentType()->setParameter(QStringLiteral("micalg"), QString::fromAscii(hashAlgo).toLower()); - // return; - // } - // // fall through (for encryption, there's no difference between - // // SMIME and SMIMEOpaque, since there is no mp/encrypted for - // // S/MIME) - // case Kleo::SMIMEOpaqueFormat: - - // qCDebug(MESSAGECOMPOSER_LOG) << "setting headers for SMIME/opaque"; - // content->contentType()->setMimeType(QByteArrayLiteral("application/pkcs7-mime")); - - // if (sign) { - // content->contentType()->setParameter(QStringLiteral("smime-type"), QString::fromAscii("signed-data")); - // } else { - // content->contentType()->setParameter(QStringLiteral("smime-type"), QString::fromAscii("enveloped-data")); - // } - // content->contentType()->setParameter(QStringLiteral("name"), QString::fromAscii("smime.p7m")); - // } -} + auto contentToSign = canonicalizeContent(content); -void setNestedContentType(KMime::Content *content, bool encrypt) -{ -// , Kleo::CryptoMessageFormat format - // switch (format) { - // case Kleo::OpenPGPMIMEFormat: - if (encrypt) { - content->contentType()->setMimeType(QByteArrayLiteral("application/octet-stream")); - } else { - content->contentType()->setMimeType(QByteArrayLiteral("application/pgp-signature")); - content->contentType()->setParameter(QStringLiteral("name"), QString::fromLatin1("signature.asc")); - content->contentDescription()->from7BitString("This is a digitally signed message part."); - } - return; - // case Kleo::SMIMEFormat: - // if (sign) { - // content->contentType()->setMimeType(QByteArrayLiteral("application/pkcs7-signature")); - // content->contentType()->setParameter(QStringLiteral("name"), QString::fromAscii("smime.p7s")); - // return; - // } - // // fall through: - // default: - // case Kleo::InlineOpenPGPFormat: - // case Kleo::SMIMEOpaqueFormat: - // ; - // } -} + auto signingResult = sign(contentToSign, signingKeys); -void setNestedContentDisposition(KMime::Content *content, bool encrypt) -{ -// Kleo::CryptoMessageFormat format, - // if (!sign && format & Kleo::OpenPGPMIMEFormat) { - if (encrypt) { - content->contentDisposition()->setDisposition(KMime::Headers::CDinline); - content->contentDisposition()->setFilename(QStringLiteral("msg.asc")); - // } else if (sign && format & Kleo::SMIMEFormat) { - // content->contentDisposition()->setDisposition(KMime::Headers::CDattachment); - // content->contentDisposition()->setFilename(QStringLiteral("smime.p7s")); + if (!signingResult) { + return makeUnexpected(signingResult.error()); } -} -// bool MessageComposer::Util::makeMultiMime(Kleo::CryptoMessageFormat format, bool sign) -// { -// switch (format) { -// default: -// case Kleo::InlineOpenPGPFormat: -// case Kleo::SMIMEOpaqueFormat: return false; -// case Kleo::OpenPGPMIMEFormat: return true; -// case Kleo::SMIMEFormat: return sign; // only on sign - there's no mp/encrypted for S/MIME -// } -// } - -KMime::Content *composeHeadersAndBody(KMime::Content *orig, QByteArray encodedBody, bool encrypt, const QByteArray &hashAlgo) -{ - // Kleo::CryptoMessageFormat format, - KMime::Content *result = new KMime::Content; + auto signedData = signingResult.value(); - // called should have tested that the signing/encryption failed - Q_ASSERT(!encodedBody.isEmpty()); - - // if (!(format & Kleo::InlineOpenPGPFormat)) { // make a MIME message - // qDebug() << "making MIME message, format:" << format; - makeToplevelContentType(result, encrypt, hashAlgo); - - // if (makeMultiMime(sign)) { // sign/enc PGPMime, sign SMIME - if (true) { // sign/enc PGPMime, sign SMIME - - const QByteArray boundary = KMime::multiPartBoundary(); - result->contentType()->setBoundary(boundary); - - result->assemble(); - //qCDebug(MESSAGECOMPOSER_LOG) << "processed header:" << result->head(); - - // Build the encapsulated MIME parts. - // Build a MIME part holding the code information - // taking the body contents returned in ciphertext. - KMime::Content *code = new KMime::Content; - setNestedContentType(code, encrypt); - setNestedContentDisposition(code, encrypt); - - if (encrypt) { // enc PGPMime (and / or sign) - - //addPublicKeyAsAttachment(content, signingKeys[0]); - - setBodyAndCTE(encodedBody, orig->contentType(), code); - - // Build a MIME part holding the version information - // taking the body contents returned in - // structuring.data.bodyTextVersion. - KMime::Content *vers = new KMime::Content; - vers->contentType()->setMimeType("application/pgp-encrypted"); - vers->contentDisposition()->setDisposition(KMime::Headers::CDattachment); - vers->contentTransferEncoding()->setEncoding(KMime::Headers::CE7Bit); - vers->setBody("Version: 1"); - - result->addContent(vers); - result->addContent(code); - } else { // sign PGPMime, sign SMIME - // if (format & Kleo::AnySMIME) { // sign SMIME - // code->contentTransferEncoding()->setEncoding(KMime::Headers::CEbase64); - // code->contentTransferEncoding()->needToEncode(); - // code->setBody(encodedBody); - // } else { // sign PGPMmime - setBodyAndCTE(encodedBody, orig->contentType(), code); - // } - result->addContent(orig); - result->addContent(code); - } - } else { //enc SMIME, sign/enc SMIMEOpaque - result->contentTransferEncoding()->setEncoding(KMime::Headers::CEbase64); - result->contentDisposition()->setDisposition(KMime::Headers::CDattachment); - result->contentDisposition()->setFilename(QStringLiteral("smime.p7m")); + KMime::Content *signedPart = createSignedPart(content, signedData, "TODO: pgp-something"); - result->assemble(); - //qCDebug(MESSAGECOMPOSER_LOG) << "processed header:" << result->head(); + auto publicKeyAppendResult = appendPublicKey(signedPart, attachedKey); - result->setBody(encodedBody); - } - // } else { // sign/enc PGPInline - // result->setHead(orig->head()); - // result->parse(); + // TODO: this is ugly (maybe use a std::unique_ptr for signedPart) + if (!publicKeyAppendResult) { + delete signedPart; + } - // // fixing ContentTransferEncoding - // setBodyAndCTE(encodedBody, orig->contentType(), result); - // } - result->assemble(); - return result; + return publicKeyAppendResult; } -// bool binaryHint(Kleo::CryptoMessageFormat f) -// { -// switch (f) { -// case Kleo::SMIMEFormat: -// case Kleo::SMIMEOpaqueFormat: -// return true; -// default: -// case Kleo::OpenPGPMIMEFormat: -// case Kleo::InlineOpenPGPFormat: -// return false; -// } -// } -// - // GpgME::SignatureMode signingMode(Kleo::CryptoMessageFormat f) - // { - // switch (f) { - // case Kleo::SMIMEOpaqueFormat: - // return GpgME::NormalSignatureMode; - // case Kleo::InlineOpenPGPFormat: - // return GpgME::Clearsigned; - // default: - // case Kleo::SMIMEFormat: - // case Kleo::OpenPGPMIMEFormat: - // return GpgME::Detached; - // } - // } - -KMime::Content *MailCrypto::processCrypto(KMime::Content *content, const std::vector &signingKeys, const std::vector &encryptionKeys, MailCrypto::Protocol protocol) +KMime::Content *MailCrypto::processCrypto(KMime::Content *content, + const std::vector &signingKeys, const std::vector &encryptionKeys, + const GpgME::Key &attachedKey, MailCrypto::Protocol protocol) { - const bool sign = !signingKeys.empty(); + qDebug() << "Attaching key:" << attachedKey.shortKeyID() << "from processCrypto"; if(!encryptionKeys.empty()) { - return createEncryptedEmail(content, encryptionKeys, sign, signingKeys); - } else if(sign) { - return createSignedEmail(content, signingKeys); + // TODO + return createEncryptedEmail(content, encryptionKeys, attachedKey, signingKeys).value(); + } else if(!signingKeys.empty()) { + // TODO + return createSignedEmail(content, signingKeys, signingKeys[0]).value(); } else { qWarning() << "Processing cryptography, but neither signing nor encrypting"; return content; } - - const QGpgME::Protocol *const proto = protocol == MailCrypto::SMIME ? QGpgME::smime() : QGpgME::openpgp(); - Q_ASSERT(proto); - - auto signingMode = GpgME::Detached; - bool armor = true; - bool textMode = false; - //const bool sign = !signingKeys.empty(); - const bool encrypt = !encryptionKeys.empty(); - - QByteArray resultContent; - QByteArray hashAlgo; - //Trust provided keys and don't check them for validity - bool alwaysTrust = true; - if (sign && encrypt) { - std::unique_ptr job(proto->signEncryptJob(armor, textMode)); - const auto res = job->exec(signingKeys, encryptionKeys, canonicalizeContent(content), alwaysTrust, resultContent); - if (res.first.error().code()) { - qWarning() << "Signing failed:" << res.first.error().asString(); - return nullptr; - } else { - hashAlgo = res.first.createdSignature(0).hashAlgorithmAsString(); - } - if (res.second.error().code()) { - qWarning() << "Encryption failed:" << res.second.error().asString(); - return nullptr; - } - } else if (sign) { - std::unique_ptr job(proto->signJob(armor, textMode)); - auto result = job->exec(signingKeys, canonicalizeContent(content), signingMode, resultContent); - if (result.error().code()) { - qWarning() << "Signing failed:" << result.error().asString(); - return nullptr; - } - hashAlgo = result.createdSignature(0).hashAlgorithmAsString(); - } else if (encrypt) { - std::unique_ptr job(proto->encryptJob(armor, textMode)); - const auto result = job->exec(encryptionKeys, canonicalizeContent(content), alwaysTrust, resultContent); - if (result.error().code()) { - qWarning() << "Encryption failed:" << result.error().asString(); - return nullptr; - } - hashAlgo = "pgp-sha1"; - } else { - qWarning() << "Not signing or encrypting"; - return nullptr; - } - - return composeHeadersAndBody(content, resultContent, encrypt, hashAlgo); -} - -KMime::Content *MailCrypto::sign(KMime::Content *content, const std::vector &signers) -{ - return processCrypto(content, signers, {}, OPENPGP); } - void MailCrypto::importKeys(const std::vector &keys) { const QGpgME::Protocol *const backend = QGpgME::openpgp(); @@ -753,4 +472,3 @@ std::vector MailCrypto::findKeys(const QStringList &filter, bool fin return keys; } - diff --git a/framework/src/domain/mime/mailcrypto.h b/framework/src/domain/mime/mailcrypto.h index 0a6c2f4c..724d6427 100644 --- a/framework/src/domain/mime/mailcrypto.h +++ b/framework/src/domain/mime/mailcrypto.h @@ -30,8 +30,7 @@ namespace MailCrypto OPENPGP, SMIME }; - KMime::Content *processCrypto(KMime::Content *content, const std::vector &signingKeys, const std::vector &encryptionKeys, MailCrypto::Protocol protocol); - KMime::Content *sign(KMime::Content *content, const std::vector &signers); + KMime::Content *processCrypto(KMime::Content *content, const std::vector &signingKeys, const std::vector &encryptionKeys, const GpgME::Key &attachedKey, MailCrypto::Protocol protocol); std::vector findKeys(const QStringList &filter, bool findPrivate = false, bool remote = false, Protocol protocol = OPENPGP); void importKeys(const std::vector &keys); }; diff --git a/framework/src/domain/mime/mailtemplates.cpp b/framework/src/domain/mime/mailtemplates.cpp index 30f9a48d..399b6aa1 100644 --- a/framework/src/domain/mime/mailtemplates.cpp +++ b/framework/src/domain/mime/mailtemplates.cpp @@ -1025,7 +1025,11 @@ static KMime::Types::Mailbox::List stringListToMailboxes(const QStringList &list return mailboxes; } -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, bool htmlBody, const QList &attachments, const std::vector &signingKeys, const std::vector &encryptionKeys) +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, bool htmlBody, + const QList &attachments, const std::vector &signingKeys, + const std::vector &encryptionKeys, const GpgME::Key &attachedKey) { auto mail = existingMessage; if (!mail) { @@ -1089,7 +1093,8 @@ KMime::Message::Ptr MailTemplates::createMessage(KMime::Message::Ptr existingMes QByteArray bodyData; if (!signingKeys.empty() || !encryptionKeys.empty()) { - auto result = MailCrypto::processCrypto(bodyPart.get(), signingKeys, encryptionKeys, MailCrypto::OPENPGP); + auto result = MailCrypto::processCrypto( + bodyPart.get(), signingKeys, encryptionKeys, attachedKey, MailCrypto::OPENPGP); if (!result) { qWarning() << "Signing failed"; return {}; diff --git a/framework/src/domain/mime/mailtemplates.h b/framework/src/domain/mime/mailtemplates.h index 9447e169..154b76a2 100644 --- a/framework/src/domain/mime/mailtemplates.h +++ b/framework/src/domain/mime/mailtemplates.h @@ -38,5 +38,5 @@ namespace MailTemplates void forward(const KMime::Message::Ptr &origMsg, const std::function &callback); QString plaintextContent(const KMime::Message::Ptr &origMsg); QString body(const KMime::Message::Ptr &msg, bool &isHtml); - 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, bool htmlBody, const QList &attachments, const std::vector &signingKeys = {}, const std::vector &encryptionKeys = {}); + 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, bool htmlBody, const QList &attachments, const std::vector &signingKeys = {}, const std::vector &encryptionKeys = {}, const GpgME::Key &attachedKey = {}); }; diff --git a/framework/src/errors.h b/framework/src/errors.h index f6c6cfab..9c48f32a 100644 --- a/framework/src/errors.h +++ b/framework/src/errors.h @@ -20,9 +20,13 @@ public: // For implicit conversions when doing makeUnexpected(other) template - constexpr explicit Unexpected(const Unexpected &error) : mValue(error.value()) {} + constexpr explicit Unexpected(const Unexpected &error) : mValue(error.value()) + { + } template - constexpr explicit Unexpected(Unexpected &&error) : mValue(std::move(error.value())) {} + constexpr explicit Unexpected(Unexpected &&error) : mValue(std::move(error.value())) + { + } constexpr const Error &value() const & { @@ -82,7 +86,7 @@ protected: constexpr void copyPartFromOther(const StorageBase &other) { - if (isValue) { + if (mIsValue) { mValue = other.mValue; } else { mError = other.mError; @@ -91,40 +95,40 @@ protected: void movePartFromOther(StorageBase &&other) { - if (isValue) { + if (mIsValue) { mValue = std::move(other.mValue); } else { mError = std::move(other.mError); } } - StorageBase(const StorageBase &other) : isValue(other.isValue) + StorageBase(const StorageBase &other) : mIsValue(other.mIsValue) { copyPartFromOther(other); } - StorageBase(StorageBase &&other) : isValue(other.isValue) + StorageBase(StorageBase &&other) : mIsValue(other.mIsValue) { movePartFromOther(std::move(other)); } constexpr StorageBase &operator=(const StorageBase &other) { - isValue = other.isValue; + mIsValue = other.mIsValue; copyPartFromOther(other); return *this; } constexpr StorageBase &operator=(StorageBase &&other) { - isValue = other.isValue; + mIsValue = other.mIsValue; movePartFromOther(other); return *this; } ~StorageBase() { - if (isValue) { + if (mIsValue) { mValue.~Type(); } else { mError.~Unexpected(); @@ -135,13 +139,13 @@ protected: template constexpr StorageBase(tags::Expected, Args &&... args) - : mValue(std::forward(args)...), isValue(true) + : mValue(std::forward(args)...), mIsValue(true) { } template constexpr StorageBase(tags::Unexpected, Args &&... args) - : mError(std::forward(args)...), isValue(false) + : mError(std::forward(args)...), mIsValue(false) { } @@ -150,7 +154,7 @@ protected: Unexpected mError; Type mValue; }; - bool isValue; + bool mIsValue; }; // Write functions here when storage related and when Type == void @@ -158,16 +162,16 @@ template struct StorageBase { protected: - constexpr StorageBase(tags::Expected) : isValue(true) {} + constexpr StorageBase(tags::Expected) : mIsValue(true) {} template constexpr StorageBase(tags::Unexpected, Args &&... args) - : mError(std::forward(args)...), isValue(false) + : mError(std::forward(args)...), mIsValue(false) { } Unexpected mError; - bool isValue; + bool mIsValue; }; // Write functions here when storage related, whether Type is void or not @@ -205,8 +209,11 @@ struct ExpectedBase : detail::Storage { } + // Warning: will crash if this is an error. You should always check this is + // an expected value before calling `.value()` constexpr const Type &value() const & { + Q_ASSERT(this->mIsValue); return this->mValue; } }; @@ -243,4 +250,13 @@ public: { return this->mError.value(); } + + constexpr bool isValue() const + { + return this->mIsValue; + } + constexpr explicit operator bool() const + { + return this->mIsValue; + } }; -- cgit v1.2.3