summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cmake/modules/FindGpgme.cmake408
-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
19 files changed, 1100 insertions, 479 deletions
diff --git a/cmake/modules/FindGpgme.cmake b/cmake/modules/FindGpgme.cmake
new file mode 100644
index 00000000..a563c568
--- /dev/null
+++ b/cmake/modules/FindGpgme.cmake
@@ -0,0 +1,408 @@
1# Code is taken from KDE project:
2# http://code.metager.de/source/xref/kde/kdepimlibs/cmake/modules/FindGpgme.cmake
3#
4# - Try to find the gpgme library
5#
6# Algorithm:
7# - Windows:
8# On Windows, there's three gpgme variants: gpgme{,-glib,-qt}.
9# - The variant used determines the event loop integration possible:
10# - gpgme: no event loop integration possible, only synchronous operations supported
11# - gpgme-glib: glib event loop integration possible, only asynchronous operations supported
12# - gpgme-qt: qt event loop integration possible, only asynchronous operations supported
13# - GPGME_{VANILLA,GLIB,QT}_{FOUND,LIBRARIES} will be set for each of the above
14# - GPGME_INCLUDES is the same for all of the above
15# - GPGME_FOUND is set if any of the above was found
16# - *nix:
17# There's also three variants: gpgme{,-pthread,-pth}.
18# - The variant used determines the multithreaded use possible:
19# - gpgme: no multithreading support available
20# - gpgme-pthread: multithreading available using POSIX threads
21# - gpgme-pth: multithreading available using GNU PTH (cooperative multithreading)
22# - GPGME_{VANILLA,PTH,PTHREAD}_{FOUND,LIBRARIES} will be set for each of the above
23# - GPGME_INCLUDES is the same for all of the above
24# - GPGME_FOUND is set if any of the above was found
25#
26# GPGME_LIBRARY_DIR - the directory where the libraries are located
27
28#
29# THIS IS ALMOST A 1:1 COPY OF FindAssuan.cmake in kdepim.
30# Any changes here likely apply there, too.
31#
32
33# do away with crappy condition repetition on else/endfoo
34set( CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS_gpgme_saved ${CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS} )
35set( CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS true )
36
37#if this is built-in, please replace, if it isn't, export into a MacroToBool.cmake of it's own
38macro( macro_bool_to_bool FOUND_VAR )
39 foreach( _current_VAR ${ARGN} )
40 if ( ${FOUND_VAR} )
41 set( ${_current_VAR} TRUE )
42 else()
43 set( ${_current_VAR} FALSE )
44 endif()
45 endforeach()
46endmacro()
47
48#include (MacroEnsureVersion)
49
50
51
52if ( WIN32 )
53
54 # On Windows, we don't have a gpgme-config script, so we need to
55 # look for the stuff ourselves:
56
57 # in cmake, AND and OR have the same precedence, there's no
58 # subexpressions, and expressions are evaluated short-circuit'ed
59 # IOW: CMake if() suxx.
60 # Starting with CMake 2.6.3 you can group if expressions with (), but we
61 # don't require 2.6.3 but 2.6.2, we can't use it. Alex
62 set( _seem_to_have_cached_gpgme false )
63 if ( GPGME_INCLUDES )
64 if ( GPGME_VANILLA_LIBRARIES OR GPGME_QT_LIBRARIES OR GPGME_GLIB_LIBRARIES )
65 set( _seem_to_have_cached_gpgme true )
66 endif()
67 endif()
68
69 if ( _seem_to_have_cached_gpgme )
70
71 macro_bool_to_bool( GPGME_VANILLA_LIBRARIES GPGME_VANILLA_FOUND )
72 macro_bool_to_bool( GPGME_GLIB_LIBRARIES GPGME_GLIB_FOUND )
73 macro_bool_to_bool( GPGME_QT_LIBRARIES GPGME_QT_FOUND )
74 # this would have been preferred:
75 #set( GPGME_*_FOUND macro_bool_to_bool(GPGME_*_LIBRARIES) )
76
77 if ( GPGME_VANILLA_FOUND OR GPGME_GLIB_FOUND OR GPGME_QT_FOUND )
78 set( GPGME_FOUND true )
79 else()
80 set( GPGME_FOUND false )
81 endif()
82
83 else()
84
85 # is this needed, of just unreflected cut'n'paste?
86 # this isn't a KDE library, after all!
87 if( NOT KDEWIN_FOUND )
88 find_package( KDEWIN REQUIRED )
89 endif()
90
91 set( GPGME_FOUND false )
92 set( GPGME_VANILLA_FOUND false )
93 set( GPGME_GLIB_FOUND false )
94 set( GPGME_QT_FOUND false )
95
96 find_path( GPGME_INCLUDES gpgme.h
97 ${CMAKE_INCLUDE_PATH}
98 ${CMAKE_INSTALL_PREFIX}/include
99 )
100
101 if (NOT WINCE)
102 find_library( _gpgme_vanilla_library NAMES gpgme libgpgme gpgme-11 libgpgme-11
103 PATHS
104 ${CMAKE_LIBRARY_PATH}
105 ${CMAKE_INSTALL_PREFIX}/lib
106 )
107 else (NOT WINCE)
108 find_library( _gpgme_vanilla_library NAMES libgpgme-11-msc
109 PATHS
110 ${CMAKE_LIBRARY_PATH}
111 ${CMAKE_INSTALL_PREFIX}/lib
112 )
113 endif()
114
115 find_library( _gpgme_glib_library NAMES gpgme-glib libgpgme-glib gpgme-glib-11 libgpgme-glib-11
116 PATHS
117 ${CMAKE_LIBRARY_PATH}
118 ${CMAKE_INSTALL_PREFIX}/lib
119 )
120
121 find_library( _gpgme_qt_library NAMES gpgme-qt libgpgme-qt gpgme-qt-11 libgpgme-qt-11
122 PATHS
123 ${CMAKE_LIBRARY_PATH}
124 ${CMAKE_INSTALL_PREFIX}/lib
125 )
126
127 if ( WINCE )
128 set( _gpg_error_library )
129 else()
130 find_library( _gpg_error_library NAMES gpg-error libgpg-error gpg-error-0 libgpg-error-0
131 PATHS
132 ${CMAKE_LIBRARY_PATH}
133 ${CMAKE_INSTALL_PREFIX}/lib
134 )
135 endif()
136
137 set( GPGME_INCLUDES ${GPGME_INCLUDES} )
138
139 if ( _gpgme_vanilla_library AND ( _gpg_error_library OR WINCE ) )
140 set( GPGME_VANILLA_LIBRARIES ${_gpgme_vanilla_library} ${_gpg_error_library} )
141 set( GPGME_VANILLA_FOUND true )
142 set( GPGME_FOUND true )
143 endif()
144
145 if ( _gpgme_glib_library AND ( _gpg_error_library OR WINCE ) )
146 set( GPGME_GLIB_LIBRARIES ${_gpgme_glib_library} ${_gpg_error_library} )
147 set( GPGME_GLIB_FOUND true )
148 set( GPGME_FOUND true )
149 endif()
150
151 if ( _gpgme_qt_library AND ( _gpg_error_library OR WINCE ) )
152 set( GPGME_QT_LIBRARIES ${_gpgme_qt_library} ${_gpg_error_library} )
153 set( GPGME_QT_FOUND true )
154 set( GPGME_FOUND true )
155 endif()
156
157 endif()
158
159 # these are Unix-only:
160 set( GPGME_PTHREAD_FOUND false )
161 set( GPGME_PTH_FOUND false )
162 set( HAVE_GPGME_PTHREAD 0 )
163 set( HAVE_GPGME_PTH 0 )
164
165 macro_bool_to_01( GPGME_FOUND HAVE_GPGME )
166 macro_bool_to_01( GPGME_VANILLA_FOUND HAVE_GPGME_VANILLA )
167 macro_bool_to_01( GPGME_GLIB_FOUND HAVE_GPGME_GLIB )
168 macro_bool_to_01( GPGME_QT_FOUND HAVE_GPGME_QT )
169
170else() # not WIN32
171
172 # On *nix, we have the gpgme-config script which can tell us all we
173 # need to know:
174
175 # see WIN32 case for an explanation of what this does:
176 set( _seem_to_have_cached_gpgme false )
177 if ( GPGME_INCLUDES )
178 if ( GPGME_VANILLA_LIBRARIES OR GPGME_PTHREAD_LIBRARIES OR GPGME_PTH_LIBRARIES )
179 set( _seem_to_have_cached_gpgme true )
180 endif()
181 endif()
182
183 if ( _seem_to_have_cached_gpgme )
184
185 macro_bool_to_bool( GPGME_VANILLA_LIBRARIES GPGME_VANILLA_FOUND )
186 macro_bool_to_bool( GPGME_PTHREAD_LIBRARIES GPGME_PTHREAD_FOUND )
187 macro_bool_to_bool( GPGME_PTH_LIBRARIES GPGME_PTH_FOUND )
188
189 if ( GPGME_VANILLA_FOUND OR GPGME_PTHREAD_FOUND OR GPGME_PTH_FOUND )
190 set( GPGME_FOUND true )
191 else()
192 set( GPGME_FOUND false )
193 endif()
194
195 else()
196
197 set( GPGME_FOUND false )
198 set( GPGME_VANILLA_FOUND false )
199 set( GPGME_PTHREAD_FOUND false )
200 set( GPGME_PTH_FOUND false )
201
202 find_program( _GPGMECONFIG_EXECUTABLE NAMES gpgme-config )
203
204 # if gpgme-config has been found
205 if ( _GPGMECONFIG_EXECUTABLE )
206
207 message( STATUS "Found gpgme-config at ${_GPGMECONFIG_EXECUTABLE}" )
208
209 exec_program( ${_GPGMECONFIG_EXECUTABLE} ARGS --version OUTPUT_VARIABLE GPGME_VERSION )
210
211# set( _GPGME_MIN_VERSION "1.1.7" )
212# macro_ensure_version( ${_GPGME_MIN_VERSION} ${GPGME_VERSION} _GPGME_INSTALLED_VERSION_OK )
213
214# if ( NOT _GPGME_INSTALLED_VERSION_OK )
215
216# message( STATUS "The installed version of gpgme is too old: ${GPGME_VERSION} (required: >= ${_GPGME_MIN_VERSION})" )
217
218# else()
219
220 message( STATUS "Found gpgme v${GPGME_VERSION}, checking for flavours..." )
221
222 exec_program( ${_GPGMECONFIG_EXECUTABLE} ARGS --libs OUTPUT_VARIABLE _gpgme_config_vanilla_libs RETURN_VALUE _ret )
223 if ( _ret )
224 set( _gpgme_config_vanilla_libs )
225 endif()
226
227 exec_program( ${_GPGMECONFIG_EXECUTABLE} ARGS --thread=pthread --libs OUTPUT_VARIABLE _gpgme_config_pthread_libs RETURN_VALUE _ret )
228 if ( _ret )
229 set( _gpgme_config_pthread_libs )
230 endif()
231
232 exec_program( ${_GPGMECONFIG_EXECUTABLE} ARGS --thread=pth --libs OUTPUT_VARIABLE _gpgme_config_pth_libs RETURN_VALUE _ret )
233 if ( _ret )
234 set( _gpgme_config_pth_libs )
235 endif()
236
237 # append -lgpg-error to the list of libraries, if necessary
238 foreach ( _flavour vanilla pthread pth )
239 if ( _gpgme_config_${_flavour}_libs AND NOT _gpgme_config_${_flavour}_libs MATCHES "lgpg-error" )
240 set( _gpgme_config_${_flavour}_libs "${_gpgme_config_${_flavour}_libs} -lgpg-error" )
241 endif()
242 endforeach()
243
244 if ( _gpgme_config_vanilla_libs OR _gpgme_config_pthread_libs OR _gpgme_config_pth_libs )
245
246 exec_program( ${_GPGMECONFIG_EXECUTABLE} ARGS --cflags OUTPUT_VARIABLE _GPGME_CFLAGS )
247
248 if ( _GPGME_CFLAGS )
249 string( REGEX REPLACE "(\r?\n)+$" " " _GPGME_CFLAGS "${_GPGME_CFLAGS}" )
250 string( REGEX REPLACE " *-I" ";" GPGME_INCLUDES "${_GPGME_CFLAGS}" )
251 endif()
252
253 foreach ( _flavour vanilla pthread pth )
254 if ( _gpgme_config_${_flavour}_libs )
255
256 set( _gpgme_library_dirs )
257 set( _gpgme_library_names )
258 string( TOUPPER "${_flavour}" _FLAVOUR )
259
260 string( REGEX REPLACE " +" ";" _gpgme_config_${_flavour}_libs "${_gpgme_config_${_flavour}_libs}" )
261
262 foreach( _flag ${_gpgme_config_${_flavour}_libs} )
263 if ( "${_flag}" MATCHES "^-L" )
264 string( REGEX REPLACE "^-L" "" _dir "${_flag}" )
265 file( TO_CMAKE_PATH "${_dir}" _dir )
266 set( _gpgme_library_dirs ${_gpgme_library_dirs} "${_dir}" )
267 elseif( "${_flag}" MATCHES "^-l" )
268 string( REGEX REPLACE "^-l" "" _name "${_flag}" )
269 set( _gpgme_library_names ${_gpgme_library_names} "${_name}" )
270 endif()
271 endforeach()
272
273 set( GPGME_${_FLAVOUR}_FOUND true )
274
275 foreach( _name ${_gpgme_library_names} )
276 set( _gpgme_${_name}_lib )
277
278 # if -L options were given, look only there
279 if ( _gpgme_library_dirs )
280 find_library( _gpgme_${_name}_lib NAMES ${_name} PATHS ${_gpgme_library_dirs} NO_DEFAULT_PATH )
281 endif()
282
283 # if not found there, look in system directories
284 if ( NOT _gpgme_${_name}_lib )
285 find_library( _gpgme_${_name}_lib NAMES ${_name} )
286 endif()
287
288 # if still not found, then the whole flavour isn't found
289 if ( NOT _gpgme_${_name}_lib )
290 if ( GPGME_${_FLAVOUR}_FOUND )
291 set( GPGME_${_FLAVOUR}_FOUND false )
292 set( _not_found_reason "dependent library ${_name} wasn't found" )
293 endif()
294 endif()
295
296 set( GPGME_${_FLAVOUR}_LIBRARIES ${GPGME_${_FLAVOUR}_LIBRARIES} "${_gpgme_${_name}_lib}" )
297 endforeach()
298
299 #check_c_library_exists_explicit( gpgme gpgme_check_version "${_GPGME_CFLAGS}" "${GPGME_LIBRARIES}" GPGME_FOUND )
300 if ( GPGME_${_FLAVOUR}_FOUND )
301 message( STATUS " Found flavour '${_flavour}', checking whether it's usable...yes" )
302 else()
303 message( STATUS " Found flavour '${_flavour}', checking whether it's usable...no" )
304 message( STATUS " (${_not_found_reason})" )
305 endif()
306 endif()
307
308 endforeach( _flavour )
309
310 # ensure that they are cached
311 # This comment above doesn't make sense, the four following lines seem to do nothing. Alex
312 set( GPGME_INCLUDES ${GPGME_INCLUDES} )
313 set( GPGME_VANILLA_LIBRARIES ${GPGME_VANILLA_LIBRARIES} )
314 set( GPGME_PTHREAD_LIBRARIES ${GPGME_PTHREAD_LIBRARIES} )
315 set( GPGME_PTH_LIBRARIES ${GPGME_PTH_LIBRARIES} )
316
317 if ( GPGME_VANILLA_FOUND OR GPGME_PTHREAD_FOUND OR GPGME_PTH_FOUND )
318 set( GPGME_FOUND true )
319 else()
320 set( GPGME_FOUND false )
321 endif()
322
323# endif()
324
325 endif()
326
327 endif()
328
329 endif()
330
331 # these are Windows-only:
332 set( GPGME_GLIB_FOUND false )
333 set( GPGME_QT_FOUND false )
334 set( HAVE_GPGME_GLIB 0 )
335 set( HAVE_GPGME_QT 0 )
336
337 # macro_bool_to_01( GPGME_FOUND HAVE_GPGME )
338 # macro_bool_to_01( GPGME_VANILLA_FOUND HAVE_GPGME_VANILLA )
339 # macro_bool_to_01( GPGME_PTHREAD_FOUND HAVE_GPGME_PTHREAD )
340 # macro_bool_to_01( GPGME_PTH_FOUND HAVE_GPGME_PTH )
341
342endif() # WIN32 | Unix
343
344
345set( _gpgme_flavours "" )
346
347if ( GPGME_VANILLA_FOUND )
348 set( _gpgme_flavours "${_gpgme_flavours} vanilla" )
349endif()
350
351if ( GPGME_GLIB_FOUND )
352 set( _gpgme_flavours "${_gpgme_flavours} Glib" )
353endif()
354
355if ( GPGME_QT_FOUND )
356 set( _gpgme_flavours "${_gpgme_flavours} Qt" )
357endif()
358
359if ( GPGME_PTHREAD_FOUND )
360 set( _gpgme_flavours "${_gpgme_flavours} pthread" )
361endif()
362
363if ( GPGME_PTH_FOUND )
364 set( _gpgme_flavours "${_gpgme_flavours} pth" )
365endif()
366
367# determine the library in one of the found flavours, can be reused e.g. by FindQgpgme.cmake, Alex
368foreach(_currentFlavour vanilla glib qt pth pthread)
369 if(NOT GPGME_LIBRARY_DIR)
370 get_filename_component(GPGME_LIBRARY_DIR "${_gpgme_${_currentFlavour}_lib}" PATH)
371 endif()
372endforeach()
373
374if ( NOT Gpgme_FIND_QUIETLY )
375
376 if ( GPGME_FOUND )
377 message( STATUS "Usable gpgme flavours found: ${_gpgme_flavours}" )
378 else()
379 message( STATUS "No usable gpgme flavours found." )
380 endif()
381
382 macro_bool_to_bool( Gpgme_FIND_REQUIRED _req )
383
384 if ( WIN32 )
385 set( _gpgme_homepage "http://www.gpg4win.org" )
386 else()
387 set( _gpgme_homepage "http://www.gnupg.org/related_software/gpgme" )
388 endif()
389
390 # macro_log_feature(
391 # GPGME_FOUND
392 # "gpgme"
393 # "GNU Privacy Guard (GPG/PGP) support"
394 # ${_gpgme_homepage}
395 # ${_req}
396 # "${_GPGME_MIN_VERSION} or greater"
397 # "Necessary to compile many PIM applications, including KMail"
398 #)
399
400else()
401
402 if ( Gpgme_FIND_REQUIRED AND NOT GPGME_FOUND )
403 message( FATAL_ERROR "Did not find GPGME" )
404 endif()
405
406endif()
407
408set( CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS_gpgme_saved )
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);