summaryrefslogtreecommitdiffstats
path: root/framework
diff options
context:
space:
mode:
authorChristian Mollekopf <chrigi_1@fastmail.fm>2018-05-05 10:39:32 +0200
committerChristian Mollekopf <chrigi_1@fastmail.fm>2018-05-06 17:21:01 +0200
commit01594e68275a09c67b5ee258e2af86598118a6a0 (patch)
tree4f859815f6455906bb656f9cc27ba5d6e4111599 /framework
parent481cb9f600caf3f45596bf78b5ba2bd07007969c (diff)
downloadkube-01594e68275a09c67b5ee258e2af86598118a6a0.tar.gz
kube-01594e68275a09c67b5ee258e2af86598118a6a0.zip
Port to gpgme only.
QGpgme and Gpgmepp are not readily available, the cmake files buggy, the buildsystem horrendous and generally just difficult to build on windows. Given that all they are is a wrapper around gpgme, we're better of without all the indirections. What we loose is: * QGpgme moved the work to separate threads (but we then blocked anyways), something that we can just do in our own code should we want to. * QGpgme has a function to prettify dn's that was used to show the signer. Also something we could bring back should we need to (don't know where it is useful atm.) Ported messagepart to gpgme Almost there Moved the crypto bits to a separate file All gpg code is in one place. All tests passing Use error codes Cleanup
Diffstat (limited to 'framework')
-rw-r--r--framework/src/CMakeLists.txt6
-rw-r--r--framework/src/domain/composercontroller.cpp34
-rw-r--r--framework/src/domain/composercontroller.h4
-rw-r--r--framework/src/domain/mime/CMakeLists.txt4
-rw-r--r--framework/src/domain/mime/attachmentmodel.cpp2
-rw-r--r--framework/src/domain/mime/crypto.cpp442
-rw-r--r--framework/src/domain/mime/crypto.h123
-rw-r--r--framework/src/domain/mime/mailcrypto.cpp192
-rw-r--r--framework/src/domain/mime/mailcrypto.h31
-rw-r--r--framework/src/domain/mime/mailtemplates.cpp4
-rw-r--r--framework/src/domain/mime/mailtemplates.h2
-rw-r--r--framework/src/domain/mime/mimetreeparser/CMakeLists.txt7
-rw-r--r--framework/src/domain/mime/mimetreeparser/autotests/CMakeLists.txt6
-rw-r--r--framework/src/domain/mime/mimetreeparser/messagepart.cpp283
-rw-r--r--framework/src/domain/mime/mimetreeparser/messagepart.h21
-rw-r--r--framework/src/domain/mime/mimetreeparser/partmetadata.h1
-rw-r--r--framework/src/domain/mime/mimetreeparser/tests/CMakeLists.txt5
-rw-r--r--framework/src/domain/mime/tests/mailtemplatetest.cpp4
18 files changed, 692 insertions, 479 deletions
diff --git a/framework/src/CMakeLists.txt b/framework/src/CMakeLists.txt
index e975e61e..a07bdb75 100644
--- a/framework/src/CMakeLists.txt
+++ b/framework/src/CMakeLists.txt
@@ -4,8 +4,7 @@ find_package(KF5Mime 4.87.0 CONFIG REQUIRED)
4find_package(KF5CalendarCore CONFIG REQUIRED) 4find_package(KF5CalendarCore CONFIG REQUIRED)
5find_package(Sink 0.6.0 CONFIG REQUIRED) 5find_package(Sink 0.6.0 CONFIG REQUIRED)
6find_package(KAsync CONFIG REQUIRED) 6find_package(KAsync CONFIG REQUIRED)
7find_package(QGpgme CONFIG REQUIRED) 7find_package(Gpgme REQUIRED)
8find_package(Gpgmepp CONFIG REQUIRED)
9find_package(KF5Codecs CONFIG REQUIRED) 8find_package(KF5Codecs CONFIG REQUIRED)
10find_package(KF5Contacts CONFIG REQUIRED) 9find_package(KF5Contacts CONFIG REQUIRED)
11 10
@@ -73,8 +72,7 @@ target_link_libraries(kubeframework
73 KF5::Codecs 72 KF5::Codecs
74 KF5::Contacts 73 KF5::Contacts
75 KAsync 74 KAsync
76 QGpgme 75 gpgme
77 Gpgmepp
78) 76)
79install(TARGETS kubeframework DESTINATION ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) 77install(TARGETS kubeframework DESTINATION ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})
80 78
diff --git a/framework/src/domain/composercontroller.cpp b/framework/src/domain/composercontroller.cpp
index fc436003..88c0c839 100644
--- a/framework/src/domain/composercontroller.cpp
+++ b/framework/src/domain/composercontroller.cpp
@@ -36,7 +36,7 @@
36#include "mime/mailcrypto.h" 36#include "mime/mailcrypto.h"
37#include "async.h" 37#include "async.h"
38 38
39std::vector<MailCrypto::Key> &operator+=(std::vector<MailCrypto::Key> &list, const std::vector<MailCrypto::Key> &add) 39std::vector<Crypto::Key> &operator+=(std::vector<Crypto::Key> &list, const std::vector<Crypto::Key> &add)
40{ 40{
41 list.insert(std::end(list), std::begin(add), std::end(add)); 41 list.insert(std::end(list), std::begin(add), std::end(add));
42 return list; 42 return list;
@@ -133,11 +133,11 @@ public:
133 mb.fromUnicodeString(addressee); 133 mb.fromUnicodeString(addressee);
134 134
135 SinkLog() << "Searching key for: " << mb.address(); 135 SinkLog() << "Searching key for: " << mb.address();
136 asyncRun<std::vector<MailCrypto::Key>>(this, 136 asyncRun<std::vector<Crypto::Key>>(this,
137 [mb] { 137 [mb] {
138 return MailCrypto::findKeys(QStringList{} << mb.address(), false, false); 138 return Crypto::findKeys(QStringList{} << mb.address(), false, false);
139 }, 139 },
140 [this, addressee, id](const std::vector<MailCrypto::Key> &keys) { 140 [this, addressee, id](const std::vector<Crypto::Key> &keys) {
141 if (!keys.empty()) { 141 if (!keys.empty()) {
142 if (keys.size() > 1) { 142 if (keys.size() > 1) {
143 SinkWarning() << "Found more than one key, encrypting to all of them."; 143 SinkWarning() << "Found more than one key, encrypting to all of them.";
@@ -227,10 +227,10 @@ void ComposerController::findPersonalKey()
227{ 227{
228 auto identity = getIdentity(); 228 auto identity = getIdentity();
229 SinkLog() << "Looking for personal key for: " << identity.address(); 229 SinkLog() << "Looking for personal key for: " << identity.address();
230 asyncRun<std::vector<MailCrypto::Key>>(this, [=] { 230 asyncRun<std::vector<Crypto::Key>>(this, [=] {
231 return MailCrypto::findKeys(QStringList{} << identity.address(), true); 231 return Crypto::findKeys(QStringList{} << identity.address(), true);
232 }, 232 },
233 [this](const std::vector<MailCrypto::Key> &keys) { 233 [this](const std::vector<Crypto::Key> &keys) {
234 if (keys.empty()) { 234 if (keys.empty()) {
235 SinkWarning() << "Failed to find a personal key."; 235 SinkWarning() << "Failed to find a personal key.";
236 } else if (keys.size() > 1) { 236 } else if (keys.size() > 1) {
@@ -419,23 +419,23 @@ void ComposerController::recordForAutocompletion(const QByteArray &addrSpec, con
419 } 419 }
420} 420}
421 421
422std::vector<MailCrypto::Key> ComposerController::getRecipientKeys() 422std::vector<Crypto::Key> ComposerController::getRecipientKeys()
423{ 423{
424 std::vector<MailCrypto::Key> keys; 424 std::vector<Crypto::Key> keys;
425 { 425 {
426 const auto list = toController()->getList<std::vector<MailCrypto::Key>>("key"); 426 const auto list = toController()->getList<std::vector<Crypto::Key>>("key");
427 for (const auto &l: list) { 427 for (const auto &l: list) {
428 keys.insert(std::end(keys), std::begin(l), std::end(l)); 428 keys.insert(std::end(keys), std::begin(l), std::end(l));
429 } 429 }
430 } 430 }
431 { 431 {
432 const auto list = ccController()->getList<std::vector<MailCrypto::Key>>("key"); 432 const auto list = ccController()->getList<std::vector<Crypto::Key>>("key");
433 for (const auto &l: list) { 433 for (const auto &l: list) {
434 keys.insert(std::end(keys), std::begin(l), std::end(l)); 434 keys.insert(std::end(keys), std::begin(l), std::end(l));
435 } 435 }
436 } 436 }
437 { 437 {
438 const auto list = bccController()->getList<std::vector<MailCrypto::Key>>("key"); 438 const auto list = bccController()->getList<std::vector<Crypto::Key>>("key");
439 for (const auto &l: list) { 439 for (const auto &l: list) {
440 keys.insert(std::end(keys), std::begin(l), std::end(l)); 440 keys.insert(std::end(keys), std::begin(l), std::end(l));
441 } 441 }
@@ -463,17 +463,17 @@ KMime::Message::Ptr ComposerController::assembleMessage()
463 }; 463 };
464 }); 464 });
465 465
466 MailCrypto::Key attachedKey; 466 Crypto::Key attachedKey;
467 std::vector<MailCrypto::Key> signingKeys; 467 std::vector<Crypto::Key> signingKeys;
468 if (getSign()) { 468 if (getSign()) {
469 signingKeys = getPersonalKeys().value<std::vector<MailCrypto::Key>>(); 469 signingKeys = getPersonalKeys().value<std::vector<Crypto::Key>>();
470 Q_ASSERT(!signingKeys.empty()); 470 Q_ASSERT(!signingKeys.empty());
471 attachedKey = signingKeys[0]; 471 attachedKey = signingKeys[0];
472 } 472 }
473 std::vector<MailCrypto::Key> encryptionKeys; 473 std::vector<Crypto::Key> encryptionKeys;
474 if (getEncrypt()) { 474 if (getEncrypt()) {
475 //Encrypt to self so we can read the sent message 475 //Encrypt to self so we can read the sent message
476 auto personalKeys = getPersonalKeys().value<std::vector<MailCrypto::Key>>(); 476 auto personalKeys = getPersonalKeys().value<std::vector<Crypto::Key>>();
477 477
478 attachedKey = personalKeys[0]; 478 attachedKey = personalKeys[0];
479 479
diff --git a/framework/src/domain/composercontroller.h b/framework/src/domain/composercontroller.h
index 8a831ed5..0d12fcce 100644
--- a/framework/src/domain/composercontroller.h
+++ b/framework/src/domain/composercontroller.h
@@ -65,7 +65,7 @@ class KUBE_EXPORT ComposerController : public Kube::Controller
65 KUBE_CONTROLLER_PROPERTY(KMime::Message::Ptr, ExistingMessage, existingMessage) 65 KUBE_CONTROLLER_PROPERTY(KMime::Message::Ptr, ExistingMessage, existingMessage)
66 KUBE_CONTROLLER_PROPERTY(Sink::ApplicationDomain::Mail, ExistingMail, existingMail) 66 KUBE_CONTROLLER_PROPERTY(Sink::ApplicationDomain::Mail, ExistingMail, existingMail)
67 67
68 KUBE_CONTROLLER_PROPERTY(/*std::vector<MailCrypto::Key>*/QVariant, PersonalKeys, personalKeys) 68 KUBE_CONTROLLER_PROPERTY(/*std::vector<Crypto::Key>*/QVariant, PersonalKeys, personalKeys)
69 KUBE_CONTROLLER_PROPERTY(bool, FoundPersonalKeys, foundPersonalKeys) 69 KUBE_CONTROLLER_PROPERTY(bool, FoundPersonalKeys, foundPersonalKeys)
70 70
71 KUBE_CONTROLLER_LISTCONTROLLER(to) 71 KUBE_CONTROLLER_LISTCONTROLLER(to)
@@ -109,7 +109,7 @@ private:
109 void setMessage(const QSharedPointer<KMime::Message> &msg); 109 void setMessage(const QSharedPointer<KMime::Message> &msg);
110 void addAttachmentPart(KMime::Content *partToAttach); 110 void addAttachmentPart(KMime::Content *partToAttach);
111 KMime::Message::Ptr assembleMessage(); 111 KMime::Message::Ptr assembleMessage();
112 std::vector<MailCrypto::Key> getRecipientKeys(); 112 std::vector<Crypto::Key> getRecipientKeys();
113 113
114 QScopedPointer<Completer> mRecipientCompleter; 114 QScopedPointer<Completer> mRecipientCompleter;
115 QScopedPointer<Selector> mIdentitySelector; 115 QScopedPointer<Selector> mIdentitySelector;
diff --git a/framework/src/domain/mime/CMakeLists.txt b/framework/src/domain/mime/CMakeLists.txt
index 1e55e461..9b4da136 100644
--- a/framework/src/domain/mime/CMakeLists.txt
+++ b/framework/src/domain/mime/CMakeLists.txt
@@ -1,9 +1,9 @@
1add_library(mailcrypto STATIC 1add_library(mailcrypto STATIC
2 mailcrypto.cpp 2 mailcrypto.cpp
3 crypto.cpp
3) 4)
4target_link_libraries(mailcrypto 5target_link_libraries(mailcrypto
5 Qt5::Core 6 Qt5::Core
6 KF5::Mime 7 KF5::Mime
7 QGpgme 8 gpgme
8 Gpgmepp
9) 9)
diff --git a/framework/src/domain/mime/attachmentmodel.cpp b/framework/src/domain/mime/attachmentmodel.cpp
index 45013f85..95a65d81 100644
--- a/framework/src/domain/mime/attachmentmodel.cpp
+++ b/framework/src/domain/mime/attachmentmodel.cpp
@@ -219,7 +219,7 @@ bool AttachmentModel::importPublicKey(const QModelIndex &index)
219 const auto part = static_cast<MimeTreeParser::MessagePart *>(index.internalPointer()); 219 const auto part = static_cast<MimeTreeParser::MessagePart *>(index.internalPointer());
220 Q_ASSERT(part); 220 Q_ASSERT(part);
221 auto pkey = part->node()->decodedContent(); 221 auto pkey = part->node()->decodedContent();
222 auto result = MailCrypto::importKey(pkey); 222 auto result = Crypto::importKey(pkey);
223 223
224 bool success = true; 224 bool success = true;
225 QString message; 225 QString message;
diff --git a/framework/src/domain/mime/crypto.cpp b/framework/src/domain/mime/crypto.cpp
new file mode 100644
index 00000000..9722dba8
--- /dev/null
+++ b/framework/src/domain/mime/crypto.cpp
@@ -0,0 +1,442 @@
1/*
2 Copyright (c) 2009 Constantin Berzan <exit3219@gmail.com>
3 Copyright (C) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
4 Copyright (c) 2010 Leo Franchi <lfranchi@kde.org>
5 Copyright (c) 2017 Christian Mollekopf <mollekopf@kolabsys.com>
6
7 This library is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Library General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or (at your
10 option) any later version.
11
12 This library is distributed in the hope that it will be useful, but WITHOUT
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
15 License for more details.
16
17 You should have received a copy of the GNU Library General Public License
18 along with this library; see the file COPYING.LIB. If not, write to the
19 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 02110-1301, USA.
21*/
22#include "crypto.h"
23
24#include "framework/src/errors.h"
25
26#include <gpgme.h>
27
28#include <QDebug>
29#include <QDateTime>
30
31#include <future>
32#include <utility>
33
34using namespace Crypto;
35
36QDebug operator<< (QDebug d, const Key &key)
37{
38 d << key.fingerprint;
39 return d;
40}
41
42QDebug operator<< (QDebug d, const Error &error)
43{
44 d << error.errorCode();
45 return d;
46}
47
48struct Data {
49 Data(const QByteArray &buffer)
50 {
51 const bool copy = false;
52 const gpgme_error_t e = gpgme_data_new_from_mem(&data, buffer.constData(), buffer.size(), int(copy));
53 if (e) {
54 qWarning() << "Failed to copy data?" << e;
55 }
56 }
57
58 ~Data()
59 {
60 gpgme_data_release(data);
61 }
62 gpgme_data_t data;
63};
64
65static gpgme_error_t checkEngine(CryptoProtocol protocol)
66{
67 gpgme_check_version(0);
68 const gpgme_protocol_t p = protocol == CMS ? GPGME_PROTOCOL_CMS : GPGME_PROTOCOL_OpenPGP;
69 return gpgme_engine_check_version(p);
70}
71
72static std::pair<gpgme_error_t, gpgme_ctx_t> createForProtocol(CryptoProtocol proto)
73{
74 if (auto e = checkEngine(proto)) {
75 qWarning() << "GPG Engine check failed." << e;
76 return std::make_pair(e, nullptr);
77 }
78 gpgme_ctx_t ctx = 0;
79 if (auto e = gpgme_new(&ctx)) {
80 return std::make_pair(e, nullptr);
81 }
82
83 switch (proto) {
84 case OpenPGP:
85 if (auto e = gpgme_set_protocol(ctx, GPGME_PROTOCOL_OpenPGP)) {
86 gpgme_release(ctx);
87 return std::make_pair(e, nullptr);
88 }
89 break;
90 case CMS:
91 if (auto e = gpgme_set_protocol(ctx, GPGME_PROTOCOL_CMS)) {
92 gpgme_release(ctx);
93 return std::make_pair(e, nullptr);
94 }
95 break;
96 default:
97 Q_ASSERT(false);
98 return std::make_pair(1, nullptr);
99 }
100 return std::make_pair(GPG_ERR_NO_ERROR, ctx);
101}
102
103
104struct Context {
105 Context(CryptoProtocol protocol = OpenPGP)
106 {
107 gpgme_error_t code;
108 std::tie(code, context) = createForProtocol(protocol);
109 error = Error{code};
110 }
111
112 ~Context()
113 {
114 gpgme_release(context);
115 }
116
117 operator bool() const
118 {
119 return !error;
120 }
121 Error error;
122 gpgme_ctx_t context;
123};
124
125
126static QByteArray toBA(gpgme_data_t out)
127{
128 size_t length = 0;
129 auto data = gpgme_data_release_and_get_mem (out, &length);
130 auto outdata = QByteArray{data, static_cast<int>(length)};
131 gpgme_free(data);
132 return outdata;
133}
134
135static std::vector<Recipient> copyRecipients(gpgme_decrypt_result_t result)
136{
137 std::vector<Recipient> recipients;
138 for (gpgme_recipient_t r = result->recipients ; r ; r = r->next) {
139 recipients.push_back({QByteArray{r->keyid}, {r->status}});
140 }
141 return recipients;
142}
143
144static std::vector<Signature> copySignatures(gpgme_verify_result_t result)
145{
146 std::vector<Signature> signatures;
147 for (gpgme_signature_t is = result->signatures ; is ; is = is->next) {
148 Signature sig;
149 sig.fingerprint = QByteArray{is->fpr};
150 sig.creationTime.setTime_t(is->timestamp);
151 sig.summary = is->summary;
152 sig.status = {is->status};
153 sig.validity = is->validity;
154 sig.validity_reason = is->validity_reason;
155 signatures.push_back(sig);
156 }
157 return signatures;
158}
159
160
161VerificationResult Crypto::verifyDetachedSignature(CryptoProtocol protocol, const QByteArray &signature, const QByteArray &text)
162{
163 Context context{protocol};
164 if (!context) {
165 qWarning() << "Failed to create context " << context.error;
166 return {{}, context.error};
167 }
168 auto ctx = context.context;
169
170 auto err = gpgme_op_verify(ctx, Data{signature}.data, Data{text}.data, 0);
171 gpgme_verify_result_t res = gpgme_op_verify_result(ctx);
172 return {copySignatures(res), {err}};
173}
174
175VerificationResult Crypto::verifyOpaqueSignature(CryptoProtocol protocol, const QByteArray &signature, QByteArray &outdata)
176{
177 Context context{protocol};
178 if (!context) {
179 qWarning() << "Failed to create context " << context.error;
180 return VerificationResult{{}, context.error};
181 }
182 auto ctx = context.context;
183
184 gpgme_data_t out;
185 const gpgme_error_t e = gpgme_data_new(&out);
186 Q_ASSERT(!e);
187 auto err = gpgme_op_verify(ctx, Data{signature}.data, 0, out);
188
189 VerificationResult result{{}, {err}};
190 if (gpgme_verify_result_t res = gpgme_op_verify_result(ctx)) {
191 result.signatures = copySignatures(res);
192 }
193
194 outdata = toBA(out);
195 return result;
196}
197
198std::pair<DecryptionResult,VerificationResult> Crypto::decryptAndVerify(CryptoProtocol protocol, const QByteArray &ciphertext, QByteArray &outdata)
199{
200 Context context{protocol};
201 if (!context) {
202 qWarning() << "Failed to create context " << context.error;
203 return std::make_pair(DecryptionResult{{}, context.error}, VerificationResult{{}, context.error});
204 }
205 auto ctx = context.context;
206
207 gpgme_data_t out;
208 if (gpgme_error_t e = gpgme_data_new(&out)) {
209 qWarning() << "Failed to allocated data" << e;
210 }
211 auto err = gpgme_op_decrypt_verify(ctx, Data{ciphertext}.data, out);
212 if (err) {
213 qWarning() << "Failed to decrypt and verify" << Error{err};
214 }
215
216 VerificationResult verificationResult{{}, {err}};
217 if (gpgme_verify_result_t res = gpgme_op_verify_result(ctx)) {
218 verificationResult.signatures = copySignatures(res);
219 }
220
221 DecryptionResult decryptionResult{{}, {err}};
222 if (gpgme_decrypt_result_t res = gpgme_op_decrypt_result(ctx)) {
223 decryptionResult.recipients = copyRecipients(res);
224 }
225
226 outdata = toBA(out);
227 return std::make_pair(decryptionResult, verificationResult);
228}
229
230ImportResult Crypto::importKeys(CryptoProtocol protocol, const QByteArray &certData)
231{
232 Context context{protocol};
233 if (!context) {
234 qWarning() << "Failed to create context " << context.error;
235 return {0, 0, 0};
236 }
237 if (auto err = gpgme_op_import(context.context, Data{certData}.data)) {
238 qWarning() << "Import failed";
239 return {0, 0, 0};
240 }
241 if (auto result = gpgme_op_import_result(context.context)) {
242 return {result->considered, result->imported, result->unchanged};
243 } else {
244 return {0, 0, 0};
245 }
246}
247
248static KeyListResult listKeys(CryptoProtocol protocol, const std::vector<const char*> &patterns, bool secretOnly, int keyListMode)
249{
250 Context context{protocol};
251 if (!context) {
252 qWarning() << "Failed to create context " << context.error;
253 return {{}, context.error};
254 }
255 auto ctx = context.context;
256
257 gpgme_set_keylist_mode(ctx, keyListMode);
258
259 KeyListResult result;
260 result.error = {GPG_ERR_NO_ERROR};
261 if (patterns.size() > 1) {
262 qWarning() << "Listing multiple patterns";
263 if (auto err = gpgme_op_keylist_ext_start(ctx, const_cast<const char **>(patterns.data()), int(secretOnly), 0)) {
264 qWarning() << "Error while listing keys";
265 result.error = {err};
266 }
267 } else if (patterns.size() == 1) {
268 qWarning() << "Listing one patterns " << patterns.data()[0];
269 if (auto err = gpgme_op_keylist_start(ctx, patterns.data()[0], int(secretOnly))) {
270 qWarning() << "Error while listing keys";
271 result.error = {err};
272 }
273 } else {
274 qWarning() << "Listing all";
275 if (auto err = gpgme_op_keylist_start(ctx, 0, int(secretOnly))) {
276 qWarning() << "Error while listing keys";
277 result.error = {err};
278 }
279 }
280
281
282 while (true) {
283 gpgme_key_t key;
284 if (auto e = gpgme_op_keylist_next(ctx, &key)) {
285 break;
286 }
287 Key k;
288 if (key->subkeys) {
289 k.keyId = QByteArray{key->subkeys->keyid};
290 k.shortKeyId = k.keyId.right(8);
291 k.fingerprint = QByteArray{key->subkeys->fpr};
292 }
293 for (gpgme_user_id_t uid = key->uids ; uid ; uid = uid->next) {
294 k.userIds.push_back(UserId{QByteArray{uid->name}, QByteArray{uid->email}, QByteArray{uid->uid}});
295 }
296 k.isExpired = key->expired;
297 result.keys.push_back(k);
298 }
299 gpgme_op_keylist_end(ctx);
300 return result;
301}
302
303/**
304 * Get the given `key` in the armor format.
305 */
306Expected<Error, QByteArray> Crypto::exportPublicKey(const Key &key)
307{
308 Context context;
309 if (!context) {
310 return makeUnexpected(Error{context.error});
311 }
312
313 gpgme_data_t out;
314 const gpgme_error_t e = gpgme_data_new(&out);
315 Q_ASSERT(!e);
316
317 qDebug() << "Exporting public key:" << key.keyId;
318 if (auto err = gpgme_op_export(context.context, key.keyId, 0, out)) {
319 return makeUnexpected(Error{err});
320 }
321
322 return toBA(out);
323}
324
325Expected<Error, QByteArray> Crypto::signAndEncrypt(const QByteArray &content, const std::vector<Key> &encryptionKeys, const std::vector<Key> &signingKeys)
326{
327 Context context;
328 if (!context) {
329 return makeUnexpected(Error{context.error});
330 }
331
332 for (const auto &signingKey : signingKeys) {
333 //TODO do we have to free those again?
334 gpgme_key_t key;
335 if (auto e = gpgme_get_key(context.context, signingKey.fingerprint, &key, /*secret*/ false)) {
336 qWarning() << "Failed to retrive signing key " << signingKey.fingerprint << e;
337 } else {
338 gpgme_signers_add(context.context, key);
339 }
340 }
341
342 gpgme_key_t * const keys = new gpgme_key_t[encryptionKeys.size() + 1];
343 gpgme_key_t * keys_it = keys;
344 for (const auto &k : encryptionKeys) {
345 gpgme_key_t key;
346 if (auto e = gpgme_get_key(context.context, k.fingerprint, &key, /*secret*/ false)) {
347 qWarning() << "Failed to retrive key " << k.fingerprint << e;
348 } else {
349 *keys_it++ = key;
350 }
351 }
352 *keys_it++ = 0;
353
354 gpgme_data_t out;
355 const gpgme_error_t e = gpgme_data_new(&out);
356 Q_ASSERT(!e);
357
358 gpgme_error_t err = !signingKeys.empty() ?
359 gpgme_op_encrypt_sign(context.context, keys, GPGME_ENCRYPT_ALWAYS_TRUST, Data{content}.data, out) :
360 gpgme_op_encrypt(context.context, keys, GPGME_ENCRYPT_ALWAYS_TRUST, Data{content}.data, out);
361 delete[] keys;
362 if (err) {
363 qWarning() << "Encryption failed:" << Error{err};
364 return makeUnexpected(Error{err});
365 }
366
367 return toBA(out);
368;
369}
370
371Expected<Error, std::pair<QByteArray, QString>>
372Crypto::sign(const QByteArray &content, const std::vector<Key> &signingKeys)
373{
374 Context context;
375 if (!context) {
376 return makeUnexpected(Error{context.error});
377 }
378
379 for (const auto &signingKey : signingKeys) {
380 //TODO do we have to free those again?
381 gpgme_key_t key;
382 if (auto e = gpgme_get_key(context.context, signingKey.fingerprint, &key, /*secret*/ false)) {
383 qWarning() << "Failed to retrive signing key " << signingKey.fingerprint << e;
384 } else {
385 gpgme_signers_add(context.context, key);
386 }
387 }
388
389 gpgme_data_t out;
390 const gpgme_error_t e = gpgme_data_new(&out);
391 Q_ASSERT(!e);
392
393 if (auto err = gpgme_op_sign(context.context, Data{content}.data, out, GPGME_SIG_MODE_DETACH)) {
394 qWarning() << "Signing failed:" << Error{err};
395 return makeUnexpected(Error{err});
396 }
397
398
399 const QByteArray algo = [&] {
400 if (gpgme_sign_result_t res = gpgme_op_sign_result(context.context)) {
401 for (gpgme_new_signature_t is = res->signatures ; is ; is = is->next) {
402 return QByteArray{gpgme_hash_algo_name(is->hash_algo)};
403 }
404 }
405 return QByteArray{};
406 }();
407 // RFC 3156 Section 5:
408 // Hash-symbols are constructed [...] by converting the text name to lower
409 // case and prefixing it with the four characters "pgp-".
410 const auto micAlg = (QString("pgp-") + algo).toLower();
411
412 return std::pair<QByteArray, QString>{toBA(out), micAlg};
413}
414
415ImportResult Crypto::importKey(const QByteArray &pkey)
416{
417 return importKeys(OpenPGP, pkey);
418}
419
420std::vector<Key> Crypto::findKeys(const QStringList &patterns, bool findPrivate, bool remote)
421{
422 QByteArrayList list;
423 std::transform(patterns.constBegin(), patterns.constEnd(), std::back_inserter(list), [] (const QString &s) { return s.toUtf8(); });
424 std::vector<char const *> pattern;
425 std::transform(list.constBegin(), list.constEnd(), std::back_inserter(pattern), [] (const QByteArray &s) { return s.constData(); });
426 pattern.push_back(0);
427
428 const KeyListResult res = listKeys(OpenPGP, pattern, findPrivate, remote ? GPGME_KEYLIST_MODE_EXTERN : GPGME_KEYLIST_MODE_LOCAL);
429 if (res.error) {
430 qWarning() << "Failed to lookup keys: " << res.error;
431 return {};
432 }
433 qDebug() << "got keys:" << res.keys.size();
434 for (const auto &key : res.keys) {
435 qDebug() << "isexpired:" << key.isExpired;
436 for (const auto &userId : key.userIds) {
437 qDebug() << "userID:" << userId.email;
438 }
439 }
440 return res.keys;
441}
442
diff --git a/framework/src/domain/mime/crypto.h b/framework/src/domain/mime/crypto.h
new file mode 100644
index 00000000..fa79785a
--- /dev/null
+++ b/framework/src/domain/mime/crypto.h
@@ -0,0 +1,123 @@
1/*
2 Copyright (c) 2016 Christian Mollekopf <mollekopf@kolabsys.com>
3
4 This library is free software; you can redistribute it and/or modify it
5 under the terms of the GNU Library General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or (at your
7 option) any later version.
8
9 This library is distributed in the hope that it will be useful, but WITHOUT
10 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
12 License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to the
16 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 02110-1301, USA.
18*/
19
20#pragma once
21
22#include "framework/src/errors.h"
23
24#include <QByteArray>
25#include <QVariant>
26
27#include <functional>
28#include <memory>
29#include <gpgme.h>
30#include <QDateTime>
31
32namespace Crypto {
33
34enum CryptoProtocol {
35 UnknownProtocol,
36 OpenPGP,
37 CMS
38};
39
40
41struct UserId {
42 QByteArray name;
43 QByteArray email;
44 QByteArray id;
45};
46
47struct Key {
48 QByteArray keyId;
49 QByteArray shortKeyId;
50 QByteArray fingerprint;
51 bool isExpired = false;
52 std::vector<UserId> userIds;
53};
54
55struct Error {
56 gpgme_error_t error;
57 gpgme_err_code_t errorCode() const {
58 return gpgme_err_code(error);
59 }
60 operator bool() const
61 {
62 return error != GPG_ERR_NO_ERROR;
63 }
64};
65
66struct Signature {
67 QByteArray fingerprint;
68 gpgme_sigsum_t summary;
69 Error status;
70 gpgme_validity_t validity;
71 gpgme_error_t validity_reason;
72 QDateTime creationTime;
73};
74
75struct VerificationResult {
76 std::vector<Signature> signatures;
77 Error error;
78};
79
80struct Recipient {
81 QByteArray keyId;
82 Error status;
83};
84
85struct DecryptionResult {
86 std::vector<Recipient> recipients;
87 Error error;
88};
89
90struct KeyListResult {
91 std::vector<Key> keys;
92 Error error;
93};
94
95
96std::vector<Key> findKeys(const QStringList &filter, bool findPrivate = false, bool remote = false);
97
98Expected<Error, QByteArray> exportPublicKey(const Key &key);
99struct ImportResult {
100 int considered;
101 int imported;
102 int unchanged;
103};
104ImportResult importKeys(CryptoProtocol protocol, const QByteArray &certData);
105ImportResult importKey(const QByteArray &key);
106
107/**
108 * Sign the given content and returns the signing data and the algorithm used
109 * for integrity check in the "pgp-<algorithm>" format.
110 */
111Expected<Error, std::pair<QByteArray, QString>>
112sign(const QByteArray &content, const std::vector<Key> &signingKeys);
113Expected<Error, QByteArray> signAndEncrypt(const QByteArray &content, const std::vector<Key> &encryptionKeys, const std::vector<Key> &signingKeys);
114
115std::pair<DecryptionResult,VerificationResult> decryptAndVerify(CryptoProtocol protocol, const QByteArray &ciphertext, QByteArray &outdata);
116VerificationResult verifyDetachedSignature(CryptoProtocol protocol, const QByteArray &signature, const QByteArray &outdata);
117VerificationResult verifyOpaqueSignature(CryptoProtocol protocol, const QByteArray &signature, QByteArray &outdata);
118};
119
120Q_DECLARE_METATYPE(Crypto::Key);
121
122QDebug operator<< (QDebug d, const Crypto::Key &);
123QDebug operator<< (QDebug d, const Crypto::Error &);
diff --git a/framework/src/domain/mime/mailcrypto.cpp b/framework/src/domain/mime/mailcrypto.cpp
index a7f3772a..e429a212 100644
--- a/framework/src/domain/mime/mailcrypto.cpp
+++ b/framework/src/domain/mime/mailcrypto.cpp
@@ -22,22 +22,7 @@
22#include "mailcrypto.h" 22#include "mailcrypto.h"
23 23
24#include "framework/src/errors.h" 24#include "framework/src/errors.h"
25 25#include "crypto.h"
26#include <QGpgME/DataProvider>
27#include <QGpgME/EncryptJob>
28#include <QGpgME/ExportJob>
29#include <QGpgME/ImportFromKeyserverJob>
30#include <QGpgME/ImportJob>
31#include <QGpgME/Protocol>
32#include <QGpgME/SignEncryptJob>
33#include <QGpgME/SignJob>
34
35#include <gpgme++/data.h>
36#include <gpgme++/encryptionresult.h>
37#include <gpgme++/global.h>
38#include <gpgme++/importresult.h>
39#include <gpgme++/keylistresult.h>
40#include <gpgme++/signingresult.h>
41 26
42#include <QDebug> 27#include <QDebug>
43 28
@@ -45,22 +30,8 @@
45#include <utility> 30#include <utility>
46 31
47using namespace MailCrypto; 32using namespace MailCrypto;
33using namespace Crypto;
48 34
49QDebug operator<< (QDebug d, const MailCrypto::Key &key)
50{
51 d << key.key.primaryFingerprint();
52 return d;
53}
54
55static std::vector<GpgME::Key> toGpgME(const std::vector<Key> keys)
56{
57 std::vector<GpgME::Key> list;
58 std::transform(keys.begin(), keys.end(), std::back_inserter(list), [] (const Key &key) { return key.key; });
59 return list;
60}
61
62// replace simple LFs by CRLFs for all MIME supporting CryptPlugs
63// according to RfC 2633, 3.1.1 Canonicalization
64static QByteArray canonicalizeContent(KMime::Content *content) 35static QByteArray canonicalizeContent(KMime::Content *content)
65{ 36{
66 // if (d->format & Kleo::InlineOpenPGPFormat) { 37 // if (d->format & Kleo::InlineOpenPGPFormat) {
@@ -125,29 +96,6 @@ static QByteArray canonicalizeContent(KMime::Content *content)
125} 96}
126 97
127/** 98/**
128 * Get the given `key` in the armor format.
129 */
130Expected<Error, QByteArray> exportPublicKey(const Key &key)
131{
132 // Not using the Qt API because it apparently blocks (the `result` signal is never
133 // triggered)
134 std::unique_ptr<GpgME::Context> ctx(GpgME::Context::createForProtocol(GpgME::OpenPGP));
135 ctx->setArmor(true);
136
137 QGpgME::QByteArrayDataProvider dp;
138 GpgME::Data data(&dp);
139
140 qDebug() << "Exporting public key:" << key.key.shortKeyID();
141 auto error = ctx->exportPublicKeys(key.key.keyID(), data);
142
143 if (error.code()) {
144 return makeUnexpected(Error{error});
145 }
146
147 return dp.data();
148}
149
150/**
151 * Create an Email with `msg` as a body and `key` as an attachment. 99 * Create an Email with `msg` as a body and `key` as an attachment.
152 * 100 *
153 * Will create the given structure: 101 * Will create the given structure:
@@ -178,7 +126,7 @@ appendPublicKey(std::unique_ptr<KMime::Content> msg, const Key &key)
178 { 126 {
179 keyAttachment->contentType()->setMimeType("application/pgp-keys"); 127 keyAttachment->contentType()->setMimeType("application/pgp-keys");
180 keyAttachment->contentDisposition()->setDisposition(KMime::Headers::CDattachment); 128 keyAttachment->contentDisposition()->setDisposition(KMime::Headers::CDattachment);
181 keyAttachment->contentDisposition()->setFilename(QString("0x") + key.key.shortKeyID() + ".asc"); 129 keyAttachment->contentDisposition()->setFilename(QString("0x") + key.shortKeyId + ".asc");
182 keyAttachment->setBody(publicKeyData); 130 keyAttachment->setBody(publicKeyData);
183 } 131 }
184 132
@@ -192,44 +140,6 @@ appendPublicKey(std::unique_ptr<KMime::Content> msg, const Key &key)
192 return result; 140 return result;
193} 141}
194 142
195Expected<Error, QByteArray> encrypt(const QByteArray &content, const std::vector<Key> &encryptionKeys)
196{
197 QByteArray resultData;
198
199 const QGpgME::Protocol *const proto = QGpgME::openpgp();
200 std::unique_ptr<QGpgME::EncryptJob> job(proto->encryptJob(/* armor = */ true));
201 const auto result = job->exec(toGpgME(encryptionKeys), content, /* alwaysTrust = */ true, resultData);
202
203 if (result.error().code()) {
204 qWarning() << "Encryption failed:" << result.error().asString();
205 return makeUnexpected(Error{result.error()});
206 }
207
208 return resultData;
209}
210
211Expected<Error, QByteArray> signAndEncrypt(const QByteArray &content,
212 const std::vector<Key> &signingKeys, const std::vector<Key> &encryptionKeys)
213{
214 QByteArray resultData;
215
216 const QGpgME::Protocol *const proto = QGpgME::openpgp();
217 std::unique_ptr<QGpgME::SignEncryptJob> job(proto->signEncryptJob(/* armor = */ true));
218 const auto result = job->exec(toGpgME(signingKeys), toGpgME(encryptionKeys), content, /* alwaysTrust = */ true, resultData);
219
220 if (result.first.error().code()) {
221 qWarning() << "Signing failed:" << result.first.error().asString();
222 return makeUnexpected(Error{result.first.error()});
223 }
224
225 if (result.second.error().code()) {
226 qWarning() << "Encryption failed:" << result.second.error().asString();
227 return makeUnexpected(Error{result.second.error()});
228 }
229
230 return resultData;
231}
232
233/** 143/**
234 * Create a message part like this (according to RFC 3156 Section 4): 144 * Create a message part like this (according to RFC 3156 Section 4):
235 * 145 *
@@ -295,11 +205,7 @@ Expected<Error, std::unique_ptr<KMime::Content>>
295createEncryptedEmail(KMime::Content *content, const std::vector<Key> &encryptionKeys, 205createEncryptedEmail(KMime::Content *content, const std::vector<Key> &encryptionKeys,
296 const Key &attachedKey, const std::vector<Key> &signingKeys = {}) 206 const Key &attachedKey, const std::vector<Key> &signingKeys = {})
297{ 207{
298 auto contentToEncrypt = canonicalizeContent(content); 208 auto encryptionResult = signAndEncrypt(canonicalizeContent(content), encryptionKeys, signingKeys);
299
300 auto encryptionResult = signingKeys.empty() ?
301 encrypt(contentToEncrypt, encryptionKeys) :
302 signAndEncrypt(contentToEncrypt, signingKeys, encryptionKeys);
303 209
304 if (!encryptionResult) { 210 if (!encryptionResult) {
305 return makeUnexpected(Error{encryptionResult.error()}); 211 return makeUnexpected(Error{encryptionResult.error()});
@@ -317,33 +223,6 @@ createEncryptedEmail(KMime::Content *content, const std::vector<Key> &encryption
317} 223}
318 224
319/** 225/**
320 * Sign the given content and returns the signing data and the algorithm used
321 * for integrity check in the "pgp-<algorithm>" format.
322 */
323Expected<Error, std::pair<QByteArray, QString>>
324sign(const QByteArray &content, const std::vector<Key> &signingKeys)
325{
326 QByteArray resultData;
327
328 const QGpgME::Protocol *const proto = QGpgME::openpgp();
329 std::unique_ptr<QGpgME::SignJob> job(proto->signJob(/* armor = */ true));
330 const auto result = job->exec(toGpgME(signingKeys), content, GpgME::Detached, resultData);
331
332 if (result.error().code()) {
333 qWarning() << "Signing failed:" << result.error().asString();
334 return makeUnexpected(Error{result.error()});
335 }
336
337 auto algo = result.createdSignature(0).hashAlgorithmAsString();
338 // RFC 3156 Section 5:
339 // Hash-symbols are constructed [...] by converting the text name to lower
340 // case and prefixing it with the four characters "pgp-".
341 auto micAlg = (QString("pgp-") + algo).toLower();
342
343 return std::pair<QByteArray, QString>{resultData, micAlg};
344}
345
346/**
347 * Create a message part like this (according to RFC 3156 Section 5): 226 * Create a message part like this (according to RFC 3156 Section 5):
348 * 227 *
349 * + `multipart/signed` 228 * + `multipart/signed`
@@ -438,66 +317,3 @@ MailCrypto::processCrypto(std::unique_ptr<KMime::Content> content, const std::ve
438 } 317 }
439} 318}
440 319
441void MailCrypto::importKeys(const std::vector<Key> &keys)
442{
443 const QGpgME::Protocol *const backend = QGpgME::openpgp();
444 Q_ASSERT(backend);
445 auto *job = backend->importFromKeyserverJob();
446 job->exec(toGpgME(keys));
447}
448
449MailCrypto::ImportResult MailCrypto::importKey(const QByteArray &pkey)
450{
451 const auto *proto = QGpgME::openpgp();
452 std::unique_ptr<QGpgME::ImportJob> job(proto->importJob());
453 auto result = job->exec(pkey);
454 return {result.numConsidered(), result.numImported(), result.numUnchanged()};
455}
456
457static GpgME::KeyListResult listKeys(const QStringList &patterns, bool secretOnly, int keyListMode, std::vector<Key> &keys)
458{
459 QByteArrayList list;
460 std::transform(patterns.constBegin(), patterns.constEnd(), std::back_inserter(list), [] (const QString &s) { return s.toUtf8(); });
461 std::vector<char const *> pattern;
462 std::transform(list.constBegin(), list.constEnd(), std::back_inserter(pattern), [] (const QByteArray &s) { return s.constData(); });
463 pattern.push_back(0);
464
465 GpgME::initializeLibrary();
466 auto ctx = QSharedPointer<GpgME::Context>{GpgME::Context::createForProtocol(GpgME::OpenPGP)};
467 ctx->setKeyListMode(keyListMode);
468 if (const GpgME::Error err = ctx->startKeyListing(pattern.data(), secretOnly)) {
469 return GpgME::KeyListResult(0, err);
470 }
471
472 GpgME::Error err;
473 do {
474 keys.push_back( Key{ctx->nextKey(err)});
475 } while ( !err );
476
477 keys.pop_back();
478
479 const GpgME::KeyListResult result = ctx->endKeyListing();
480 ctx->cancelPendingOperation();
481 return result;
482}
483
484std::vector<Key> MailCrypto::findKeys(const QStringList &filter, bool findPrivate, bool remote)
485{
486 std::vector<Key> keys;
487 GpgME::KeyListResult res = listKeys(filter, findPrivate, remote ? GpgME::Extern : GpgME::Local, keys);
488 if (res.error()) {
489 qWarning() << "Failed to lookup keys: " << res.error().asString();
490 return {};
491 }
492 qWarning() << "got keys:" << keys.size();
493
494 for (auto i = keys.begin(); i != keys.end(); ++i) {
495 qWarning() << "key isnull:" << i->key.isNull() << "isexpired:" << i->key.isExpired();
496 qWarning() << "key numuserIds:" << i->key.numUserIDs();
497 for (uint k = 0; k < i->key.numUserIDs(); ++k) {
498 qWarning() << "userIDs:" << i->key.userID(k).email();
499 }
500 }
501
502 return keys;
503}
diff --git a/framework/src/domain/mime/mailcrypto.h b/framework/src/domain/mime/mailcrypto.h
index c9247859..6f19063d 100644
--- a/framework/src/domain/mime/mailcrypto.h
+++ b/framework/src/domain/mime/mailcrypto.h
@@ -22,42 +22,19 @@
22#include "framework/src/errors.h" 22#include "framework/src/errors.h"
23 23
24#include <KMime/Message> 24#include <KMime/Message>
25#include <gpgme++/key.h>
26 25
27#include <QByteArray> 26#include <QByteArray>
28#include <QVariant> 27#include <QVariant>
29 28
30#include <functional> 29#include <functional>
31#include <memory> 30#include <memory>
31#include "crypto.h"
32 32
33namespace MailCrypto { 33namespace MailCrypto {
34 34
35struct Key { 35Expected<Crypto::Error, std::unique_ptr<KMime::Content>>
36 GpgME::Key key; 36processCrypto(std::unique_ptr<KMime::Content> content, const std::vector<Crypto::Key> &signingKeys,
37}; 37 const std::vector<Crypto::Key> &encryptionKeys, const Crypto::Key &attachedKey);
38
39struct Error {
40 GpgME::Error key;
41};
42
43Expected<Error, std::unique_ptr<KMime::Content>>
44processCrypto(std::unique_ptr<KMime::Content> content, const std::vector<Key> &signingKeys,
45 const std::vector<Key> &encryptionKeys, const Key &attachedKey);
46
47std::vector<Key> findKeys(const QStringList &filter, bool findPrivate = false, bool remote = false);
48
49void importKeys(const std::vector<Key> &keys);
50
51struct ImportResult {
52 int considered;
53 int imported;
54 int unchanged;
55};
56
57ImportResult importKey(const QByteArray &key);
58 38
59}; // namespace MailCrypto 39}; // namespace MailCrypto
60 40
61Q_DECLARE_METATYPE(MailCrypto::Key);
62
63QDebug operator<< (QDebug d, const MailCrypto::Key &);
diff --git a/framework/src/domain/mime/mailtemplates.cpp b/framework/src/domain/mime/mailtemplates.cpp
index ac2b2b72..c7ae481e 100644
--- a/framework/src/domain/mime/mailtemplates.cpp
+++ b/framework/src/domain/mime/mailtemplates.cpp
@@ -1027,8 +1027,8 @@ static KMime::Types::Mailbox::List stringListToMailboxes(const QStringList &list
1027KMime::Message::Ptr MailTemplates::createMessage(KMime::Message::Ptr existingMessage, 1027KMime::Message::Ptr MailTemplates::createMessage(KMime::Message::Ptr existingMessage,
1028 const QStringList &to, const QStringList &cc, const QStringList &bcc, 1028 const QStringList &to, const QStringList &cc, const QStringList &bcc,
1029 const KMime::Types::Mailbox &from, const QString &subject, const QString &body, bool htmlBody, 1029 const KMime::Types::Mailbox &from, const QString &subject, const QString &body, bool htmlBody,
1030 const QList<Attachment> &attachments, const std::vector<MailCrypto::Key> &signingKeys, 1030 const QList<Attachment> &attachments, const std::vector<Crypto::Key> &signingKeys,
1031 const std::vector<MailCrypto::Key> &encryptionKeys, const MailCrypto::Key &attachedKey) 1031 const std::vector<Crypto::Key> &encryptionKeys, const Crypto::Key &attachedKey)
1032{ 1032{
1033 auto mail = existingMessage; 1033 auto mail = existingMessage;
1034 if (!mail) { 1034 if (!mail) {
diff --git a/framework/src/domain/mime/mailtemplates.h b/framework/src/domain/mime/mailtemplates.h
index a894d120..0188120b 100644
--- a/framework/src/domain/mime/mailtemplates.h
+++ b/framework/src/domain/mime/mailtemplates.h
@@ -38,5 +38,5 @@ namespace MailTemplates
38 void forward(const KMime::Message::Ptr &origMsg, const std::function<void(const KMime::Message::Ptr &result)> &callback); 38 void forward(const KMime::Message::Ptr &origMsg, const std::function<void(const KMime::Message::Ptr &result)> &callback);
39 QString plaintextContent(const KMime::Message::Ptr &origMsg); 39 QString plaintextContent(const KMime::Message::Ptr &origMsg);
40 QString body(const KMime::Message::Ptr &msg, bool &isHtml); 40 QString body(const KMime::Message::Ptr &msg, bool &isHtml);
41 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<Attachment> &attachments, const std::vector<MailCrypto::Key> &signingKeys = {}, const std::vector<MailCrypto::Key> &encryptionKeys = {}, const MailCrypto::Key &attachedKey = {}); 41 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<Attachment> &attachments, const std::vector<Crypto::Key> &signingKeys = {}, const std::vector<Crypto::Key> &encryptionKeys = {}, const Crypto::Key &attachedKey = {});
42}; 42};
diff --git a/framework/src/domain/mime/mimetreeparser/CMakeLists.txt b/framework/src/domain/mime/mimetreeparser/CMakeLists.txt
index b35bd958..c3f317ee 100644
--- a/framework/src/domain/mime/mimetreeparser/CMakeLists.txt
+++ b/framework/src/domain/mime/mimetreeparser/CMakeLists.txt
@@ -2,9 +2,8 @@ set(CMAKE_CXX_VISIBILITY_PRESET default)
2 2
3find_package(Qt5 COMPONENTS REQUIRED Core Gui) 3find_package(Qt5 COMPONENTS REQUIRED Core Gui)
4find_package(KF5Mime 4.87.0 CONFIG REQUIRED) 4find_package(KF5Mime 4.87.0 CONFIG REQUIRED)
5find_package(QGpgme CONFIG REQUIRED)
6find_package(Gpgmepp CONFIG REQUIRED)
7find_package(KF5Codecs CONFIG REQUIRED) 5find_package(KF5Codecs CONFIG REQUIRED)
6find_package(Gpgme REQUIRED)
8 7
9# target_include_directories does not handle empty include paths 8# target_include_directories does not handle empty include paths
10include_directories(${GPGME_INCLUDES}) 9include_directories(${GPGME_INCLUDES})
@@ -49,8 +48,8 @@ target_link_libraries(kube_otp
49 48
50target_link_libraries(kube_otp 49target_link_libraries(kube_otp
51 PRIVATE 50 PRIVATE
52 QGpgme 51 gpgme
53 Gpgmepp 52 mailcrypto
54 KF5::Codecs 53 KF5::Codecs
55 Qt5::Gui 54 Qt5::Gui
56) 55)
diff --git a/framework/src/domain/mime/mimetreeparser/autotests/CMakeLists.txt b/framework/src/domain/mime/mimetreeparser/autotests/CMakeLists.txt
index f0b1f5f5..e67884e0 100644
--- a/framework/src/domain/mime/mimetreeparser/autotests/CMakeLists.txt
+++ b/framework/src/domain/mime/mimetreeparser/autotests/CMakeLists.txt
@@ -15,7 +15,7 @@ macro(add_mimetreeparser_unittest _source)
15 ecm_add_test(${_source} util.cpp setupenv.cpp 15 ecm_add_test(${_source} util.cpp setupenv.cpp
16 TEST_NAME ${_name} 16 TEST_NAME ${_name}
17 NAME_PREFIX "mimetreeparser-" 17 NAME_PREFIX "mimetreeparser-"
18 LINK_LIBRARIES kube_otp Qt5::Test KF5::Mime Gpgmepp 18 LINK_LIBRARIES kube_otp Qt5::Test KF5::Mime gpgme
19 ) 19 )
20endmacro () 20endmacro ()
21 21
@@ -24,7 +24,7 @@ macro(add_mimetreeparser_class_unittest _source _additionalSource)
24 ecm_add_test(${_source} ${_additionalSource} 24 ecm_add_test(${_source} ${_additionalSource}
25 TEST_NAME ${_name} 25 TEST_NAME ${_name}
26 NAME_PREFIX "mimetreeparser-" 26 NAME_PREFIX "mimetreeparser-"
27 LINK_LIBRARIES kube_otp Qt5::Test KF5::Mime Gpgmepp 27 LINK_LIBRARIES kube_otp Qt5::Test KF5::Mime gpgme
28 ) 28 )
29endmacro () 29endmacro ()
30 30
@@ -37,7 +37,7 @@ macro(add_mimetreeparser_crypto_unittest _source)
37 kube_otp 37 kube_otp
38 Qt5::Test 38 Qt5::Test
39 KF5::Mime 39 KF5::Mime
40 Gpgmepp 40 gpgme
41 ) 41 )
42 add_gpg_crypto_test(${_name} mimetreeparser-${_name}) 42 add_gpg_crypto_test(${_name} mimetreeparser-${_name})
43endmacro () 43endmacro ()
diff --git a/framework/src/domain/mime/mimetreeparser/messagepart.cpp b/framework/src/domain/mime/mimetreeparser/messagepart.cpp
index 2fd4f496..4bed80da 100644
--- a/framework/src/domain/mime/mimetreeparser/messagepart.cpp
+++ b/framework/src/domain/mime/mimetreeparser/messagepart.cpp
@@ -27,102 +27,12 @@
27 27
28#include <KMime/Content> 28#include <KMime/Content>
29 29
30#include <QGpgME/DN> 30#include <crypto.h>
31#include <QGpgME/DataProvider>
32
33#include <gpgme++/context.h>
34#include <gpgme++/data.h>
35#include <gpgme++/verificationresult.h>
36#include <gpgme++/key.h>
37#include <gpgme++/keylistresult.h>
38#include <gpgme++/decryptionresult.h>
39#include <gpgme++/importresult.h>
40#include <gpgme.h>
41
42 31
43#include <QTextCodec> 32#include <QTextCodec>
44#include <sstream>
45 33
46using namespace MimeTreeParser; 34using namespace MimeTreeParser;
47 35using namespace Crypto;
48static GpgME::Data fromBA(const QByteArray &ba)
49{
50 return {ba.data(), static_cast<size_t>(ba.size()), false};
51}
52
53
54static GpgME::Protocol toGpgMe(CryptoProtocol p)
55{
56 switch (p) {
57 case UnknownProtocol:
58 return GpgME::UnknownProtocol;
59 case CMS:
60 return GpgME::CMS;
61 case OpenPGP:
62 return GpgME::OpenPGP;
63 }
64 return GpgME::UnknownProtocol;
65}
66
67static QSharedPointer<GpgME::Context> gpgContext(CryptoProtocol protocol)
68{
69 GpgME::initializeLibrary();
70 auto error = GpgME::checkEngine(toGpgMe(protocol));
71 if (error) {
72 qWarning() << "Engine check failed: " << error.asString();
73 }
74 auto ctx = QSharedPointer<GpgME::Context>(GpgME::Context::createForProtocol(toGpgMe(protocol)));
75 Q_ASSERT(ctx);
76 return ctx;
77}
78
79static GpgME::VerificationResult verifyDetachedSignature(CryptoProtocol protocol, const QByteArray &signature, const QByteArray &text)
80{
81 return gpgContext(protocol)->verifyDetachedSignature(fromBA(signature), fromBA(text));
82}
83
84static GpgME::VerificationResult verifyOpaqueSignature(CryptoProtocol protocol, const QByteArray &signature, QByteArray &outdata)
85{
86 QGpgME::QByteArrayDataProvider out;
87 GpgME::Data wrapper(&out);
88 const auto result = gpgContext(protocol)->verifyOpaqueSignature(fromBA(signature), wrapper);
89 outdata = out.data();
90 return result;
91}
92
93
94static std::pair<GpgME::DecryptionResult,GpgME::VerificationResult> decryptAndVerify(CryptoProtocol protocol, const QByteArray &ciphertext, QByteArray &outdata)
95{
96 QGpgME::QByteArrayDataProvider out;
97 GpgME::Data wrapper(&out);
98 const std::pair<GpgME::DecryptionResult,GpgME::VerificationResult> res = gpgContext(protocol)->decryptAndVerify(fromBA(ciphertext), wrapper);
99 outdata = out.data();
100 return res;
101}
102
103static void importKeys(CryptoProtocol protocol, const QByteArray &certData)
104{
105 gpgContext(protocol)->importKeys(fromBA(certData));
106}
107
108static GpgME::KeyListResult listKeys(CryptoProtocol protocol, const char *pattern, bool secretOnly, std::vector<GpgME::Key> &keys) {
109 auto ctx = gpgContext(protocol);
110 if (const GpgME::Error err = ctx->startKeyListing(pattern, secretOnly)) {
111 return GpgME::KeyListResult( 0, err );
112 }
113
114 GpgME::Error err;
115 do {
116 keys.push_back( ctx->nextKey(err));
117 } while ( !err );
118
119 keys.pop_back();
120
121 const GpgME::KeyListResult result = ctx->endKeyListing();
122 ctx->cancelPendingOperation();
123 return result;
124}
125
126 36
127//------MessagePart----------------------- 37//------MessagePart-----------------------
128MessagePart::MessagePart(ObjectTreeParser *otp, const QString &text, KMime::Content *node) 38MessagePart::MessagePart(ObjectTreeParser *otp, const QString &text, KMime::Content *node)
@@ -771,10 +681,7 @@ SignedMessagePart::SignedMessagePart(ObjectTreeParser *otp,
771{ 681{
772 mMetaData.isSigned = true; 682 mMetaData.isSigned = true;
773 mMetaData.isGoodSignature = false; 683 mMetaData.isGoodSignature = false;
774 //FIXME
775 // mMetaData.keyTrust = GpgME::Signature::Unknown;
776 mMetaData.status = tr("Wrong Crypto Plug-In."); 684 mMetaData.status = tr("Wrong Crypto Plug-In.");
777 mMetaData.status_code = GPGME_SIG_STAT_NONE;
778} 685}
779 686
780SignedMessagePart::~SignedMessagePart() 687SignedMessagePart::~SignedMessagePart()
@@ -792,99 +699,65 @@ bool SignedMessagePart::isSigned() const
792 return mMetaData.isSigned; 699 return mMetaData.isSigned;
793} 700}
794 701
795static int signatureToStatus(const GpgME::Signature &sig)
796{
797 switch (sig.status().code()) {
798 case GPG_ERR_NO_ERROR:
799 return GPGME_SIG_STAT_GOOD;
800 case GPG_ERR_BAD_SIGNATURE:
801 return GPGME_SIG_STAT_BAD;
802 case GPG_ERR_NO_PUBKEY:
803 return GPGME_SIG_STAT_NOKEY;
804 case GPG_ERR_NO_DATA:
805 return GPGME_SIG_STAT_NOSIG;
806 case GPG_ERR_SIG_EXPIRED:
807 return GPGME_SIG_STAT_GOOD_EXP;
808 case GPG_ERR_KEY_EXPIRED:
809 return GPGME_SIG_STAT_GOOD_EXPKEY;
810 default:
811 return GPGME_SIG_STAT_ERROR;
812 }
813}
814 702
815QString prettifyDN(const char *uid) 703static QString prettifyDN(const char *uid)
816{ 704{
817 return QGpgME::DN(uid).prettyDN(); 705 // We used to use QGpgME::DN::prettyDN here. But I'm not sure what we actually need it for.
706 return QString::fromUtf8(uid);
818} 707}
819 708
820void SignedMessagePart::sigStatusToMetaData(const GpgME::Signature &signature) 709void SignedMessagePart::sigStatusToMetaData(const Signature &signature)
821{ 710{
822 GpgME::Key key; 711 mMetaData.isGoodSignature = signature.status & GPG_ERR_NO_ERROR;
823 mMetaData.status_code = signatureToStatus(signature);
824 mMetaData.isGoodSignature = mMetaData.status_code & GPGME_SIG_STAT_GOOD;
825 // save extended signature status flags 712 // save extended signature status flags
826 auto summary = signature.summary(); 713 auto summary = signature.summary;
827 mMetaData.keyMissing = summary & GpgME::Signature::KeyMissing; 714 mMetaData.keyMissing = summary & GPGME_SIGSUM_KEY_MISSING;
828 mMetaData.keyExpired = summary & GpgME::Signature::KeyExpired; 715 mMetaData.keyExpired = summary & GPGME_SIGSUM_KEY_EXPIRED;
829 mMetaData.keyRevoked = summary & GpgME::Signature::KeyRevoked; 716 mMetaData.keyRevoked = summary & GPGME_SIGSUM_KEY_REVOKED;
830 mMetaData.sigExpired = summary & GpgME::Signature::SigExpired; 717 mMetaData.sigExpired = summary & GPGME_SIGSUM_SIG_EXPIRED;
831 mMetaData.crlMissing = summary & GpgME::Signature::CrlMissing; 718 mMetaData.crlMissing = summary & GPGME_SIGSUM_CRL_MISSING;
832 mMetaData.crlTooOld = summary & GpgME::Signature::CrlTooOld; 719 mMetaData.crlTooOld = summary & GPGME_SIGSUM_CRL_TOO_OLD;
833 720
834 if (mMetaData.isGoodSignature && !key.keyID()) { 721 Key key;
722 if (mMetaData.isGoodSignature) {
835 // Search for the key by its fingerprint so that we can check for trust etc. 723 // Search for the key by its fingerprint so that we can check for trust etc.
836 std::vector<GpgME::Key> found_keys; 724 const auto keys = findKeys({signature.fingerprint});
837 auto res = listKeys(mProtocol, signature.fingerprint(), false, found_keys); 725 if (keys.size() > 1) {
838 if (res.error()) {
839 qCDebug(MIMETREEPARSER_LOG) << "Error while searching key for Fingerprint: " << signature.fingerprint();
840 }
841 if (found_keys.size() > 1) {
842 // Should not happen 726 // Should not happen
843 qCDebug(MIMETREEPARSER_LOG) << "Oops: Found more then one Key for Fingerprint: " << signature.fingerprint(); 727 qCDebug(MIMETREEPARSER_LOG) << "Oops: Found more then one Key for Fingerprint: " << signature.fingerprint;
844 } 728 }
845 if (found_keys.empty()) { 729 if (keys.empty()) {
846 // Should not happen at this point 730 // Should not happen at this point
847 qCWarning(MIMETREEPARSER_LOG) << "Oops: Found no Key for Fingerprint: " << signature.fingerprint(); 731 qCWarning(MIMETREEPARSER_LOG) << "Oops: Found no Key for Fingerprint: " << signature.fingerprint;
848 } else { 732 } else {
849 key = found_keys[0]; 733 key = keys[0];
850 } 734 }
851 } 735 }
852 736
853 if (key.keyID()) { 737 mMetaData.keyId = key.keyId;
854 mMetaData.keyId = key.keyID();
855 }
856 if (mMetaData.keyId.isEmpty()) { 738 if (mMetaData.keyId.isEmpty()) {
857 mMetaData.keyId = signature.fingerprint(); 739 mMetaData.keyId = signature.fingerprint;
858 } 740 }
859 auto keyTrust = signature.validity(); 741 mMetaData.keyIsTrusted = signature.validity == GPGME_VALIDITY_FULL || signature.validity == GPGME_VALIDITY_ULTIMATE;
860 mMetaData.keyIsTrusted = keyTrust & GpgME::Signature::Full || keyTrust & GpgME::Signature::Ultimate; 742 if (!key.userIds.empty()) {
861 if (key.numUserIDs() > 0 && key.userID(0).id()) { 743 mMetaData.signer = prettifyDN(key.userIds[0].id);
862 mMetaData.signer = prettifyDN(key.userID(0).id()); 744 }
863 } 745 for (const auto &userId : key.userIds) {
864 for (uint iMail = 0; iMail < key.numUserIDs(); ++iMail) { 746 QString email = QString::fromUtf8(userId.email);
865 // The following if /should/ always result in TRUE but we 747 // ### work around gpgme 0.3.QString text() const Q_DECL_OVERRIDE;x / cryptplug bug where the
866 // won't trust implicitely the plugin that gave us these data. 748 // ### email addresses are specified as angle-addr, not addr-spec:
867 if (key.userID(iMail).email()) { 749 if (email.startsWith(QLatin1Char('<')) && email.endsWith(QLatin1Char('>'))) {
868 QString email = QString::fromUtf8(key.userID(iMail).email()); 750 email = email.mid(1, email.length() - 2);
869 // ### work around gpgme 0.3.QString text() const Q_DECL_OVERRIDE;x / cryptplug bug where the 751 }
870 // ### email addresses are specified as angle-addr, not addr-spec: 752 if (!email.isEmpty()) {
871 if (email.startsWith(QLatin1Char('<')) && email.endsWith(QLatin1Char('>'))) { 753 mMetaData.signerMailAddresses.append(email);
872 email = email.mid(1, email.length() - 2);
873 }
874 if (!email.isEmpty()) {
875 mMetaData.signerMailAddresses.append(email);
876 }
877 } 754 }
878 } 755 }
879 756
880 if (signature.creationTime()) { 757 mMetaData.creationTime = signature.creationTime;
881 mMetaData.creationTime.setTime_t(signature.creationTime());
882 } else {
883 mMetaData.creationTime = QDateTime();
884 }
885 if (mMetaData.signer.isEmpty()) { 758 if (mMetaData.signer.isEmpty()) {
886 if (key.numUserIDs() > 0 && key.userID(0).name()) { 759 if (!key.userIds.empty()) {
887 mMetaData.signer = prettifyDN(key.userID(0).name()); 760 mMetaData.signer = prettifyDN(key.userIds[0].name);
888 } 761 }
889 if (!mMetaData.signerMailAddresses.empty()) { 762 if (!mMetaData.signerMailAddresses.empty()) {
890 if (mMetaData.signer.isEmpty()) { 763 if (mMetaData.signer.isEmpty()) {
@@ -924,18 +797,13 @@ void SignedMessagePart::startVerificationDetached(const QByteArray &text, KMime:
924 } 797 }
925 798
926 mMetaData.isSigned = false; 799 mMetaData.isSigned = false;
927 //FIXME
928 // mMetaData.keyTrust = GpgME::Signature::Unknown;
929 mMetaData.status = tr("Wrong Crypto Plug-In."); 800 mMetaData.status = tr("Wrong Crypto Plug-In.");
930 mMetaData.status_code = GPGME_SIG_STAT_NONE;
931 801
932 if (!signature.isEmpty()) { 802 if (!signature.isEmpty()) {
933 auto result = verifyDetachedSignature(mProtocol, signature, text); 803 setVerificationResult(verifyDetachedSignature(mProtocol, signature, text), false, text);
934 setVerificationResult(result, false, text);
935 } else { 804 } else {
936 QByteArray outdata; 805 QByteArray outdata;
937 auto result = verifyOpaqueSignature(mProtocol, text, outdata); 806 setVerificationResult(verifyOpaqueSignature(mProtocol, text, outdata), false, outdata);
938 setVerificationResult(result, false, outdata);
939 } 807 }
940 808
941 if (!mMetaData.isSigned) { 809 if (!mMetaData.isSigned) {
@@ -943,11 +811,11 @@ void SignedMessagePart::startVerificationDetached(const QByteArray &text, KMime:
943 } 811 }
944} 812}
945 813
946void SignedMessagePart::setVerificationResult(const GpgME::VerificationResult &result, bool parseText, const QByteArray &plainText) 814void SignedMessagePart::setVerificationResult(const VerificationResult &result, bool parseText, const QByteArray &plainText)
947{ 815{
948 auto signatures = result.signatures(); 816 auto signatures = result.signatures;
949 // FIXME 817 // FIXME
950 // mMetaData.auditLogError = result.error(); 818 // mMetaData.auditLogError = result.error;
951 if (!signatures.empty()) { 819 if (!signatures.empty()) {
952 mMetaData.isSigned = true; 820 mMetaData.isSigned = true;
953 sigStatusToMetaData(signatures.front()); 821 sigStatusToMetaData(signatures.front());
@@ -994,10 +862,7 @@ EncryptedMessagePart::EncryptedMessagePart(ObjectTreeParser *otp,
994 mMetaData.isGoodSignature = false; 862 mMetaData.isGoodSignature = false;
995 mMetaData.isEncrypted = false; 863 mMetaData.isEncrypted = false;
996 mMetaData.isDecryptable = false; 864 mMetaData.isDecryptable = false;
997 //FIXME
998 // mMetaData.keyTrust = GpgME::Signature::Unknown;
999 mMetaData.status = tr("Wrong Crypto Plug-In."); 865 mMetaData.status = tr("Wrong Crypto Plug-In.");
1000 mMetaData.status_code = GPGME_SIG_STAT_NONE;
1001} 866}
1002 867
1003EncryptedMessagePart::~EncryptedMessagePart() 868EncryptedMessagePart::~EncryptedMessagePart()
@@ -1055,59 +920,61 @@ bool EncryptedMessagePart::okDecryptMIME(KMime::Content &data)
1055 920
1056 const QByteArray ciphertext = data.decodedContent(); 921 const QByteArray ciphertext = data.decodedContent();
1057 QByteArray plainText; 922 QByteArray plainText;
1058 const auto res = decryptAndVerify(mProtocol, ciphertext, plainText); 923 DecryptionResult decryptResult;
1059 const GpgME::DecryptionResult &decryptResult = res.first; 924 VerificationResult verifyResult;
1060 const GpgME::VerificationResult &verifyResult = res.second; 925 std::tie(decryptResult, verifyResult) = decryptAndVerify(mProtocol, ciphertext, plainText);
1061 mMetaData.isSigned = verifyResult.signatures().size() > 0; 926 mMetaData.isSigned = verifyResult.signatures.size() > 0;
1062 927
1063 if (verifyResult.signatures().size() > 0) { 928 if (verifyResult.signatures.size() > 0) {
1064 //We simply attach a signed message part to indicate that this content is also signed 929 //We simply attach a signed message part to indicate that this content is also signed
1065 auto subPart = SignedMessagePart::Ptr(new SignedMessagePart(mOtp, QString::fromUtf8(plainText), mProtocol, mFromAddress, nullptr, nullptr)); 930 auto subPart = SignedMessagePart::Ptr(new SignedMessagePart(mOtp, QString::fromUtf8(plainText), mProtocol, mFromAddress, nullptr, nullptr));
1066 subPart->setVerificationResult(verifyResult, true, plainText); 931 subPart->setVerificationResult(verifyResult, true, plainText);
1067 appendSubPart(subPart); 932 appendSubPart(subPart);
1068 } 933 }
1069 934
1070 if (decryptResult.error() && mMetaData.isSigned) { 935 if (decryptResult.error && mMetaData.isSigned) {
1071 //Only a signed part 936 //Only a signed part
1072 mMetaData.isEncrypted = false; 937 mMetaData.isEncrypted = false;
1073 mDecryptedData = plainText; 938 mDecryptedData = plainText;
1074 return true; 939 return true;
1075 } 940 }
1076 941
1077 if (mMetaData.isEncrypted && decryptResult.numRecipients() > 0) { 942 if (mMetaData.isEncrypted && decryptResult.recipients.size()) {
1078 mMetaData.keyId = decryptResult.recipient(0).keyID(); 943 mMetaData.keyId = decryptResult.recipients.at(0).keyId;
1079 } 944 }
1080 945
1081 if (!decryptResult.error()) { 946 if (!decryptResult.error) {
1082 mDecryptedData = plainText; 947 mDecryptedData = plainText;
1083 setText(QString::fromUtf8(mDecryptedData.constData())); 948 setText(QString::fromUtf8(mDecryptedData.constData()));
1084 } else { 949 } else {
1085 mMetaData.isEncrypted = decryptResult.error().code() != GPG_ERR_NO_DATA; 950 const auto errorCode = decryptResult.error.errorCode();
1086 mMetaData.errorText = QString::fromLocal8Bit(decryptResult.error().asString()); 951 mMetaData.isEncrypted = errorCode != GPG_ERR_NO_DATA;
1087 952 qWarning() << "Failed to decrypt : " << decryptResult.error;
1088 std::stringstream ss; 953
1089 ss << decryptResult << '\n' << verifyResult; 954 const bool noSecretKeyAvilable = [&] {
1090 qWarning() << "Decryption failed: " << ss.str().c_str(); 955 foreach (const auto &recipient, decryptResult.recipients) {
1091 956 if (!(recipient.status.errorCode() == GPG_ERR_NO_SECKEY)) {
1092 bool passphraseError = decryptResult.error().isCanceled() || decryptResult.error().code() == GPG_ERR_NO_SECKEY; 957 return false;
1093 958 }
1094 auto noSecKey = true; 959 }
1095 foreach (const GpgME::DecryptionResult::Recipient &recipient, decryptResult.recipients()) { 960 return true;
1096 noSecKey &= (recipient.status().code() == GPG_ERR_NO_SECKEY); 961 }();
1097 } 962 bool passphraseError = errorCode == GPG_ERR_CANCELED || errorCode == GPG_ERR_NO_SECKEY;
1098 if (!passphraseError && !noSecKey) { // GpgME do not detect passphrase error correctly 963 //We only get a decryption failed error when we enter the wrong passphrase....
964 if (!passphraseError && !noSecretKeyAvilable) {
1099 passphraseError = true; 965 passphraseError = true;
1100 } 966 }
1101 967
1102 if(noSecKey) { 968 if(noSecretKeyAvilable) {
1103 mError = NoKeyError; 969 mError = NoKeyError;
1104 mMetaData.errorText = tr("Could not decrypt the data. ") + tr("No key found for recepients."); 970 mMetaData.errorText = tr("Could not decrypt the data. ") + tr("No key found for recepients.");
1105 } else if (passphraseError) { 971 } else if (passphraseError) {
1106 mError = PassphraseError; 972 mError = PassphraseError;
973 // mMetaData.errorText = QString::fromLocal8Bit(decryptResult.error().asString());
1107 } else { 974 } else {
1108 mError = UnknownError; 975 mError = UnknownError;
1109 mMetaData.errorText = tr("Could not decrypt the data. ") 976 mMetaData.errorText = tr("Could not decrypt the data. ");
1110 + tr("Error: %1").arg(mMetaData.errorText); 977 // + tr("Error: %1").arg(QString::fromLocal8Bit(decryptResult.error().asString()));
1111 } 978 }
1112 return false; 979 return false;
1113 } 980 }
diff --git a/framework/src/domain/mime/mimetreeparser/messagepart.h b/framework/src/domain/mime/mimetreeparser/messagepart.h
index 3c07ca88..c576699e 100644
--- a/framework/src/domain/mime/mimetreeparser/messagepart.h
+++ b/framework/src/domain/mime/mimetreeparser/messagepart.h
@@ -23,6 +23,7 @@
23#include "util.h" 23#include "util.h"
24#include "enums.h" 24#include "enums.h"
25#include "partmetadata.h" 25#include "partmetadata.h"
26#include <crypto.h>
26 27
27#include <KMime/Message> 28#include <KMime/Message>
28 29
@@ -32,13 +33,6 @@
32class QTextCodec; 33class QTextCodec;
33class PartPrivate; 34class PartPrivate;
34 35
35namespace GpgME
36{
37class ImportResult;
38class VerificationResult;
39class Signature;
40}
41
42namespace KMime 36namespace KMime
43{ 37{
44class Content; 38class Content;
@@ -55,11 +49,10 @@ class MultiPartAlternativeBodyPartFormatter;
55class SignedMessagePart; 49class SignedMessagePart;
56class EncryptedMessagePart; 50class EncryptedMessagePart;
57 51
58enum CryptoProtocol { 52using Crypto::CryptoProtocol;
59 UnknownProtocol, 53using Crypto::CryptoProtocol::CMS;
60 OpenPGP, 54using Crypto::CryptoProtocol::OpenPGP;
61 CMS 55using Crypto::CryptoProtocol::UnknownProtocol;
62};
63 56
64class MessagePart : public QObject 57class MessagePart : public QObject
65{ 58{
@@ -366,8 +359,8 @@ public:
366 QString htmlContent() const Q_DECL_OVERRIDE; 359 QString htmlContent() const Q_DECL_OVERRIDE;
367 360
368private: 361private:
369 void sigStatusToMetaData(const GpgME::Signature &signature); 362 void sigStatusToMetaData(const Crypto::Signature &signature);
370 void setVerificationResult(const GpgME::VerificationResult &result, bool parseText, const QByteArray &plainText); 363 void setVerificationResult(const Crypto::VerificationResult &result, bool parseText, const QByteArray &plainText);
371 364
372protected: 365protected:
373 CryptoProtocol mProtocol; 366 CryptoProtocol mProtocol;
diff --git a/framework/src/domain/mime/mimetreeparser/partmetadata.h b/framework/src/domain/mime/mimetreeparser/partmetadata.h
index 44a9cf7e..583eb454 100644
--- a/framework/src/domain/mime/mimetreeparser/partmetadata.h
+++ b/framework/src/domain/mime/mimetreeparser/partmetadata.h
@@ -37,7 +37,6 @@ public:
37 QByteArray keyId; 37 QByteArray keyId;
38 bool keyIsTrusted = false; 38 bool keyIsTrusted = false;
39 QString status; // to be used for unknown plug-ins 39 QString status; // to be used for unknown plug-ins
40 int status_code; // to be used for i18n of OpenPGP and S/MIME CryptPlugs
41 QString errorText; 40 QString errorText;
42 QDateTime creationTime; 41 QDateTime creationTime;
43 QString decryptionError; 42 QString decryptionError;
diff --git a/framework/src/domain/mime/mimetreeparser/tests/CMakeLists.txt b/framework/src/domain/mime/mimetreeparser/tests/CMakeLists.txt
index 9937ab06..fcb1988a 100644
--- a/framework/src/domain/mime/mimetreeparser/tests/CMakeLists.txt
+++ b/framework/src/domain/mime/mimetreeparser/tests/CMakeLists.txt
@@ -15,12 +15,11 @@ target_link_libraries(mimetreeparsertest
15 Qt5::Core 15 Qt5::Core
16 Qt5::Test 16 Qt5::Test
17 KF5::Mime 17 KF5::Mime
18 Gpgmepp 18 gpgme
19 QGpgme
20) 19)
21 20
22ecm_add_test(gpgerrortest.cpp 21ecm_add_test(gpgerrortest.cpp
23 TEST_NAME "gpgerrortest" 22 TEST_NAME "gpgerrortest"
24 NAME_PREFIX "mimetreeparser-" 23 NAME_PREFIX "mimetreeparser-"
25 LINK_LIBRARIES Qt5::Core Qt5::Test kube_otp Gpgmepp QGpgme 24 LINK_LIBRARIES Qt5::Core Qt5::Test kube_otp gpgme
26) 25)
diff --git a/framework/src/domain/mime/tests/mailtemplatetest.cpp b/framework/src/domain/mime/tests/mailtemplatetest.cpp
index 75debd75..20ea8c5e 100644
--- a/framework/src/domain/mime/tests/mailtemplatetest.cpp
+++ b/framework/src/domain/mime/tests/mailtemplatetest.cpp
@@ -356,7 +356,7 @@ private slots:
356 QString body = "body"; 356 QString body = "body";
357 QList<Attachment> attachments; 357 QList<Attachment> attachments;
358 358
359 auto keys = MailCrypto::findKeys({}, true, false); 359 auto keys = Crypto::findKeys({}, true, false);
360 auto result = MailTemplates::createMessage({}, to, cc, bcc, from, subject, body, false, attachments, keys, {}, keys[0]); 360 auto result = MailTemplates::createMessage({}, to, cc, bcc, from, subject, body, false, attachments, keys, {}, keys[0]);
361 361
362 QVERIFY(result); 362 QVERIFY(result);
@@ -398,7 +398,7 @@ private slots:
398 QString body = "body"; 398 QString body = "body";
399 QList<Attachment> attachments = {{"name", "filename", "mimetype", true, "inlineAttachment"}, {"name", "filename", "mimetype", false, "nonInlineAttachment"}}; 399 QList<Attachment> attachments = {{"name", "filename", "mimetype", true, "inlineAttachment"}, {"name", "filename", "mimetype", false, "nonInlineAttachment"}};
400 400
401 auto result = MailTemplates::createMessage({}, to, cc, bcc, from, subject, body, false, attachments, MailCrypto::findKeys({}, true, false)); 401 auto result = MailTemplates::createMessage({}, to, cc, bcc, from, subject, body, false, attachments, Crypto::findKeys({}, true, false));
402 402
403 QVERIFY(result); 403 QVERIFY(result);
404 QCOMPARE(result->subject()->asUnicodeString(), subject); 404 QCOMPARE(result->subject()->asUnicodeString(), subject);