diff --git a/src/filter/autotests/gpgdata/text-plain.msg b/src/filter/autotests/gpgdata/text-plain.msg index 4195616..6b411ca 100644 --- a/src/filter/autotests/gpgdata/text-plain.msg +++ b/src/filter/autotests/gpgdata/text-plain.msg @@ -1,8 +1,8 @@ -From: Daniel Vratil +From: "Daniel Vratil (test)" To: KMail Test Date: Tue, 01 Aug 2017 07:50:04 +0000 Subject: It's time to get schwifty! Content-Type: text/plain MIME-Version: 1.0 Show me what you got! diff --git a/src/filter/autotests/gpgdata/text-plain.msg.pgp b/src/filter/autotests/gpgdata/text-plain.msg.pgp index af8de48..f73924b 100644 --- a/src/filter/autotests/gpgdata/text-plain.msg.pgp +++ b/src/filter/autotests/gpgdata/text-plain.msg.pgp @@ -1,32 +1,32 @@ -From: Daniel Vratil +From: "Daniel Vratil (test)" To: KMail Test Date: Tue, 01 Aug 2017 07:50:04 +0000 Subject: It's time to get schwifty! Content-Type: multipart/encrypted; boundary="nextPart"; protocol="application/pgp-encrypted" MIME-Version: 1.0 --nextPart Content-Type: application/pgp-encrypted Content-Disposition: attachment Content-Transfer-Encoding: 7Bit Version: 1 --nextPart Content-Type: application/octet-stream Content-Disposition: inline; filename="msg.asc" Content-Transfer-Encoding: 7Bit -----BEGIN PGP MESSAGE----- hQEMA3IXuHpXwGcWAQf/dYk2TJpb6J6Ji9mA4RzMr5CUyiMkDlOe6+l4BVtRHsLm g9Bwe4znqCenyf/B6MdGThPAFvYRcN2UPbLEElrZQ5vsCyYAG8sespy3sVQChA1H 063pLvYZDxV/FdA2ckduaxQND9kNdfSONn7+olTaKgDiKA1oSgZ+dvRHG5cr/xQ+ AoYA/iQ65dT3MQyvWF1Iw+AKrbhR8XRfB77Hl+qL/vr62pbnVVA2rHjEu1Tjz04I 6oS55zoetonBVYiSIpQLRu5Uu0ys8jUY29qGgwd/VlBB2Y/YcYY3OTRbkvYBJ2Yd hc1ca4YCEaJ/9e8BuhLfBzh4J9EZgJ3NYRTm3FZRadJrAftlxAnsG1HmvE3kNV6Q zHWxlIQZQE+sSr7dbnpyObpHaiAaU1toaH22uKRh+9ZBhhiNYhCHk1KnkUKG7gdR Wys2qPAhuMVZryoW0uQyX4lA5ZjsetBSxUVffjjHZKYP56dyLzpvMGqWvK0= =DWvh -----END PGP MESSAGE----- --nextPart-- diff --git a/src/filter/autotests/gpgdata/text-plain.msg.smime b/src/filter/autotests/gpgdata/text-plain.msg.smime index 9704af2..47fa23d 100644 --- a/src/filter/autotests/gpgdata/text-plain.msg.smime +++ b/src/filter/autotests/gpgdata/text-plain.msg.smime @@ -1,21 +1,21 @@ -From: Daniel Vratil +From: "Daniel Vratil (test)" To: KMail Test Date: Tue, 01 Aug 2017 07:50:04 +0000 Subject: It's time to get schwifty! Content-Type: application/pkcs7-mime; name="smime.p7m"; smime-type="enveloped-data" Content-Transfer-Encoding: base64 Content-Disposition: attachment; filename="smime.p7m" MIME-Version: 1.0 MIAGCSqGSIb3DQEHA6CAMIACAQAxggFYMIIBVAIBADA8MDAxCzAJBgNVBAYTAkNa MQwwCgYDVQQKEwNLREUxEzARBgNVBAMTCktNYWlsIFRlc3QCCDilUKgKqCPGMA0G CSqGSIb3DQEBAQUABIIBAIGrbP6RHuRHdMhJpXuyRjzWPdApinxmUBtvPCxwfLK7 0ZHa11jAwJbIvouuqblReZX/eCYbBYsgW5aSFn1dSV/lUQ49gOTRHnRXEBrQGK4z gdNK+g+axqZO4zUtKyEDpKi7bnS2i/aCi5Q61PWFSkjgucrUuxyGP82iK5WQpcuk 0c7xR8cxaZf/tpQGnFRZ6OlUVLi2iSbv+ewbrZnaQgu3XAKQcJHrZlArgtR3cneA X8vpCDE5R/DpZ9onJLwd/KI1EaoEjeZoa6VMVm324uxG2vAaubvg9p4lBR0Q69hL MIpt8KGgz4psbAiE838U5jh77o5lxQmviolmuyEtZXswgAYJKoZIhvcNAQcBMB0G CWCGSAFlAwQBAgQQrA0/KpKc4c8gmw1PZUNJXqCABDAgX5OMUtnouELP2pOKz4TN pLHtscYX9+1XD4Rn+hqPOZ7aytiFOvpdn2qUqHtqhT8EEDumRT2M6mQ6rouGaXk7 DDAAAAAAAAAAAAAA diff --git a/src/util/cryptoutils.cpp b/src/util/cryptoutils.cpp index 44cfe0e..a56f560 100644 --- a/src/util/cryptoutils.cpp +++ b/src/util/cryptoutils.cpp @@ -1,208 +1,208 @@ /* * Copyright (c) 2017 Daniel Vrátil * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include "cryptoutils.h" #include "mailcommon_debug.h" #include #include #include #include #include #include #include using namespace MailCommon; bool CryptoUtils::isInlinePGP(const KMime::Content *part) { // Find if the message body starts with --BEGIN PGP MESSAGE-- - we can't just // use contains(), because that would also qualify messages that mention the // string, but are not actually encrypted const auto body = part->body(); for (auto c = body.cbegin(), end = body.cend(); c != end; ++c) { if (!c) { // huh? return false; // empty body -> not encrypted } // Is it a white space? Let's check next one if (isspace(*c)) { continue; } // First non-white space character in the body - if it's BEGIN PGP MESSAGE // then the message is encrypted, otherwise it's not. if (strncmp(c, "-----BEGIN PGP MESSAGE-----", sizeof("-----BEGIN PGP MESSAGE-----") - 1) == 0) { return true; } else { return false; } } return false; } bool CryptoUtils::isPGP(const KMime::Content *part, bool allowOctetStream) { const auto ct = static_cast(part->headerByType("Content-Type")); return ct && (ct->isSubtype("pgp-encrypted") || ct->isSubtype("encrypted") || (allowOctetStream && ct->isMimeType("application/octet-stream"))); } bool CryptoUtils::isSMIME(const KMime::Content *part) { const auto ct = static_cast(part->headerByType("Content-Type")); return ct && (ct->isSubtype("pkcs7-mime") || ct->isSubtype("x-pkcs7-mime")); } bool CryptoUtils::isEncrypted(const KMime::Message *msg) { // KMime::isEncrypted does not cover all cases - mostly only deals with // mime types. if (KMime::isEncrypted(const_cast(msg))) { return true; } return isInlinePGP(msg); } KMime::Message::Ptr CryptoUtils::decryptMessage(const KMime::Message::Ptr &msg, bool &wasEncrypted) { GpgME::Protocol protoName = GpgME::UnknownProtocol; bool inlinePGP = false; bool multipart = false; if (msg->contentType(false) && msg->contentType(false)->isMimeType("multipart/encrypted")) { multipart = true; const auto subparts = msg->contents(); for (auto subpart : subparts) { if (isPGP(subpart, true)) { protoName = GpgME::OpenPGP; break; } else if (isSMIME(subpart)) { protoName = GpgME::CMS; break; } } } else { if (isPGP(msg.data())) { protoName = GpgME::OpenPGP; } else if (isSMIME(msg.data())) { protoName = GpgME::CMS; } else if (isInlinePGP(msg.data())) { protoName = GpgME::OpenPGP; inlinePGP = true; } } if (protoName == GpgME::UnknownProtocol) { // Not encrypted, or we don't recognize the encryption wasEncrypted = false; return {}; } const auto proto = (protoName == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime(); wasEncrypted = true; QByteArray outData; auto inData = multipart ? msg->encodedContent() : msg->decodedContent(); // decodedContent in fact returns decoded body auto decrypt = proto->decryptJob(); #if GPGMEPP_VERSION_MAJOR > 1 || (GPGMEPP_VERSION_MAJOR == 1 && GPGMEPP_VERSION_MINOR >= 9) if (inlinePGP) { auto ctx = QGpgME::Job::context(decrypt); ctx->setDecryptionFlags(GpgME::Context::DecryptUnwrap); } #endif auto result = decrypt->exec(inData, outData); if (result.error()) { // unknown key, invalid algo, or general error qCWarning(MAILCOMMON_LOG) << "Failed to decrypt:" << result.error().asString(); return {}; } if (inlinePGP) { inData = outData; auto verify = proto->verifyOpaqueJob(true); auto result = verify->exec(inData, outData); if (result.error()) { qCWarning(MAILCOMMON_LOG) << "Failed to verify:" << result.error().asString(); return {}; } } KMime::Content decCt; if (inlinePGP) { decCt.setBody(KMime::CRLFtoLF(outData)); } else { decCt.setContent(KMime::CRLFtoLF(outData)); } decCt.parse(); decCt.assemble(); return assembleMessage(msg, &decCt); } void CryptoUtils::copyHeader(const KMime::Headers::Base *header, KMime::Message::Ptr msg) { auto newHdr = KMime::Headers::createHeader(header->type()); if (!newHdr) { newHdr = new KMime::Headers::Generic(header->type()); } - newHdr->fromUnicodeString(header->asUnicodeString(), "UTF-8"); + newHdr->from7BitString(header->as7BitString(false)); msg->appendHeader(newHdr); } bool CryptoUtils::isContentHeader(const KMime::Headers::Base *header) { return header->is("Content-Type") || header->is("Content-Transfer-Encoding") || header->is("Content-Disposition"); } KMime::Message::Ptr CryptoUtils::assembleMessage(const KMime::Message::Ptr &orig, const KMime::Content *newContent) { auto out = KMime::Message::Ptr::create(); // Use the new content as message content out->setBody(const_cast(newContent)->encodedBody()); out->parse(); // Copy over headers from the original message, except for CT, CTE and CD // headers, we want to preserve those from the new content QVector headers = orig->headers(); for (const auto hdr : qAsConst(headers)) { if (isContentHeader(hdr)) { continue; } copyHeader(hdr, out); } // Overwrite some headers by those provided by the new content headers = newContent->headers(); for (const auto hdr : qAsConst(headers)) { if (isContentHeader(hdr)) { copyHeader(hdr, out); } } out->assemble(); out->parse(); return out; }