From 01594e68275a09c67b5ee258e2af86598118a6a0 Mon Sep 17 00:00:00 2001 From: Christian Mollekopf Date: Sat, 5 May 2018 10:39:32 +0200 Subject: Port to gpgme only. QGpgme and Gpgmepp are not readily available, the cmake files buggy, the buildsystem horrendous and generally just difficult to build on windows. Given that all they are is a wrapper around gpgme, we're better of without all the indirections. What we loose is: * QGpgme moved the work to separate threads (but we then blocked anyways), something that we can just do in our own code should we want to. * QGpgme has a function to prettify dn's that was used to show the signer. Also something we could bring back should we need to (don't know where it is useful atm.) Ported messagepart to gpgme Almost there Moved the crypto bits to a separate file All gpg code is in one place. All tests passing Use error codes Cleanup --- cmake/modules/FindGpgme.cmake | 408 +++++++++++++++++++ framework/src/CMakeLists.txt | 6 +- framework/src/domain/composercontroller.cpp | 34 +- framework/src/domain/composercontroller.h | 4 +- framework/src/domain/mime/CMakeLists.txt | 4 +- framework/src/domain/mime/attachmentmodel.cpp | 2 +- framework/src/domain/mime/crypto.cpp | 442 +++++++++++++++++++++ framework/src/domain/mime/crypto.h | 123 ++++++ framework/src/domain/mime/mailcrypto.cpp | 192 +-------- framework/src/domain/mime/mailcrypto.h | 31 +- framework/src/domain/mime/mailtemplates.cpp | 4 +- framework/src/domain/mime/mailtemplates.h | 2 +- .../src/domain/mime/mimetreeparser/CMakeLists.txt | 7 +- .../mime/mimetreeparser/autotests/CMakeLists.txt | 6 +- .../src/domain/mime/mimetreeparser/messagepart.cpp | 283 ++++--------- .../src/domain/mime/mimetreeparser/messagepart.h | 21 +- .../src/domain/mime/mimetreeparser/partmetadata.h | 1 - .../mime/mimetreeparser/tests/CMakeLists.txt | 5 +- .../src/domain/mime/tests/mailtemplatetest.cpp | 4 +- 19 files changed, 1100 insertions(+), 479 deletions(-) create mode 100644 cmake/modules/FindGpgme.cmake create mode 100644 framework/src/domain/mime/crypto.cpp create mode 100644 framework/src/domain/mime/crypto.h 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 @@ +# Code is taken from KDE project: +# http://code.metager.de/source/xref/kde/kdepimlibs/cmake/modules/FindGpgme.cmake +# +# - Try to find the gpgme library +# +# Algorithm: +# - Windows: +# On Windows, there's three gpgme variants: gpgme{,-glib,-qt}. +# - The variant used determines the event loop integration possible: +# - gpgme: no event loop integration possible, only synchronous operations supported +# - gpgme-glib: glib event loop integration possible, only asynchronous operations supported +# - gpgme-qt: qt event loop integration possible, only asynchronous operations supported +# - GPGME_{VANILLA,GLIB,QT}_{FOUND,LIBRARIES} will be set for each of the above +# - GPGME_INCLUDES is the same for all of the above +# - GPGME_FOUND is set if any of the above was found +# - *nix: +# There's also three variants: gpgme{,-pthread,-pth}. +# - The variant used determines the multithreaded use possible: +# - gpgme: no multithreading support available +# - gpgme-pthread: multithreading available using POSIX threads +# - gpgme-pth: multithreading available using GNU PTH (cooperative multithreading) +# - GPGME_{VANILLA,PTH,PTHREAD}_{FOUND,LIBRARIES} will be set for each of the above +# - GPGME_INCLUDES is the same for all of the above +# - GPGME_FOUND is set if any of the above was found +# +# GPGME_LIBRARY_DIR - the directory where the libraries are located + +# +# THIS IS ALMOST A 1:1 COPY OF FindAssuan.cmake in kdepim. +# Any changes here likely apply there, too. +# + +# do away with crappy condition repetition on else/endfoo +set( CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS_gpgme_saved ${CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS} ) +set( CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS true ) + +#if this is built-in, please replace, if it isn't, export into a MacroToBool.cmake of it's own +macro( macro_bool_to_bool FOUND_VAR ) + foreach( _current_VAR ${ARGN} ) + if ( ${FOUND_VAR} ) + set( ${_current_VAR} TRUE ) + else() + set( ${_current_VAR} FALSE ) + endif() + endforeach() +endmacro() + +#include (MacroEnsureVersion) + + + +if ( WIN32 ) + + # On Windows, we don't have a gpgme-config script, so we need to + # look for the stuff ourselves: + + # in cmake, AND and OR have the same precedence, there's no + # subexpressions, and expressions are evaluated short-circuit'ed + # IOW: CMake if() suxx. + # Starting with CMake 2.6.3 you can group if expressions with (), but we + # don't require 2.6.3 but 2.6.2, we can't use it. Alex + set( _seem_to_have_cached_gpgme false ) + if ( GPGME_INCLUDES ) + if ( GPGME_VANILLA_LIBRARIES OR GPGME_QT_LIBRARIES OR GPGME_GLIB_LIBRARIES ) + set( _seem_to_have_cached_gpgme true ) + endif() + endif() + + if ( _seem_to_have_cached_gpgme ) + + macro_bool_to_bool( GPGME_VANILLA_LIBRARIES GPGME_VANILLA_FOUND ) + macro_bool_to_bool( GPGME_GLIB_LIBRARIES GPGME_GLIB_FOUND ) + macro_bool_to_bool( GPGME_QT_LIBRARIES GPGME_QT_FOUND ) + # this would have been preferred: + #set( GPGME_*_FOUND macro_bool_to_bool(GPGME_*_LIBRARIES) ) + + if ( GPGME_VANILLA_FOUND OR GPGME_GLIB_FOUND OR GPGME_QT_FOUND ) + set( GPGME_FOUND true ) + else() + set( GPGME_FOUND false ) + endif() + + else() + + # is this needed, of just unreflected cut'n'paste? + # this isn't a KDE library, after all! + if( NOT KDEWIN_FOUND ) + find_package( KDEWIN REQUIRED ) + endif() + + set( GPGME_FOUND false ) + set( GPGME_VANILLA_FOUND false ) + set( GPGME_GLIB_FOUND false ) + set( GPGME_QT_FOUND false ) + + find_path( GPGME_INCLUDES gpgme.h + ${CMAKE_INCLUDE_PATH} + ${CMAKE_INSTALL_PREFIX}/include + ) + + if (NOT WINCE) + find_library( _gpgme_vanilla_library NAMES gpgme libgpgme gpgme-11 libgpgme-11 + PATHS + ${CMAKE_LIBRARY_PATH} + ${CMAKE_INSTALL_PREFIX}/lib + ) + else (NOT WINCE) + find_library( _gpgme_vanilla_library NAMES libgpgme-11-msc + PATHS + ${CMAKE_LIBRARY_PATH} + ${CMAKE_INSTALL_PREFIX}/lib + ) + endif() + + find_library( _gpgme_glib_library NAMES gpgme-glib libgpgme-glib gpgme-glib-11 libgpgme-glib-11 + PATHS + ${CMAKE_LIBRARY_PATH} + ${CMAKE_INSTALL_PREFIX}/lib + ) + + find_library( _gpgme_qt_library NAMES gpgme-qt libgpgme-qt gpgme-qt-11 libgpgme-qt-11 + PATHS + ${CMAKE_LIBRARY_PATH} + ${CMAKE_INSTALL_PREFIX}/lib + ) + + if ( WINCE ) + set( _gpg_error_library ) + else() + find_library( _gpg_error_library NAMES gpg-error libgpg-error gpg-error-0 libgpg-error-0 + PATHS + ${CMAKE_LIBRARY_PATH} + ${CMAKE_INSTALL_PREFIX}/lib + ) + endif() + + set( GPGME_INCLUDES ${GPGME_INCLUDES} ) + + if ( _gpgme_vanilla_library AND ( _gpg_error_library OR WINCE ) ) + set( GPGME_VANILLA_LIBRARIES ${_gpgme_vanilla_library} ${_gpg_error_library} ) + set( GPGME_VANILLA_FOUND true ) + set( GPGME_FOUND true ) + endif() + + if ( _gpgme_glib_library AND ( _gpg_error_library OR WINCE ) ) + set( GPGME_GLIB_LIBRARIES ${_gpgme_glib_library} ${_gpg_error_library} ) + set( GPGME_GLIB_FOUND true ) + set( GPGME_FOUND true ) + endif() + + if ( _gpgme_qt_library AND ( _gpg_error_library OR WINCE ) ) + set( GPGME_QT_LIBRARIES ${_gpgme_qt_library} ${_gpg_error_library} ) + set( GPGME_QT_FOUND true ) + set( GPGME_FOUND true ) + endif() + + endif() + + # these are Unix-only: + set( GPGME_PTHREAD_FOUND false ) + set( GPGME_PTH_FOUND false ) + set( HAVE_GPGME_PTHREAD 0 ) + set( HAVE_GPGME_PTH 0 ) + + macro_bool_to_01( GPGME_FOUND HAVE_GPGME ) + macro_bool_to_01( GPGME_VANILLA_FOUND HAVE_GPGME_VANILLA ) + macro_bool_to_01( GPGME_GLIB_FOUND HAVE_GPGME_GLIB ) + macro_bool_to_01( GPGME_QT_FOUND HAVE_GPGME_QT ) + +else() # not WIN32 + + # On *nix, we have the gpgme-config script which can tell us all we + # need to know: + + # see WIN32 case for an explanation of what this does: + set( _seem_to_have_cached_gpgme false ) + if ( GPGME_INCLUDES ) + if ( GPGME_VANILLA_LIBRARIES OR GPGME_PTHREAD_LIBRARIES OR GPGME_PTH_LIBRARIES ) + set( _seem_to_have_cached_gpgme true ) + endif() + endif() + + if ( _seem_to_have_cached_gpgme ) + + macro_bool_to_bool( GPGME_VANILLA_LIBRARIES GPGME_VANILLA_FOUND ) + macro_bool_to_bool( GPGME_PTHREAD_LIBRARIES GPGME_PTHREAD_FOUND ) + macro_bool_to_bool( GPGME_PTH_LIBRARIES GPGME_PTH_FOUND ) + + if ( GPGME_VANILLA_FOUND OR GPGME_PTHREAD_FOUND OR GPGME_PTH_FOUND ) + set( GPGME_FOUND true ) + else() + set( GPGME_FOUND false ) + endif() + + else() + + set( GPGME_FOUND false ) + set( GPGME_VANILLA_FOUND false ) + set( GPGME_PTHREAD_FOUND false ) + set( GPGME_PTH_FOUND false ) + + find_program( _GPGMECONFIG_EXECUTABLE NAMES gpgme-config ) + + # if gpgme-config has been found + if ( _GPGMECONFIG_EXECUTABLE ) + + message( STATUS "Found gpgme-config at ${_GPGMECONFIG_EXECUTABLE}" ) + + exec_program( ${_GPGMECONFIG_EXECUTABLE} ARGS --version OUTPUT_VARIABLE GPGME_VERSION ) + +# set( _GPGME_MIN_VERSION "1.1.7" ) +# macro_ensure_version( ${_GPGME_MIN_VERSION} ${GPGME_VERSION} _GPGME_INSTALLED_VERSION_OK ) + +# if ( NOT _GPGME_INSTALLED_VERSION_OK ) + +# message( STATUS "The installed version of gpgme is too old: ${GPGME_VERSION} (required: >= ${_GPGME_MIN_VERSION})" ) + +# else() + + message( STATUS "Found gpgme v${GPGME_VERSION}, checking for flavours..." ) + + exec_program( ${_GPGMECONFIG_EXECUTABLE} ARGS --libs OUTPUT_VARIABLE _gpgme_config_vanilla_libs RETURN_VALUE _ret ) + if ( _ret ) + set( _gpgme_config_vanilla_libs ) + endif() + + exec_program( ${_GPGMECONFIG_EXECUTABLE} ARGS --thread=pthread --libs OUTPUT_VARIABLE _gpgme_config_pthread_libs RETURN_VALUE _ret ) + if ( _ret ) + set( _gpgme_config_pthread_libs ) + endif() + + exec_program( ${_GPGMECONFIG_EXECUTABLE} ARGS --thread=pth --libs OUTPUT_VARIABLE _gpgme_config_pth_libs RETURN_VALUE _ret ) + if ( _ret ) + set( _gpgme_config_pth_libs ) + endif() + + # append -lgpg-error to the list of libraries, if necessary + foreach ( _flavour vanilla pthread pth ) + if ( _gpgme_config_${_flavour}_libs AND NOT _gpgme_config_${_flavour}_libs MATCHES "lgpg-error" ) + set( _gpgme_config_${_flavour}_libs "${_gpgme_config_${_flavour}_libs} -lgpg-error" ) + endif() + endforeach() + + if ( _gpgme_config_vanilla_libs OR _gpgme_config_pthread_libs OR _gpgme_config_pth_libs ) + + exec_program( ${_GPGMECONFIG_EXECUTABLE} ARGS --cflags OUTPUT_VARIABLE _GPGME_CFLAGS ) + + if ( _GPGME_CFLAGS ) + string( REGEX REPLACE "(\r?\n)+$" " " _GPGME_CFLAGS "${_GPGME_CFLAGS}" ) + string( REGEX REPLACE " *-I" ";" GPGME_INCLUDES "${_GPGME_CFLAGS}" ) + endif() + + foreach ( _flavour vanilla pthread pth ) + if ( _gpgme_config_${_flavour}_libs ) + + set( _gpgme_library_dirs ) + set( _gpgme_library_names ) + string( TOUPPER "${_flavour}" _FLAVOUR ) + + string( REGEX REPLACE " +" ";" _gpgme_config_${_flavour}_libs "${_gpgme_config_${_flavour}_libs}" ) + + foreach( _flag ${_gpgme_config_${_flavour}_libs} ) + if ( "${_flag}" MATCHES "^-L" ) + string( REGEX REPLACE "^-L" "" _dir "${_flag}" ) + file( TO_CMAKE_PATH "${_dir}" _dir ) + set( _gpgme_library_dirs ${_gpgme_library_dirs} "${_dir}" ) + elseif( "${_flag}" MATCHES "^-l" ) + string( REGEX REPLACE "^-l" "" _name "${_flag}" ) + set( _gpgme_library_names ${_gpgme_library_names} "${_name}" ) + endif() + endforeach() + + set( GPGME_${_FLAVOUR}_FOUND true ) + + foreach( _name ${_gpgme_library_names} ) + set( _gpgme_${_name}_lib ) + + # if -L options were given, look only there + if ( _gpgme_library_dirs ) + find_library( _gpgme_${_name}_lib NAMES ${_name} PATHS ${_gpgme_library_dirs} NO_DEFAULT_PATH ) + endif() + + # if not found there, look in system directories + if ( NOT _gpgme_${_name}_lib ) + find_library( _gpgme_${_name}_lib NAMES ${_name} ) + endif() + + # if still not found, then the whole flavour isn't found + if ( NOT _gpgme_${_name}_lib ) + if ( GPGME_${_FLAVOUR}_FOUND ) + set( GPGME_${_FLAVOUR}_FOUND false ) + set( _not_found_reason "dependent library ${_name} wasn't found" ) + endif() + endif() + + set( GPGME_${_FLAVOUR}_LIBRARIES ${GPGME_${_FLAVOUR}_LIBRARIES} "${_gpgme_${_name}_lib}" ) + endforeach() + + #check_c_library_exists_explicit( gpgme gpgme_check_version "${_GPGME_CFLAGS}" "${GPGME_LIBRARIES}" GPGME_FOUND ) + if ( GPGME_${_FLAVOUR}_FOUND ) + message( STATUS " Found flavour '${_flavour}', checking whether it's usable...yes" ) + else() + message( STATUS " Found flavour '${_flavour}', checking whether it's usable...no" ) + message( STATUS " (${_not_found_reason})" ) + endif() + endif() + + endforeach( _flavour ) + + # ensure that they are cached + # This comment above doesn't make sense, the four following lines seem to do nothing. Alex + set( GPGME_INCLUDES ${GPGME_INCLUDES} ) + set( GPGME_VANILLA_LIBRARIES ${GPGME_VANILLA_LIBRARIES} ) + set( GPGME_PTHREAD_LIBRARIES ${GPGME_PTHREAD_LIBRARIES} ) + set( GPGME_PTH_LIBRARIES ${GPGME_PTH_LIBRARIES} ) + + if ( GPGME_VANILLA_FOUND OR GPGME_PTHREAD_FOUND OR GPGME_PTH_FOUND ) + set( GPGME_FOUND true ) + else() + set( GPGME_FOUND false ) + endif() + +# endif() + + endif() + + endif() + + endif() + + # these are Windows-only: + set( GPGME_GLIB_FOUND false ) + set( GPGME_QT_FOUND false ) + set( HAVE_GPGME_GLIB 0 ) + set( HAVE_GPGME_QT 0 ) + + # macro_bool_to_01( GPGME_FOUND HAVE_GPGME ) + # macro_bool_to_01( GPGME_VANILLA_FOUND HAVE_GPGME_VANILLA ) + # macro_bool_to_01( GPGME_PTHREAD_FOUND HAVE_GPGME_PTHREAD ) + # macro_bool_to_01( GPGME_PTH_FOUND HAVE_GPGME_PTH ) + +endif() # WIN32 | Unix + + +set( _gpgme_flavours "" ) + +if ( GPGME_VANILLA_FOUND ) + set( _gpgme_flavours "${_gpgme_flavours} vanilla" ) +endif() + +if ( GPGME_GLIB_FOUND ) + set( _gpgme_flavours "${_gpgme_flavours} Glib" ) +endif() + +if ( GPGME_QT_FOUND ) + set( _gpgme_flavours "${_gpgme_flavours} Qt" ) +endif() + +if ( GPGME_PTHREAD_FOUND ) + set( _gpgme_flavours "${_gpgme_flavours} pthread" ) +endif() + +if ( GPGME_PTH_FOUND ) + set( _gpgme_flavours "${_gpgme_flavours} pth" ) +endif() + +# determine the library in one of the found flavours, can be reused e.g. by FindQgpgme.cmake, Alex +foreach(_currentFlavour vanilla glib qt pth pthread) + if(NOT GPGME_LIBRARY_DIR) + get_filename_component(GPGME_LIBRARY_DIR "${_gpgme_${_currentFlavour}_lib}" PATH) + endif() +endforeach() + +if ( NOT Gpgme_FIND_QUIETLY ) + + if ( GPGME_FOUND ) + message( STATUS "Usable gpgme flavours found: ${_gpgme_flavours}" ) + else() + message( STATUS "No usable gpgme flavours found." ) + endif() + + macro_bool_to_bool( Gpgme_FIND_REQUIRED _req ) + + if ( WIN32 ) + set( _gpgme_homepage "http://www.gpg4win.org" ) + else() + set( _gpgme_homepage "http://www.gnupg.org/related_software/gpgme" ) + endif() + + # macro_log_feature( + # GPGME_FOUND + # "gpgme" + # "GNU Privacy Guard (GPG/PGP) support" + # ${_gpgme_homepage} + # ${_req} + # "${_GPGME_MIN_VERSION} or greater" + # "Necessary to compile many PIM applications, including KMail" + #) + +else() + + if ( Gpgme_FIND_REQUIRED AND NOT GPGME_FOUND ) + message( FATAL_ERROR "Did not find GPGME" ) + endif() + +endif() + +set( 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) find_package(KF5CalendarCore CONFIG REQUIRED) find_package(Sink 0.6.0 CONFIG REQUIRED) find_package(KAsync CONFIG REQUIRED) -find_package(QGpgme CONFIG REQUIRED) -find_package(Gpgmepp CONFIG REQUIRED) +find_package(Gpgme REQUIRED) find_package(KF5Codecs CONFIG REQUIRED) find_package(KF5Contacts CONFIG REQUIRED) @@ -73,8 +72,7 @@ target_link_libraries(kubeframework KF5::Codecs KF5::Contacts KAsync - QGpgme - Gpgmepp + gpgme ) install(TARGETS kubeframework DESTINATION ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) 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 @@ #include "mime/mailcrypto.h" #include "async.h" -std::vector &operator+=(std::vector &list, const std::vector &add) +std::vector &operator+=(std::vector &list, const std::vector &add) { list.insert(std::end(list), std::begin(add), std::end(add)); return list; @@ -133,11 +133,11 @@ public: mb.fromUnicodeString(addressee); SinkLog() << "Searching key for: " << mb.address(); - asyncRun>(this, + asyncRun>(this, [mb] { - return MailCrypto::findKeys(QStringList{} << mb.address(), false, false); + return Crypto::findKeys(QStringList{} << mb.address(), false, false); }, - [this, addressee, id](const std::vector &keys) { + [this, addressee, id](const std::vector &keys) { if (!keys.empty()) { if (keys.size() > 1) { SinkWarning() << "Found more than one key, encrypting to all of them."; @@ -227,10 +227,10 @@ void ComposerController::findPersonalKey() { auto identity = getIdentity(); SinkLog() << "Looking for personal key for: " << identity.address(); - asyncRun>(this, [=] { - return MailCrypto::findKeys(QStringList{} << identity.address(), true); + asyncRun>(this, [=] { + return Crypto::findKeys(QStringList{} << identity.address(), true); }, - [this](const std::vector &keys) { + [this](const std::vector &keys) { if (keys.empty()) { SinkWarning() << "Failed to find a personal key."; } else if (keys.size() > 1) { @@ -419,23 +419,23 @@ void ComposerController::recordForAutocompletion(const QByteArray &addrSpec, con } } -std::vector ComposerController::getRecipientKeys() +std::vector ComposerController::getRecipientKeys() { - std::vector keys; + std::vector keys; { - const auto list = toController()->getList>("key"); + const auto list = toController()->getList>("key"); for (const auto &l: list) { keys.insert(std::end(keys), std::begin(l), std::end(l)); } } { - const auto list = ccController()->getList>("key"); + const auto list = ccController()->getList>("key"); for (const auto &l: list) { keys.insert(std::end(keys), std::begin(l), std::end(l)); } } { - const auto list = bccController()->getList>("key"); + const auto list = bccController()->getList>("key"); for (const auto &l: list) { keys.insert(std::end(keys), std::begin(l), std::end(l)); } @@ -463,17 +463,17 @@ KMime::Message::Ptr ComposerController::assembleMessage() }; }); - MailCrypto::Key attachedKey; - std::vector signingKeys; + Crypto::Key attachedKey; + std::vector signingKeys; if (getSign()) { - signingKeys = getPersonalKeys().value>(); + signingKeys = getPersonalKeys().value>(); Q_ASSERT(!signingKeys.empty()); attachedKey = signingKeys[0]; } - std::vector encryptionKeys; + std::vector encryptionKeys; if (getEncrypt()) { //Encrypt to self so we can read the sent message - auto personalKeys = getPersonalKeys().value>(); + auto personalKeys = getPersonalKeys().value>(); attachedKey = personalKeys[0]; 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 KUBE_CONTROLLER_PROPERTY(KMime::Message::Ptr, ExistingMessage, existingMessage) KUBE_CONTROLLER_PROPERTY(Sink::ApplicationDomain::Mail, ExistingMail, existingMail) - KUBE_CONTROLLER_PROPERTY(/*std::vector*/QVariant, PersonalKeys, personalKeys) + KUBE_CONTROLLER_PROPERTY(/*std::vector*/QVariant, PersonalKeys, personalKeys) KUBE_CONTROLLER_PROPERTY(bool, FoundPersonalKeys, foundPersonalKeys) KUBE_CONTROLLER_LISTCONTROLLER(to) @@ -109,7 +109,7 @@ private: void setMessage(const QSharedPointer &msg); void addAttachmentPart(KMime::Content *partToAttach); KMime::Message::Ptr assembleMessage(); - std::vector getRecipientKeys(); + std::vector getRecipientKeys(); QScopedPointer mRecipientCompleter; QScopedPointer 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 @@ add_library(mailcrypto STATIC mailcrypto.cpp + crypto.cpp ) target_link_libraries(mailcrypto Qt5::Core KF5::Mime - QGpgme - Gpgmepp + gpgme ) 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) const auto part = static_cast(index.internalPointer()); Q_ASSERT(part); auto pkey = part->node()->decodedContent(); - auto result = MailCrypto::importKey(pkey); + auto result = Crypto::importKey(pkey); bool success = true; 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 @@ +/* + Copyright (c) 2009 Constantin Berzan + Copyright (C) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com + Copyright (c) 2010 Leo Franchi + Copyright (c) 2017 Christian Mollekopf + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ +#include "crypto.h" + +#include "framework/src/errors.h" + +#include + +#include +#include + +#include +#include + +using namespace Crypto; + +QDebug operator<< (QDebug d, const Key &key) +{ + d << key.fingerprint; + return d; +} + +QDebug operator<< (QDebug d, const Error &error) +{ + d << error.errorCode(); + return d; +} + +struct Data { + Data(const QByteArray &buffer) + { + const bool copy = false; + const gpgme_error_t e = gpgme_data_new_from_mem(&data, buffer.constData(), buffer.size(), int(copy)); + if (e) { + qWarning() << "Failed to copy data?" << e; + } + } + + ~Data() + { + gpgme_data_release(data); + } + gpgme_data_t data; +}; + +static gpgme_error_t checkEngine(CryptoProtocol protocol) +{ + gpgme_check_version(0); + const gpgme_protocol_t p = protocol == CMS ? GPGME_PROTOCOL_CMS : GPGME_PROTOCOL_OpenPGP; + return gpgme_engine_check_version(p); +} + +static std::pair createForProtocol(CryptoProtocol proto) +{ + if (auto e = checkEngine(proto)) { + qWarning() << "GPG Engine check failed." << e; + return std::make_pair(e, nullptr); + } + gpgme_ctx_t ctx = 0; + if (auto e = gpgme_new(&ctx)) { + return std::make_pair(e, nullptr); + } + + switch (proto) { + case OpenPGP: + if (auto e = gpgme_set_protocol(ctx, GPGME_PROTOCOL_OpenPGP)) { + gpgme_release(ctx); + return std::make_pair(e, nullptr); + } + break; + case CMS: + if (auto e = gpgme_set_protocol(ctx, GPGME_PROTOCOL_CMS)) { + gpgme_release(ctx); + return std::make_pair(e, nullptr); + } + break; + default: + Q_ASSERT(false); + return std::make_pair(1, nullptr); + } + return std::make_pair(GPG_ERR_NO_ERROR, ctx); +} + + +struct Context { + Context(CryptoProtocol protocol = OpenPGP) + { + gpgme_error_t code; + std::tie(code, context) = createForProtocol(protocol); + error = Error{code}; + } + + ~Context() + { + gpgme_release(context); + } + + operator bool() const + { + return !error; + } + Error error; + gpgme_ctx_t context; +}; + + +static QByteArray toBA(gpgme_data_t out) +{ + size_t length = 0; + auto data = gpgme_data_release_and_get_mem (out, &length); + auto outdata = QByteArray{data, static_cast(length)}; + gpgme_free(data); + return outdata; +} + +static std::vector copyRecipients(gpgme_decrypt_result_t result) +{ + std::vector recipients; + for (gpgme_recipient_t r = result->recipients ; r ; r = r->next) { + recipients.push_back({QByteArray{r->keyid}, {r->status}}); + } + return recipients; +} + +static std::vector copySignatures(gpgme_verify_result_t result) +{ + std::vector signatures; + for (gpgme_signature_t is = result->signatures ; is ; is = is->next) { + Signature sig; + sig.fingerprint = QByteArray{is->fpr}; + sig.creationTime.setTime_t(is->timestamp); + sig.summary = is->summary; + sig.status = {is->status}; + sig.validity = is->validity; + sig.validity_reason = is->validity_reason; + signatures.push_back(sig); + } + return signatures; +} + + +VerificationResult Crypto::verifyDetachedSignature(CryptoProtocol protocol, const QByteArray &signature, const QByteArray &text) +{ + Context context{protocol}; + if (!context) { + qWarning() << "Failed to create context " << context.error; + return {{}, context.error}; + } + auto ctx = context.context; + + auto err = gpgme_op_verify(ctx, Data{signature}.data, Data{text}.data, 0); + gpgme_verify_result_t res = gpgme_op_verify_result(ctx); + return {copySignatures(res), {err}}; +} + +VerificationResult Crypto::verifyOpaqueSignature(CryptoProtocol protocol, const QByteArray &signature, QByteArray &outdata) +{ + Context context{protocol}; + if (!context) { + qWarning() << "Failed to create context " << context.error; + return VerificationResult{{}, context.error}; + } + auto ctx = context.context; + + gpgme_data_t out; + const gpgme_error_t e = gpgme_data_new(&out); + Q_ASSERT(!e); + auto err = gpgme_op_verify(ctx, Data{signature}.data, 0, out); + + VerificationResult result{{}, {err}}; + if (gpgme_verify_result_t res = gpgme_op_verify_result(ctx)) { + result.signatures = copySignatures(res); + } + + outdata = toBA(out); + return result; +} + +std::pair Crypto::decryptAndVerify(CryptoProtocol protocol, const QByteArray &ciphertext, QByteArray &outdata) +{ + Context context{protocol}; + if (!context) { + qWarning() << "Failed to create context " << context.error; + return std::make_pair(DecryptionResult{{}, context.error}, VerificationResult{{}, context.error}); + } + auto ctx = context.context; + + gpgme_data_t out; + if (gpgme_error_t e = gpgme_data_new(&out)) { + qWarning() << "Failed to allocated data" << e; + } + auto err = gpgme_op_decrypt_verify(ctx, Data{ciphertext}.data, out); + if (err) { + qWarning() << "Failed to decrypt and verify" << Error{err}; + } + + VerificationResult verificationResult{{}, {err}}; + if (gpgme_verify_result_t res = gpgme_op_verify_result(ctx)) { + verificationResult.signatures = copySignatures(res); + } + + DecryptionResult decryptionResult{{}, {err}}; + if (gpgme_decrypt_result_t res = gpgme_op_decrypt_result(ctx)) { + decryptionResult.recipients = copyRecipients(res); + } + + outdata = toBA(out); + return std::make_pair(decryptionResult, verificationResult); +} + +ImportResult Crypto::importKeys(CryptoProtocol protocol, const QByteArray &certData) +{ + Context context{protocol}; + if (!context) { + qWarning() << "Failed to create context " << context.error; + return {0, 0, 0}; + } + if (auto err = gpgme_op_import(context.context, Data{certData}.data)) { + qWarning() << "Import failed"; + return {0, 0, 0}; + } + if (auto result = gpgme_op_import_result(context.context)) { + return {result->considered, result->imported, result->unchanged}; + } else { + return {0, 0, 0}; + } +} + +static KeyListResult listKeys(CryptoProtocol protocol, const std::vector &patterns, bool secretOnly, int keyListMode) +{ + Context context{protocol}; + if (!context) { + qWarning() << "Failed to create context " << context.error; + return {{}, context.error}; + } + auto ctx = context.context; + + gpgme_set_keylist_mode(ctx, keyListMode); + + KeyListResult result; + result.error = {GPG_ERR_NO_ERROR}; + if (patterns.size() > 1) { + qWarning() << "Listing multiple patterns"; + if (auto err = gpgme_op_keylist_ext_start(ctx, const_cast(patterns.data()), int(secretOnly), 0)) { + qWarning() << "Error while listing keys"; + result.error = {err}; + } + } else if (patterns.size() == 1) { + qWarning() << "Listing one patterns " << patterns.data()[0]; + if (auto err = gpgme_op_keylist_start(ctx, patterns.data()[0], int(secretOnly))) { + qWarning() << "Error while listing keys"; + result.error = {err}; + } + } else { + qWarning() << "Listing all"; + if (auto err = gpgme_op_keylist_start(ctx, 0, int(secretOnly))) { + qWarning() << "Error while listing keys"; + result.error = {err}; + } + } + + + while (true) { + gpgme_key_t key; + if (auto e = gpgme_op_keylist_next(ctx, &key)) { + break; + } + Key k; + if (key->subkeys) { + k.keyId = QByteArray{key->subkeys->keyid}; + k.shortKeyId = k.keyId.right(8); + k.fingerprint = QByteArray{key->subkeys->fpr}; + } + for (gpgme_user_id_t uid = key->uids ; uid ; uid = uid->next) { + k.userIds.push_back(UserId{QByteArray{uid->name}, QByteArray{uid->email}, QByteArray{uid->uid}}); + } + k.isExpired = key->expired; + result.keys.push_back(k); + } + gpgme_op_keylist_end(ctx); + return result; +} + +/** + * Get the given `key` in the armor format. + */ +Expected Crypto::exportPublicKey(const Key &key) +{ + Context context; + if (!context) { + return makeUnexpected(Error{context.error}); + } + + gpgme_data_t out; + const gpgme_error_t e = gpgme_data_new(&out); + Q_ASSERT(!e); + + qDebug() << "Exporting public key:" << key.keyId; + if (auto err = gpgme_op_export(context.context, key.keyId, 0, out)) { + return makeUnexpected(Error{err}); + } + + return toBA(out); +} + +Expected Crypto::signAndEncrypt(const QByteArray &content, const std::vector &encryptionKeys, const std::vector &signingKeys) +{ + Context context; + if (!context) { + return makeUnexpected(Error{context.error}); + } + + for (const auto &signingKey : signingKeys) { + //TODO do we have to free those again? + gpgme_key_t key; + if (auto e = gpgme_get_key(context.context, signingKey.fingerprint, &key, /*secret*/ false)) { + qWarning() << "Failed to retrive signing key " << signingKey.fingerprint << e; + } else { + gpgme_signers_add(context.context, key); + } + } + + gpgme_key_t * const keys = new gpgme_key_t[encryptionKeys.size() + 1]; + gpgme_key_t * keys_it = keys; + for (const auto &k : encryptionKeys) { + gpgme_key_t key; + if (auto e = gpgme_get_key(context.context, k.fingerprint, &key, /*secret*/ false)) { + qWarning() << "Failed to retrive key " << k.fingerprint << e; + } else { + *keys_it++ = key; + } + } + *keys_it++ = 0; + + gpgme_data_t out; + const gpgme_error_t e = gpgme_data_new(&out); + Q_ASSERT(!e); + + gpgme_error_t err = !signingKeys.empty() ? + gpgme_op_encrypt_sign(context.context, keys, GPGME_ENCRYPT_ALWAYS_TRUST, Data{content}.data, out) : + gpgme_op_encrypt(context.context, keys, GPGME_ENCRYPT_ALWAYS_TRUST, Data{content}.data, out); + delete[] keys; + if (err) { + qWarning() << "Encryption failed:" << Error{err}; + return makeUnexpected(Error{err}); + } + + return toBA(out); +; +} + +Expected> +Crypto::sign(const QByteArray &content, const std::vector &signingKeys) +{ + Context context; + if (!context) { + return makeUnexpected(Error{context.error}); + } + + for (const auto &signingKey : signingKeys) { + //TODO do we have to free those again? + gpgme_key_t key; + if (auto e = gpgme_get_key(context.context, signingKey.fingerprint, &key, /*secret*/ false)) { + qWarning() << "Failed to retrive signing key " << signingKey.fingerprint << e; + } else { + gpgme_signers_add(context.context, key); + } + } + + gpgme_data_t out; + const gpgme_error_t e = gpgme_data_new(&out); + Q_ASSERT(!e); + + if (auto err = gpgme_op_sign(context.context, Data{content}.data, out, GPGME_SIG_MODE_DETACH)) { + qWarning() << "Signing failed:" << Error{err}; + return makeUnexpected(Error{err}); + } + + + const QByteArray algo = [&] { + if (gpgme_sign_result_t res = gpgme_op_sign_result(context.context)) { + for (gpgme_new_signature_t is = res->signatures ; is ; is = is->next) { + return QByteArray{gpgme_hash_algo_name(is->hash_algo)}; + } + } + return QByteArray{}; + }(); + // RFC 3156 Section 5: + // Hash-symbols are constructed [...] by converting the text name to lower + // case and prefixing it with the four characters "pgp-". + const auto micAlg = (QString("pgp-") + algo).toLower(); + + return std::pair{toBA(out), micAlg}; +} + +ImportResult Crypto::importKey(const QByteArray &pkey) +{ + return importKeys(OpenPGP, pkey); +} + +std::vector Crypto::findKeys(const QStringList &patterns, bool findPrivate, bool remote) +{ + QByteArrayList list; + std::transform(patterns.constBegin(), patterns.constEnd(), std::back_inserter(list), [] (const QString &s) { return s.toUtf8(); }); + std::vector pattern; + std::transform(list.constBegin(), list.constEnd(), std::back_inserter(pattern), [] (const QByteArray &s) { return s.constData(); }); + pattern.push_back(0); + + const KeyListResult res = listKeys(OpenPGP, pattern, findPrivate, remote ? GPGME_KEYLIST_MODE_EXTERN : GPGME_KEYLIST_MODE_LOCAL); + if (res.error) { + qWarning() << "Failed to lookup keys: " << res.error; + return {}; + } + qDebug() << "got keys:" << res.keys.size(); + for (const auto &key : res.keys) { + qDebug() << "isexpired:" << key.isExpired; + for (const auto &userId : key.userIds) { + qDebug() << "userID:" << userId.email; + } + } + return res.keys; +} + 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 @@ +/* + Copyright (c) 2016 Christian Mollekopf + + This library is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This library is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. +*/ + +#pragma once + +#include "framework/src/errors.h" + +#include +#include + +#include +#include +#include +#include + +namespace Crypto { + +enum CryptoProtocol { + UnknownProtocol, + OpenPGP, + CMS +}; + + +struct UserId { + QByteArray name; + QByteArray email; + QByteArray id; +}; + +struct Key { + QByteArray keyId; + QByteArray shortKeyId; + QByteArray fingerprint; + bool isExpired = false; + std::vector userIds; +}; + +struct Error { + gpgme_error_t error; + gpgme_err_code_t errorCode() const { + return gpgme_err_code(error); + } + operator bool() const + { + return error != GPG_ERR_NO_ERROR; + } +}; + +struct Signature { + QByteArray fingerprint; + gpgme_sigsum_t summary; + Error status; + gpgme_validity_t validity; + gpgme_error_t validity_reason; + QDateTime creationTime; +}; + +struct VerificationResult { + std::vector signatures; + Error error; +}; + +struct Recipient { + QByteArray keyId; + Error status; +}; + +struct DecryptionResult { + std::vector recipients; + Error error; +}; + +struct KeyListResult { + std::vector keys; + Error error; +}; + + +std::vector findKeys(const QStringList &filter, bool findPrivate = false, bool remote = false); + +Expected exportPublicKey(const Key &key); +struct ImportResult { + int considered; + int imported; + int unchanged; +}; +ImportResult importKeys(CryptoProtocol protocol, const QByteArray &certData); +ImportResult importKey(const QByteArray &key); + +/** + * Sign the given content and returns the signing data and the algorithm used + * for integrity check in the "pgp-" format. + */ +Expected> +sign(const QByteArray &content, const std::vector &signingKeys); +Expected signAndEncrypt(const QByteArray &content, const std::vector &encryptionKeys, const std::vector &signingKeys); + +std::pair decryptAndVerify(CryptoProtocol protocol, const QByteArray &ciphertext, QByteArray &outdata); +VerificationResult verifyDetachedSignature(CryptoProtocol protocol, const QByteArray &signature, const QByteArray &outdata); +VerificationResult verifyOpaqueSignature(CryptoProtocol protocol, const QByteArray &signature, QByteArray &outdata); +}; + +Q_DECLARE_METATYPE(Crypto::Key); + +QDebug operator<< (QDebug d, const Crypto::Key &); +QDebug 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 @@ #include "mailcrypto.h" #include "framework/src/errors.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include +#include "crypto.h" #include @@ -45,22 +30,8 @@ #include using namespace MailCrypto; +using namespace Crypto; -QDebug operator<< (QDebug d, const MailCrypto::Key &key) -{ - d << key.key.primaryFingerprint(); - return d; -} - -static std::vector toGpgME(const std::vector keys) -{ - std::vector list; - std::transform(keys.begin(), keys.end(), std::back_inserter(list), [] (const Key &key) { return key.key; }); - return list; -} - -// replace simple LFs by CRLFs for all MIME supporting CryptPlugs -// according to RfC 2633, 3.1.1 Canonicalization static QByteArray canonicalizeContent(KMime::Content *content) { // if (d->format & Kleo::InlineOpenPGPFormat) { @@ -124,29 +95,6 @@ static QByteArray canonicalizeContent(KMime::Content *content) } -/** - * Get the given `key` in the armor format. - */ -Expected exportPublicKey(const Key &key) -{ - // Not using the Qt API because it apparently blocks (the `result` signal is never - // triggered) - std::unique_ptr ctx(GpgME::Context::createForProtocol(GpgME::OpenPGP)); - ctx->setArmor(true); - - QGpgME::QByteArrayDataProvider dp; - GpgME::Data data(&dp); - - qDebug() << "Exporting public key:" << key.key.shortKeyID(); - auto error = ctx->exportPublicKeys(key.key.keyID(), data); - - if (error.code()) { - return makeUnexpected(Error{error}); - } - - return dp.data(); -} - /** * Create an Email with `msg` as a body and `key` as an attachment. * @@ -178,7 +126,7 @@ appendPublicKey(std::unique_ptr msg, const Key &key) { keyAttachment->contentType()->setMimeType("application/pgp-keys"); keyAttachment->contentDisposition()->setDisposition(KMime::Headers::CDattachment); - keyAttachment->contentDisposition()->setFilename(QString("0x") + key.key.shortKeyID() + ".asc"); + keyAttachment->contentDisposition()->setFilename(QString("0x") + key.shortKeyId + ".asc"); keyAttachment->setBody(publicKeyData); } @@ -192,44 +140,6 @@ appendPublicKey(std::unique_ptr msg, const Key &key) return result; } -Expected encrypt(const QByteArray &content, const std::vector &encryptionKeys) -{ - QByteArray resultData; - - const QGpgME::Protocol *const proto = QGpgME::openpgp(); - std::unique_ptr job(proto->encryptJob(/* armor = */ true)); - const auto result = job->exec(toGpgME(encryptionKeys), content, /* alwaysTrust = */ true, resultData); - - if (result.error().code()) { - qWarning() << "Encryption failed:" << result.error().asString(); - return makeUnexpected(Error{result.error()}); - } - - return resultData; -} - -Expected signAndEncrypt(const QByteArray &content, - const std::vector &signingKeys, const std::vector &encryptionKeys) -{ - QByteArray resultData; - - const QGpgME::Protocol *const proto = QGpgME::openpgp(); - std::unique_ptr job(proto->signEncryptJob(/* armor = */ true)); - const auto result = job->exec(toGpgME(signingKeys), toGpgME(encryptionKeys), content, /* alwaysTrust = */ true, resultData); - - if (result.first.error().code()) { - qWarning() << "Signing failed:" << result.first.error().asString(); - return makeUnexpected(Error{result.first.error()}); - } - - if (result.second.error().code()) { - qWarning() << "Encryption failed:" << result.second.error().asString(); - return makeUnexpected(Error{result.second.error()}); - } - - return resultData; -} - /** * Create a message part like this (according to RFC 3156 Section 4): * @@ -295,11 +205,7 @@ Expected> createEncryptedEmail(KMime::Content *content, const std::vector &encryptionKeys, const Key &attachedKey, const std::vector &signingKeys = {}) { - auto contentToEncrypt = canonicalizeContent(content); - - auto encryptionResult = signingKeys.empty() ? - encrypt(contentToEncrypt, encryptionKeys) : - signAndEncrypt(contentToEncrypt, signingKeys, encryptionKeys); + auto encryptionResult = signAndEncrypt(canonicalizeContent(content), encryptionKeys, signingKeys); if (!encryptionResult) { return makeUnexpected(Error{encryptionResult.error()}); @@ -316,33 +222,6 @@ createEncryptedEmail(KMime::Content *content, const std::vector &encryption return publicKeyAppendResult; } -/** - * Sign the given content and returns the signing data and the algorithm used - * for integrity check in the "pgp-" format. - */ -Expected> -sign(const QByteArray &content, const std::vector &signingKeys) -{ - QByteArray resultData; - - const QGpgME::Protocol *const proto = QGpgME::openpgp(); - std::unique_ptr job(proto->signJob(/* armor = */ true)); - const auto result = job->exec(toGpgME(signingKeys), content, GpgME::Detached, resultData); - - if (result.error().code()) { - qWarning() << "Signing failed:" << result.error().asString(); - return makeUnexpected(Error{result.error()}); - } - - auto algo = result.createdSignature(0).hashAlgorithmAsString(); - // RFC 3156 Section 5: - // Hash-symbols are constructed [...] by converting the text name to lower - // case and prefixing it with the four characters "pgp-". - auto micAlg = (QString("pgp-") + algo).toLower(); - - return std::pair{resultData, micAlg}; -} - /** * Create a message part like this (according to RFC 3156 Section 5): * @@ -438,66 +317,3 @@ MailCrypto::processCrypto(std::unique_ptr content, const std::ve } } -void MailCrypto::importKeys(const std::vector &keys) -{ - const QGpgME::Protocol *const backend = QGpgME::openpgp(); - Q_ASSERT(backend); - auto *job = backend->importFromKeyserverJob(); - job->exec(toGpgME(keys)); -} - -MailCrypto::ImportResult MailCrypto::importKey(const QByteArray &pkey) -{ - const auto *proto = QGpgME::openpgp(); - std::unique_ptr job(proto->importJob()); - auto result = job->exec(pkey); - return {result.numConsidered(), result.numImported(), result.numUnchanged()}; -} - -static GpgME::KeyListResult listKeys(const QStringList &patterns, bool secretOnly, int keyListMode, std::vector &keys) -{ - QByteArrayList list; - std::transform(patterns.constBegin(), patterns.constEnd(), std::back_inserter(list), [] (const QString &s) { return s.toUtf8(); }); - std::vector pattern; - std::transform(list.constBegin(), list.constEnd(), std::back_inserter(pattern), [] (const QByteArray &s) { return s.constData(); }); - pattern.push_back(0); - - GpgME::initializeLibrary(); - auto ctx = QSharedPointer{GpgME::Context::createForProtocol(GpgME::OpenPGP)}; - ctx->setKeyListMode(keyListMode); - if (const GpgME::Error err = ctx->startKeyListing(pattern.data(), secretOnly)) { - return GpgME::KeyListResult(0, err); - } - - GpgME::Error err; - do { - keys.push_back( Key{ctx->nextKey(err)}); - } while ( !err ); - - keys.pop_back(); - - const GpgME::KeyListResult result = ctx->endKeyListing(); - ctx->cancelPendingOperation(); - return result; -} - -std::vector MailCrypto::findKeys(const QStringList &filter, bool findPrivate, bool remote) -{ - std::vector keys; - GpgME::KeyListResult res = listKeys(filter, findPrivate, remote ? GpgME::Extern : GpgME::Local, keys); - if (res.error()) { - qWarning() << "Failed to lookup keys: " << res.error().asString(); - return {}; - } - qWarning() << "got keys:" << keys.size(); - - for (auto i = keys.begin(); i != keys.end(); ++i) { - qWarning() << "key isnull:" << i->key.isNull() << "isexpired:" << i->key.isExpired(); - qWarning() << "key numuserIds:" << i->key.numUserIDs(); - for (uint k = 0; k < i->key.numUserIDs(); ++k) { - qWarning() << "userIDs:" << i->key.userID(k).email(); - } - } - - return keys; -} 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 @@ #include "framework/src/errors.h" #include -#include #include #include #include #include +#include "crypto.h" namespace MailCrypto { -struct Key { - GpgME::Key key; -}; - -struct Error { - GpgME::Error key; -}; - -Expected> -processCrypto(std::unique_ptr content, const std::vector &signingKeys, - const std::vector &encryptionKeys, const Key &attachedKey); - -std::vector findKeys(const QStringList &filter, bool findPrivate = false, bool remote = false); - -void importKeys(const std::vector &keys); - -struct ImportResult { - int considered; - int imported; - int unchanged; -}; - -ImportResult importKey(const QByteArray &key); +Expected> +processCrypto(std::unique_ptr content, const std::vector &signingKeys, + const std::vector &encryptionKeys, const Crypto::Key &attachedKey); }; // namespace MailCrypto -Q_DECLARE_METATYPE(MailCrypto::Key); - -QDebug 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 KMime::Message::Ptr MailTemplates::createMessage(KMime::Message::Ptr existingMessage, const QStringList &to, const QStringList &cc, const QStringList &bcc, const KMime::Types::Mailbox &from, const QString &subject, const QString &body, bool htmlBody, - const QList &attachments, const std::vector &signingKeys, - const std::vector &encryptionKeys, const MailCrypto::Key &attachedKey) + const QList &attachments, const std::vector &signingKeys, + const std::vector &encryptionKeys, const Crypto::Key &attachedKey) { auto mail = existingMessage; 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 void forward(const KMime::Message::Ptr &origMsg, const std::function &callback); QString plaintextContent(const KMime::Message::Ptr &origMsg); QString body(const KMime::Message::Ptr &msg, bool &isHtml); - KMime::Message::Ptr createMessage(KMime::Message::Ptr existingMessage, const QStringList &to, const QStringList &cc, const QStringList &bcc, const KMime::Types::Mailbox &from, const QString &subject, const QString &body, bool htmlBody, const QList &attachments, const std::vector &signingKeys = {}, const std::vector &encryptionKeys = {}, const MailCrypto::Key &attachedKey = {}); + KMime::Message::Ptr createMessage(KMime::Message::Ptr existingMessage, const QStringList &to, const QStringList &cc, const QStringList &bcc, const KMime::Types::Mailbox &from, const QString &subject, const QString &body, bool htmlBody, const QList &attachments, const std::vector &signingKeys = {}, const std::vector &encryptionKeys = {}, const Crypto::Key &attachedKey = {}); }; 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) find_package(Qt5 COMPONENTS REQUIRED Core Gui) find_package(KF5Mime 4.87.0 CONFIG REQUIRED) -find_package(QGpgme CONFIG REQUIRED) -find_package(Gpgmepp CONFIG REQUIRED) find_package(KF5Codecs CONFIG REQUIRED) +find_package(Gpgme REQUIRED) # target_include_directories does not handle empty include paths include_directories(${GPGME_INCLUDES}) @@ -49,8 +48,8 @@ target_link_libraries(kube_otp target_link_libraries(kube_otp PRIVATE - QGpgme - Gpgmepp + gpgme + mailcrypto KF5::Codecs Qt5::Gui ) 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) ecm_add_test(${_source} util.cpp setupenv.cpp TEST_NAME ${_name} NAME_PREFIX "mimetreeparser-" - LINK_LIBRARIES kube_otp Qt5::Test KF5::Mime Gpgmepp + LINK_LIBRARIES kube_otp Qt5::Test KF5::Mime gpgme ) endmacro () @@ -24,7 +24,7 @@ macro(add_mimetreeparser_class_unittest _source _additionalSource) ecm_add_test(${_source} ${_additionalSource} TEST_NAME ${_name} NAME_PREFIX "mimetreeparser-" - LINK_LIBRARIES kube_otp Qt5::Test KF5::Mime Gpgmepp + LINK_LIBRARIES kube_otp Qt5::Test KF5::Mime gpgme ) endmacro () @@ -37,7 +37,7 @@ macro(add_mimetreeparser_crypto_unittest _source) kube_otp Qt5::Test KF5::Mime - Gpgmepp + gpgme ) add_gpg_crypto_test(${_name} mimetreeparser-${_name}) endmacro () 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 @@ #include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - +#include #include -#include using namespace MimeTreeParser; - -static GpgME::Data fromBA(const QByteArray &ba) -{ - return {ba.data(), static_cast(ba.size()), false}; -} - - -static GpgME::Protocol toGpgMe(CryptoProtocol p) -{ - switch (p) { - case UnknownProtocol: - return GpgME::UnknownProtocol; - case CMS: - return GpgME::CMS; - case OpenPGP: - return GpgME::OpenPGP; - } - return GpgME::UnknownProtocol; -} - -static QSharedPointer gpgContext(CryptoProtocol protocol) -{ - GpgME::initializeLibrary(); - auto error = GpgME::checkEngine(toGpgMe(protocol)); - if (error) { - qWarning() << "Engine check failed: " << error.asString(); - } - auto ctx = QSharedPointer(GpgME::Context::createForProtocol(toGpgMe(protocol))); - Q_ASSERT(ctx); - return ctx; -} - -static GpgME::VerificationResult verifyDetachedSignature(CryptoProtocol protocol, const QByteArray &signature, const QByteArray &text) -{ - return gpgContext(protocol)->verifyDetachedSignature(fromBA(signature), fromBA(text)); -} - -static GpgME::VerificationResult verifyOpaqueSignature(CryptoProtocol protocol, const QByteArray &signature, QByteArray &outdata) -{ - QGpgME::QByteArrayDataProvider out; - GpgME::Data wrapper(&out); - const auto result = gpgContext(protocol)->verifyOpaqueSignature(fromBA(signature), wrapper); - outdata = out.data(); - return result; -} - - -static std::pair decryptAndVerify(CryptoProtocol protocol, const QByteArray &ciphertext, QByteArray &outdata) -{ - QGpgME::QByteArrayDataProvider out; - GpgME::Data wrapper(&out); - const std::pair res = gpgContext(protocol)->decryptAndVerify(fromBA(ciphertext), wrapper); - outdata = out.data(); - return res; -} - -static void importKeys(CryptoProtocol protocol, const QByteArray &certData) -{ - gpgContext(protocol)->importKeys(fromBA(certData)); -} - -static GpgME::KeyListResult listKeys(CryptoProtocol protocol, const char *pattern, bool secretOnly, std::vector &keys) { - auto ctx = gpgContext(protocol); - if (const GpgME::Error err = ctx->startKeyListing(pattern, secretOnly)) { - return GpgME::KeyListResult( 0, err ); - } - - GpgME::Error err; - do { - keys.push_back( ctx->nextKey(err)); - } while ( !err ); - - keys.pop_back(); - - const GpgME::KeyListResult result = ctx->endKeyListing(); - ctx->cancelPendingOperation(); - return result; -} - +using namespace Crypto; //------MessagePart----------------------- MessagePart::MessagePart(ObjectTreeParser *otp, const QString &text, KMime::Content *node) @@ -771,10 +681,7 @@ SignedMessagePart::SignedMessagePart(ObjectTreeParser *otp, { mMetaData.isSigned = true; mMetaData.isGoodSignature = false; - //FIXME - // mMetaData.keyTrust = GpgME::Signature::Unknown; mMetaData.status = tr("Wrong Crypto Plug-In."); - mMetaData.status_code = GPGME_SIG_STAT_NONE; } SignedMessagePart::~SignedMessagePart() @@ -792,99 +699,65 @@ bool SignedMessagePart::isSigned() const return mMetaData.isSigned; } -static int signatureToStatus(const GpgME::Signature &sig) -{ - switch (sig.status().code()) { - case GPG_ERR_NO_ERROR: - return GPGME_SIG_STAT_GOOD; - case GPG_ERR_BAD_SIGNATURE: - return GPGME_SIG_STAT_BAD; - case GPG_ERR_NO_PUBKEY: - return GPGME_SIG_STAT_NOKEY; - case GPG_ERR_NO_DATA: - return GPGME_SIG_STAT_NOSIG; - case GPG_ERR_SIG_EXPIRED: - return GPGME_SIG_STAT_GOOD_EXP; - case GPG_ERR_KEY_EXPIRED: - return GPGME_SIG_STAT_GOOD_EXPKEY; - default: - return GPGME_SIG_STAT_ERROR; - } -} -QString prettifyDN(const char *uid) +static QString prettifyDN(const char *uid) { - return QGpgME::DN(uid).prettyDN(); + // We used to use QGpgME::DN::prettyDN here. But I'm not sure what we actually need it for. + return QString::fromUtf8(uid); } -void SignedMessagePart::sigStatusToMetaData(const GpgME::Signature &signature) +void SignedMessagePart::sigStatusToMetaData(const Signature &signature) { - GpgME::Key key; - mMetaData.status_code = signatureToStatus(signature); - mMetaData.isGoodSignature = mMetaData.status_code & GPGME_SIG_STAT_GOOD; + mMetaData.isGoodSignature = signature.status & GPG_ERR_NO_ERROR; // save extended signature status flags - auto summary = signature.summary(); - mMetaData.keyMissing = summary & GpgME::Signature::KeyMissing; - mMetaData.keyExpired = summary & GpgME::Signature::KeyExpired; - mMetaData.keyRevoked = summary & GpgME::Signature::KeyRevoked; - mMetaData.sigExpired = summary & GpgME::Signature::SigExpired; - mMetaData.crlMissing = summary & GpgME::Signature::CrlMissing; - mMetaData.crlTooOld = summary & GpgME::Signature::CrlTooOld; - - if (mMetaData.isGoodSignature && !key.keyID()) { + auto summary = signature.summary; + mMetaData.keyMissing = summary & GPGME_SIGSUM_KEY_MISSING; + mMetaData.keyExpired = summary & GPGME_SIGSUM_KEY_EXPIRED; + mMetaData.keyRevoked = summary & GPGME_SIGSUM_KEY_REVOKED; + mMetaData.sigExpired = summary & GPGME_SIGSUM_SIG_EXPIRED; + mMetaData.crlMissing = summary & GPGME_SIGSUM_CRL_MISSING; + mMetaData.crlTooOld = summary & GPGME_SIGSUM_CRL_TOO_OLD; + + Key key; + if (mMetaData.isGoodSignature) { // Search for the key by its fingerprint so that we can check for trust etc. - std::vector found_keys; - auto res = listKeys(mProtocol, signature.fingerprint(), false, found_keys); - if (res.error()) { - qCDebug(MIMETREEPARSER_LOG) << "Error while searching key for Fingerprint: " << signature.fingerprint(); - } - if (found_keys.size() > 1) { + const auto keys = findKeys({signature.fingerprint}); + if (keys.size() > 1) { // Should not happen - qCDebug(MIMETREEPARSER_LOG) << "Oops: Found more then one Key for Fingerprint: " << signature.fingerprint(); + qCDebug(MIMETREEPARSER_LOG) << "Oops: Found more then one Key for Fingerprint: " << signature.fingerprint; } - if (found_keys.empty()) { + if (keys.empty()) { // Should not happen at this point - qCWarning(MIMETREEPARSER_LOG) << "Oops: Found no Key for Fingerprint: " << signature.fingerprint(); + qCWarning(MIMETREEPARSER_LOG) << "Oops: Found no Key for Fingerprint: " << signature.fingerprint; } else { - key = found_keys[0]; + key = keys[0]; } } - if (key.keyID()) { - mMetaData.keyId = key.keyID(); - } + mMetaData.keyId = key.keyId; if (mMetaData.keyId.isEmpty()) { - mMetaData.keyId = signature.fingerprint(); - } - auto keyTrust = signature.validity(); - mMetaData.keyIsTrusted = keyTrust & GpgME::Signature::Full || keyTrust & GpgME::Signature::Ultimate; - if (key.numUserIDs() > 0 && key.userID(0).id()) { - mMetaData.signer = prettifyDN(key.userID(0).id()); - } - for (uint iMail = 0; iMail < key.numUserIDs(); ++iMail) { - // The following if /should/ always result in TRUE but we - // won't trust implicitely the plugin that gave us these data. - if (key.userID(iMail).email()) { - QString email = QString::fromUtf8(key.userID(iMail).email()); - // ### work around gpgme 0.3.QString text() const Q_DECL_OVERRIDE;x / cryptplug bug where the - // ### email addresses are specified as angle-addr, not addr-spec: - if (email.startsWith(QLatin1Char('<')) && email.endsWith(QLatin1Char('>'))) { - email = email.mid(1, email.length() - 2); - } - if (!email.isEmpty()) { - mMetaData.signerMailAddresses.append(email); - } + mMetaData.keyId = signature.fingerprint; + } + mMetaData.keyIsTrusted = signature.validity == GPGME_VALIDITY_FULL || signature.validity == GPGME_VALIDITY_ULTIMATE; + if (!key.userIds.empty()) { + mMetaData.signer = prettifyDN(key.userIds[0].id); + } + for (const auto &userId : key.userIds) { + QString email = QString::fromUtf8(userId.email); + // ### work around gpgme 0.3.QString text() const Q_DECL_OVERRIDE;x / cryptplug bug where the + // ### email addresses are specified as angle-addr, not addr-spec: + if (email.startsWith(QLatin1Char('<')) && email.endsWith(QLatin1Char('>'))) { + email = email.mid(1, email.length() - 2); + } + if (!email.isEmpty()) { + mMetaData.signerMailAddresses.append(email); } } - if (signature.creationTime()) { - mMetaData.creationTime.setTime_t(signature.creationTime()); - } else { - mMetaData.creationTime = QDateTime(); - } + mMetaData.creationTime = signature.creationTime; if (mMetaData.signer.isEmpty()) { - if (key.numUserIDs() > 0 && key.userID(0).name()) { - mMetaData.signer = prettifyDN(key.userID(0).name()); + if (!key.userIds.empty()) { + mMetaData.signer = prettifyDN(key.userIds[0].name); } if (!mMetaData.signerMailAddresses.empty()) { if (mMetaData.signer.isEmpty()) { @@ -924,18 +797,13 @@ void SignedMessagePart::startVerificationDetached(const QByteArray &text, KMime: } mMetaData.isSigned = false; - //FIXME - // mMetaData.keyTrust = GpgME::Signature::Unknown; mMetaData.status = tr("Wrong Crypto Plug-In."); - mMetaData.status_code = GPGME_SIG_STAT_NONE; if (!signature.isEmpty()) { - auto result = verifyDetachedSignature(mProtocol, signature, text); - setVerificationResult(result, false, text); + setVerificationResult(verifyDetachedSignature(mProtocol, signature, text), false, text); } else { QByteArray outdata; - auto result = verifyOpaqueSignature(mProtocol, text, outdata); - setVerificationResult(result, false, outdata); + setVerificationResult(verifyOpaqueSignature(mProtocol, text, outdata), false, outdata); } if (!mMetaData.isSigned) { @@ -943,11 +811,11 @@ void SignedMessagePart::startVerificationDetached(const QByteArray &text, KMime: } } -void SignedMessagePart::setVerificationResult(const GpgME::VerificationResult &result, bool parseText, const QByteArray &plainText) +void SignedMessagePart::setVerificationResult(const VerificationResult &result, bool parseText, const QByteArray &plainText) { - auto signatures = result.signatures(); + auto signatures = result.signatures; // FIXME - // mMetaData.auditLogError = result.error(); + // mMetaData.auditLogError = result.error; if (!signatures.empty()) { mMetaData.isSigned = true; sigStatusToMetaData(signatures.front()); @@ -994,10 +862,7 @@ EncryptedMessagePart::EncryptedMessagePart(ObjectTreeParser *otp, mMetaData.isGoodSignature = false; mMetaData.isEncrypted = false; mMetaData.isDecryptable = false; - //FIXME - // mMetaData.keyTrust = GpgME::Signature::Unknown; mMetaData.status = tr("Wrong Crypto Plug-In."); - mMetaData.status_code = GPGME_SIG_STAT_NONE; } EncryptedMessagePart::~EncryptedMessagePart() @@ -1055,59 +920,61 @@ bool EncryptedMessagePart::okDecryptMIME(KMime::Content &data) const QByteArray ciphertext = data.decodedContent(); QByteArray plainText; - const auto res = decryptAndVerify(mProtocol, ciphertext, plainText); - const GpgME::DecryptionResult &decryptResult = res.first; - const GpgME::VerificationResult &verifyResult = res.second; - mMetaData.isSigned = verifyResult.signatures().size() > 0; + DecryptionResult decryptResult; + VerificationResult verifyResult; + std::tie(decryptResult, verifyResult) = decryptAndVerify(mProtocol, ciphertext, plainText); + mMetaData.isSigned = verifyResult.signatures.size() > 0; - if (verifyResult.signatures().size() > 0) { + if (verifyResult.signatures.size() > 0) { //We simply attach a signed message part to indicate that this content is also signed auto subPart = SignedMessagePart::Ptr(new SignedMessagePart(mOtp, QString::fromUtf8(plainText), mProtocol, mFromAddress, nullptr, nullptr)); subPart->setVerificationResult(verifyResult, true, plainText); appendSubPart(subPart); } - if (decryptResult.error() && mMetaData.isSigned) { + if (decryptResult.error && mMetaData.isSigned) { //Only a signed part mMetaData.isEncrypted = false; mDecryptedData = plainText; return true; } - if (mMetaData.isEncrypted && decryptResult.numRecipients() > 0) { - mMetaData.keyId = decryptResult.recipient(0).keyID(); + if (mMetaData.isEncrypted && decryptResult.recipients.size()) { + mMetaData.keyId = decryptResult.recipients.at(0).keyId; } - if (!decryptResult.error()) { + if (!decryptResult.error) { mDecryptedData = plainText; setText(QString::fromUtf8(mDecryptedData.constData())); } else { - mMetaData.isEncrypted = decryptResult.error().code() != GPG_ERR_NO_DATA; - mMetaData.errorText = QString::fromLocal8Bit(decryptResult.error().asString()); - - std::stringstream ss; - ss << decryptResult << '\n' << verifyResult; - qWarning() << "Decryption failed: " << ss.str().c_str(); - - bool passphraseError = decryptResult.error().isCanceled() || decryptResult.error().code() == GPG_ERR_NO_SECKEY; - - auto noSecKey = true; - foreach (const GpgME::DecryptionResult::Recipient &recipient, decryptResult.recipients()) { - noSecKey &= (recipient.status().code() == GPG_ERR_NO_SECKEY); - } - if (!passphraseError && !noSecKey) { // GpgME do not detect passphrase error correctly + const auto errorCode = decryptResult.error.errorCode(); + mMetaData.isEncrypted = errorCode != GPG_ERR_NO_DATA; + qWarning() << "Failed to decrypt : " << decryptResult.error; + + const bool noSecretKeyAvilable = [&] { + foreach (const auto &recipient, decryptResult.recipients) { + if (!(recipient.status.errorCode() == GPG_ERR_NO_SECKEY)) { + return false; + } + } + return true; + }(); + bool passphraseError = errorCode == GPG_ERR_CANCELED || errorCode == GPG_ERR_NO_SECKEY; + //We only get a decryption failed error when we enter the wrong passphrase.... + if (!passphraseError && !noSecretKeyAvilable) { passphraseError = true; } - if(noSecKey) { + if(noSecretKeyAvilable) { mError = NoKeyError; mMetaData.errorText = tr("Could not decrypt the data. ") + tr("No key found for recepients."); } else if (passphraseError) { mError = PassphraseError; + // mMetaData.errorText = QString::fromLocal8Bit(decryptResult.error().asString()); } else { mError = UnknownError; - mMetaData.errorText = tr("Could not decrypt the data. ") - + tr("Error: %1").arg(mMetaData.errorText); + mMetaData.errorText = tr("Could not decrypt the data. "); + // + tr("Error: %1").arg(QString::fromLocal8Bit(decryptResult.error().asString())); } return false; } 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 @@ #include "util.h" #include "enums.h" #include "partmetadata.h" +#include #include @@ -32,13 +33,6 @@ class QTextCodec; class PartPrivate; -namespace GpgME -{ -class ImportResult; -class VerificationResult; -class Signature; -} - namespace KMime { class Content; @@ -55,11 +49,10 @@ class MultiPartAlternativeBodyPartFormatter; class SignedMessagePart; class EncryptedMessagePart; -enum CryptoProtocol { - UnknownProtocol, - OpenPGP, - CMS -}; +using Crypto::CryptoProtocol; +using Crypto::CryptoProtocol::CMS; +using Crypto::CryptoProtocol::OpenPGP; +using Crypto::CryptoProtocol::UnknownProtocol; class MessagePart : public QObject { @@ -366,8 +359,8 @@ public: QString htmlContent() const Q_DECL_OVERRIDE; private: - void sigStatusToMetaData(const GpgME::Signature &signature); - void setVerificationResult(const GpgME::VerificationResult &result, bool parseText, const QByteArray &plainText); + void sigStatusToMetaData(const Crypto::Signature &signature); + void setVerificationResult(const Crypto::VerificationResult &result, bool parseText, const QByteArray &plainText); protected: 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: QByteArray keyId; bool keyIsTrusted = false; QString status; // to be used for unknown plug-ins - int status_code; // to be used for i18n of OpenPGP and S/MIME CryptPlugs QString errorText; QDateTime creationTime; 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 Qt5::Core Qt5::Test KF5::Mime - Gpgmepp - QGpgme + gpgme ) ecm_add_test(gpgerrortest.cpp TEST_NAME "gpgerrortest" NAME_PREFIX "mimetreeparser-" - LINK_LIBRARIES Qt5::Core Qt5::Test kube_otp Gpgmepp QGpgme + LINK_LIBRARIES Qt5::Core Qt5::Test kube_otp gpgme ) 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: QString body = "body"; QList attachments; - auto keys = MailCrypto::findKeys({}, true, false); + auto keys = Crypto::findKeys({}, true, false); auto result = MailTemplates::createMessage({}, to, cc, bcc, from, subject, body, false, attachments, keys, {}, keys[0]); QVERIFY(result); @@ -398,7 +398,7 @@ private slots: QString body = "body"; QList attachments = {{"name", "filename", "mimetype", true, "inlineAttachment"}, {"name", "filename", "mimetype", false, "nonInlineAttachment"}}; - auto result = MailTemplates::createMessage({}, to, cc, bcc, from, subject, body, false, attachments, MailCrypto::findKeys({}, true, false)); + auto result = MailTemplates::createMessage({}, to, cc, bcc, from, subject, body, false, attachments, Crypto::findKeys({}, true, false)); QVERIFY(result); QCOMPARE(result->subject()->asUnicodeString(), subject); -- cgit v1.2.3