summaryrefslogtreecommitdiffstats
path: root/framework/src
diff options
context:
space:
mode:
Diffstat (limited to 'framework/src')
-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);