diff --git a/.kde-ci.yml b/.kde-ci.yml
index adef041..801e0f8 100644
--- a/.kde-ci.yml
+++ b/.kde-ci.yml
@@ -1,14 +1,15 @@
 # SPDX-FileCopyrightText: None
 # SPDX-License-Identifier: CC0-1.0
 
 Dependencies:
 - 'on': ['Linux', 'FreeBSD']
   'require':
     'frameworks/extra-cmake-modules': '@latest-kf6'
     'frameworks/kcalendarcore' : '@latest-kf6'
     'frameworks/kwidgetsaddons' : '@latest-kf6'
     'pim/kmime' : '@latest-kf6'
     'pim/kmbox' : '@latest-kf6'
+    'pim/libkleo' : '@latest-kf6'
 
 Options:
   require-passing-tests-on: ['Linux']
diff --git a/CMakeLists.txt b/CMakeLists.txt
index bdfa232..0e66a73 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,106 +1,109 @@
 # SPDX-FileCopyrightText: 2023 Carl Schwan <carl.schwan@gnupg.com>
 # SPDX-License-Identifier: BSD-3-Clause
 
 cmake_minimum_required(VERSION 3.16 FATAL_ERROR)
 
 set(PIM_VERSION "5.240.40")
 project(MimeTreeParserNG VERSION ${PIM_VERSION})
 
 # ECM setup
 set(KF_MIN_VERSION "5.240.0")
 find_package(ECM ${KF_MIN_VERSION} CONFIG REQUIRED)
 set(CMAKE_MODULE_PATH
     ${CMAKE_MODULE_PATH}
     ${ECM_MODULE_PATH}
     ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules
 )
 set(QT_REQUIRED_VERSION "6.4.0")
 
 include(KDEInstallDirs)
 include(KDECMakeSettings)
 include(KDECompilerSettings NO_POLICY_SCOPE)
 
 include(GenerateExportHeader)
 include(ECMGenerateHeaders)
 include(ECMGeneratePriFile)
 
 include(ECMQmlModule)
 include(ECMSetupVersion)
 include(FeatureSummary)
 include(KDEGitCommitHooks)
 include(KDEClangFormat)
 file(GLOB_RECURSE ALL_CLANG_FORMAT_SOURCE_FILES *.cpp *.h *.c)
 kde_clang_format(${ALL_CLANG_FORMAT_SOURCE_FILES})
 
 include(ECMQtDeclareLoggingCategory)
 include(ECMDeprecationSettings)
 include(ECMAddQch)
 
 set(KF_MIN_VERSION "5.240.0")
 set(KF_MAJOR_VERSION "6")
 set(KPIM_MIME_VERSION "5.240.0")
+set(KPIM_MIME_VERSION "5.240.40")
+set(KPIM_LIBKLEO_VERSION "5.240.40")
 
 ecm_setup_version(PROJECT
     VARIABLE_PREFIX MIMETREEPARSERNG
     VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/mimetreeparserng_version.h"
     PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/KPim6MimeTreeParserCoreConfigVersion.cmake"
     SOVERSION 6
 )
 
 configure_file(mimetreeparserng-version.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/src/mimetreeparserng-version.h @ONLY)
 
 option(BUILD_QCH "Build API documentation in QCH format (for e.g. Qt Assistant, Qt Creator & KDevelop)" OFF)
 add_feature_info(QCH ${BUILD_QCH} "API documentation in QCH format (for e.g. Qt Assistant, Qt Creator & KDevelop)")
 
 if(BUILD_TESTING)
    add_definitions(-DBUILD_TESTING)
 endif()
 
 ########### Find packages ###########
 find_package(Qt6Gui ${QT_MIN_VERSION} CONFIG REQUIRED)
 find_package(KF6I18n ${KF_MIN_VERSION} CONFIG REQUIRED)
 find_package(KF6Codecs ${KF_MIN_VERSION} CONFIG REQUIRED)
 find_package(KF6CalendarCore ${KF_MIN_VERSION} CONFIG REQUIRED)
 find_package(KF6WidgetsAddons ${QT_MIN_VERSION} CONFIG)
 find_package(KPim6Mime ${KPIM_MIME_VERSION} CONFIG REQUIRED)
+find_package(KPim6Libkleo ${KPIM_LIBKLEO_VERSION} CONFIG REQUIRED)
 find_package(Gpgme REQUIRED)
 
 find_package(Qt6Quick ${QT_MIN_VERSION} CONFIG)
 find_package(Qt6Widgets ${QT_MIN_VERSION} CONFIG)
 find_package(Qt6Core5Compat ${QT_MIN_VERSION} CONFIG)
 
 if (BUILD_TESTING)
     find_package(Qt6Test ${QT_MIN_VERSION} CONFIG REQUIRED)
 endif()
 
 ########### Targets ###########
 
 ecm_set_disabled_deprecation_versions(QT 6.4 KF 5.105.0)
 
 option(USE_UNITY_CMAKE_SUPPORT "Use UNITY cmake support (speedup compile time)" OFF)
 
 set(COMPILE_WITH_UNITY_CMAKE_SUPPORT OFF)
 if (USE_UNITY_CMAKE_SUPPORT)
     set(COMPILE_WITH_UNITY_CMAKE_SUPPORT ON)
     add_definitions(-DCOMPILE_WITH_UNITY_CMAKE_SUPPORT)
 endif()
 
 add_subdirectory(src)
 if (BUILD_TESTING)
     add_subdirectory(examples)
 endif()
 
 install(FILES
    ${CMAKE_CURRENT_BINARY_DIR}/mimetreeparserng_version.h
    DESTINATION ${KDE_INSTALL_INCLUDEDIR}/KPim6/MimeTreeParserCore
    COMPONENT Devel
 )
 ecm_qt_install_logging_categories(
     EXPORT MIMETREEPARSERNG
     FILE mimetreeparser2.categories
     DESTINATION ${KDE_INSTALL_LOGGINGCATEGORIESDIR}
 )
 
 kde_configure_git_pre_commit_hook(CHECKS CLANG_FORMAT)
 ki18n_install(po)
 feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES)
diff --git a/examples/qml/fileopener.cpp b/examples/qml/fileopener.cpp
index 30d4752..dac1a10 100644
--- a/examples/qml/fileopener.cpp
+++ b/examples/qml/fileopener.cpp
@@ -1,84 +1,124 @@
 // SPDX-FileCopyrightText: 2023 Carl Schwan <carl.schwan@gnupg.com>
 // SPDX-License-Identifier: LGPL-2.0-or-later
 
 #include "fileopener.h"
 #include <KMbox/MBox>
 #include <MimeTreeParserCore/ObjectTreeParser>
 #include <QDebug>
 #include <QFile>
 #include <QMimeDatabase>
 #include <QMimeType>
 
 void FileOpener::open(const QUrl &url)
 {
     const auto fileName = url.fileName();
     QMimeDatabase db;
     QMimeType mime = db.mimeTypeForFile(fileName);
 
     if (mime.inherits(QStringLiteral("application/mbox"))) {
         KMBox::MBox mbox;
         const auto ok = mbox.load(url.toLocalFile());
         if (ok) {
             const auto entries = mbox.entries();
             if (!entries.isEmpty()) {
                 KMime::Message::Ptr message(mbox.readMessage(entries[0]));
                 Q_EMIT messageOpened(message);
                 return;
             }
         }
     }
 
     QFile file(url.toLocalFile());
     file.open(QIODevice::ReadOnly);
     if (!file.isOpen()) {
         qWarning() << "Could not open file";
         return;
     }
 
     const auto content = file.readAll();
 
     if (content.length() == 0) {
         qWarning() << "File is empty";
         return;
     }
 
     KMime::Message::Ptr message(new KMime::Message);
 
     if (mime.inherits(QStringLiteral("application/pgp-encrypted")) || fileName.endsWith(QStringLiteral(".asc"))) {
         auto contentType = message->contentType();
         contentType->setMimeType("multipart/encrypted");
         contentType->setBoundary(KMime::multiPartBoundary());
         contentType->setParameter(QStringLiteral("protocol"), QStringLiteral("application/pgp-encrypted"));
         contentType->setCategory(KMime::Headers::CCcontainer);
 
         auto cte = message->contentTransferEncoding();
         cte->setEncoding(KMime::Headers::CE7Bit);
         cte->setDecoded(true);
 
         auto pgpEncrypted = new KMime::Content;
         pgpEncrypted->contentType()->setMimeType("application/pgp-encrypted");
         auto contentDisposition = new KMime::Headers::ContentDisposition;
         contentDisposition->setDisposition(KMime::Headers::CDattachment);
         pgpEncrypted->appendHeader(contentDisposition);
         pgpEncrypted->setBody("Version: 1");
         message->addContent(pgpEncrypted);
 
         auto encryptedContent = new KMime::Content;
         encryptedContent->contentType()->setMimeType("application/octet-stream");
         contentDisposition = new KMime::Headers::ContentDisposition;
         contentDisposition->setDisposition(KMime::Headers::CDinline);
         contentDisposition->setFilename(QStringLiteral("msg.asc"));
         encryptedContent->appendHeader(contentDisposition);
         encryptedContent->setBody(content);
         message->addContent(encryptedContent);
 
         message->assemble();
     } else {
-        message->setContent(content);
-        message->parse();
+        int startOfMessage = 0;
+        if (content.startsWith("From ")) {
+            startOfMessage = content.indexOf('\n');
+            if (startOfMessage == -1) {
+                return;
+            }
+            startOfMessage += 1; // the message starts after the '\n'
+        }
+        QVector<KMime::Message::Ptr> listMessages;
+
+        // check for multiple messages in the file
+        int endOfMessage = content.indexOf("\nFrom ", startOfMessage);
+        while (endOfMessage != -1) {
+            auto msg = new KMime::Message;
+            msg->setContent(KMime::CRLFtoLF(content.mid(startOfMessage, endOfMessage - startOfMessage)));
+            msg->parse();
+            if (!msg->hasContent()) {
+                delete msg;
+                msg = nullptr;
+                return;
+            }
+            KMime::Message::Ptr mMsg(msg);
+            listMessages << mMsg;
+            startOfMessage = endOfMessage + 1;
+            endOfMessage = content.indexOf("\nFrom ", startOfMessage);
+        }
+        if (endOfMessage == -1) {
+            endOfMessage = content.length();
+            auto msg = new KMime::Message;
+            msg->setContent(KMime::CRLFtoLF(content.mid(startOfMessage, endOfMessage - startOfMessage)));
+            msg->parse();
+            if (!msg->hasContent()) {
+                delete msg;
+                msg = nullptr;
+                return;
+            }
+            KMime::Message::Ptr mMsg(msg);
+            listMessages << mMsg;
+        }
+        if (listMessages.count() > 0) {
+            message = listMessages[0];
+        }
     }
 
     Q_EMIT messageOpened(message);
 }
 
 #include "moc_fileopener.cpp"
diff --git a/po/ca/mimetreeparser.po b/po/ca/mimetreeparser.po
index 2e456f4..f08ba69 100644
--- a/po/ca/mimetreeparser.po
+++ b/po/ca/mimetreeparser.po
@@ -1,341 +1,464 @@
 # Translation of mimetreeparser.po to Catalan
 # Copyright (C) 2023 This_file_is_part_of_KDE
 # This file is distributed under the license LGPL version 2.1 or
 # version 3 or later versions approved by the membership of KDE e.V.
 #
 # Josep M. Ferrer <txemaq@gmail.com>, 2023.
 msgid ""
 msgstr ""
 "Project-Id-Version: mimetreeparser\n"
 "Report-Msgid-Bugs-To: https://bugs.kde.org\n"
-"POT-Creation-Date: 2023-08-15 00:46+0000\n"
-"PO-Revision-Date: 2023-08-12 11:00+0200\n"
+"POT-Creation-Date: 2023-08-27 00:47+0000\n"
+"PO-Revision-Date: 2023-08-27 11:00+0200\n"
 "Last-Translator: Josep M. Ferrer <txemaq@gmail.com>\n"
 "Language-Team: Catalan <kde-i18n-ca@kde.org>\n"
 "Language: ca\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Lokalize 22.12.3\n"
+"X-Generator: Lokalize 23.04.3\n"
 
-#: core/attachmentmodel.cpp:88
+#: core/attachmentmodel.cpp:90
 #, kde-format
 msgctxt "@title:column"
 msgid "Name"
 msgstr "Nom"
 
-#: core/attachmentmodel.cpp:90
+#: core/attachmentmodel.cpp:92
 #, kde-format
 msgctxt "@title:column"
 msgid "Size"
 msgstr "Mida"
 
-#: core/attachmentmodel.cpp:92
+#: core/attachmentmodel.cpp:94
 #, kde-format
 msgctxt "@title:column"
 msgid "Encrypted"
 msgstr "Encriptat"
 
-#: core/attachmentmodel.cpp:94
+#: core/attachmentmodel.cpp:96
 #, kde-format
 msgctxt "@title:column"
 msgid "Signed"
 msgstr "Signat"
 
-#: core/attachmentmodel.cpp:196
+#: core/attachmentmodel.cpp:198
 #, kde-format
 msgctxt "@info"
 msgid "Failed to save attachment."
 msgstr "Ha fallat en desar l'adjunt."
 
-#: core/attachmentmodel.cpp:228
+#: core/attachmentmodel.cpp:230
 #, kde-format
 msgctxt "@info"
 msgid "Saved the attachment to disk: %1"
 msgstr "S'ha desat l'adjunt al disc: %1"
 
-#: core/attachmentmodel.cpp:245
+#: core/attachmentmodel.cpp:247
 #, kde-format
 msgctxt "@info"
 msgid "Failed to open attachment."
 msgstr "Ha fallat en obrir l'adjunt."
 
-#: core/attachmentmodel.cpp:250
+#: core/attachmentmodel.cpp:252
 #, kde-format
 msgctxt "@info"
 msgid "Failed to save attachment for opening."
 msgstr "Ha fallat en desar l'adjunt per a obrir-lo."
 
-#: core/attachmentmodel.cpp:268
+#: core/attachmentmodel.cpp:273
 #, kde-format
 msgctxt "@info"
 msgid "No keys were found in this attachment"
 msgstr "No s'ha trobat cap clau en aquest adjunt"
 
-#: core/attachmentmodel.cpp:271
+#: core/attachmentmodel.cpp:276
 #, kde-format
 msgctxt "@info"
 msgid "one key imported"
 msgid_plural "%1 keys imported"
 msgstr[0] "s'ha importat una clau"
 msgstr[1] "s'han importat %1 claus"
 
-#: core/attachmentmodel.cpp:274
+#: core/attachmentmodel.cpp:279
 #, kde-format
 msgctxt "@info"
 msgid "one key was already imported"
 msgid_plural "%1 keys were already imported"
 msgstr[0] "ja s'havia importat una clau"
 msgstr[1] "ja s'havien importat %1 claus"
 
 #: core/mailtemplates.cpp:103
 #, kde-format
 msgid "fwd"
 msgstr "reen"
 
 #: core/mailtemplates.cpp:132
 #, kde-format
 msgid "re"
 msgstr "re"
 
 #: core/messageparser.cpp:158
 #, kde-format
 msgctxt "displayed when a mail has unknown sender, receiver or date"
 msgid "Unknown"
 msgstr "Desconegut"
 
-#: core/messagepart.cpp:618 core/messagepart.cpp:709 core/messagepart.cpp:783
+#: core/messagepart.cpp:625 core/messagepart.cpp:660 core/messagepart.cpp:835
 #, kde-format
 msgctxt "@info:status"
 msgid "Wrong Crypto Plug-In."
 msgstr "Connector criptogràfic erroni."
 
-#: core/messagepart.cpp:857
+#: core/messagepart.cpp:986
 #, kde-format
-msgctxt "@info"
-msgid "Could not decrypt the data: no key found for recipients."
-msgstr ""
-"No s'han pogut desencriptar les dades: no s'ha trobat la clau dels "
-"destinataris."
+msgid "No appropriate crypto plug-in was found."
+msgstr "No s'ha trobat cap connector criptogràfic apropiat."
 
-#: core/messagepart.cpp:862
+#: core/messagepart.cpp:988
 #, kde-format
-msgctxt "@info"
-msgid "Could not decrypt the data."
-msgstr "No s'han pogut desencriptar les dades."
+msgid "Crypto plug-in \"%1\" cannot decrypt messages."
+msgstr "El connector criptogràfic «%1» no pot desencriptar missatges."
+
+#: core/messagepart.cpp:990
+#, kde-format
+msgid "Crypto plug-in \"%1\" could not decrypt the data."
+msgstr "El connector criptogràfic «%1» no pot desencriptar les dades."
 
-#: core/partmodel.cpp:483
+#: core/messagepart.cpp:991 widgets/messageviewer.cpp:295
+#, kde-format
+msgid "Error: %1"
+msgstr "Error: %1"
+
+#: core/partmodel.cpp:487
 #, kde-format
 msgctxt "@info:status"
 msgid "No key available."
 msgstr "No hi ha cap clau disponible."
 
-#: core/partmodel.cpp:485
+#: core/partmodel.cpp:489
 #, kde-format
 msgctxt "@info:status"
 msgid "Wrong passphrase."
 msgstr "Frase de pas incorrecta."
 
-#: quick/qml/MailViewer.qml:39
+#: quick/qml/MailViewer.qml:38
 #, kde-format
 msgctxt "@title:window"
 msgid "Message viewer"
 msgstr "Visualitzador de missatges"
 
-#: quick/qml/MailViewer.qml:76
+#: quick/qml/MailViewer.qml:75
 #, kde-format
 msgctxt "@label"
 msgid "Subject:"
 msgstr "Assumpte:"
 
-#: quick/qml/MailViewer.qml:98
+#: quick/qml/MailViewer.qml:96
 #, kde-format
 msgctxt "@label"
 msgid "From:"
 msgstr "De:"
 
-#: quick/qml/MailViewer.qml:115
+#: quick/qml/MailViewer.qml:112
 #, kde-format
 msgctxt "@label"
 msgid "Sender:"
 msgstr "Remitent:"
 
-#: quick/qml/MailViewer.qml:132
+#: quick/qml/MailViewer.qml:128
 #, kde-format
 msgctxt "@label"
 msgid "To:"
 msgstr "A:"
 
-#: quick/qml/MailViewer.qml:190
+#: quick/qml/MailViewer.qml:185
 #, kde-format
 msgctxt "@action:button"
 msgid "Save attachment"
 msgstr "Desa l'adjunt"
 
 #: quick/qml/private/AttachmentDelegate.qml:56
 #, kde-format
 msgctxt "@action:button"
 msgid "Import key"
 msgstr "Importa la clau"
 
 #: quick/qml/private/Banner.qml:188
 #, kde-format
 msgctxt "@action:button"
 msgid "Close"
 msgstr "Tanca"
 
 #: quick/qml/private/ErrorPart.qml:27
 #, kde-format
 msgid "An error occurred: %1"
 msgstr "S'ha produït un error: %1"
 
-#: quick/qml/private/ICalPart.qml:28
+#: quick/qml/private/ICalPart.qml:27
 #, kde-format
 msgid "This mail contains an invitation"
 msgstr "Aquest correu conté una invitació"
 
-#: quick/qml/private/MailPart.qml:42
+#: quick/qml/private/MailPart.qml:41
 #, kde-format
 msgctxt "@info"
 msgid "sent by %1 on %2"
 msgstr "enviat per %1 a %2"
 
-#: quick/qml/private/MailPartModel.qml:54
+#: quick/qml/private/MailPartModel.qml:55
 #, kde-format
 msgctxt "@label"
 msgid "This message has been signed using the key %1."
 msgstr "Aquest missatge s'ha signat emprant la clau %1."
 
-#: quick/qml/private/MailPartModel.qml:55
+#: quick/qml/private/MailPartModel.qml:56 widgets/messagecontainerwidget.cpp:78
 #, kde-format
 msgctxt "@label"
 msgid "The key details are not available."
 msgstr "Els detalls de la clau no estan disponibles."
 
-#: quick/qml/private/MailPartModel.qml:57
+#: quick/qml/private/MailPartModel.qml:58
 #, kde-format
 msgctxt "@label"
 msgid "This message has been signed using the key %1 by %2."
 msgstr "Aquest missatge s'ha signat emprant la clau %1 de %2."
 
-#: quick/qml/private/MailPartModel.qml:59
+#: quick/qml/private/MailPartModel.qml:60
 #, kde-format
 msgctxt "@label"
 msgid "The key was revoked."
 msgstr "S'ha revocat la clau."
 
-#: quick/qml/private/MailPartModel.qml:62
+#: quick/qml/private/MailPartModel.qml:63
 #, kde-format
 msgctxt "@label"
 msgid "The key has expired."
 msgstr "La clau ha caducat."
 
-#: quick/qml/private/MailPartModel.qml:65
+#: quick/qml/private/MailPartModel.qml:66
 #, kde-format
 msgctxt "@label"
 msgid "You are trusting this key."
 msgstr "Esteu confiant en aquesta clau."
 
-#: quick/qml/private/MailPartModel.qml:68
+#: quick/qml/private/MailPartModel.qml:69
+#: widgets/messagecontainerwidget.cpp:108
 #, kde-format
 msgctxt "@label"
 msgid "The signature is invalid."
 msgstr "La signatura no és vàlida."
 
-#: quick/qml/private/MailPartModel.qml:97
+#: quick/qml/private/MailPartModel.qml:98
+#: widgets/messagecontainerwidget.cpp:205
 #, kde-format
 msgid "This message is encrypted but we don't have the key for it."
 msgstr "Aquest missatge està encriptat, però no es disposa de la seva clau."
 
-#: quick/qml/private/MailPartModel.qml:97
+#: quick/qml/private/MailPartModel.qml:98
 #, kde-format
 msgid "This message is encrypted to the key: %1"
 msgstr "Aquest missatge està encriptat amb la clau: %1"
 
-#: widgets/messageviewer.cpp:54
+#: widgets/messagecontainerwidget.cpp:66
+#, kde-format
+msgctxt "@label"
+msgid ""
+"This message has been signed VS-NfD compliant using the key <a href=\"%1\">"
+"%2</a>."
+msgstr ""
+"Aquest missatge s'ha signat conforme amb VS-NfD utilitzant la clau <a href="
+"\"%1\">%2</a>."
+
+#: widgets/messagecontainerwidget.cpp:73
+#, kde-format
+msgctxt "@label"
+msgid "This message has been signed using the key <a href=\"%1\">%2</a>."
+msgstr "Aquest missatge s'ha signat emprant la clau <a href=\"%1\">%2</a>."
+
+#: widgets/messagecontainerwidget.cpp:81
+#, kde-format
+msgctxt "@label"
+msgid "This message has been signed VS-NfD compliant by %1."
+msgstr "Aquest missatge s'ha signat conforme amb VS-NfD per %1."
+
+#: widgets/messagecontainerwidget.cpp:83
+#, kde-format
+msgctxt "@label"
+msgid "This message has been signed by %1."
+msgstr "Aquest missatge s'ha signat per %1."
+
+#: widgets/messagecontainerwidget.cpp:86
+#, kde-format
+msgctxt "@label"
+msgid "The <a href=\"%1\">key</a> was revoked."
+msgstr "S'ha revocat la <a href=\"%1\">clau</a>."
+
+#: widgets/messagecontainerwidget.cpp:89
+#, kde-format
+msgctxt "@label"
+msgid "The <a href=\"%1\">key</a> was expired."
+msgstr "La <a href=\"%1\">clau</a> ha caducat."
+
+#: widgets/messagecontainerwidget.cpp:94
+#, kde-format
+msgctxt "@label"
+msgid ""
+"The signature is valid, but the <a href=\"%1\">key</a>'s validity is unknown."
+msgstr ""
+"La signatura és vàlida, però no es coneix la validesa de la <a href="
+"\"%1\">clau</a>."
+
+#: widgets/messagecontainerwidget.cpp:97
+#, kde-format
+msgctxt "@label"
+msgid ""
+"The signature is valid and the <a href=\"%1\">key</a> is marginally trusted."
+msgstr ""
+"La signatura és vàlida, però la <a href=\"%1\">clau</a> és de confiança "
+"parcial."
+
+#: widgets/messagecontainerwidget.cpp:99
+#, kde-format
+msgctxt "@label"
+msgid "The signature is valid and the <a href=\"%1\">key</a> is fully trusted."
+msgstr ""
+"La signatura és vàlida i la <a href=\"%1\">clau</a> és de plena confiança."
+
+#: widgets/messagecontainerwidget.cpp:102
+#, kde-format
+msgctxt "@label"
+msgid ""
+"The signature is valid and the <a href=\"%1\">key</a> is ultimately trusted."
+msgstr ""
+"La signatura és vàlida i la <a href=\"%1\">clau</a> és de confiança absoluta."
+
+#: widgets/messagecontainerwidget.cpp:104
+#, kde-format
+msgctxt "@label"
+msgid "The signature is valid, but the <a href=\"%1\">key</a> is untrusted."
+msgstr ""
+"La signatura és vàlida, però la <a href=\"%1\">clau</a> no és de confiança."
+
+#: widgets/messagecontainerwidget.cpp:203
+#, kde-format
+msgid ""
+"This message is VS-NfD compliant encrypted but we don't have the key for it."
+msgstr ""
+"Aquest missatge està encriptat conforme amb VS-NfD, però no es disposa de la "
+"seva clau."
+
+#: widgets/messagecontainerwidget.cpp:209
+#, kde-format
+msgid "This message is VS-NfD compliant encrypted."
+msgstr "Aquest missatge està encriptat conforme amb VS-NfD."
+
+#: widgets/messagecontainerwidget.cpp:211
+#, kde-format
+msgid "This message is encrypted."
+msgstr "Aquest missatge està encriptat."
+
+#: widgets/messagecontainerwidget.cpp:220
+#, kde-format
+msgid "The message is encrypted for the following keys:"
+msgstr "Aquest missatge està encriptat amb les claus següents:"
+
+#: widgets/messagecontainerwidget.cpp:237
+#, kde-format
+msgid "Unknown key"
+msgstr "Clau desconeguda"
+
+#: widgets/messageviewer.cpp:57
 #, kde-format
 msgid "&Save Attachment As..."
 msgstr "De&sa l'adjunt com a..."
 
-#: widgets/messageviewer.cpp:59
+#: widgets/messageviewer.cpp:62
 #, kde-format
 msgctxt "to open"
 msgid "Open"
 msgstr "Obre"
 
-#: widgets/messageviewer.cpp:64
+#: widgets/messageviewer.cpp:67
 #, kde-format
 msgctxt "@action:inmenu"
 msgid "Import public key"
 msgstr "Importa la clau pública"
 
-#: widgets/messageviewer.cpp:192
+#: widgets/messageviewer.cpp:236
 #, kde-format
 msgid "Invitation"
 msgstr "Invitació"
 
-#: widgets/messageviewer.cpp:195
+#: widgets/messageviewer.cpp:239
 #, kde-format
 msgid "&Summary:"
 msgstr "&Resum:"
 
-#: widgets/messageviewer.cpp:196
+#: widgets/messageviewer.cpp:240
 #, kde-format
 msgid "&Organizer:"
 msgstr "&Organitzador:"
 
-#: widgets/messageviewer.cpp:198
+#: widgets/messageviewer.cpp:242
 #, kde-format
 msgid "&Location:"
 msgstr "&Emplaçament:"
 
-#: widgets/messageviewer.cpp:200
+#: widgets/messageviewer.cpp:244
 #, kde-format
 msgid "&Start date:"
 msgstr "Data d'&inici:"
 
-#: widgets/messageviewer.cpp:202
+#: widgets/messageviewer.cpp:246
 #, kde-format
 msgid "&End date:"
 msgstr "Data &final:"
 
-#: widgets/messageviewer.cpp:205
+#: widgets/messageviewer.cpp:249
 #, kde-format
 msgid "&Details:"
 msgstr "&Detalls:"
 
-#: widgets/messageviewer.cpp:215
+#: widgets/messageviewer.cpp:271
 #, kde-format
 msgid "Encapsulated email"
 msgstr "Correu electrònic encapsulat"
 
-#: widgets/messageviewer.cpp:223
+#: widgets/messageviewer.cpp:279
 #, kde-format
 msgid "From:"
 msgstr "De:"
 
-#: widgets/messageviewer.cpp:224
+#: widgets/messageviewer.cpp:280
 #, kde-format
 msgid "Date:"
 msgstr "Data:"
 
-#: widgets/messageviewer.cpp:237
-#, kde-format
-msgid "Error: %1"
-msgstr "Error: %1"
-
-#: widgets/messageviewer.cpp:257
+#: widgets/messageviewer.cpp:315
 #, kde-format
 msgid "&Subject:"
 msgstr "A&ssumpte:"
 
-#: widgets/messageviewer.cpp:260
+#: widgets/messageviewer.cpp:318
 #, kde-format
 msgid "&From:"
 msgstr "&De:"
 
-#: widgets/messageviewer.cpp:263
+#: widgets/messageviewer.cpp:321
 #, kde-format
 msgid "&To:"
 msgstr "&A:"
+
+#: widgets/urlhandler.cpp:37
+#, kde-format
+msgid "Could not start certificate manager. Please check your installation."
+msgstr ""
+"No s'ha pogut iniciar el gestor de certificats. Comproveu aquesta "
+"instal·lació."
+
+#: widgets/urlhandler.cpp:39
+#, kde-format
+msgid "KMail Error"
+msgstr "S'ha produït un error al KMail"
diff --git a/po/ca@valencia/mimetreeparser.po b/po/ca@valencia/mimetreeparser.po
index d260e0b..7093dc5 100644
--- a/po/ca@valencia/mimetreeparser.po
+++ b/po/ca@valencia/mimetreeparser.po
@@ -1,341 +1,463 @@
 # Translation of mimetreeparser.po to Catalan (Valencian)
 # Copyright (C) 2023 This_file_is_part_of_KDE
 # This file is distributed under the license LGPL version 2.1 or
 # version 3 or later versions approved by the membership of KDE e.V.
 #
 # Josep M. Ferrer <txemaq@gmail.com>, 2023.
 msgid ""
 msgstr ""
 "Project-Id-Version: mimetreeparser\n"
 "Report-Msgid-Bugs-To: https://bugs.kde.org\n"
-"POT-Creation-Date: 2023-08-15 00:46+0000\n"
-"PO-Revision-Date: 2023-08-12 11:00+0200\n"
+"POT-Creation-Date: 2023-08-27 00:47+0000\n"
+"PO-Revision-Date: 2023-08-27 11:00+0200\n"
 "Last-Translator: Josep M. Ferrer <txemaq@gmail.com>\n"
 "Language-Team: Catalan <kde-i18n-ca@kde.org>\n"
 "Language: ca@valencia\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Lokalize 22.12.3\n"
+"X-Generator: Lokalize 23.04.3\n"
 
-#: core/attachmentmodel.cpp:88
+#: core/attachmentmodel.cpp:90
 #, kde-format
 msgctxt "@title:column"
 msgid "Name"
 msgstr "Nom"
 
-#: core/attachmentmodel.cpp:90
+#: core/attachmentmodel.cpp:92
 #, kde-format
 msgctxt "@title:column"
 msgid "Size"
 msgstr "Mida"
 
-#: core/attachmentmodel.cpp:92
+#: core/attachmentmodel.cpp:94
 #, kde-format
 msgctxt "@title:column"
 msgid "Encrypted"
 msgstr "Encriptat"
 
-#: core/attachmentmodel.cpp:94
+#: core/attachmentmodel.cpp:96
 #, kde-format
 msgctxt "@title:column"
 msgid "Signed"
 msgstr "Signat"
 
-#: core/attachmentmodel.cpp:196
+#: core/attachmentmodel.cpp:198
 #, kde-format
 msgctxt "@info"
 msgid "Failed to save attachment."
 msgstr "No s'ha pogut guardar l'adjunt."
 
-#: core/attachmentmodel.cpp:228
+#: core/attachmentmodel.cpp:230
 #, kde-format
 msgctxt "@info"
 msgid "Saved the attachment to disk: %1"
 msgstr "S'ha guardat l'adjunt al disc: %1"
 
-#: core/attachmentmodel.cpp:245
+#: core/attachmentmodel.cpp:247
 #, kde-format
 msgctxt "@info"
 msgid "Failed to open attachment."
 msgstr "No s'ha pogut obrir l'adjunt."
 
-#: core/attachmentmodel.cpp:250
+#: core/attachmentmodel.cpp:252
 #, kde-format
 msgctxt "@info"
 msgid "Failed to save attachment for opening."
 msgstr "No s'ha pogut guardar l'adjunt per a obrir-lo."
 
-#: core/attachmentmodel.cpp:268
+#: core/attachmentmodel.cpp:273
 #, kde-format
 msgctxt "@info"
 msgid "No keys were found in this attachment"
 msgstr "No s'ha trobat cap clau en este adjunt"
 
-#: core/attachmentmodel.cpp:271
+#: core/attachmentmodel.cpp:276
 #, kde-format
 msgctxt "@info"
 msgid "one key imported"
 msgid_plural "%1 keys imported"
 msgstr[0] "s'ha importat una clau"
 msgstr[1] "s'han importat %1 claus"
 
-#: core/attachmentmodel.cpp:274
+#: core/attachmentmodel.cpp:279
 #, kde-format
 msgctxt "@info"
 msgid "one key was already imported"
 msgid_plural "%1 keys were already imported"
 msgstr[0] "ja s'havia importat una clau"
 msgstr[1] "ja s'havien importat %1 claus"
 
 #: core/mailtemplates.cpp:103
 #, kde-format
 msgid "fwd"
 msgstr "reen"
 
 #: core/mailtemplates.cpp:132
 #, kde-format
 msgid "re"
 msgstr "re"
 
 #: core/messageparser.cpp:158
 #, kde-format
 msgctxt "displayed when a mail has unknown sender, receiver or date"
 msgid "Unknown"
 msgstr "Desconegut"
 
-#: core/messagepart.cpp:618 core/messagepart.cpp:709 core/messagepart.cpp:783
+#: core/messagepart.cpp:625 core/messagepart.cpp:660 core/messagepart.cpp:835
 #, kde-format
 msgctxt "@info:status"
 msgid "Wrong Crypto Plug-In."
 msgstr "Connector criptogràfic erroni."
 
-#: core/messagepart.cpp:857
+#: core/messagepart.cpp:986
 #, kde-format
-msgctxt "@info"
-msgid "Could not decrypt the data: no key found for recipients."
-msgstr ""
-"No s'han pogut desencriptar les dades: no s'ha trobat la clau dels "
-"destinataris."
+msgid "No appropriate crypto plug-in was found."
+msgstr "No s'ha trobat cap connector criptogràfic apropiat."
 
-#: core/messagepart.cpp:862
+#: core/messagepart.cpp:988
 #, kde-format
-msgctxt "@info"
-msgid "Could not decrypt the data."
-msgstr "No s'han pogut desencriptar les dades."
+msgid "Crypto plug-in \"%1\" cannot decrypt messages."
+msgstr "El connector criptogràfic «%1» no pot desencriptar missatges."
+
+#: core/messagepart.cpp:990
+#, kde-format
+msgid "Crypto plug-in \"%1\" could not decrypt the data."
+msgstr "El connector criptogràfic «%1» no pot desencriptar les dades."
 
-#: core/partmodel.cpp:483
+#: core/messagepart.cpp:991 widgets/messageviewer.cpp:295
+#, kde-format
+msgid "Error: %1"
+msgstr "S'ha produït un error: %1"
+
+#: core/partmodel.cpp:487
 #, kde-format
 msgctxt "@info:status"
 msgid "No key available."
 msgstr "No hi ha cap clau disponible."
 
-#: core/partmodel.cpp:485
+#: core/partmodel.cpp:489
 #, kde-format
 msgctxt "@info:status"
 msgid "Wrong passphrase."
 msgstr "Frase de pas incorrecta."
 
-#: quick/qml/MailViewer.qml:39
+#: quick/qml/MailViewer.qml:38
 #, kde-format
 msgctxt "@title:window"
 msgid "Message viewer"
 msgstr "Visor de missatges"
 
-#: quick/qml/MailViewer.qml:76
+#: quick/qml/MailViewer.qml:75
 #, kde-format
 msgctxt "@label"
 msgid "Subject:"
 msgstr "Assumpte:"
 
-#: quick/qml/MailViewer.qml:98
+#: quick/qml/MailViewer.qml:96
 #, kde-format
 msgctxt "@label"
 msgid "From:"
 msgstr "De:"
 
-#: quick/qml/MailViewer.qml:115
+#: quick/qml/MailViewer.qml:112
 #, kde-format
 msgctxt "@label"
 msgid "Sender:"
 msgstr "Remitent:"
 
-#: quick/qml/MailViewer.qml:132
+#: quick/qml/MailViewer.qml:128
 #, kde-format
 msgctxt "@label"
 msgid "To:"
 msgstr "A:"
 
-#: quick/qml/MailViewer.qml:190
+#: quick/qml/MailViewer.qml:185
 #, kde-format
 msgctxt "@action:button"
 msgid "Save attachment"
 msgstr "Guarda l'adjunt"
 
 #: quick/qml/private/AttachmentDelegate.qml:56
 #, kde-format
 msgctxt "@action:button"
 msgid "Import key"
 msgstr "Importa la clau"
 
 #: quick/qml/private/Banner.qml:188
 #, kde-format
 msgctxt "@action:button"
 msgid "Close"
 msgstr "Tanca"
 
 #: quick/qml/private/ErrorPart.qml:27
 #, kde-format
 msgid "An error occurred: %1"
 msgstr "S'ha produït un error: %1"
 
-#: quick/qml/private/ICalPart.qml:28
+#: quick/qml/private/ICalPart.qml:27
 #, kde-format
 msgid "This mail contains an invitation"
 msgstr "Este correu conté una invitació"
 
-#: quick/qml/private/MailPart.qml:42
+#: quick/qml/private/MailPart.qml:41
 #, kde-format
 msgctxt "@info"
 msgid "sent by %1 on %2"
 msgstr "enviat per %1 a %2"
 
-#: quick/qml/private/MailPartModel.qml:54
+#: quick/qml/private/MailPartModel.qml:55
 #, kde-format
 msgctxt "@label"
 msgid "This message has been signed using the key %1."
 msgstr "Este missatge s'ha signat emprant la clau %1."
 
-#: quick/qml/private/MailPartModel.qml:55
+#: quick/qml/private/MailPartModel.qml:56 widgets/messagecontainerwidget.cpp:78
 #, kde-format
 msgctxt "@label"
 msgid "The key details are not available."
 msgstr "Els detalls de la clau no estan disponibles."
 
-#: quick/qml/private/MailPartModel.qml:57
+#: quick/qml/private/MailPartModel.qml:58
 #, kde-format
 msgctxt "@label"
 msgid "This message has been signed using the key %1 by %2."
 msgstr "Este missatge s'ha signat emprant la clau %1 de %2."
 
-#: quick/qml/private/MailPartModel.qml:59
+#: quick/qml/private/MailPartModel.qml:60
 #, kde-format
 msgctxt "@label"
 msgid "The key was revoked."
 msgstr "S'ha revocat la clau."
 
-#: quick/qml/private/MailPartModel.qml:62
+#: quick/qml/private/MailPartModel.qml:63
 #, kde-format
 msgctxt "@label"
 msgid "The key has expired."
 msgstr "La clau ha caducat."
 
-#: quick/qml/private/MailPartModel.qml:65
+#: quick/qml/private/MailPartModel.qml:66
 #, kde-format
 msgctxt "@label"
 msgid "You are trusting this key."
 msgstr "Esteu confiant en esta clau."
 
-#: quick/qml/private/MailPartModel.qml:68
+#: quick/qml/private/MailPartModel.qml:69
+#: widgets/messagecontainerwidget.cpp:108
 #, kde-format
 msgctxt "@label"
 msgid "The signature is invalid."
 msgstr "La signatura no és vàlida."
 
-#: quick/qml/private/MailPartModel.qml:97
+#: quick/qml/private/MailPartModel.qml:98
+#: widgets/messagecontainerwidget.cpp:205
 #, kde-format
 msgid "This message is encrypted but we don't have the key for it."
 msgstr "Este missatge està encriptat, però no es disposa de la seua clau."
 
-#: quick/qml/private/MailPartModel.qml:97
+#: quick/qml/private/MailPartModel.qml:98
 #, kde-format
 msgid "This message is encrypted to the key: %1"
 msgstr "Este missatge està encriptat amb la clau: %1"
 
-#: widgets/messageviewer.cpp:54
+#: widgets/messagecontainerwidget.cpp:66
+#, kde-format
+msgctxt "@label"
+msgid ""
+"This message has been signed VS-NfD compliant using the key <a href=\"%1\">"
+"%2</a>."
+msgstr ""
+"Este missatge s'ha signat conforme amb VS-NfD utilitzant la clau <a href="
+"\"%1\">%2</a>."
+
+#: widgets/messagecontainerwidget.cpp:73
+#, kde-format
+msgctxt "@label"
+msgid "This message has been signed using the key <a href=\"%1\">%2</a>."
+msgstr "Este missatge s'ha signat emprant la clau <a href=\"%1\">%2</a>."
+
+#: widgets/messagecontainerwidget.cpp:81
+#, kde-format
+msgctxt "@label"
+msgid "This message has been signed VS-NfD compliant by %1."
+msgstr "Este missatge s'ha signat conforme amb VS-NfD per %1."
+
+#: widgets/messagecontainerwidget.cpp:83
+#, kde-format
+msgctxt "@label"
+msgid "This message has been signed by %1."
+msgstr "Este missatge s'ha signat per %1."
+
+#: widgets/messagecontainerwidget.cpp:86
+#, kde-format
+msgctxt "@label"
+msgid "The <a href=\"%1\">key</a> was revoked."
+msgstr "S'ha revocat la <a href=\"%1\">clau</a>."
+
+#: widgets/messagecontainerwidget.cpp:89
+#, kde-format
+msgctxt "@label"
+msgid "The <a href=\"%1\">key</a> was expired."
+msgstr "La <a href=\"%1\">clau</a> ha caducat."
+
+#: widgets/messagecontainerwidget.cpp:94
+#, kde-format
+msgctxt "@label"
+msgid ""
+"The signature is valid, but the <a href=\"%1\">key</a>'s validity is unknown."
+msgstr ""
+"La signatura és vàlida, però no es coneix la validesa de la <a href="
+"\"%1\">clau</a>."
+
+#: widgets/messagecontainerwidget.cpp:97
+#, kde-format
+msgctxt "@label"
+msgid ""
+"The signature is valid and the <a href=\"%1\">key</a> is marginally trusted."
+msgstr ""
+"La signatura és vàlida, però la <a href=\"%1\">clau</a> és de confiança "
+"parcial."
+
+#: widgets/messagecontainerwidget.cpp:99
+#, kde-format
+msgctxt "@label"
+msgid "The signature is valid and the <a href=\"%1\">key</a> is fully trusted."
+msgstr ""
+"La signatura és vàlida i la <a href=\"%1\">clau</a> és de plena confiança."
+
+#: widgets/messagecontainerwidget.cpp:102
+#, kde-format
+msgctxt "@label"
+msgid ""
+"The signature is valid and the <a href=\"%1\">key</a> is ultimately trusted."
+msgstr ""
+"La signatura és vàlida i la <a href=\"%1\">clau</a> és de confiança absoluta."
+
+#: widgets/messagecontainerwidget.cpp:104
+#, kde-format
+msgctxt "@label"
+msgid "The signature is valid, but the <a href=\"%1\">key</a> is untrusted."
+msgstr ""
+"La signatura és vàlida, però la <a href=\"%1\">clau</a> no és de confiança."
+
+#: widgets/messagecontainerwidget.cpp:203
+#, kde-format
+msgid ""
+"This message is VS-NfD compliant encrypted but we don't have the key for it."
+msgstr ""
+"Este missatge està encriptat conforme amb VS-NfD, però no es disposa de la "
+"seua clau."
+
+#: widgets/messagecontainerwidget.cpp:209
+#, kde-format
+msgid "This message is VS-NfD compliant encrypted."
+msgstr "Este missatge està encriptat conforme amb VS-NfD."
+
+#: widgets/messagecontainerwidget.cpp:211
+#, kde-format
+msgid "This message is encrypted."
+msgstr "Este missatge està encriptat."
+
+#: widgets/messagecontainerwidget.cpp:220
+#, kde-format
+msgid "The message is encrypted for the following keys:"
+msgstr "Este missatge està encriptat amb les claus següents:"
+
+#: widgets/messagecontainerwidget.cpp:237
+#, kde-format
+msgid "Unknown key"
+msgstr "Clau desconeguda"
+
+#: widgets/messageviewer.cpp:57
 #, kde-format
 msgid "&Save Attachment As..."
 msgstr "Guar&da l'adjunt com a..."
 
-#: widgets/messageviewer.cpp:59
+#: widgets/messageviewer.cpp:62
 #, kde-format
 msgctxt "to open"
 msgid "Open"
 msgstr "Obri"
 
-#: widgets/messageviewer.cpp:64
+#: widgets/messageviewer.cpp:67
 #, kde-format
 msgctxt "@action:inmenu"
 msgid "Import public key"
 msgstr "Importa la clau pública"
 
-#: widgets/messageviewer.cpp:192
+#: widgets/messageviewer.cpp:236
 #, kde-format
 msgid "Invitation"
 msgstr "Invitació"
 
-#: widgets/messageviewer.cpp:195
+#: widgets/messageviewer.cpp:239
 #, kde-format
 msgid "&Summary:"
 msgstr "&Resum:"
 
-#: widgets/messageviewer.cpp:196
+#: widgets/messageviewer.cpp:240
 #, kde-format
 msgid "&Organizer:"
 msgstr "&Organitzador:"
 
-#: widgets/messageviewer.cpp:198
+#: widgets/messageviewer.cpp:242
 #, kde-format
 msgid "&Location:"
 msgstr "&Emplaçament:"
 
-#: widgets/messageviewer.cpp:200
+#: widgets/messageviewer.cpp:244
 #, kde-format
 msgid "&Start date:"
 msgstr "Data d'&inici:"
 
-#: widgets/messageviewer.cpp:202
+#: widgets/messageviewer.cpp:246
 #, kde-format
 msgid "&End date:"
 msgstr "Data &final:"
 
-#: widgets/messageviewer.cpp:205
+#: widgets/messageviewer.cpp:249
 #, kde-format
 msgid "&Details:"
 msgstr "&Detalls:"
 
-#: widgets/messageviewer.cpp:215
+#: widgets/messageviewer.cpp:271
 #, kde-format
 msgid "Encapsulated email"
 msgstr "Correu electrònic encapsulat"
 
-#: widgets/messageviewer.cpp:223
+#: widgets/messageviewer.cpp:279
 #, kde-format
 msgid "From:"
 msgstr "De:"
 
-#: widgets/messageviewer.cpp:224
+#: widgets/messageviewer.cpp:280
 #, kde-format
 msgid "Date:"
 msgstr "Data:"
 
-#: widgets/messageviewer.cpp:237
-#, kde-format
-msgid "Error: %1"
-msgstr "S'ha produït un error: %1"
-
-#: widgets/messageviewer.cpp:257
+#: widgets/messageviewer.cpp:315
 #, kde-format
 msgid "&Subject:"
 msgstr "A&ssumpte:"
 
-#: widgets/messageviewer.cpp:260
+#: widgets/messageviewer.cpp:318
 #, kde-format
 msgid "&From:"
 msgstr "&De:"
 
-#: widgets/messageviewer.cpp:263
+#: widgets/messageviewer.cpp:321
 #, kde-format
 msgid "&To:"
 msgstr "&A:"
+
+#: widgets/urlhandler.cpp:37
+#, kde-format
+msgid "Could not start certificate manager. Please check your installation."
+msgstr ""
+"No s'ha pogut iniciar el gestor de certificats. Comproveu esta instal·lació."
+
+#: widgets/urlhandler.cpp:39
+#, kde-format
+msgid "KMail Error"
+msgstr "S'ha produït un error a KMail"
diff --git a/po/de/mimetreeparser.po b/po/de/mimetreeparser.po
index 0a20719..e7767dc 100644
--- a/po/de/mimetreeparser.po
+++ b/po/de/mimetreeparser.po
@@ -1,341 +1,477 @@
 # German translations for mimetreeparser package.
 # Copyright (C) 2023 This file is copyright:
 # This file is distributed under the same license as the mimetreeparser package.
 # Frederik Schwarzer <schwarzer@kde.org>, 2023.
 #
 # Automatically generated, 2023.
 msgid ""
 msgstr ""
 "Project-Id-Version: mimetreeparser\n"
 "Report-Msgid-Bugs-To: https://bugs.kde.org\n"
-"POT-Creation-Date: 2023-08-15 00:46+0000\n"
+"POT-Creation-Date: 2023-08-27 00:47+0000\n"
 "PO-Revision-Date: 2023-08-13 16:14+0200\n"
 "Last-Translator: Frederik Schwarzer <schwarzer@kde.org>\n"
 "Language-Team: German <kde-i18n-de@kde.org>\n"
 "Language: de\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 "X-Generator: Lokalize 23.11.70\n"
 
-#: core/attachmentmodel.cpp:88
+#: core/attachmentmodel.cpp:90
 #, kde-format
 msgctxt "@title:column"
 msgid "Name"
 msgstr "Name"
 
-#: core/attachmentmodel.cpp:90
+#: core/attachmentmodel.cpp:92
 #, kde-format
 msgctxt "@title:column"
 msgid "Size"
 msgstr "Größe"
 
-#: core/attachmentmodel.cpp:92
+#: core/attachmentmodel.cpp:94
 #, kde-format
 msgctxt "@title:column"
 msgid "Encrypted"
 msgstr "Verschlüsselt"
 
-#: core/attachmentmodel.cpp:94
+#: core/attachmentmodel.cpp:96
 #, kde-format
 msgctxt "@title:column"
 msgid "Signed"
 msgstr "Signiert"
 
-#: core/attachmentmodel.cpp:196
+#: core/attachmentmodel.cpp:198
 #, kde-format
 msgctxt "@info"
 msgid "Failed to save attachment."
 msgstr "Der Anhang kann nicht gespeichert werden."
 
-#: core/attachmentmodel.cpp:228
+#: core/attachmentmodel.cpp:230
 #, kde-format
 msgctxt "@info"
 msgid "Saved the attachment to disk: %1"
 msgstr "Anhang auf Datenträger gespeichert: %1"
 
-#: core/attachmentmodel.cpp:245
+#: core/attachmentmodel.cpp:247
 #, kde-format
 msgctxt "@info"
 msgid "Failed to open attachment."
 msgstr "Der Anhang kann nicht geöffnet werden."
 
-#: core/attachmentmodel.cpp:250
+#: core/attachmentmodel.cpp:252
 #, kde-format
 msgctxt "@info"
 msgid "Failed to save attachment for opening."
 msgstr "Der Anhang kann nicht zum Öffnen gespeichert werden."
 
-#: core/attachmentmodel.cpp:268
+#: core/attachmentmodel.cpp:273
 #, kde-format
 msgctxt "@info"
 msgid "No keys were found in this attachment"
 msgstr "In diesem Anhang wurden keine Schlüssel gefunden"
 
-#: core/attachmentmodel.cpp:271
+#: core/attachmentmodel.cpp:276
 #, kde-format
 msgctxt "@info"
 msgid "one key imported"
 msgid_plural "%1 keys imported"
 msgstr[0] "Ein Schlüssel importiert"
 msgstr[1] "%1 Schlüssel importiert"
 
-#: core/attachmentmodel.cpp:274
+#: core/attachmentmodel.cpp:279
 #, kde-format
 msgctxt "@info"
 msgid "one key was already imported"
 msgid_plural "%1 keys were already imported"
 msgstr[0] "Ein Schlüssel war bereits importiert"
 msgstr[1] "%1 Schlüssel waren bereits importiert"
 
 #: core/mailtemplates.cpp:103
 #, kde-format
 msgid "fwd"
 msgstr "fwd"
 
 #: core/mailtemplates.cpp:132
 #, kde-format
 msgid "re"
 msgstr "re"
 
 #: core/messageparser.cpp:158
 #, kde-format
 msgctxt "displayed when a mail has unknown sender, receiver or date"
 msgid "Unknown"
 msgstr "Unbekannt"
 
-#: core/messagepart.cpp:618 core/messagepart.cpp:709 core/messagepart.cpp:783
+#: core/messagepart.cpp:625 core/messagepart.cpp:660 core/messagepart.cpp:835
 #, kde-format
 msgctxt "@info:status"
 msgid "Wrong Crypto Plug-In."
 msgstr "Falsches Kryptografie-Modul."
 
-#: core/messagepart.cpp:857
+#: core/messagepart.cpp:986
 #, kde-format
-msgctxt "@info"
-msgid "Could not decrypt the data: no key found for recipients."
+msgid "No appropriate crypto plug-in was found."
 msgstr ""
-"Die Daten können nicht entschlüsselt werden: Kein Schlüssel für Empfänger "
-"gefunden."
 
-#: core/messagepart.cpp:862
+#: core/messagepart.cpp:988
 #, kde-format
-msgctxt "@info"
-msgid "Could not decrypt the data."
+msgid "Crypto plug-in \"%1\" cannot decrypt messages."
+msgstr ""
+
+#: core/messagepart.cpp:990
+#, fuzzy, kde-format
+#| msgctxt "@info"
+#| msgid "Could not decrypt the data."
+msgid "Crypto plug-in \"%1\" could not decrypt the data."
 msgstr "Die Daten können nicht entschlüsselt werden."
 
-#: core/partmodel.cpp:483
+#: core/messagepart.cpp:991 widgets/messageviewer.cpp:295
+#, kde-format
+msgid "Error: %1"
+msgstr "Fehler: %1"
+
+#: core/partmodel.cpp:487
 #, kde-format
 msgctxt "@info:status"
 msgid "No key available."
 msgstr "Kein Schlüssel verfügbar."
 
-#: core/partmodel.cpp:485
+#: core/partmodel.cpp:489
 #, kde-format
 msgctxt "@info:status"
 msgid "Wrong passphrase."
 msgstr "Falsche Passphrase."
 
-#: quick/qml/MailViewer.qml:39
+#: quick/qml/MailViewer.qml:38
 #, kde-format
 msgctxt "@title:window"
 msgid "Message viewer"
 msgstr "Nachrichtenanzeige"
 
-#: quick/qml/MailViewer.qml:76
+#: quick/qml/MailViewer.qml:75
 #, kde-format
 msgctxt "@label"
 msgid "Subject:"
 msgstr "Betreff:"
 
-#: quick/qml/MailViewer.qml:98
+#: quick/qml/MailViewer.qml:96
 #, kde-format
 msgctxt "@label"
 msgid "From:"
 msgstr "Von:"
 
-#: quick/qml/MailViewer.qml:115
+#: quick/qml/MailViewer.qml:112
 #, kde-format
 msgctxt "@label"
 msgid "Sender:"
 msgstr "Absender:"
 
-#: quick/qml/MailViewer.qml:132
+#: quick/qml/MailViewer.qml:128
 #, kde-format
 msgctxt "@label"
 msgid "To:"
 msgstr "An:"
 
-#: quick/qml/MailViewer.qml:190
+#: quick/qml/MailViewer.qml:185
 #, kde-format
 msgctxt "@action:button"
 msgid "Save attachment"
 msgstr "Anhang speichern"
 
 #: quick/qml/private/AttachmentDelegate.qml:56
 #, kde-format
 msgctxt "@action:button"
 msgid "Import key"
 msgstr "Schlüssel importieren"
 
 #: quick/qml/private/Banner.qml:188
 #, kde-format
 msgctxt "@action:button"
 msgid "Close"
 msgstr "Schließen"
 
 #: quick/qml/private/ErrorPart.qml:27
 #, kde-format
 msgid "An error occurred: %1"
 msgstr "Es ist ein Fehler aufgetreten: %1"
 
-#: quick/qml/private/ICalPart.qml:28
+#: quick/qml/private/ICalPart.qml:27
 #, kde-format
 msgid "This mail contains an invitation"
 msgstr "Diese E-Mail enthält eine Einladung"
 
-#: quick/qml/private/MailPart.qml:42
+#: quick/qml/private/MailPart.qml:41
 #, kde-format
 msgctxt "@info"
 msgid "sent by %1 on %2"
 msgstr "Gesendet von %1 am %2"
 
-#: quick/qml/private/MailPartModel.qml:54
+#: quick/qml/private/MailPartModel.qml:55
 #, kde-format
 msgctxt "@label"
 msgid "This message has been signed using the key %1."
 msgstr "Diese Nachricht wurde mit dem Schlüssel %1 signiert."
 
-#: quick/qml/private/MailPartModel.qml:55
+#: quick/qml/private/MailPartModel.qml:56 widgets/messagecontainerwidget.cpp:78
 #, kde-format
 msgctxt "@label"
 msgid "The key details are not available."
 msgstr "Es sind keine Details für den Schlüssel verfügbar."
 
-#: quick/qml/private/MailPartModel.qml:57
+#: quick/qml/private/MailPartModel.qml:58
 #, kde-format
 msgctxt "@label"
 msgid "This message has been signed using the key %1 by %2."
 msgstr "Diese Nachricht wurde mit dem Schlüssel %1 von %2 signiert."
 
-#: quick/qml/private/MailPartModel.qml:59
+#: quick/qml/private/MailPartModel.qml:60
 #, kde-format
 msgctxt "@label"
 msgid "The key was revoked."
 msgstr "Der Schlüssel wurde zurückgezogen."
 
-#: quick/qml/private/MailPartModel.qml:62
+#: quick/qml/private/MailPartModel.qml:63
 #, kde-format
 msgctxt "@label"
 msgid "The key has expired."
 msgstr "Der Schlüssel ist abgelaufen."
 
-#: quick/qml/private/MailPartModel.qml:65
+#: quick/qml/private/MailPartModel.qml:66
 #, kde-format
 msgctxt "@label"
 msgid "You are trusting this key."
 msgstr "Sie vertrauen diesem Schlüssel."
 
-#: quick/qml/private/MailPartModel.qml:68
+#: quick/qml/private/MailPartModel.qml:69
+#: widgets/messagecontainerwidget.cpp:108
 #, kde-format
 msgctxt "@label"
 msgid "The signature is invalid."
 msgstr "Die Signatur ist ungültig."
 
-#: quick/qml/private/MailPartModel.qml:97
+#: quick/qml/private/MailPartModel.qml:98
+#: widgets/messagecontainerwidget.cpp:205
 #, kde-format
 msgid "This message is encrypted but we don't have the key for it."
 msgstr "Diese Nachricht ist verschlüsselt, aber es fehlt der Schlüssel."
 
-#: quick/qml/private/MailPartModel.qml:97
+#: quick/qml/private/MailPartModel.qml:98
 #, kde-format
 msgid "This message is encrypted to the key: %1"
 msgstr "Diese Nachricht ist mit folgendem Schlüssel verschlüsselt: %1"
 
-#: widgets/messageviewer.cpp:54
+#: widgets/messagecontainerwidget.cpp:66
+#, fuzzy, kde-format
+#| msgctxt "@label"
+#| msgid "This message has been signed using the key %1."
+msgctxt "@label"
+msgid ""
+"This message has been signed VS-NfD compliant using the key <a href=\"%1\">"
+"%2</a>."
+msgstr "Diese Nachricht wurde mit dem Schlüssel %1 signiert."
+
+#: widgets/messagecontainerwidget.cpp:73
+#, fuzzy, kde-format
+#| msgctxt "@label"
+#| msgid "This message has been signed using the key %1."
+msgctxt "@label"
+msgid "This message has been signed using the key <a href=\"%1\">%2</a>."
+msgstr "Diese Nachricht wurde mit dem Schlüssel %1 signiert."
+
+#: widgets/messagecontainerwidget.cpp:81
+#, fuzzy, kde-format
+#| msgctxt "@label"
+#| msgid "This message has been signed using the key %1."
+msgctxt "@label"
+msgid "This message has been signed VS-NfD compliant by %1."
+msgstr "Diese Nachricht wurde mit dem Schlüssel %1 signiert."
+
+#: widgets/messagecontainerwidget.cpp:83
+#, fuzzy, kde-format
+#| msgctxt "@label"
+#| msgid "This message has been signed using the key %1."
+msgctxt "@label"
+msgid "This message has been signed by %1."
+msgstr "Diese Nachricht wurde mit dem Schlüssel %1 signiert."
+
+#: widgets/messagecontainerwidget.cpp:86
+#, fuzzy, kde-format
+#| msgctxt "@label"
+#| msgid "The key was revoked."
+msgctxt "@label"
+msgid "The <a href=\"%1\">key</a> was revoked."
+msgstr "Der Schlüssel wurde zurückgezogen."
+
+#: widgets/messagecontainerwidget.cpp:89
+#, fuzzy, kde-format
+#| msgctxt "@label"
+#| msgid "The key has expired."
+msgctxt "@label"
+msgid "The <a href=\"%1\">key</a> was expired."
+msgstr "Der Schlüssel ist abgelaufen."
+
+#: widgets/messagecontainerwidget.cpp:94
+#, kde-format
+msgctxt "@label"
+msgid ""
+"The signature is valid, but the <a href=\"%1\">key</a>'s validity is unknown."
+msgstr ""
+
+#: widgets/messagecontainerwidget.cpp:97
+#, kde-format
+msgctxt "@label"
+msgid ""
+"The signature is valid and the <a href=\"%1\">key</a> is marginally trusted."
+msgstr ""
+
+#: widgets/messagecontainerwidget.cpp:99
+#, kde-format
+msgctxt "@label"
+msgid "The signature is valid and the <a href=\"%1\">key</a> is fully trusted."
+msgstr ""
+
+#: widgets/messagecontainerwidget.cpp:102
+#, kde-format
+msgctxt "@label"
+msgid ""
+"The signature is valid and the <a href=\"%1\">key</a> is ultimately trusted."
+msgstr ""
+
+#: widgets/messagecontainerwidget.cpp:104
+#, kde-format
+msgctxt "@label"
+msgid "The signature is valid, but the <a href=\"%1\">key</a> is untrusted."
+msgstr ""
+
+#: widgets/messagecontainerwidget.cpp:203
+#, fuzzy, kde-format
+#| msgid "This message is encrypted but we don't have the key for it."
+msgid ""
+"This message is VS-NfD compliant encrypted but we don't have the key for it."
+msgstr "Diese Nachricht ist verschlüsselt, aber es fehlt der Schlüssel."
+
+#: widgets/messagecontainerwidget.cpp:209
+#, fuzzy, kde-format
+#| msgid "This message is encrypted to the key: %1"
+msgid "This message is VS-NfD compliant encrypted."
+msgstr "Diese Nachricht ist mit folgendem Schlüssel verschlüsselt: %1"
+
+#: widgets/messagecontainerwidget.cpp:211
+#, fuzzy, kde-format
+#| msgid "This message is encrypted to the key: %1"
+msgid "This message is encrypted."
+msgstr "Diese Nachricht ist mit folgendem Schlüssel verschlüsselt: %1"
+
+#: widgets/messagecontainerwidget.cpp:220
+#, fuzzy, kde-format
+#| msgid "This message is encrypted to the key: %1"
+msgid "The message is encrypted for the following keys:"
+msgstr "Diese Nachricht ist mit folgendem Schlüssel verschlüsselt: %1"
+
+#: widgets/messagecontainerwidget.cpp:237
+#, fuzzy, kde-format
+#| msgctxt "displayed when a mail has unknown sender, receiver or date"
+#| msgid "Unknown"
+msgid "Unknown key"
+msgstr "Unbekannt"
+
+#: widgets/messageviewer.cpp:57
 #, kde-format
 msgid "&Save Attachment As..."
 msgstr "Anhang speichern &unter ..."
 
-#: widgets/messageviewer.cpp:59
+#: widgets/messageviewer.cpp:62
 #, kde-format
 msgctxt "to open"
 msgid "Open"
 msgstr "Öffnen"
 
-#: widgets/messageviewer.cpp:64
+#: widgets/messageviewer.cpp:67
 #, kde-format
 msgctxt "@action:inmenu"
 msgid "Import public key"
 msgstr "Öffentlichen Schlüssel importieren"
 
-#: widgets/messageviewer.cpp:192
+#: widgets/messageviewer.cpp:236
 #, kde-format
 msgid "Invitation"
 msgstr "Einladung"
 
-#: widgets/messageviewer.cpp:195
+#: widgets/messageviewer.cpp:239
 #, kde-format
 msgid "&Summary:"
 msgstr "Zu&sammenfassung:"
 
-#: widgets/messageviewer.cpp:196
+#: widgets/messageviewer.cpp:240
 #, kde-format
 msgid "&Organizer:"
 msgstr "&Organisator:"
 
-#: widgets/messageviewer.cpp:198
+#: widgets/messageviewer.cpp:242
 #, kde-format
 msgid "&Location:"
 msgstr "&Ort:"
 
-#: widgets/messageviewer.cpp:200
+#: widgets/messageviewer.cpp:244
 #, kde-format
 msgid "&Start date:"
 msgstr "&Anfangsdatum:"
 
-#: widgets/messageviewer.cpp:202
+#: widgets/messageviewer.cpp:246
 #, kde-format
 msgid "&End date:"
 msgstr "&Enddatum:"
 
-#: widgets/messageviewer.cpp:205
+#: widgets/messageviewer.cpp:249
 #, kde-format
 msgid "&Details:"
 msgstr "&Details:"
 
-#: widgets/messageviewer.cpp:215
+#: widgets/messageviewer.cpp:271
 #, kde-format
 msgid "Encapsulated email"
 msgstr "Eingebettete E-Mail"
 
-#: widgets/messageviewer.cpp:223
+#: widgets/messageviewer.cpp:279
 #, kde-format
 msgid "From:"
 msgstr "Von:"
 
-#: widgets/messageviewer.cpp:224
+#: widgets/messageviewer.cpp:280
 #, kde-format
 msgid "Date:"
 msgstr "Datum:"
 
-#: widgets/messageviewer.cpp:237
-#, kde-format
-msgid "Error: %1"
-msgstr "Fehler: %1"
-
-#: widgets/messageviewer.cpp:257
+#: widgets/messageviewer.cpp:315
 #, kde-format
 msgid "&Subject:"
 msgstr "&Betreff:"
 
-#: widgets/messageviewer.cpp:260
+#: widgets/messageviewer.cpp:318
 #, kde-format
 msgid "&From:"
 msgstr "&Von:"
 
-#: widgets/messageviewer.cpp:263
+#: widgets/messageviewer.cpp:321
 #, kde-format
 msgid "&To:"
 msgstr "&An:"
+
+#: widgets/urlhandler.cpp:37
+#, kde-format
+msgid "Could not start certificate manager. Please check your installation."
+msgstr ""
+
+#: widgets/urlhandler.cpp:39
+#, kde-format
+msgid "KMail Error"
+msgstr ""
+
+#~ msgctxt "@info"
+#~ msgid "Could not decrypt the data: no key found for recipients."
+#~ msgstr ""
+#~ "Die Daten können nicht entschlüsselt werden: Kein Schlüssel für Empfänger "
+#~ "gefunden."
diff --git a/po/es/mimetreeparser.po b/po/es/mimetreeparser.po
index 9e54254..9cba205 100644
--- a/po/es/mimetreeparser.po
+++ b/po/es/mimetreeparser.po
@@ -1,339 +1,463 @@
 # Spanish translations for mimetreeparser.po package.
 # Copyright (C) 2023 This file is copyright:
 # This file is distributed under the same license as the mimetreeparser package.
 #
 # Automatically generated, 2023.
 # Eloy Cuadra <ecuadra@eloihr.net>, 2023.
 msgid ""
 msgstr ""
 "Project-Id-Version: mimetreeparser\n"
 "Report-Msgid-Bugs-To: https://bugs.kde.org\n"
-"POT-Creation-Date: 2023-08-15 00:46+0000\n"
-"PO-Revision-Date: 2023-08-14 17:48+0200\n"
+"POT-Creation-Date: 2023-08-27 00:47+0000\n"
+"PO-Revision-Date: 2023-08-27 03:59+0200\n"
 "Last-Translator: Eloy Cuadra <ecuadra@eloihr.net>\n"
 "Language-Team: Spanish <kde-l10n-es@kde.org>\n"
 "Language: es\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=n != 1;\n"
 "X-Generator: Lokalize 23.04.3\n"
 
-#: core/attachmentmodel.cpp:88
+#: core/attachmentmodel.cpp:90
 #, kde-format
 msgctxt "@title:column"
 msgid "Name"
 msgstr "Nombre"
 
-#: core/attachmentmodel.cpp:90
+#: core/attachmentmodel.cpp:92
 #, kde-format
 msgctxt "@title:column"
 msgid "Size"
 msgstr "Tamaño"
 
-#: core/attachmentmodel.cpp:92
+#: core/attachmentmodel.cpp:94
 #, kde-format
 msgctxt "@title:column"
 msgid "Encrypted"
 msgstr "Cifrado"
 
-#: core/attachmentmodel.cpp:94
+#: core/attachmentmodel.cpp:96
 #, kde-format
 msgctxt "@title:column"
 msgid "Signed"
 msgstr "Firmado"
 
-#: core/attachmentmodel.cpp:196
+#: core/attachmentmodel.cpp:198
 #, kde-format
 msgctxt "@info"
 msgid "Failed to save attachment."
-msgstr ""
+msgstr "No se ha podido guardar el adjunto."
 
-#: core/attachmentmodel.cpp:228
+#: core/attachmentmodel.cpp:230
 #, kde-format
 msgctxt "@info"
 msgid "Saved the attachment to disk: %1"
-msgstr ""
+msgstr "Adjunto guardado en disco: %1"
 
-#: core/attachmentmodel.cpp:245
+#: core/attachmentmodel.cpp:247
 #, kde-format
 msgctxt "@info"
 msgid "Failed to open attachment."
-msgstr ""
+msgstr "No se ha podido abrir el adjunto."
 
-#: core/attachmentmodel.cpp:250
+#: core/attachmentmodel.cpp:252
 #, kde-format
 msgctxt "@info"
 msgid "Failed to save attachment for opening."
-msgstr ""
+msgstr "No se ha podido guardar el adjunto para su apertura."
 
-#: core/attachmentmodel.cpp:268
+#: core/attachmentmodel.cpp:273
 #, kde-format
 msgctxt "@info"
 msgid "No keys were found in this attachment"
-msgstr ""
+msgstr "No se ha encontrado ninguna clave en este adjunto"
 
-#: core/attachmentmodel.cpp:271
+#: core/attachmentmodel.cpp:276
 #, kde-format
 msgctxt "@info"
 msgid "one key imported"
 msgid_plural "%1 keys imported"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "1 clave importada"
+msgstr[1] "%1 claves importadas"
 
-#: core/attachmentmodel.cpp:274
+#: core/attachmentmodel.cpp:279
 #, kde-format
 msgctxt "@info"
 msgid "one key was already imported"
 msgid_plural "%1 keys were already imported"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "1 clave ya estaba importada"
+msgstr[1] "%1 claves ya estaban importadas"
 
 #: core/mailtemplates.cpp:103
 #, kde-format
 msgid "fwd"
-msgstr ""
+msgstr "fwd"
 
 #: core/mailtemplates.cpp:132
 #, kde-format
 msgid "re"
-msgstr ""
+msgstr "re"
 
 #: core/messageparser.cpp:158
 #, kde-format
 msgctxt "displayed when a mail has unknown sender, receiver or date"
 msgid "Unknown"
 msgstr "Desconocido"
 
-#: core/messagepart.cpp:618 core/messagepart.cpp:709 core/messagepart.cpp:783
+#: core/messagepart.cpp:625 core/messagepart.cpp:660 core/messagepart.cpp:835
 #, kde-format
 msgctxt "@info:status"
 msgid "Wrong Crypto Plug-In."
-msgstr ""
+msgstr "Complemento criptográfico erróneo."
 
-#: core/messagepart.cpp:857
+#: core/messagepart.cpp:986
 #, kde-format
-msgctxt "@info"
-msgid "Could not decrypt the data: no key found for recipients."
-msgstr ""
+msgid "No appropriate crypto plug-in was found."
+msgstr "No se ha encontrado ningún complemento criptográfico adecuado."
 
-#: core/messagepart.cpp:862
+#: core/messagepart.cpp:988
 #, kde-format
-msgctxt "@info"
-msgid "Could not decrypt the data."
-msgstr ""
+msgid "Crypto plug-in \"%1\" cannot decrypt messages."
+msgstr "El complemento criptográfico «%1» no puede descifrar mensajes."
+
+#: core/messagepart.cpp:990
+#, kde-format
+msgid "Crypto plug-in \"%1\" could not decrypt the data."
+msgstr "El complemento criptográfico «%1» no ha podido descifrar los datos."
+
+#: core/messagepart.cpp:991 widgets/messageviewer.cpp:295
+#, kde-format
+msgid "Error: %1"
+msgstr "Error: %1"
 
-#: core/partmodel.cpp:483
+#: core/partmodel.cpp:487
 #, kde-format
 msgctxt "@info:status"
 msgid "No key available."
-msgstr ""
+msgstr "No hay ninguna clave disponible."
 
-#: core/partmodel.cpp:485
+#: core/partmodel.cpp:489
 #, kde-format
 msgctxt "@info:status"
 msgid "Wrong passphrase."
-msgstr ""
+msgstr "Contraseña larga errónea."
 
-#: quick/qml/MailViewer.qml:39
+#: quick/qml/MailViewer.qml:38
 #, kde-format
 msgctxt "@title:window"
 msgid "Message viewer"
 msgstr "Visor de mensaje"
 
-#: quick/qml/MailViewer.qml:76
+#: quick/qml/MailViewer.qml:75
 #, kde-format
 msgctxt "@label"
 msgid "Subject:"
 msgstr "Asunto:"
 
-#: quick/qml/MailViewer.qml:98
+#: quick/qml/MailViewer.qml:96
 #, kde-format
 msgctxt "@label"
 msgid "From:"
 msgstr "De:"
 
-#: quick/qml/MailViewer.qml:115
+#: quick/qml/MailViewer.qml:112
 #, kde-format
 msgctxt "@label"
 msgid "Sender:"
 msgstr "Remitente:"
 
-#: quick/qml/MailViewer.qml:132
+#: quick/qml/MailViewer.qml:128
 #, kde-format
 msgctxt "@label"
 msgid "To:"
 msgstr "Para:"
 
-#: quick/qml/MailViewer.qml:190
+#: quick/qml/MailViewer.qml:185
 #, kde-format
 msgctxt "@action:button"
 msgid "Save attachment"
 msgstr "Guardar adjunto"
 
 #: quick/qml/private/AttachmentDelegate.qml:56
 #, kde-format
 msgctxt "@action:button"
 msgid "Import key"
 msgstr "Importar clave"
 
 #: quick/qml/private/Banner.qml:188
 #, kde-format
 msgctxt "@action:button"
 msgid "Close"
 msgstr "Cerrar"
 
 #: quick/qml/private/ErrorPart.qml:27
 #, kde-format
 msgid "An error occurred: %1"
 msgstr "Ha ocurrido un error: %1"
 
-#: quick/qml/private/ICalPart.qml:28
+#: quick/qml/private/ICalPart.qml:27
 #, kde-format
 msgid "This mail contains an invitation"
 msgstr "Este mensaje contiene una invitación"
 
-#: quick/qml/private/MailPart.qml:42
+#: quick/qml/private/MailPart.qml:41
 #, kde-format
 msgctxt "@info"
 msgid "sent by %1 on %2"
 msgstr "enviado por %1 el %2"
 
-#: quick/qml/private/MailPartModel.qml:54
+#: quick/qml/private/MailPartModel.qml:55
 #, kde-format
 msgctxt "@label"
 msgid "This message has been signed using the key %1."
 msgstr "Este mensaje ha sido firmado usando la clave %1."
 
-#: quick/qml/private/MailPartModel.qml:55
+#: quick/qml/private/MailPartModel.qml:56 widgets/messagecontainerwidget.cpp:78
 #, kde-format
 msgctxt "@label"
 msgid "The key details are not available."
 msgstr "Los detalles de la clave no están disponibles."
 
-#: quick/qml/private/MailPartModel.qml:57
+#: quick/qml/private/MailPartModel.qml:58
 #, kde-format
 msgctxt "@label"
 msgid "This message has been signed using the key %1 by %2."
 msgstr "Este mensaje ha sido firmado usando la clave %1 por %2."
 
-#: quick/qml/private/MailPartModel.qml:59
+#: quick/qml/private/MailPartModel.qml:60
 #, kde-format
 msgctxt "@label"
 msgid "The key was revoked."
 msgstr "La clave ha sido revocada."
 
-#: quick/qml/private/MailPartModel.qml:62
+#: quick/qml/private/MailPartModel.qml:63
 #, kde-format
 msgctxt "@label"
 msgid "The key has expired."
 msgstr "La clave ha expirado."
 
-#: quick/qml/private/MailPartModel.qml:65
+#: quick/qml/private/MailPartModel.qml:66
 #, kde-format
 msgctxt "@label"
 msgid "You are trusting this key."
-msgstr ""
+msgstr "Esta clave es de su confianza."
 
-#: quick/qml/private/MailPartModel.qml:68
+#: quick/qml/private/MailPartModel.qml:69
+#: widgets/messagecontainerwidget.cpp:108
 #, kde-format
 msgctxt "@label"
 msgid "The signature is invalid."
 msgstr "La firma no es válida."
 
-#: quick/qml/private/MailPartModel.qml:97
+#: quick/qml/private/MailPartModel.qml:98
+#: widgets/messagecontainerwidget.cpp:205
 #, kde-format
 msgid "This message is encrypted but we don't have the key for it."
-msgstr ""
+msgstr "Este mensaje está cifrado, pero no disponemos de la clave para él."
 
-#: quick/qml/private/MailPartModel.qml:97
+#: quick/qml/private/MailPartModel.qml:98
 #, kde-format
 msgid "This message is encrypted to the key: %1"
+msgstr "Este mensaje está cifrado para la clave: %1"
+
+#: widgets/messagecontainerwidget.cpp:66
+#, kde-format
+msgctxt "@label"
+msgid ""
+"This message has been signed VS-NfD compliant using the key <a href=\"%1\">"
+"%2</a>."
 msgstr ""
+"Este mensaje ha sido firmado de conformidad con VS-NfD usando la clave <a "
+"href=\"%1\">%2</a>."
 
-#: widgets/messageviewer.cpp:54
+#: widgets/messagecontainerwidget.cpp:73
+#, kde-format
+msgctxt "@label"
+msgid "This message has been signed using the key <a href=\"%1\">%2</a>."
+msgstr "Este mensaje ha sido firmado usando la clave <a href=\"%1\">%2</a>."
+
+#: widgets/messagecontainerwidget.cpp:81
+#, kde-format
+msgctxt "@label"
+msgid "This message has been signed VS-NfD compliant by %1."
+msgstr "Este mensaje ha sido firmado de conformidad con VS-NfD por %1."
+
+#: widgets/messagecontainerwidget.cpp:83
+#, kde-format
+msgctxt "@label"
+msgid "This message has been signed by %1."
+msgstr "Este mensaje ha sido firmado por %1."
+
+#: widgets/messagecontainerwidget.cpp:86
+#, kde-format
+msgctxt "@label"
+msgid "The <a href=\"%1\">key</a> was revoked."
+msgstr "La <a href=\"%1\">clave</a> ha sido revocada."
+
+#: widgets/messagecontainerwidget.cpp:89
+#, kde-format
+msgctxt "@label"
+msgid "The <a href=\"%1\">key</a> was expired."
+msgstr "La <a href=\"%1\">clave</a> ha expirado."
+
+#: widgets/messagecontainerwidget.cpp:94
+#, kde-format
+msgctxt "@label"
+msgid ""
+"The signature is valid, but the <a href=\"%1\">key</a>'s validity is unknown."
+msgstr ""
+"La firma es válida, pero se desconoce la validez de la <a href=\"%1\">clave</"
+"a>."
+
+#: widgets/messagecontainerwidget.cpp:97
+#, kde-format
+msgctxt "@label"
+msgid ""
+"The signature is valid and the <a href=\"%1\">key</a> is marginally trusted."
+msgstr ""
+"La firma es válida y la <a href=\"%1\">clave</a> es de confianza marginal."
+
+#: widgets/messagecontainerwidget.cpp:99
+#, kde-format
+msgctxt "@label"
+msgid "The signature is valid and the <a href=\"%1\">key</a> is fully trusted."
+msgstr ""
+"La firma es válida y la <a href=\"%1\">clave</a> es de confianza total."
+
+#: widgets/messagecontainerwidget.cpp:102
+#, kde-format
+msgctxt "@label"
+msgid ""
+"The signature is valid and the <a href=\"%1\">key</a> is ultimately trusted."
+msgstr ""
+"La firma es válida y la <a href=\"%1\">clave</a> es definitivamente de "
+"confianza."
+
+#: widgets/messagecontainerwidget.cpp:104
+#, kde-format
+msgctxt "@label"
+msgid "The signature is valid, but the <a href=\"%1\">key</a> is untrusted."
+msgstr ""
+"La firma es válida, pero la <a href=\"%1\">clave</a> no es de confianza."
+
+#: widgets/messagecontainerwidget.cpp:203
+#, kde-format
+msgid ""
+"This message is VS-NfD compliant encrypted but we don't have the key for it."
+msgstr ""
+"Este mensaje está cifrado de conformidad con VS-NfD, pero no disponemos de "
+"la clave para él."
+
+#: widgets/messagecontainerwidget.cpp:209
+#, kde-format
+msgid "This message is VS-NfD compliant encrypted."
+msgstr "Este mensaje está cifrado de conformidad con VS-NfD."
+
+#: widgets/messagecontainerwidget.cpp:211
+#, kde-format
+msgid "This message is encrypted."
+msgstr "Este mensaje está cifrado."
+
+#: widgets/messagecontainerwidget.cpp:220
+#, kde-format
+msgid "The message is encrypted for the following keys:"
+msgstr "El mensaje está cifrado para las siguientes claves:"
+
+#: widgets/messagecontainerwidget.cpp:237
+#, kde-format
+msgid "Unknown key"
+msgstr "Clave desconocida"
+
+#: widgets/messageviewer.cpp:57
 #, kde-format
 msgid "&Save Attachment As..."
 msgstr "&Guardar adjunto como..."
 
-#: widgets/messageviewer.cpp:59
+#: widgets/messageviewer.cpp:62
 #, kde-format
 msgctxt "to open"
 msgid "Open"
 msgstr "Abrir"
 
-#: widgets/messageviewer.cpp:64
+#: widgets/messageviewer.cpp:67
 #, kde-format
 msgctxt "@action:inmenu"
 msgid "Import public key"
 msgstr "Importar clave pública"
 
-#: widgets/messageviewer.cpp:192
+#: widgets/messageviewer.cpp:236
 #, kde-format
 msgid "Invitation"
 msgstr "Invitación"
 
-#: widgets/messageviewer.cpp:195
+#: widgets/messageviewer.cpp:239
 #, kde-format
 msgid "&Summary:"
 msgstr "&Resumen:"
 
-#: widgets/messageviewer.cpp:196
+#: widgets/messageviewer.cpp:240
 #, kde-format
 msgid "&Organizer:"
 msgstr "&Organizador:"
 
-#: widgets/messageviewer.cpp:198
+#: widgets/messageviewer.cpp:242
 #, kde-format
 msgid "&Location:"
 msgstr "&Ubicación:"
 
-#: widgets/messageviewer.cpp:200
+#: widgets/messageviewer.cpp:244
 #, kde-format
 msgid "&Start date:"
 msgstr "Fecha de &inicio:"
 
-#: widgets/messageviewer.cpp:202
+#: widgets/messageviewer.cpp:246
 #, kde-format
 msgid "&End date:"
 msgstr "&Fecha final:"
 
-#: widgets/messageviewer.cpp:205
+#: widgets/messageviewer.cpp:249
 #, kde-format
 msgid "&Details:"
 msgstr "&Detalles:"
 
-#: widgets/messageviewer.cpp:215
+#: widgets/messageviewer.cpp:271
 #, kde-format
 msgid "Encapsulated email"
 msgstr "Mensaje encapsulado"
 
-#: widgets/messageviewer.cpp:223
+#: widgets/messageviewer.cpp:279
 #, kde-format
 msgid "From:"
 msgstr "De:"
 
-#: widgets/messageviewer.cpp:224
+#: widgets/messageviewer.cpp:280
 #, kde-format
 msgid "Date:"
 msgstr "Fecha:"
 
-#: widgets/messageviewer.cpp:237
-#, kde-format
-msgid "Error: %1"
-msgstr "Error: %1"
-
-#: widgets/messageviewer.cpp:257
+#: widgets/messageviewer.cpp:315
 #, kde-format
 msgid "&Subject:"
 msgstr "A&sunto:"
 
-#: widgets/messageviewer.cpp:260
+#: widgets/messageviewer.cpp:318
 #, kde-format
 msgid "&From:"
 msgstr "&De:"
 
-#: widgets/messageviewer.cpp:263
+#: widgets/messageviewer.cpp:321
 #, kde-format
 msgid "&To:"
 msgstr "&Para:"
+
+#: widgets/urlhandler.cpp:37
+#, kde-format
+msgid "Could not start certificate manager. Please check your installation."
+msgstr ""
+"No se ha podido iniciar el gestor de certificados. Compruebe su instalación."
+
+#: widgets/urlhandler.cpp:39
+#, kde-format
+msgid "KMail Error"
+msgstr "Error de KMail"
diff --git a/po/fr/mimetreeparser.po b/po/fr/mimetreeparser.po
index 5b97495..f1bdc9d 100644
--- a/po/fr/mimetreeparser.po
+++ b/po/fr/mimetreeparser.po
@@ -1,332 +1,444 @@
 msgid ""
 msgstr ""
 "Project-Id-Version: mimetreeparser\n"
 "Report-Msgid-Bugs-To: https://bugs.kde.org\n"
-"POT-Creation-Date: 2023-08-15 00:46+0000\n"
+"POT-Creation-Date: 2023-08-27 00:47+0000\n"
 "PO-Revision-Date: 2023-08-12 10:26+0200\n"
 "Last-Translator: KDE Francophone <kde-francophone@kde.org>\n"
 "Language-Team: KDE Francophone <kde-francophone@kde.org>\n"
 "Language: fr\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=(n > 1);\n"
 
-#: core/attachmentmodel.cpp:88
+#: core/attachmentmodel.cpp:90
 #, kde-format
 msgctxt "@title:column"
 msgid "Name"
 msgstr ""
 
-#: core/attachmentmodel.cpp:90
+#: core/attachmentmodel.cpp:92
 #, kde-format
 msgctxt "@title:column"
 msgid "Size"
 msgstr ""
 
-#: core/attachmentmodel.cpp:92
+#: core/attachmentmodel.cpp:94
 #, kde-format
 msgctxt "@title:column"
 msgid "Encrypted"
 msgstr ""
 
-#: core/attachmentmodel.cpp:94
+#: core/attachmentmodel.cpp:96
 #, kde-format
 msgctxt "@title:column"
 msgid "Signed"
 msgstr ""
 
-#: core/attachmentmodel.cpp:196
+#: core/attachmentmodel.cpp:198
 #, kde-format
 msgctxt "@info"
 msgid "Failed to save attachment."
 msgstr ""
 
-#: core/attachmentmodel.cpp:228
+#: core/attachmentmodel.cpp:230
 #, kde-format
 msgctxt "@info"
 msgid "Saved the attachment to disk: %1"
 msgstr ""
 
-#: core/attachmentmodel.cpp:245
+#: core/attachmentmodel.cpp:247
 #, kde-format
 msgctxt "@info"
 msgid "Failed to open attachment."
 msgstr ""
 
-#: core/attachmentmodel.cpp:250
+#: core/attachmentmodel.cpp:252
 #, kde-format
 msgctxt "@info"
 msgid "Failed to save attachment for opening."
 msgstr ""
 
-#: core/attachmentmodel.cpp:268
+#: core/attachmentmodel.cpp:273
 #, kde-format
 msgctxt "@info"
 msgid "No keys were found in this attachment"
 msgstr ""
 
-#: core/attachmentmodel.cpp:271
+#: core/attachmentmodel.cpp:276
 #, kde-format
 msgctxt "@info"
 msgid "one key imported"
 msgid_plural "%1 keys imported"
 msgstr[0] ""
 msgstr[1] ""
 
-#: core/attachmentmodel.cpp:274
+#: core/attachmentmodel.cpp:279
 #, kde-format
 msgctxt "@info"
 msgid "one key was already imported"
 msgid_plural "%1 keys were already imported"
 msgstr[0] ""
 msgstr[1] ""
 
 #: core/mailtemplates.cpp:103
 #, kde-format
 msgid "fwd"
 msgstr ""
 
 #: core/mailtemplates.cpp:132
 #, kde-format
 msgid "re"
 msgstr ""
 
 #: core/messageparser.cpp:158
 #, kde-format
 msgctxt "displayed when a mail has unknown sender, receiver or date"
 msgid "Unknown"
 msgstr ""
 
-#: core/messagepart.cpp:618 core/messagepart.cpp:709 core/messagepart.cpp:783
+#: core/messagepart.cpp:625 core/messagepart.cpp:660 core/messagepart.cpp:835
 #, kde-format
 msgctxt "@info:status"
 msgid "Wrong Crypto Plug-In."
 msgstr ""
 
-#: core/messagepart.cpp:857
+#: core/messagepart.cpp:986
 #, kde-format
-msgctxt "@info"
-msgid "Could not decrypt the data: no key found for recipients."
+msgid "No appropriate crypto plug-in was found."
 msgstr ""
 
-#: core/messagepart.cpp:862
+#: core/messagepart.cpp:988
 #, kde-format
-msgctxt "@info"
-msgid "Could not decrypt the data."
+msgid "Crypto plug-in \"%1\" cannot decrypt messages."
+msgstr ""
+
+#: core/messagepart.cpp:990
+#, kde-format
+msgid "Crypto plug-in \"%1\" could not decrypt the data."
 msgstr ""
 
-#: core/partmodel.cpp:483
+#: core/messagepart.cpp:991 widgets/messageviewer.cpp:295
+#, kde-format
+msgid "Error: %1"
+msgstr ""
+
+#: core/partmodel.cpp:487
 #, kde-format
 msgctxt "@info:status"
 msgid "No key available."
 msgstr ""
 
-#: core/partmodel.cpp:485
+#: core/partmodel.cpp:489
 #, kde-format
 msgctxt "@info:status"
 msgid "Wrong passphrase."
 msgstr ""
 
-#: quick/qml/MailViewer.qml:39
+#: quick/qml/MailViewer.qml:38
 #, kde-format
 msgctxt "@title:window"
 msgid "Message viewer"
 msgstr ""
 
-#: quick/qml/MailViewer.qml:76
+#: quick/qml/MailViewer.qml:75
 #, kde-format
 msgctxt "@label"
 msgid "Subject:"
 msgstr ""
 
-#: quick/qml/MailViewer.qml:98
+#: quick/qml/MailViewer.qml:96
 #, kde-format
 msgctxt "@label"
 msgid "From:"
 msgstr ""
 
-#: quick/qml/MailViewer.qml:115
+#: quick/qml/MailViewer.qml:112
 #, kde-format
 msgctxt "@label"
 msgid "Sender:"
 msgstr ""
 
-#: quick/qml/MailViewer.qml:132
+#: quick/qml/MailViewer.qml:128
 #, kde-format
 msgctxt "@label"
 msgid "To:"
 msgstr ""
 
-#: quick/qml/MailViewer.qml:190
+#: quick/qml/MailViewer.qml:185
 #, kde-format
 msgctxt "@action:button"
 msgid "Save attachment"
 msgstr ""
 
 #: quick/qml/private/AttachmentDelegate.qml:56
 #, kde-format
 msgctxt "@action:button"
 msgid "Import key"
 msgstr ""
 
 #: quick/qml/private/Banner.qml:188
 #, kde-format
 msgctxt "@action:button"
 msgid "Close"
 msgstr ""
 
 #: quick/qml/private/ErrorPart.qml:27
 #, kde-format
 msgid "An error occurred: %1"
 msgstr ""
 
-#: quick/qml/private/ICalPart.qml:28
+#: quick/qml/private/ICalPart.qml:27
 #, kde-format
 msgid "This mail contains an invitation"
 msgstr ""
 
-#: quick/qml/private/MailPart.qml:42
+#: quick/qml/private/MailPart.qml:41
 #, kde-format
 msgctxt "@info"
 msgid "sent by %1 on %2"
 msgstr ""
 
-#: quick/qml/private/MailPartModel.qml:54
+#: quick/qml/private/MailPartModel.qml:55
 #, kde-format
 msgctxt "@label"
 msgid "This message has been signed using the key %1."
 msgstr ""
 
-#: quick/qml/private/MailPartModel.qml:55
+#: quick/qml/private/MailPartModel.qml:56 widgets/messagecontainerwidget.cpp:78
 #, kde-format
 msgctxt "@label"
 msgid "The key details are not available."
 msgstr ""
 
-#: quick/qml/private/MailPartModel.qml:57
+#: quick/qml/private/MailPartModel.qml:58
 #, kde-format
 msgctxt "@label"
 msgid "This message has been signed using the key %1 by %2."
 msgstr ""
 
-#: quick/qml/private/MailPartModel.qml:59
+#: quick/qml/private/MailPartModel.qml:60
 #, kde-format
 msgctxt "@label"
 msgid "The key was revoked."
 msgstr ""
 
-#: quick/qml/private/MailPartModel.qml:62
+#: quick/qml/private/MailPartModel.qml:63
 #, kde-format
 msgctxt "@label"
 msgid "The key has expired."
 msgstr ""
 
-#: quick/qml/private/MailPartModel.qml:65
+#: quick/qml/private/MailPartModel.qml:66
 #, kde-format
 msgctxt "@label"
 msgid "You are trusting this key."
 msgstr ""
 
-#: quick/qml/private/MailPartModel.qml:68
+#: quick/qml/private/MailPartModel.qml:69
+#: widgets/messagecontainerwidget.cpp:108
 #, kde-format
 msgctxt "@label"
 msgid "The signature is invalid."
 msgstr ""
 
-#: quick/qml/private/MailPartModel.qml:97
+#: quick/qml/private/MailPartModel.qml:98
+#: widgets/messagecontainerwidget.cpp:205
 #, kde-format
 msgid "This message is encrypted but we don't have the key for it."
 msgstr ""
 
-#: quick/qml/private/MailPartModel.qml:97
+#: quick/qml/private/MailPartModel.qml:98
 #, kde-format
 msgid "This message is encrypted to the key: %1"
 msgstr ""
 
-#: widgets/messageviewer.cpp:54
+#: widgets/messagecontainerwidget.cpp:66
+#, kde-format
+msgctxt "@label"
+msgid ""
+"This message has been signed VS-NfD compliant using the key <a href=\"%1\">"
+"%2</a>."
+msgstr ""
+
+#: widgets/messagecontainerwidget.cpp:73
+#, kde-format
+msgctxt "@label"
+msgid "This message has been signed using the key <a href=\"%1\">%2</a>."
+msgstr ""
+
+#: widgets/messagecontainerwidget.cpp:81
+#, kde-format
+msgctxt "@label"
+msgid "This message has been signed VS-NfD compliant by %1."
+msgstr ""
+
+#: widgets/messagecontainerwidget.cpp:83
+#, kde-format
+msgctxt "@label"
+msgid "This message has been signed by %1."
+msgstr ""
+
+#: widgets/messagecontainerwidget.cpp:86
+#, kde-format
+msgctxt "@label"
+msgid "The <a href=\"%1\">key</a> was revoked."
+msgstr ""
+
+#: widgets/messagecontainerwidget.cpp:89
+#, kde-format
+msgctxt "@label"
+msgid "The <a href=\"%1\">key</a> was expired."
+msgstr ""
+
+#: widgets/messagecontainerwidget.cpp:94
+#, kde-format
+msgctxt "@label"
+msgid ""
+"The signature is valid, but the <a href=\"%1\">key</a>'s validity is unknown."
+msgstr ""
+
+#: widgets/messagecontainerwidget.cpp:97
+#, kde-format
+msgctxt "@label"
+msgid ""
+"The signature is valid and the <a href=\"%1\">key</a> is marginally trusted."
+msgstr ""
+
+#: widgets/messagecontainerwidget.cpp:99
+#, kde-format
+msgctxt "@label"
+msgid "The signature is valid and the <a href=\"%1\">key</a> is fully trusted."
+msgstr ""
+
+#: widgets/messagecontainerwidget.cpp:102
+#, kde-format
+msgctxt "@label"
+msgid ""
+"The signature is valid and the <a href=\"%1\">key</a> is ultimately trusted."
+msgstr ""
+
+#: widgets/messagecontainerwidget.cpp:104
+#, kde-format
+msgctxt "@label"
+msgid "The signature is valid, but the <a href=\"%1\">key</a> is untrusted."
+msgstr ""
+
+#: widgets/messagecontainerwidget.cpp:203
+#, kde-format
+msgid ""
+"This message is VS-NfD compliant encrypted but we don't have the key for it."
+msgstr ""
+
+#: widgets/messagecontainerwidget.cpp:209
+#, kde-format
+msgid "This message is VS-NfD compliant encrypted."
+msgstr ""
+
+#: widgets/messagecontainerwidget.cpp:211
+#, kde-format
+msgid "This message is encrypted."
+msgstr ""
+
+#: widgets/messagecontainerwidget.cpp:220
+#, kde-format
+msgid "The message is encrypted for the following keys:"
+msgstr ""
+
+#: widgets/messagecontainerwidget.cpp:237
+#, kde-format
+msgid "Unknown key"
+msgstr ""
+
+#: widgets/messageviewer.cpp:57
 #, kde-format
 msgid "&Save Attachment As..."
 msgstr ""
 
-#: widgets/messageviewer.cpp:59
+#: widgets/messageviewer.cpp:62
 #, kde-format
 msgctxt "to open"
 msgid "Open"
 msgstr ""
 
-#: widgets/messageviewer.cpp:64
+#: widgets/messageviewer.cpp:67
 #, kde-format
 msgctxt "@action:inmenu"
 msgid "Import public key"
 msgstr ""
 
-#: widgets/messageviewer.cpp:192
+#: widgets/messageviewer.cpp:236
 #, kde-format
 msgid "Invitation"
 msgstr ""
 
-#: widgets/messageviewer.cpp:195
+#: widgets/messageviewer.cpp:239
 #, kde-format
 msgid "&Summary:"
 msgstr ""
 
-#: widgets/messageviewer.cpp:196
+#: widgets/messageviewer.cpp:240
 #, kde-format
 msgid "&Organizer:"
 msgstr ""
 
-#: widgets/messageviewer.cpp:198
+#: widgets/messageviewer.cpp:242
 #, kde-format
 msgid "&Location:"
 msgstr ""
 
-#: widgets/messageviewer.cpp:200
+#: widgets/messageviewer.cpp:244
 #, kde-format
 msgid "&Start date:"
 msgstr ""
 
-#: widgets/messageviewer.cpp:202
+#: widgets/messageviewer.cpp:246
 #, kde-format
 msgid "&End date:"
 msgstr ""
 
-#: widgets/messageviewer.cpp:205
+#: widgets/messageviewer.cpp:249
 #, kde-format
 msgid "&Details:"
 msgstr ""
 
-#: widgets/messageviewer.cpp:215
+#: widgets/messageviewer.cpp:271
 #, kde-format
 msgid "Encapsulated email"
 msgstr ""
 
-#: widgets/messageviewer.cpp:223
+#: widgets/messageviewer.cpp:279
 #, kde-format
 msgid "From:"
 msgstr ""
 
-#: widgets/messageviewer.cpp:224
+#: widgets/messageviewer.cpp:280
 #, kde-format
 msgid "Date:"
 msgstr ""
 
-#: widgets/messageviewer.cpp:237
-#, kde-format
-msgid "Error: %1"
-msgstr ""
-
-#: widgets/messageviewer.cpp:257
+#: widgets/messageviewer.cpp:315
 #, kde-format
 msgid "&Subject:"
 msgstr ""
 
-#: widgets/messageviewer.cpp:260
+#: widgets/messageviewer.cpp:318
 #, kde-format
 msgid "&From:"
 msgstr ""
 
-#: widgets/messageviewer.cpp:263
+#: widgets/messageviewer.cpp:321
 #, kde-format
 msgid "&To:"
 msgstr ""
+
+#: widgets/urlhandler.cpp:37
+#, kde-format
+msgid "Could not start certificate manager. Please check your installation."
+msgstr ""
+
+#: widgets/urlhandler.cpp:39
+#, kde-format
+msgid "KMail Error"
+msgstr ""
diff --git a/po/it/mimetreeparser.po b/po/it/mimetreeparser.po
new file mode 100644
index 0000000..e891d20
--- /dev/null
+++ b/po/it/mimetreeparser.po
@@ -0,0 +1,467 @@
+# Copyright (C) YEAR This file is copyright:
+# This file is distributed under the same license as the mimetreeparser package.
+# Vincenzo Reale <smart2128vr@gmail.com>, 2023.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: mimetreeparser\n"
+"Report-Msgid-Bugs-To: https://bugs.kde.org\n"
+"POT-Creation-Date: 2023-08-27 00:47+0000\n"
+"PO-Revision-Date: 2023-08-18 17:47+0200\n"
+"Last-Translator: Vincenzo Reale <smart2128vr@gmail.com>\n"
+"Language-Team: Italian <kde-i18n-it@kde.org>\n"
+"Language: it\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Lokalize 23.04.3\n"
+
+#: core/attachmentmodel.cpp:90
+#, kde-format
+msgctxt "@title:column"
+msgid "Name"
+msgstr "Nome"
+
+#: core/attachmentmodel.cpp:92
+#, kde-format
+msgctxt "@title:column"
+msgid "Size"
+msgstr "Dimensione"
+
+#: core/attachmentmodel.cpp:94
+#, kde-format
+msgctxt "@title:column"
+msgid "Encrypted"
+msgstr "Cifrato"
+
+#: core/attachmentmodel.cpp:96
+#, kde-format
+msgctxt "@title:column"
+msgid "Signed"
+msgstr "Firmato"
+
+#: core/attachmentmodel.cpp:198
+#, kde-format
+msgctxt "@info"
+msgid "Failed to save attachment."
+msgstr "Salvataggio dell'allegato non riuscito."
+
+#: core/attachmentmodel.cpp:230
+#, kde-format
+msgctxt "@info"
+msgid "Saved the attachment to disk: %1"
+msgstr "Allegato salvato su disco:%1"
+
+#: core/attachmentmodel.cpp:247
+#, kde-format
+msgctxt "@info"
+msgid "Failed to open attachment."
+msgstr "Apertura dell'allegato non riuscita."
+
+#: core/attachmentmodel.cpp:252
+#, kde-format
+msgctxt "@info"
+msgid "Failed to save attachment for opening."
+msgstr "Salvataggio dell'allegato per l'apertura non riuscito"
+
+#: core/attachmentmodel.cpp:273
+#, kde-format
+msgctxt "@info"
+msgid "No keys were found in this attachment"
+msgstr "Non è stata trovata alcuna chiave in questo allegato"
+
+#: core/attachmentmodel.cpp:276
+#, kde-format
+msgctxt "@info"
+msgid "one key imported"
+msgid_plural "%1 keys imported"
+msgstr[0] "una chiave importata"
+msgstr[1] "%1 chiavi importate"
+
+#: core/attachmentmodel.cpp:279
+#, kde-format
+msgctxt "@info"
+msgid "one key was already imported"
+msgid_plural "%1 keys were already imported"
+msgstr[0] "una chiave è già stata importata"
+msgstr[1] "%1 chiavi sono già state importate"
+
+#: core/mailtemplates.cpp:103
+#, kde-format
+msgid "fwd"
+msgstr "fwd"
+
+#: core/mailtemplates.cpp:132
+#, kde-format
+msgid "re"
+msgstr "re"
+
+#: core/messageparser.cpp:158
+#, kde-format
+msgctxt "displayed when a mail has unknown sender, receiver or date"
+msgid "Unknown"
+msgstr "Sconosciuto"
+
+#: core/messagepart.cpp:625 core/messagepart.cpp:660 core/messagepart.cpp:835
+#, kde-format
+msgctxt "@info:status"
+msgid "Wrong Crypto Plug-In."
+msgstr "Estensione di cifratura errata."
+
+#: core/messagepart.cpp:986
+#, kde-format
+msgid "No appropriate crypto plug-in was found."
+msgstr "Non è stata trovata nessuna estensione crittografica valida."
+
+#: core/messagepart.cpp:988
+#, kde-format
+msgid "Crypto plug-in \"%1\" cannot decrypt messages."
+msgstr "L'estensione crittografica «%1» non può decifrare i messaggi."
+
+#: core/messagepart.cpp:990
+#, kde-format
+msgid "Crypto plug-in \"%1\" could not decrypt the data."
+msgstr "L'estensione crittografica «%1» non può decifrare i dati."
+
+#: core/messagepart.cpp:991 widgets/messageviewer.cpp:295
+#, kde-format
+msgid "Error: %1"
+msgstr "Errore: %1"
+
+#: core/partmodel.cpp:487
+#, kde-format
+msgctxt "@info:status"
+msgid "No key available."
+msgstr "Nessuna chiave disponibile."
+
+#: core/partmodel.cpp:489
+#, kde-format
+msgctxt "@info:status"
+msgid "Wrong passphrase."
+msgstr "Frase segreta errata."
+
+#: quick/qml/MailViewer.qml:38
+#, kde-format
+msgctxt "@title:window"
+msgid "Message viewer"
+msgstr "Visualizzatore del messaggio"
+
+#: quick/qml/MailViewer.qml:75
+#, kde-format
+msgctxt "@label"
+msgid "Subject:"
+msgstr "Oggetto:"
+
+#: quick/qml/MailViewer.qml:96
+#, kde-format
+msgctxt "@label"
+msgid "From:"
+msgstr "Da:"
+
+#: quick/qml/MailViewer.qml:112
+#, kde-format
+msgctxt "@label"
+msgid "Sender:"
+msgstr "Mittente:"
+
+#: quick/qml/MailViewer.qml:128
+#, kde-format
+msgctxt "@label"
+msgid "To:"
+msgstr "A:"
+
+#: quick/qml/MailViewer.qml:185
+#, kde-format
+msgctxt "@action:button"
+msgid "Save attachment"
+msgstr "Salva allegato"
+
+#: quick/qml/private/AttachmentDelegate.qml:56
+#, kde-format
+msgctxt "@action:button"
+msgid "Import key"
+msgstr "Importa chiave"
+
+#: quick/qml/private/Banner.qml:188
+#, kde-format
+msgctxt "@action:button"
+msgid "Close"
+msgstr "Chiudi"
+
+#: quick/qml/private/ErrorPart.qml:27
+#, kde-format
+msgid "An error occurred: %1"
+msgstr "Si è verificato un errore: %1"
+
+#: quick/qml/private/ICalPart.qml:27
+#, kde-format
+msgid "This mail contains an invitation"
+msgstr "Questo messaggio contiene un invito"
+
+#: quick/qml/private/MailPart.qml:41
+#, kde-format
+msgctxt "@info"
+msgid "sent by %1 on %2"
+msgstr "inviato da %1 il %2"
+
+#: quick/qml/private/MailPartModel.qml:55
+#, kde-format
+msgctxt "@label"
+msgid "This message has been signed using the key %1."
+msgstr "Questo messaggio è stato firmato utilizzando la chiave %1."
+
+#: quick/qml/private/MailPartModel.qml:56 widgets/messagecontainerwidget.cpp:78
+#, kde-format
+msgctxt "@label"
+msgid "The key details are not available."
+msgstr "I dettagli della chiave non sono disponibili."
+
+#: quick/qml/private/MailPartModel.qml:58
+#, kde-format
+msgctxt "@label"
+msgid "This message has been signed using the key %1 by %2."
+msgstr "Questo messaggio è stato firmato utilizzando la chiave %1 di %2."
+
+#: quick/qml/private/MailPartModel.qml:60
+#, kde-format
+msgctxt "@label"
+msgid "The key was revoked."
+msgstr "La chiave è stata revocata."
+
+#: quick/qml/private/MailPartModel.qml:63
+#, kde-format
+msgctxt "@label"
+msgid "The key has expired."
+msgstr "La chiave è scaduta."
+
+#: quick/qml/private/MailPartModel.qml:66
+#, kde-format
+msgctxt "@label"
+msgid "You are trusting this key."
+msgstr "Ti fidi di questa chiave."
+
+#: quick/qml/private/MailPartModel.qml:69
+#: widgets/messagecontainerwidget.cpp:108
+#, kde-format
+msgctxt "@label"
+msgid "The signature is invalid."
+msgstr "La firma non è valida."
+
+#: quick/qml/private/MailPartModel.qml:98
+#: widgets/messagecontainerwidget.cpp:205
+#, kde-format
+msgid "This message is encrypted but we don't have the key for it."
+msgstr "Questo messaggio è cifrato, ma non abbiamo la chiave relativa."
+
+#: quick/qml/private/MailPartModel.qml:98
+#, kde-format
+msgid "This message is encrypted to the key: %1"
+msgstr "Questo messaggio è cifrato con la chiave: %1"
+
+#: widgets/messagecontainerwidget.cpp:66
+#, fuzzy, kde-format
+#| msgctxt "@label"
+#| msgid "This message has been signed using the key %1."
+msgctxt "@label"
+msgid ""
+"This message has been signed VS-NfD compliant using the key <a href=\"%1\">"
+"%2</a>."
+msgstr "Questo messaggio è stato firmato utilizzando la chiave %1."
+
+#: widgets/messagecontainerwidget.cpp:73
+#, fuzzy, kde-format
+#| msgctxt "@label"
+#| msgid "This message has been signed using the key %1."
+msgctxt "@label"
+msgid "This message has been signed using the key <a href=\"%1\">%2</a>."
+msgstr "Questo messaggio è stato firmato utilizzando la chiave %1."
+
+#: widgets/messagecontainerwidget.cpp:81
+#, fuzzy, kde-format
+#| msgctxt "@label"
+#| msgid "This message has been signed using the key %1."
+msgctxt "@label"
+msgid "This message has been signed VS-NfD compliant by %1."
+msgstr "Questo messaggio è stato firmato utilizzando la chiave %1."
+
+#: widgets/messagecontainerwidget.cpp:83
+#, fuzzy, kde-format
+#| msgctxt "@label"
+#| msgid "This message has been signed using the key %1."
+msgctxt "@label"
+msgid "This message has been signed by %1."
+msgstr "Questo messaggio è stato firmato utilizzando la chiave %1."
+
+#: widgets/messagecontainerwidget.cpp:86
+#, fuzzy, kde-format
+#| msgctxt "@label"
+#| msgid "The key was revoked."
+msgctxt "@label"
+msgid "The <a href=\"%1\">key</a> was revoked."
+msgstr "La chiave è stata revocata."
+
+#: widgets/messagecontainerwidget.cpp:89
+#, fuzzy, kde-format
+#| msgctxt "@label"
+#| msgid "The key has expired."
+msgctxt "@label"
+msgid "The <a href=\"%1\">key</a> was expired."
+msgstr "La chiave è scaduta."
+
+#: widgets/messagecontainerwidget.cpp:94
+#, kde-format
+msgctxt "@label"
+msgid ""
+"The signature is valid, but the <a href=\"%1\">key</a>'s validity is unknown."
+msgstr ""
+
+#: widgets/messagecontainerwidget.cpp:97
+#, kde-format
+msgctxt "@label"
+msgid ""
+"The signature is valid and the <a href=\"%1\">key</a> is marginally trusted."
+msgstr ""
+
+#: widgets/messagecontainerwidget.cpp:99
+#, kde-format
+msgctxt "@label"
+msgid "The signature is valid and the <a href=\"%1\">key</a> is fully trusted."
+msgstr ""
+
+#: widgets/messagecontainerwidget.cpp:102
+#, kde-format
+msgctxt "@label"
+msgid ""
+"The signature is valid and the <a href=\"%1\">key</a> is ultimately trusted."
+msgstr ""
+
+#: widgets/messagecontainerwidget.cpp:104
+#, kde-format
+msgctxt "@label"
+msgid "The signature is valid, but the <a href=\"%1\">key</a> is untrusted."
+msgstr ""
+
+#: widgets/messagecontainerwidget.cpp:203
+#, fuzzy, kde-format
+#| msgid "This message is encrypted but we don't have the key for it."
+msgid ""
+"This message is VS-NfD compliant encrypted but we don't have the key for it."
+msgstr "Questo messaggio è cifrato, ma non abbiamo la chiave relativa."
+
+#: widgets/messagecontainerwidget.cpp:209
+#, fuzzy, kde-format
+#| msgid "This message is encrypted to the key: %1"
+msgid "This message is VS-NfD compliant encrypted."
+msgstr "Questo messaggio è cifrato con la chiave: %1"
+
+#: widgets/messagecontainerwidget.cpp:211
+#, fuzzy, kde-format
+#| msgid "This message is encrypted to the key: %1"
+msgid "This message is encrypted."
+msgstr "Questo messaggio è cifrato con la chiave: %1"
+
+#: widgets/messagecontainerwidget.cpp:220
+#, fuzzy, kde-format
+#| msgid "This message is encrypted to the key: %1"
+msgid "The message is encrypted for the following keys:"
+msgstr "Questo messaggio è cifrato con la chiave: %1"
+
+#: widgets/messagecontainerwidget.cpp:237
+#, fuzzy, kde-format
+#| msgctxt "displayed when a mail has unknown sender, receiver or date"
+#| msgid "Unknown"
+msgid "Unknown key"
+msgstr "Sconosciuto"
+
+#: widgets/messageviewer.cpp:57
+#, kde-format
+msgid "&Save Attachment As..."
+msgstr "&Salva allegato come..."
+
+#: widgets/messageviewer.cpp:62
+#, kde-format
+msgctxt "to open"
+msgid "Open"
+msgstr "Apri"
+
+#: widgets/messageviewer.cpp:67
+#, kde-format
+msgctxt "@action:inmenu"
+msgid "Import public key"
+msgstr "Importa chiave pubblica"
+
+#: widgets/messageviewer.cpp:236
+#, kde-format
+msgid "Invitation"
+msgstr "Invito"
+
+#: widgets/messageviewer.cpp:239
+#, kde-format
+msgid "&Summary:"
+msgstr "&Riepilogo:"
+
+#: widgets/messageviewer.cpp:240
+#, kde-format
+msgid "&Organizer:"
+msgstr "&Organizzatore:"
+
+#: widgets/messageviewer.cpp:242
+#, kde-format
+msgid "&Location:"
+msgstr "&Posizione:"
+
+#: widgets/messageviewer.cpp:244
+#, kde-format
+msgid "&Start date:"
+msgstr "Data di &inizio:"
+
+#: widgets/messageviewer.cpp:246
+#, kde-format
+msgid "&End date:"
+msgstr "Data di &fine:"
+
+#: widgets/messageviewer.cpp:249
+#, kde-format
+msgid "&Details:"
+msgstr "&Dettagli:"
+
+#: widgets/messageviewer.cpp:271
+#, kde-format
+msgid "Encapsulated email"
+msgstr "Messaggio incapsulato"
+
+#: widgets/messageviewer.cpp:279
+#, kde-format
+msgid "From:"
+msgstr "Da:"
+
+#: widgets/messageviewer.cpp:280
+#, kde-format
+msgid "Date:"
+msgstr "Data:"
+
+#: widgets/messageviewer.cpp:315
+#, kde-format
+msgid "&Subject:"
+msgstr "&Oggetto:"
+
+#: widgets/messageviewer.cpp:318
+#, kde-format
+msgid "&From:"
+msgstr "&Da:"
+
+#: widgets/messageviewer.cpp:321
+#, kde-format
+msgid "&To:"
+msgstr "&A:"
+
+#: widgets/urlhandler.cpp:37
+#, kde-format
+msgid "Could not start certificate manager. Please check your installation."
+msgstr ""
+
+#: widgets/urlhandler.cpp:39
+#, kde-format
+msgid "KMail Error"
+msgstr ""
diff --git a/po/ka/mimetreeparser.po b/po/ka/mimetreeparser.po
new file mode 100644
index 0000000..f9ed5ef
--- /dev/null
+++ b/po/ka/mimetreeparser.po
@@ -0,0 +1,460 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR This file is copyright:
+# This file is distributed under the same license as the mimetreeparser package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: mimetreeparser\n"
+"Report-Msgid-Bugs-To: https://bugs.kde.org\n"
+"POT-Creation-Date: 2023-08-27 00:47+0000\n"
+"PO-Revision-Date: 2023-08-27 11:09+0200\n"
+"Last-Translator: Temuri Doghonadze <temuri.doghonadze@gmail.com>\n"
+"Language-Team: Georgian <kde-i18n-doc@kde.org>\n"
+"Language: ka\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"X-Generator: Poedit 3.3.2\n"
+
+#: core/attachmentmodel.cpp:90
+#, kde-format
+msgctxt "@title:column"
+msgid "Name"
+msgstr "სახელი"
+
+#: core/attachmentmodel.cpp:92
+#, kde-format
+msgctxt "@title:column"
+msgid "Size"
+msgstr "ზომა"
+
+#: core/attachmentmodel.cpp:94
+#, kde-format
+msgctxt "@title:column"
+msgid "Encrypted"
+msgstr "დაშიფრული"
+
+#: core/attachmentmodel.cpp:96
+#, kde-format
+msgctxt "@title:column"
+msgid "Signed"
+msgstr "ხელმოწერილი"
+
+#: core/attachmentmodel.cpp:198
+#, kde-format
+msgctxt "@info"
+msgid "Failed to save attachment."
+msgstr "მიმაგრებული ფაილის შენახვის შეცდომა."
+
+#: core/attachmentmodel.cpp:230
+#, kde-format
+msgctxt "@info"
+msgid "Saved the attachment to disk: %1"
+msgstr "მიმაგრებული ფაილი შენახულია დისკზე: %1"
+
+#: core/attachmentmodel.cpp:247
+#, kde-format
+msgctxt "@info"
+msgid "Failed to open attachment."
+msgstr "მიმაგრებული ფაილის გახსნა შეუძლებელია."
+
+#: core/attachmentmodel.cpp:252
+#, kde-format
+msgctxt "@info"
+msgid "Failed to save attachment for opening."
+msgstr "მიმაგრებული ფაილის გასახსნელად შენახვის შეცდომა."
+
+#: core/attachmentmodel.cpp:273
+#, kde-format
+msgctxt "@info"
+msgid "No keys were found in this attachment"
+msgstr "ამ მიმაგრებულ ფაილში გასაღებები ვერ ვიპოვე"
+
+#: core/attachmentmodel.cpp:276
+#, kde-format
+msgctxt "@info"
+msgid "one key imported"
+msgid_plural "%1 keys imported"
+msgstr[0] "შემოტანილია 1 გასაღები"
+msgstr[1] "შემოტანილია %1 გასაღები"
+
+#: core/attachmentmodel.cpp:279
+#, kde-format
+msgctxt "@info"
+msgid "one key was already imported"
+msgid_plural "%1 keys were already imported"
+msgstr[0] "1 გასაღები უკვე შემოტანილია"
+msgstr[1] "%1 გასაღები უკვე შემოტანილია"
+
+#: core/mailtemplates.cpp:103
+#, kde-format
+msgid "fwd"
+msgstr "გადგზ"
+
+#: core/mailtemplates.cpp:132
+#, kde-format
+msgid "re"
+msgstr "პას"
+
+#: core/messageparser.cpp:158
+#, kde-format
+msgctxt "displayed when a mail has unknown sender, receiver or date"
+msgid "Unknown"
+msgstr "უცნობი"
+
+#: core/messagepart.cpp:625 core/messagepart.cpp:660 core/messagepart.cpp:835
+#, kde-format
+msgctxt "@info:status"
+msgid "Wrong Crypto Plug-In."
+msgstr "კრიპტოს არასწორი დამატება."
+
+#: core/messagepart.cpp:986
+#, kde-format
+msgid "No appropriate crypto plug-in was found."
+msgstr "კრიპტოს შესაბამისი დამატება ვერ ვიპოვე."
+
+#: core/messagepart.cpp:988
+#, kde-format
+msgid "Crypto plug-in \"%1\" cannot decrypt messages."
+msgstr "კრიპტოს დამატებას \"%1\" შეტყობინებების გაშიფვრა არ შეუძლია."
+
+#: core/messagepart.cpp:990
+#, kde-format
+msgid "Crypto plug-in \"%1\" could not decrypt the data."
+msgstr "კრიპტოს დამატებას \"%1\" მონაცემების გაშიფვრა არ შეუძლია."
+
+#: core/messagepart.cpp:991 widgets/messageviewer.cpp:295
+#, kde-format
+msgid "Error: %1"
+msgstr "შეცდომა: %1"
+
+#: core/partmodel.cpp:487
+#, kde-format
+msgctxt "@info:status"
+msgid "No key available."
+msgstr "გასაღები ხელმიუწვდომელია."
+
+#: core/partmodel.cpp:489
+#, kde-format
+msgctxt "@info:status"
+msgid "Wrong passphrase."
+msgstr "არასწორი საიდუმლო ფრაზა."
+
+#: quick/qml/MailViewer.qml:38
+#, kde-format
+msgctxt "@title:window"
+msgid "Message viewer"
+msgstr "შეტყობინების ნახვა"
+
+#: quick/qml/MailViewer.qml:75
+#, kde-format
+msgctxt "@label"
+msgid "Subject:"
+msgstr "სათაური:"
+
+#: quick/qml/MailViewer.qml:96
+#, kde-format
+msgctxt "@label"
+msgid "From:"
+msgstr "ვისგან:"
+
+#: quick/qml/MailViewer.qml:112
+#, kde-format
+msgctxt "@label"
+msgid "Sender:"
+msgstr "გამგზავნი:"
+
+#: quick/qml/MailViewer.qml:128
+#, kde-format
+msgctxt "@label"
+msgid "To:"
+msgstr "ვის:"
+
+#: quick/qml/MailViewer.qml:185
+#, kde-format
+msgctxt "@action:button"
+msgid "Save attachment"
+msgstr "მიმაგრებული ფაილის შენახვა"
+
+#: quick/qml/private/AttachmentDelegate.qml:56
+#, kde-format
+msgctxt "@action:button"
+msgid "Import key"
+msgstr "გასაღების შემოტანა"
+
+#: quick/qml/private/Banner.qml:188
+#, kde-format
+msgctxt "@action:button"
+msgid "Close"
+msgstr "დახურვა"
+
+#: quick/qml/private/ErrorPart.qml:27
+#, kde-format
+msgid "An error occurred: %1"
+msgstr "შეცდომა: %1"
+
+#: quick/qml/private/ICalPart.qml:27
+#, kde-format
+msgid "This mail contains an invitation"
+msgstr "ეს ელფოსტა მოსაწვევს შეიცავს"
+
+#: quick/qml/private/MailPart.qml:41
+#, kde-format
+msgctxt "@info"
+msgid "sent by %1 on %2"
+msgstr "გაგზავნილია %1-ის მიერ. თარიღი %2"
+
+#: quick/qml/private/MailPartModel.qml:55
+#, kde-format
+msgctxt "@label"
+msgid "This message has been signed using the key %1."
+msgstr "ეს შეტყობინება ხელმოწერილია გასაღებით %1."
+
+#: quick/qml/private/MailPartModel.qml:56 widgets/messagecontainerwidget.cpp:78
+#, kde-format
+msgctxt "@label"
+msgid "The key details are not available."
+msgstr "გასაღების დეტალები ხელმიუწვდომელია."
+
+#: quick/qml/private/MailPartModel.qml:58
+#, kde-format
+msgctxt "@label"
+msgid "This message has been signed using the key %1 by %2."
+msgstr "ეს შეტყობინება ხელმოწერილია გასაღებით %1, %2-ის მიერ."
+
+#: quick/qml/private/MailPartModel.qml:60
+#, kde-format
+msgctxt "@label"
+msgid "The key was revoked."
+msgstr "გასაღები გაუქმებულია."
+
+#: quick/qml/private/MailPartModel.qml:63
+#, kde-format
+msgctxt "@label"
+msgid "The key has expired."
+msgstr "გასაღების ვადაგასულია."
+
+#: quick/qml/private/MailPartModel.qml:66
+#, kde-format
+msgctxt "@label"
+msgid "You are trusting this key."
+msgstr "თქვენ ენდობით ამ გასაღებს."
+
+#: quick/qml/private/MailPartModel.qml:69
+#: widgets/messagecontainerwidget.cpp:108
+#, kde-format
+msgctxt "@label"
+msgid "The signature is invalid."
+msgstr "ხელმოწერა არასწორია."
+
+#: quick/qml/private/MailPartModel.qml:98
+#: widgets/messagecontainerwidget.cpp:205
+#, kde-format
+msgid "This message is encrypted but we don't have the key for it."
+msgstr "შეტყობინება დაშიფრულია, მაგრამ მისი გასაღები არ გვაქვს."
+
+#: quick/qml/private/MailPartModel.qml:98
+#, kde-format
+msgid "This message is encrypted to the key: %1"
+msgstr "შეტყობინება დაშიფრულია ამ გასაღებამდე: %1"
+
+#: widgets/messagecontainerwidget.cpp:66
+#, kde-format
+msgctxt "@label"
+msgid ""
+"This message has been signed VS-NfD compliant using the key <a href=\"%1\">"
+"%2</a>."
+msgstr ""
+"გასაღების ხელმოწერა თავსებადია VS-NfD-სთან. გამოყენებულია გასაღები<a href="
+"\"%1\">%2</a>."
+
+#: widgets/messagecontainerwidget.cpp:73
+#, kde-format
+msgctxt "@label"
+msgid "This message has been signed using the key <a href=\"%1\">%2</a>."
+msgstr "ეს შეტყობინება ხელმოწერილია გასაღებით <a href=\"%1\">%2</a>."
+
+#: widgets/messagecontainerwidget.cpp:81
+#, kde-format
+msgctxt "@label"
+msgid "This message has been signed VS-NfD compliant by %1."
+msgstr "ამ შეტყობინების ხელმოწერა თავსებადია VS-NfD-სთან. ავტორია %1."
+
+#: widgets/messagecontainerwidget.cpp:83
+#, kde-format
+msgctxt "@label"
+msgid "This message has been signed by %1."
+msgstr "შეტყობინება ხელმოწერილია %1-ის მიერ."
+
+#: widgets/messagecontainerwidget.cpp:86
+#, kde-format
+msgctxt "@label"
+msgid "The <a href=\"%1\">key</a> was revoked."
+msgstr "<a href=\"%1\">გასაღები</a> გაუქმებულია."
+
+#: widgets/messagecontainerwidget.cpp:89
+#, kde-format
+msgctxt "@label"
+msgid "The <a href=\"%1\">key</a> was expired."
+msgstr "<a href=\"%1\">გასაღები</a>ს ვადა ამოიწურა."
+
+#: widgets/messagecontainerwidget.cpp:94
+#, kde-format
+msgctxt "@label"
+msgid ""
+"The signature is valid, but the <a href=\"%1\">key</a>'s validity is unknown."
+msgstr ""
+"ხელმოწერა სწორია, მაგრამ <a href=\"%1\">გასაღების</a> გადამოწმება "
+"შეუძლებელია."
+
+#: widgets/messagecontainerwidget.cpp:97
+#, kde-format
+msgctxt "@label"
+msgid ""
+"The signature is valid and the <a href=\"%1\">key</a> is marginally trusted."
+msgstr "ხელმოწერია სწორია და <a href=\"%1\">გასაღები</a> ოდნავ საეჭვოა."
+
+#: widgets/messagecontainerwidget.cpp:99
+#, kde-format
+msgctxt "@label"
+msgid "The signature is valid and the <a href=\"%1\">key</a> is fully trusted."
+msgstr "ხელმოწერია სწორია და <a href=\"%1\">გასაღები</a> სრულად სანდოა."
+
+#: widgets/messagecontainerwidget.cpp:102
+#, kde-format
+msgctxt "@label"
+msgid ""
+"The signature is valid and the <a href=\"%1\">key</a> is ultimately trusted."
+msgstr "ხელმოწერა სწორია და <a href=\"%1\">გასაღები</a> უპირობოდ სანდოა."
+
+#: widgets/messagecontainerwidget.cpp:104
+#, kde-format
+msgctxt "@label"
+msgid "The signature is valid, but the <a href=\"%1\">key</a> is untrusted."
+msgstr "ხელმოწერია სწორია, მაგრამ <a href=\"%1\">გასაღები</a> სანდო არაა."
+
+#: widgets/messagecontainerwidget.cpp:203
+#, kde-format
+msgid ""
+"This message is VS-NfD compliant encrypted but we don't have the key for it."
+msgstr ""
+"შეტყობინება დაშიფრულია VS-NfD-სთან თავსებადად, მაგრამ მისი გასაღები არ "
+"გვაქვს."
+
+#: widgets/messagecontainerwidget.cpp:209
+#, kde-format
+msgid "This message is VS-NfD compliant encrypted."
+msgstr "ეს შეტყობინება დაშიფრულია VS-NfD-სთან თავსებადი შიფრით."
+
+#: widgets/messagecontainerwidget.cpp:211
+#, kde-format
+msgid "This message is encrypted."
+msgstr "ეს შეტყობინება დაშიფრულია."
+
+#: widgets/messagecontainerwidget.cpp:220
+#, kde-format
+msgid "The message is encrypted for the following keys:"
+msgstr "შეტყობინება დაშიფრულია შემდეგი გასაღებებისთვის:"
+
+#: widgets/messagecontainerwidget.cpp:237
+#, kde-format
+msgid "Unknown key"
+msgstr "უცნობი გასაღები"
+
+#: widgets/messageviewer.cpp:57
+#, kde-format
+msgid "&Save Attachment As..."
+msgstr "მიმაგრებული ფაილის &შენახვა, როგორც…"
+
+#: widgets/messageviewer.cpp:62
+#, kde-format
+msgctxt "to open"
+msgid "Open"
+msgstr "გახსნა"
+
+#: widgets/messageviewer.cpp:67
+#, kde-format
+msgctxt "@action:inmenu"
+msgid "Import public key"
+msgstr "საჯარო გასაღების შემოტანა"
+
+#: widgets/messageviewer.cpp:236
+#, kde-format
+msgid "Invitation"
+msgstr "მოწვევა"
+
+#: widgets/messageviewer.cpp:239
+#, kde-format
+msgid "&Summary:"
+msgstr "&შეჯამება:"
+
+#: widgets/messageviewer.cpp:240
+#, kde-format
+msgid "&Organizer:"
+msgstr "&ორგანიზატორი:"
+
+#: widgets/messageviewer.cpp:242
+#, kde-format
+msgid "&Location:"
+msgstr "&მდებარეობა:"
+
+#: widgets/messageviewer.cpp:244
+#, kde-format
+msgid "&Start date:"
+msgstr "&დაწყების თარიღი:"
+
+#: widgets/messageviewer.cpp:246
+#, kde-format
+msgid "&End date:"
+msgstr "&დასრულების დრო:"
+
+#: widgets/messageviewer.cpp:249
+#, kde-format
+msgid "&Details:"
+msgstr "&დეტალები:"
+
+#: widgets/messageviewer.cpp:271
+#, kde-format
+msgid "Encapsulated email"
+msgstr "ჩასმული შეტყობინება"
+
+#: widgets/messageviewer.cpp:279
+#, kde-format
+msgid "From:"
+msgstr "ვისგან:"
+
+#: widgets/messageviewer.cpp:280
+#, kde-format
+msgid "Date:"
+msgstr "თარიღი:"
+
+#: widgets/messageviewer.cpp:315
+#, kde-format
+msgid "&Subject:"
+msgstr "&სათაური:"
+
+#: widgets/messageviewer.cpp:318
+#, kde-format
+msgid "&From:"
+msgstr "&ვისგან:"
+
+#: widgets/messageviewer.cpp:321
+#, kde-format
+msgid "&To:"
+msgstr "&ვის:"
+
+#: widgets/urlhandler.cpp:37
+#, kde-format
+msgid "Could not start certificate manager. Please check your installation."
+msgstr "სერტიფიკატების მმართველის გაშვების შეცდომა. გადაამოწმეთ ფაილები."
+
+#: widgets/urlhandler.cpp:39
+#, kde-format
+msgid "KMail Error"
+msgstr "KMail-ის შეცდომა"
+
+#~ msgctxt "@info"
+#~ msgid "Could not decrypt the data: no key found for recipients."
+#~ msgstr "მონაცემების გაშიფვრის შეცდომა: მიმღებებისთვის გასაღები ვერ ვიპოვე."
diff --git a/po/ko/mimetreeparser.po b/po/ko/mimetreeparser.po
new file mode 100644
index 0000000..c527175
--- /dev/null
+++ b/po/ko/mimetreeparser.po
@@ -0,0 +1,450 @@
+# Copyright (C) YEAR This file is copyright:
+# This file is distributed under the same license as the mimetreeparser package.
+# Shinjo Park <kde@peremen.name>, 2023.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: mimetreeparser\n"
+"Report-Msgid-Bugs-To: https://bugs.kde.org\n"
+"POT-Creation-Date: 2023-08-27 00:47+0000\n"
+"PO-Revision-Date: 2023-08-27 01:20+0200\n"
+"Last-Translator: Shinjo Park <kde@peremen.name>\n"
+"Language-Team: Korean <kde-kr@kde.org>\n"
+"Language: ko\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+"X-Generator: Lokalize 22.12.3\n"
+
+#: core/attachmentmodel.cpp:90
+#, kde-format
+msgctxt "@title:column"
+msgid "Name"
+msgstr "이름"
+
+#: core/attachmentmodel.cpp:92
+#, kde-format
+msgctxt "@title:column"
+msgid "Size"
+msgstr "크기"
+
+#: core/attachmentmodel.cpp:94
+#, kde-format
+msgctxt "@title:column"
+msgid "Encrypted"
+msgstr "암호화됨"
+
+#: core/attachmentmodel.cpp:96
+#, kde-format
+msgctxt "@title:column"
+msgid "Signed"
+msgstr "서명됨"
+
+#: core/attachmentmodel.cpp:198
+#, kde-format
+msgctxt "@info"
+msgid "Failed to save attachment."
+msgstr "첨부 파일을 저장할 수 없습니다."
+
+#: core/attachmentmodel.cpp:230
+#, kde-format
+msgctxt "@info"
+msgid "Saved the attachment to disk: %1"
+msgstr "첨부 파일을 디스크에 저장함: %1"
+
+#: core/attachmentmodel.cpp:247
+#, kde-format
+msgctxt "@info"
+msgid "Failed to open attachment."
+msgstr "첨부 파일을 열 수 없습니다."
+
+#: core/attachmentmodel.cpp:252
+#, kde-format
+msgctxt "@info"
+msgid "Failed to save attachment for opening."
+msgstr "열 첨부 파일을 저장할 수 없습니다."
+
+#: core/attachmentmodel.cpp:273
+#, kde-format
+msgctxt "@info"
+msgid "No keys were found in this attachment"
+msgstr "이 첨부 파일에서 키를 찾을 수 없음"
+
+#: core/attachmentmodel.cpp:276
+#, kde-format
+msgctxt "@info"
+msgid "one key imported"
+msgid_plural "%1 keys imported"
+msgstr[0] "키 %1개를 가져옴"
+
+#: core/attachmentmodel.cpp:279
+#, kde-format
+msgctxt "@info"
+msgid "one key was already imported"
+msgid_plural "%1 keys were already imported"
+msgstr[0] "키 %1개는 이미 가져옴"
+
+#: core/mailtemplates.cpp:103
+#, kde-format
+msgid "fwd"
+msgstr "fwd"
+
+#: core/mailtemplates.cpp:132
+#, kde-format
+msgid "re"
+msgstr "re"
+
+#: core/messageparser.cpp:158
+#, kde-format
+msgctxt "displayed when a mail has unknown sender, receiver or date"
+msgid "Unknown"
+msgstr "알 수 없음"
+
+#: core/messagepart.cpp:625 core/messagepart.cpp:660 core/messagepart.cpp:835
+#, kde-format
+msgctxt "@info:status"
+msgid "Wrong Crypto Plug-In."
+msgstr "잘못된 암호화 플러그인입니다."
+
+#: core/messagepart.cpp:986
+#, kde-format
+msgid "No appropriate crypto plug-in was found."
+msgstr "적절한 암호화 플러그인을 찾을 수 없습니다."
+
+#: core/messagepart.cpp:988
+#, kde-format
+msgid "Crypto plug-in \"%1\" cannot decrypt messages."
+msgstr "암호화 플러그인 \"%1\"에서 메시지를 복호화할 수 없습니다."
+
+#: core/messagepart.cpp:990
+#, kde-format
+msgid "Crypto plug-in \"%1\" could not decrypt the data."
+msgstr "암호화 플러그인 \"%1\"에서 데이터를 복호화할 수 없습니다."
+
+#: core/messagepart.cpp:991 widgets/messageviewer.cpp:295
+#, kde-format
+msgid "Error: %1"
+msgstr "오류: %1"
+
+#: core/partmodel.cpp:487
+#, kde-format
+msgctxt "@info:status"
+msgid "No key available."
+msgstr "키를 사용할 수 없습니다."
+
+#: core/partmodel.cpp:489
+#, kde-format
+msgctxt "@info:status"
+msgid "Wrong passphrase."
+msgstr "암구호가 잘못되었습니다."
+
+#: quick/qml/MailViewer.qml:38
+#, kde-format
+msgctxt "@title:window"
+msgid "Message viewer"
+msgstr "메시지 뷰어"
+
+#: quick/qml/MailViewer.qml:75
+#, kde-format
+msgctxt "@label"
+msgid "Subject:"
+msgstr "제목:"
+
+#: quick/qml/MailViewer.qml:96
+#, kde-format
+msgctxt "@label"
+msgid "From:"
+msgstr "보낸 사람:"
+
+#: quick/qml/MailViewer.qml:112
+#, kde-format
+msgctxt "@label"
+msgid "Sender:"
+msgstr "전송한 사람:"
+
+#: quick/qml/MailViewer.qml:128
+#, kde-format
+msgctxt "@label"
+msgid "To:"
+msgstr "받는 사람:"
+
+#: quick/qml/MailViewer.qml:185
+#, kde-format
+msgctxt "@action:button"
+msgid "Save attachment"
+msgstr "첨부 파일 저장"
+
+#: quick/qml/private/AttachmentDelegate.qml:56
+#, kde-format
+msgctxt "@action:button"
+msgid "Import key"
+msgstr "키 가져오기..."
+
+#: quick/qml/private/Banner.qml:188
+#, kde-format
+msgctxt "@action:button"
+msgid "Close"
+msgstr "닫기"
+
+#: quick/qml/private/ErrorPart.qml:27
+#, kde-format
+msgid "An error occurred: %1"
+msgstr "오류 발생: %1"
+
+#: quick/qml/private/ICalPart.qml:27
+#, kde-format
+msgid "This mail contains an invitation"
+msgstr "이 메일에는 초대장이 첨부되어 있습니다"
+
+#: quick/qml/private/MailPart.qml:41
+#, kde-format
+msgctxt "@info"
+msgid "sent by %1 on %2"
+msgstr "%1 님이 %2에 보냄"
+
+#: quick/qml/private/MailPartModel.qml:55
+#, kde-format
+msgctxt "@label"
+msgid "This message has been signed using the key %1."
+msgstr "이 메시지는 키 %1(으)로 서명되었습니다."
+
+#: quick/qml/private/MailPartModel.qml:56 widgets/messagecontainerwidget.cpp:78
+#, kde-format
+msgctxt "@label"
+msgid "The key details are not available."
+msgstr "키 정보를 알 수 없습니다."
+
+#: quick/qml/private/MailPartModel.qml:58
+#, kde-format
+msgctxt "@label"
+msgid "This message has been signed using the key %1 by %2."
+msgstr "%2 님이 키 %1(으)로 이 메시지에 서명했습니다."
+
+#: quick/qml/private/MailPartModel.qml:60
+#, kde-format
+msgctxt "@label"
+msgid "The key was revoked."
+msgstr "키가 취소되었습니다."
+
+#: quick/qml/private/MailPartModel.qml:63
+#, kde-format
+msgctxt "@label"
+msgid "The key has expired."
+msgstr "키가 만료되었습니다."
+
+#: quick/qml/private/MailPartModel.qml:66
+#, kde-format
+msgctxt "@label"
+msgid "You are trusting this key."
+msgstr "이 키를 신뢰하고 있습니다."
+
+#: quick/qml/private/MailPartModel.qml:69
+#: widgets/messagecontainerwidget.cpp:108
+#, kde-format
+msgctxt "@label"
+msgid "The signature is invalid."
+msgstr "서명이 잘못되었습니다."
+
+#: quick/qml/private/MailPartModel.qml:98
+#: widgets/messagecontainerwidget.cpp:205
+#, kde-format
+msgid "This message is encrypted but we don't have the key for it."
+msgstr "메시지가 암호화되어 있지만 키가 없습니다."
+
+#: quick/qml/private/MailPartModel.qml:98
+#, kde-format
+msgid "This message is encrypted to the key: %1"
+msgstr "이 메시지는 다음 키로 암호화되어 있습니다: %1"
+
+#: widgets/messagecontainerwidget.cpp:66
+#, kde-format
+msgctxt "@label"
+msgid ""
+"This message has been signed VS-NfD compliant using the key <a href=\"%1\">"
+"%2</a>."
+msgstr ""
+"이 메시지는 VS-NfD를 준수하는 방법으로 키 <a href=\"%1\">%2</a>(으)로 서명되"
+"었습니다."
+
+#: widgets/messagecontainerwidget.cpp:73
+#, kde-format
+msgctxt "@label"
+msgid "This message has been signed using the key <a href=\"%1\">%2</a>."
+msgstr "이 메시지는 키 <a href=\"%1\">%2</a>(으)로 서명되었습니다."
+
+#: widgets/messagecontainerwidget.cpp:81
+#, kde-format
+msgctxt "@label"
+msgid "This message has been signed VS-NfD compliant by %1."
+msgstr "%1이(가) 이 메시지에 VS-NfD를 준수하는 방법으로 서명했습니다."
+
+#: widgets/messagecontainerwidget.cpp:83
+#, kde-format
+msgctxt "@label"
+msgid "This message has been signed by %1."
+msgstr "%1이(가) 메시지에 서명했습니다."
+
+#: widgets/messagecontainerwidget.cpp:86
+#, kde-format
+msgctxt "@label"
+msgid "The <a href=\"%1\">key</a> was revoked."
+msgstr "<a href=\"%1\">키</a>가 취소되었습니다."
+
+#: widgets/messagecontainerwidget.cpp:89
+#, kde-format
+msgctxt "@label"
+msgid "The <a href=\"%1\">key</a> was expired."
+msgstr "<a href=\"%1\">키</a>가 만료되었습니다."
+
+#: widgets/messagecontainerwidget.cpp:94
+#, kde-format
+msgctxt "@label"
+msgid ""
+"The signature is valid, but the <a href=\"%1\">key</a>'s validity is unknown."
+msgstr "서명이 올바르지만 <a href=\"%1\">키</a> 유효성을 알 수 없습니다."
+
+#: widgets/messagecontainerwidget.cpp:97
+#, kde-format
+msgctxt "@label"
+msgid ""
+"The signature is valid and the <a href=\"%1\">key</a> is marginally trusted."
+msgstr "서명이 올바르지만 <a href=\"%1\">키</a>를 그럭저럭 신뢰합니다."
+
+#: widgets/messagecontainerwidget.cpp:99
+#, kde-format
+msgctxt "@label"
+msgid "The signature is valid and the <a href=\"%1\">key</a> is fully trusted."
+msgstr "서명이 올바르고 <a href=\"%1\">키</a>를 웬만큼 신뢰합니다."
+
+#: widgets/messagecontainerwidget.cpp:102
+#, kde-format
+msgctxt "@label"
+msgid ""
+"The signature is valid and the <a href=\"%1\">key</a> is ultimately trusted."
+msgstr "서명이 올바르고 <a href=\"%1\">키</a>를 완전히 신뢰합니다."
+
+#: widgets/messagecontainerwidget.cpp:104
+#, kde-format
+msgctxt "@label"
+msgid "The signature is valid, but the <a href=\"%1\">key</a> is untrusted."
+msgstr "서명이 올바르지만 <a href=\"%1\">키</a>를 신뢰할 수 없습니다."
+
+#: widgets/messagecontainerwidget.cpp:203
+#, kde-format
+msgid ""
+"This message is VS-NfD compliant encrypted but we don't have the key for it."
+msgstr "이 메시지는 VS-NfD를 준수하는 방법으로 암호화되었지만 키가 없습니다."
+
+#: widgets/messagecontainerwidget.cpp:209
+#, kde-format
+msgid "This message is VS-NfD compliant encrypted."
+msgstr "이 메시지는 VS-NfD를 준수하는 방법으로 암호화되었습니다."
+
+#: widgets/messagecontainerwidget.cpp:211
+#, kde-format
+msgid "This message is encrypted."
+msgstr "이 메시지는 암호화되어 있습니다."
+
+#: widgets/messagecontainerwidget.cpp:220
+#, kde-format
+msgid "The message is encrypted for the following keys:"
+msgstr "이 메시지는 다음 키로 암호화되어 있습니다:"
+
+#: widgets/messagecontainerwidget.cpp:237
+#, fuzzy, kde-format
+#| msgid "Unknow key"
+msgid "Unknown key"
+msgstr "알 수 없는 키"
+
+#: widgets/messageviewer.cpp:57
+#, kde-format
+msgid "&Save Attachment As..."
+msgstr "다른 이름으로 첨부 파일 저장(&S)..."
+
+#: widgets/messageviewer.cpp:62
+#, kde-format
+msgctxt "to open"
+msgid "Open"
+msgstr "열기"
+
+#: widgets/messageviewer.cpp:67
+#, kde-format
+msgctxt "@action:inmenu"
+msgid "Import public key"
+msgstr "공개 키 가져오기"
+
+#: widgets/messageviewer.cpp:236
+#, kde-format
+msgid "Invitation"
+msgstr "초대장"
+
+#: widgets/messageviewer.cpp:239
+#, kde-format
+msgid "&Summary:"
+msgstr "개요(&S):"
+
+#: widgets/messageviewer.cpp:240
+#, kde-format
+msgid "&Organizer:"
+msgstr "주최(&O):"
+
+#: widgets/messageviewer.cpp:242
+#, kde-format
+msgid "&Location:"
+msgstr "위치(&L):"
+
+#: widgets/messageviewer.cpp:244
+#, kde-format
+msgid "&Start date:"
+msgstr "시작 날짜(&S):"
+
+#: widgets/messageviewer.cpp:246
+#, kde-format
+msgid "&End date:"
+msgstr "종료 날짜(&E):"
+
+#: widgets/messageviewer.cpp:249
+#, kde-format
+msgid "&Details:"
+msgstr "자세히(&D):"
+
+#: widgets/messageviewer.cpp:271
+#, kde-format
+msgid "Encapsulated email"
+msgstr "캡슐화된 이메일"
+
+#: widgets/messageviewer.cpp:279
+#, kde-format
+msgid "From:"
+msgstr "보낸 사람:"
+
+#: widgets/messageviewer.cpp:280
+#, kde-format
+msgid "Date:"
+msgstr "날짜:"
+
+#: widgets/messageviewer.cpp:315
+#, kde-format
+msgid "&Subject:"
+msgstr "제목(&S):"
+
+#: widgets/messageviewer.cpp:318
+#, kde-format
+msgid "&From:"
+msgstr "보낸 사람(&F):"
+
+#: widgets/messageviewer.cpp:321
+#, kde-format
+msgid "&To:"
+msgstr "받는 사람(&T):"
+
+#: widgets/urlhandler.cpp:37
+#, kde-format
+msgid "Could not start certificate manager. Please check your installation."
+msgstr "인증서 관리자를 시작할 수 없습니다. 설치 상태를 확인하십시오."
+
+#: widgets/urlhandler.cpp:39
+#, kde-format
+msgid "KMail Error"
+msgstr "KMail 오류"
diff --git a/po/nl/mimetreeparser.po b/po/nl/mimetreeparser.po
index 1482730..5416abb 100644
--- a/po/nl/mimetreeparser.po
+++ b/po/nl/mimetreeparser.po
@@ -1,339 +1,470 @@
 # Copyright (C) YEAR This file is copyright:
 # This file is distributed under the same license as the mimetreeparser package.
 #
 # Freek de Kruijf <freekdekruijf@kde.nl>, 2023.
 msgid ""
 msgstr ""
 "Project-Id-Version: mimetreeparser\n"
 "Report-Msgid-Bugs-To: https://bugs.kde.org\n"
-"POT-Creation-Date: 2023-08-15 00:46+0000\n"
-"PO-Revision-Date: 2023-08-12 09:45+0200\n"
+"POT-Creation-Date: 2023-08-27 00:47+0000\n"
+"PO-Revision-Date: 2023-08-27 18:13+0200\n"
 "Last-Translator: Freek de Kruijf <freekdekruijf@kde.nl>\n"
 "Language-Team: \n"
 "Language: nl\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=n != 1;\n"
 "X-Generator: Lokalize 23.04.3\n"
 
-#: core/attachmentmodel.cpp:88
+#: core/attachmentmodel.cpp:90
 #, kde-format
 msgctxt "@title:column"
 msgid "Name"
 msgstr "Naam"
 
-#: core/attachmentmodel.cpp:90
+#: core/attachmentmodel.cpp:92
 #, kde-format
 msgctxt "@title:column"
 msgid "Size"
 msgstr "Grootte"
 
-#: core/attachmentmodel.cpp:92
+#: core/attachmentmodel.cpp:94
 #, kde-format
 msgctxt "@title:column"
 msgid "Encrypted"
 msgstr "Versleuteld"
 
-#: core/attachmentmodel.cpp:94
+#: core/attachmentmodel.cpp:96
 #, kde-format
 msgctxt "@title:column"
 msgid "Signed"
 msgstr "Gesigneerd"
 
-#: core/attachmentmodel.cpp:196
+#: core/attachmentmodel.cpp:198
 #, kde-format
 msgctxt "@info"
 msgid "Failed to save attachment."
 msgstr "Bijlage opslaan is mislukt."
 
-#: core/attachmentmodel.cpp:228
+#: core/attachmentmodel.cpp:230
 #, kde-format
 msgctxt "@info"
 msgid "Saved the attachment to disk: %1"
 msgstr "De bijlage is op schijf opgeslagen: %1"
 
-#: core/attachmentmodel.cpp:245
+#: core/attachmentmodel.cpp:247
 #, kde-format
 msgctxt "@info"
 msgid "Failed to open attachment."
 msgstr "Bijlage openen is mislukt."
 
-#: core/attachmentmodel.cpp:250
+#: core/attachmentmodel.cpp:252
 #, kde-format
 msgctxt "@info"
 msgid "Failed to save attachment for opening."
 msgstr "Bijlage openen voor opslaan is mislukt."
 
-#: core/attachmentmodel.cpp:268
+#: core/attachmentmodel.cpp:273
 #, kde-format
 msgctxt "@info"
 msgid "No keys were found in this attachment"
 msgstr "Er zijn geen sleutels in deze bijlage gevonden"
 
-#: core/attachmentmodel.cpp:271
+#: core/attachmentmodel.cpp:276
 #, kde-format
 msgctxt "@info"
 msgid "one key imported"
 msgid_plural "%1 keys imported"
 msgstr[0] "één sleutel geïmporteerd"
 msgstr[1] ""
 "<para><emphasis strong='true'>%1 sleutels geïmporteerd:</emphasis></para>"
 
-#: core/attachmentmodel.cpp:274
+#: core/attachmentmodel.cpp:279
 #, kde-format
 msgctxt "@info"
 msgid "one key was already imported"
 msgid_plural "%1 keys were already imported"
 msgstr[0] "er is al één sleutel geïmporteerd"
 msgstr[1] "er zijn al %1 sleutels geïmporteerd"
 
 #: core/mailtemplates.cpp:103
 #, kde-format
 msgid "fwd"
 msgstr "fwd"
 
 #: core/mailtemplates.cpp:132
 #, kde-format
 msgid "re"
 msgstr "re"
 
 #: core/messageparser.cpp:158
 #, kde-format
 msgctxt "displayed when a mail has unknown sender, receiver or date"
 msgid "Unknown"
 msgstr "Onbekend"
 
-#: core/messagepart.cpp:618 core/messagepart.cpp:709 core/messagepart.cpp:783
+#: core/messagepart.cpp:625 core/messagepart.cpp:660 core/messagepart.cpp:835
 #, kde-format
 msgctxt "@info:status"
 msgid "Wrong Crypto Plug-In."
 msgstr "Onjuiste crypto-plug-in."
 
-#: core/messagepart.cpp:857
+#: core/messagepart.cpp:986
 #, kde-format
-msgctxt "@info"
-msgid "Could not decrypt the data: no key found for recipients."
-msgstr ""
-"Kon de gegevens niet ontcijferen: geen sleutel gevonden voor ontvangers."
+msgid "No appropriate crypto plug-in was found."
+msgstr "Geen geschikte Crypto-plug-in gevonden."
 
-#: core/messagepart.cpp:862
+#: core/messagepart.cpp:988
 #, kde-format
-msgctxt "@info"
-msgid "Could not decrypt the data."
-msgstr "Kon de gegevens niet ontcijferen."
+msgid "Crypto plug-in \"%1\" cannot decrypt messages."
+msgstr "Crypto-plug-in \"%1\" kan geen berichten ontcijferen."
+
+#: core/messagepart.cpp:990
+#, kde-format
+msgid "Crypto plug-in \"%1\" could not decrypt the data."
+msgstr "De crypto-plug-in \"%1\" kon de gegevens niet ontcijferen."
 
-#: core/partmodel.cpp:483
+#: core/messagepart.cpp:991 widgets/messageviewer.cpp:295
+#, kde-format
+msgid "Error: %1"
+msgstr "Fout: %1"
+
+#: core/partmodel.cpp:487
 #, kde-format
 msgctxt "@info:status"
 msgid "No key available."
 msgstr "Geen sleutel beschikbaar."
 
-#: core/partmodel.cpp:485
+#: core/partmodel.cpp:489
 #, kde-format
 msgctxt "@info:status"
 msgid "Wrong passphrase."
 msgstr "Verkeerde wachtwoordzin."
 
-#: quick/qml/MailViewer.qml:39
+#: quick/qml/MailViewer.qml:38
 #, kde-format
 msgctxt "@title:window"
 msgid "Message viewer"
 msgstr "Viewer van meldingen"
 
-#: quick/qml/MailViewer.qml:76
+#: quick/qml/MailViewer.qml:75
 #, kde-format
 msgctxt "@label"
 msgid "Subject:"
 msgstr "Onderwerp:"
 
-#: quick/qml/MailViewer.qml:98
+#: quick/qml/MailViewer.qml:96
 #, kde-format
 msgctxt "@label"
 msgid "From:"
 msgstr "Van:"
 
-#: quick/qml/MailViewer.qml:115
+#: quick/qml/MailViewer.qml:112
 #, kde-format
 msgctxt "@label"
 msgid "Sender:"
 msgstr "Afzender:"
 
-#: quick/qml/MailViewer.qml:132
+#: quick/qml/MailViewer.qml:128
 #, kde-format
 msgctxt "@label"
 msgid "To:"
 msgstr "Aan:"
 
-#: quick/qml/MailViewer.qml:190
+#: quick/qml/MailViewer.qml:185
 #, kde-format
 msgctxt "@action:button"
 msgid "Save attachment"
 msgstr "Bijlage opslaan"
 
 #: quick/qml/private/AttachmentDelegate.qml:56
 #, kde-format
 msgctxt "@action:button"
 msgid "Import key"
 msgstr "Sleutel importeren"
 
 #: quick/qml/private/Banner.qml:188
 #, kde-format
 msgctxt "@action:button"
 msgid "Close"
 msgstr "Sluiten"
 
 #: quick/qml/private/ErrorPart.qml:27
 #, kde-format
 msgid "An error occurred: %1"
 msgstr "Er is een fout opgetreden: %1"
 
-#: quick/qml/private/ICalPart.qml:28
+#: quick/qml/private/ICalPart.qml:27
 #, kde-format
 msgid "This mail contains an invitation"
 msgstr "Deze e-mail bevat een uitnodiging"
 
-#: quick/qml/private/MailPart.qml:42
+#: quick/qml/private/MailPart.qml:41
 #, kde-format
 msgctxt "@info"
 msgid "sent by %1 on %2"
 msgstr "verzonden door %1 op %2"
 
-#: quick/qml/private/MailPartModel.qml:54
+#: quick/qml/private/MailPartModel.qml:55
 #, kde-format
 msgctxt "@label"
 msgid "This message has been signed using the key %1."
 msgstr "Dit bericht was ondertekend met de sleutel %1."
 
-#: quick/qml/private/MailPartModel.qml:55
+#: quick/qml/private/MailPartModel.qml:56 widgets/messagecontainerwidget.cpp:78
 #, kde-format
 msgctxt "@label"
 msgid "The key details are not available."
 msgstr "De details van de sleutel zijn niet beschikbaar."
 
-#: quick/qml/private/MailPartModel.qml:57
+#: quick/qml/private/MailPartModel.qml:58
 #, kde-format
 msgctxt "@label"
 msgid "This message has been signed using the key %1 by %2."
 msgstr "Dit bericht was ondertekend met de sleutel %1 door %2."
 
-#: quick/qml/private/MailPartModel.qml:59
+#: quick/qml/private/MailPartModel.qml:60
 #, kde-format
 msgctxt "@label"
 msgid "The key was revoked."
 msgstr "De sleutel is ingetrokken."
 
-#: quick/qml/private/MailPartModel.qml:62
+#: quick/qml/private/MailPartModel.qml:63
 #, kde-format
 msgctxt "@label"
 msgid "The key has expired."
 msgstr "De sleutel is verlopen."
 
-#: quick/qml/private/MailPartModel.qml:65
+#: quick/qml/private/MailPartModel.qml:66
 #, kde-format
 msgctxt "@label"
 msgid "You are trusting this key."
 msgstr "Deze sleutel wordt door u vertrouwd."
 
-#: quick/qml/private/MailPartModel.qml:68
+#: quick/qml/private/MailPartModel.qml:69
+#: widgets/messagecontainerwidget.cpp:108
 #, kde-format
 msgctxt "@label"
 msgid "The signature is invalid."
 msgstr "De ondertekening is ongeldig."
 
-#: quick/qml/private/MailPartModel.qml:97
+#: quick/qml/private/MailPartModel.qml:98
+#: widgets/messagecontainerwidget.cpp:205
 #, kde-format
 msgid "This message is encrypted but we don't have the key for it."
 msgstr "Dit bericht is versleuteld maar we hebben de sleutel ervoor niet."
 
-#: quick/qml/private/MailPartModel.qml:97
+#: quick/qml/private/MailPartModel.qml:98
 #, kde-format
 msgid "This message is encrypted to the key: %1"
 msgstr "Dit bericht is versleuteld met de sleutel: %1"
 
-#: widgets/messageviewer.cpp:54
+#: widgets/messagecontainerwidget.cpp:66
+#, kde-format
+msgctxt "@label"
+msgid ""
+"This message has been signed VS-NfD compliant using the key <a href=\"%1\">"
+"%2</a>."
+msgstr ""
+"Dit bericht was ondertekend overeenkomstig VS-NfD met de sleutel <a href="
+"\"%1\">%2</a>."
+
+#: widgets/messagecontainerwidget.cpp:73
+#, kde-format
+msgctxt "@label"
+msgid "This message has been signed using the key <a href=\"%1\">%2</a>."
+msgstr "Dit bericht was ondertekend met de sleutel <a href=\"%1\">%2</a>."
+
+#: widgets/messagecontainerwidget.cpp:81
+#, kde-format
+msgctxt "@label"
+msgid "This message has been signed VS-NfD compliant by %1."
+msgstr "Dit bericht was ondertekend overeenkomstig VS-NfD door %1."
+
+#: widgets/messagecontainerwidget.cpp:83
+#, kde-format
+msgctxt "@label"
+msgid "This message has been signed by %1."
+msgstr "Dit bericht was ondertekend door %1."
+
+#: widgets/messagecontainerwidget.cpp:86
+#, kde-format
+msgctxt "@label"
+msgid "The <a href=\"%1\">key</a> was revoked."
+msgstr "De <a href=\"%1\">sleutel</a> is ingetrokken."
+
+#: widgets/messagecontainerwidget.cpp:89
+#, kde-format
+msgctxt "@label"
+msgid "The <a href=\"%1\">key</a> was expired."
+msgstr "De <a href=\"%1\">sleutel</a> is verlopen."
+
+#: widgets/messagecontainerwidget.cpp:94
+#, kde-format
+msgctxt "@label"
+msgid ""
+"The signature is valid, but the <a href=\"%1\">key</a>'s validity is unknown."
+msgstr ""
+"De ondertekening is geldig, maar de geldigheid van de <a href="
+"\"%1\">sleutel</a> is onbekend."
+
+#: widgets/messagecontainerwidget.cpp:97
+#, kde-format
+msgctxt "@label"
+msgid ""
+"The signature is valid and the <a href=\"%1\">key</a> is marginally trusted."
+msgstr ""
+"De ondertekening is geldig en de <a href=\"%1\">sleutel</a> is gedeeltelijk "
+"betrouwbaar."
+
+#: widgets/messagecontainerwidget.cpp:99
+#, kde-format
+msgctxt "@label"
+msgid "The signature is valid and the <a href=\"%1\">key</a> is fully trusted."
+msgstr ""
+"De ondertekening is geldig en de <a href=\"%1\">sleutel</a> is volledig "
+"betrouwbaar."
+
+#: widgets/messagecontainerwidget.cpp:102
+#, kde-format
+msgctxt "@label"
+msgid ""
+"The signature is valid and the <a href=\"%1\">key</a> is ultimately trusted."
+msgstr ""
+"De ondertekening is geldig en de <a href=\"%1\">sleutel</a> is uiterst "
+"betrouwbaar."
+
+#: widgets/messagecontainerwidget.cpp:104
+#, kde-format
+msgctxt "@label"
+msgid "The signature is valid, but the <a href=\"%1\">key</a> is untrusted."
+msgstr ""
+"De ondertekening is geldig, maar de <a href=\"%1\">sleutel</a> is "
+"onbetrouwbaar."
+
+#: widgets/messagecontainerwidget.cpp:203
+#, kde-format
+msgid ""
+"This message is VS-NfD compliant encrypted but we don't have the key for it."
+msgstr ""
+"Dit bericht is overeenkomstig VS-NfD versleuteld maar we hebben de sleutel "
+"ervoor niet."
+
+#: widgets/messagecontainerwidget.cpp:209
+#, kde-format
+msgid "This message is VS-NfD compliant encrypted."
+msgstr "Dit bericht is overeenkomstig VS-NfD versleuteld."
+
+#: widgets/messagecontainerwidget.cpp:211
+#, kde-format
+msgid "This message is encrypted."
+msgstr "Dit bericht is versleuteld."
+
+#: widgets/messagecontainerwidget.cpp:220
+#, kde-format
+msgid "The message is encrypted for the following keys:"
+msgstr "Het bericht is versleuteld voor de volgende sleutels:"
+
+#: widgets/messagecontainerwidget.cpp:237
+#, kde-format
+msgid "Unknown key"
+msgstr "Onbekende sleutel"
+
+#: widgets/messageviewer.cpp:57
 #, kde-format
 msgid "&Save Attachment As..."
 msgstr "Bijlage &opslaan als..."
 
-#: widgets/messageviewer.cpp:59
+#: widgets/messageviewer.cpp:62
 #, kde-format
 msgctxt "to open"
 msgid "Open"
 msgstr "Openen"
 
-#: widgets/messageviewer.cpp:64
+#: widgets/messageviewer.cpp:67
 #, kde-format
 msgctxt "@action:inmenu"
 msgid "Import public key"
 msgstr "Publieke sleutel importeren"
 
-#: widgets/messageviewer.cpp:192
+#: widgets/messageviewer.cpp:236
 #, kde-format
 msgid "Invitation"
 msgstr "Uitnodiging"
 
-#: widgets/messageviewer.cpp:195
+#: widgets/messageviewer.cpp:239
 #, kde-format
 msgid "&Summary:"
 msgstr "&Samenvatting:"
 
-#: widgets/messageviewer.cpp:196
+#: widgets/messageviewer.cpp:240
 #, kde-format
 msgid "&Organizer:"
 msgstr "&Organisator:"
 
-#: widgets/messageviewer.cpp:198
+#: widgets/messageviewer.cpp:242
 #, kde-format
 msgid "&Location:"
 msgstr "&Locatie:"
 
-#: widgets/messageviewer.cpp:200
+#: widgets/messageviewer.cpp:244
 #, kde-format
 msgid "&Start date:"
 msgstr "&Begindatum:"
 
-#: widgets/messageviewer.cpp:202
+#: widgets/messageviewer.cpp:246
 #, kde-format
 msgid "&End date:"
 msgstr "&Einddatum:"
 
-#: widgets/messageviewer.cpp:205
+#: widgets/messageviewer.cpp:249
 #, kde-format
 msgid "&Details:"
 msgstr "&Details:"
 
-#: widgets/messageviewer.cpp:215
+#: widgets/messageviewer.cpp:271
 #, kde-format
 msgid "Encapsulated email"
 msgstr "Ingebed e-mailbericht"
 
-#: widgets/messageviewer.cpp:223
+#: widgets/messageviewer.cpp:279
 #, kde-format
 msgid "From:"
 msgstr "Van:"
 
-#: widgets/messageviewer.cpp:224
+#: widgets/messageviewer.cpp:280
 #, kde-format
 msgid "Date:"
 msgstr "Datum:"
 
-#: widgets/messageviewer.cpp:237
-#, kde-format
-msgid "Error: %1"
-msgstr "Fout: %1"
-
-#: widgets/messageviewer.cpp:257
+#: widgets/messageviewer.cpp:315
 #, kde-format
 msgid "&Subject:"
 msgstr "&Onderwerp:"
 
-#: widgets/messageviewer.cpp:260
+#: widgets/messageviewer.cpp:318
 #, kde-format
 msgid "&From:"
 msgstr "&Van:"
 
-#: widgets/messageviewer.cpp:263
+#: widgets/messageviewer.cpp:321
 #, kde-format
 msgid "&To:"
 msgstr "&Aan:"
+
+#: widgets/urlhandler.cpp:37
+#, kde-format
+msgid "Could not start certificate manager. Please check your installation."
+msgstr ""
+"Het Certificaatbeheer kon niet worden gestart. Controleer uw installatie."
+
+#: widgets/urlhandler.cpp:39
+#, kde-format
+msgid "KMail Error"
+msgstr "KMail - foutmelding"
+
+#~ msgctxt "@info"
+#~ msgid "Could not decrypt the data: no key found for recipients."
+#~ msgstr ""
+#~ "Kon de gegevens niet ontcijferen: geen sleutel gevonden voor ontvangers."
diff --git a/po/sl/mimetreeparser.po b/po/sl/mimetreeparser.po
index 7e21f85..4043b44 100644
--- a/po/sl/mimetreeparser.po
+++ b/po/sl/mimetreeparser.po
@@ -1,343 +1,466 @@
 # Copyright (C) YEAR This file is copyright:
 # This file is distributed under the same license as the mimetreeparser package.
 #
 # Matjaž Jeran <matjaz.jeran@amis.net>, 2023.
 msgid ""
 msgstr ""
 "Project-Id-Version: mimetreeparser\n"
 "Report-Msgid-Bugs-To: https://bugs.kde.org\n"
-"POT-Creation-Date: 2023-08-15 00:46+0000\n"
-"PO-Revision-Date: 2023-08-13 10:10+0200\n"
+"POT-Creation-Date: 2023-08-27 00:47+0000\n"
+"PO-Revision-Date: 2023-08-27 06:45+0200\n"
 "Last-Translator: Matjaž Jeran <matjaz.jeran@amis.net>\n"
 "Language-Team: Slovenian <lugos-slo@lugos.si>\n"
 "Language: sl\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=4; plural=(n%100==1 ? 1 : n%100==2 ? 2 : n%100==3 || n"
 "%100==4 ? 3 : 0);\n"
-"X-Generator: Lokalize 23.04.3\n"
+"X-Generator: Poedit 3.3.2\n"
 
-#: core/attachmentmodel.cpp:88
+#: core/attachmentmodel.cpp:90
 #, kde-format
 msgctxt "@title:column"
 msgid "Name"
 msgstr "Ime"
 
-#: core/attachmentmodel.cpp:90
+#: core/attachmentmodel.cpp:92
 #, kde-format
 msgctxt "@title:column"
 msgid "Size"
 msgstr "Velikost"
 
-#: core/attachmentmodel.cpp:92
+#: core/attachmentmodel.cpp:94
 #, kde-format
 msgctxt "@title:column"
 msgid "Encrypted"
 msgstr "Šifrirano"
 
-#: core/attachmentmodel.cpp:94
+#: core/attachmentmodel.cpp:96
 #, kde-format
 msgctxt "@title:column"
 msgid "Signed"
 msgstr "Podpisano"
 
-#: core/attachmentmodel.cpp:196
+#: core/attachmentmodel.cpp:198
 #, kde-format
 msgctxt "@info"
 msgid "Failed to save attachment."
 msgstr "Shranjevanje priloge ni uspelo."
 
-#: core/attachmentmodel.cpp:228
+#: core/attachmentmodel.cpp:230
 #, kde-format
 msgctxt "@info"
 msgid "Saved the attachment to disk: %1"
 msgstr "Priponka shranjena na disk: %1"
 
-#: core/attachmentmodel.cpp:245
+#: core/attachmentmodel.cpp:247
 #, kde-format
 msgctxt "@info"
 msgid "Failed to open attachment."
 msgstr "Priponke ni bilo mogoče odpreti."
 
-#: core/attachmentmodel.cpp:250
+#: core/attachmentmodel.cpp:252
 #, kde-format
 msgctxt "@info"
 msgid "Failed to save attachment for opening."
 msgstr "Priponke ni bilo mogoče shraniti za odpiranje."
 
-#: core/attachmentmodel.cpp:268
+#: core/attachmentmodel.cpp:273
 #, kde-format
 msgctxt "@info"
 msgid "No keys were found in this attachment"
 msgstr "V tej prilogi ni bilo najdenih nobenih ključev"
 
-#: core/attachmentmodel.cpp:271
+#: core/attachmentmodel.cpp:276
 #, kde-format
 msgctxt "@info"
 msgid "one key imported"
 msgid_plural "%1 keys imported"
 msgstr[0] "%1 ključ uvožen"
 msgstr[1] "%1 ključa uvožena"
 msgstr[2] "%1 ključi uvoženi"
 msgstr[3] "%1 ključev uvoženo"
 
-#: core/attachmentmodel.cpp:274
+#: core/attachmentmodel.cpp:279
 #, kde-format
 msgctxt "@info"
 msgid "one key was already imported"
 msgid_plural "%1 keys were already imported"
 msgstr[0] "%1 ključ je že uvožen"
 msgstr[1] "%1 ključa sta že uvožena"
 msgstr[2] "%1 ključi so že uvoženi"
 msgstr[3] "%1 ključev je že uvoženih"
 
 #: core/mailtemplates.cpp:103
 #, kde-format
 msgid "fwd"
 msgstr "naprej"
 
 #: core/mailtemplates.cpp:132
 #, kde-format
 msgid "re"
 msgstr "odg"
 
 #: core/messageparser.cpp:158
 #, kde-format
 msgctxt "displayed when a mail has unknown sender, receiver or date"
 msgid "Unknown"
 msgstr "Neznano"
 
-#: core/messagepart.cpp:618 core/messagepart.cpp:709 core/messagepart.cpp:783
+#: core/messagepart.cpp:625 core/messagepart.cpp:660 core/messagepart.cpp:835
 #, kde-format
 msgctxt "@info:status"
 msgid "Wrong Crypto Plug-In."
 msgstr "Napačen kripto vtičnik."
 
-#: core/messagepart.cpp:857
+#: core/messagepart.cpp:986
 #, kde-format
-msgctxt "@info"
-msgid "Could not decrypt the data: no key found for recipients."
-msgstr ""
-"Podatkov ni bilo mogoče dešifrirati: ključ za prejemnike ni bil najden."
+msgid "No appropriate crypto plug-in was found."
+msgstr "Ni najdenega ustreznega kripto-vtičnika."
 
-#: core/messagepart.cpp:862
+#: core/messagepart.cpp:988
 #, kde-format
-msgctxt "@info"
-msgid "Could not decrypt the data."
-msgstr "Podatkov ni bilo mogoče dešifrirati."
+msgid "Crypto plug-in \"%1\" cannot decrypt messages."
+msgstr "Kripto-vtičnik \"%1\" ne more dešifrirati sporočil."
+
+#: core/messagepart.cpp:990
+#, kde-format
+msgid "Crypto plug-in \"%1\" could not decrypt the data."
+msgstr "Kripto-vtičnik \"%1\" ni mogel dešifrirati podatkov."
+
+#: core/messagepart.cpp:991 widgets/messageviewer.cpp:295
+#, kde-format
+msgid "Error: %1"
+msgstr "Napaka: %1"
 
-#: core/partmodel.cpp:483
+#: core/partmodel.cpp:487
 #, kde-format
 msgctxt "@info:status"
 msgid "No key available."
 msgstr "Ključ ni na voljo."
 
-#: core/partmodel.cpp:485
+#: core/partmodel.cpp:489
 #, kde-format
 msgctxt "@info:status"
 msgid "Wrong passphrase."
 msgstr "Napačno geslo."
 
-#: quick/qml/MailViewer.qml:39
+#: quick/qml/MailViewer.qml:38
 #, kde-format
 msgctxt "@title:window"
 msgid "Message viewer"
 msgstr "Pregledovalnik sporočil"
 
-#: quick/qml/MailViewer.qml:76
+#: quick/qml/MailViewer.qml:75
 #, kde-format
 msgctxt "@label"
 msgid "Subject:"
 msgstr "Zadeva:"
 
-#: quick/qml/MailViewer.qml:98
+#: quick/qml/MailViewer.qml:96
 #, kde-format
 msgctxt "@label"
 msgid "From:"
 msgstr "Od:"
 
-#: quick/qml/MailViewer.qml:115
+#: quick/qml/MailViewer.qml:112
 #, kde-format
 msgctxt "@label"
 msgid "Sender:"
 msgstr "Pošiljatelj:"
 
-#: quick/qml/MailViewer.qml:132
+#: quick/qml/MailViewer.qml:128
 #, kde-format
 msgctxt "@label"
 msgid "To:"
 msgstr "Za:"
 
-#: quick/qml/MailViewer.qml:190
+#: quick/qml/MailViewer.qml:185
 #, kde-format
 msgctxt "@action:button"
 msgid "Save attachment"
 msgstr "Shrani prilogo"
 
 #: quick/qml/private/AttachmentDelegate.qml:56
 #, kde-format
 msgctxt "@action:button"
 msgid "Import key"
 msgstr "Uvozi ključ"
 
 #: quick/qml/private/Banner.qml:188
 #, kde-format
 msgctxt "@action:button"
 msgid "Close"
 msgstr "Zapri"
 
 #: quick/qml/private/ErrorPart.qml:27
 #, kde-format
 msgid "An error occurred: %1"
 msgstr "Prišlo je do napake: %1"
 
-#: quick/qml/private/ICalPart.qml:28
+#: quick/qml/private/ICalPart.qml:27
 #, kde-format
 msgid "This mail contains an invitation"
 msgstr "Ta pošta vsebuje povabilo"
 
-#: quick/qml/private/MailPart.qml:42
+#: quick/qml/private/MailPart.qml:41
 #, kde-format
 msgctxt "@info"
 msgid "sent by %1 on %2"
 msgstr "poslal %1 dne %2"
 
-#: quick/qml/private/MailPartModel.qml:54
+#: quick/qml/private/MailPartModel.qml:55
 #, kde-format
 msgctxt "@label"
 msgid "This message has been signed using the key %1."
 msgstr "To sporočilo je bilo podpisano s ključem %1."
 
-#: quick/qml/private/MailPartModel.qml:55
+#: quick/qml/private/MailPartModel.qml:56 widgets/messagecontainerwidget.cpp:78
 #, kde-format
 msgctxt "@label"
 msgid "The key details are not available."
 msgstr "Podrobnosti ključa niso na voljo."
 
-#: quick/qml/private/MailPartModel.qml:57
+#: quick/qml/private/MailPartModel.qml:58
 #, kde-format
 msgctxt "@label"
 msgid "This message has been signed using the key %1 by %2."
 msgstr "To sporočilo je s ključem %1 podpisal %2."
 
-#: quick/qml/private/MailPartModel.qml:59
+#: quick/qml/private/MailPartModel.qml:60
 #, kde-format
 msgctxt "@label"
 msgid "The key was revoked."
 msgstr "Ključ je bil preklican."
 
-#: quick/qml/private/MailPartModel.qml:62
+#: quick/qml/private/MailPartModel.qml:63
 #, kde-format
 msgctxt "@label"
 msgid "The key has expired."
 msgstr "Ključ je potekel."
 
-#: quick/qml/private/MailPartModel.qml:65
+#: quick/qml/private/MailPartModel.qml:66
 #, kde-format
 msgctxt "@label"
 msgid "You are trusting this key."
 msgstr "Temu ključu zaupate."
 
-#: quick/qml/private/MailPartModel.qml:68
+#: quick/qml/private/MailPartModel.qml:69
+#: widgets/messagecontainerwidget.cpp:108
 #, kde-format
 msgctxt "@label"
 msgid "The signature is invalid."
 msgstr "Podpis je neveljaven."
 
-#: quick/qml/private/MailPartModel.qml:97
+#: quick/qml/private/MailPartModel.qml:98
+#: widgets/messagecontainerwidget.cpp:205
 #, kde-format
 msgid "This message is encrypted but we don't have the key for it."
 msgstr "To sporočilo je šifrirano, vendar nimamo ključa zanj."
 
-#: quick/qml/private/MailPartModel.qml:97
+#: quick/qml/private/MailPartModel.qml:98
 #, kde-format
 msgid "This message is encrypted to the key: %1"
 msgstr "To sporočilo je šifrirano na ključ: %1"
 
-#: widgets/messageviewer.cpp:54
+#: widgets/messagecontainerwidget.cpp:66
+#, kde-format
+msgctxt "@label"
+msgid ""
+"This message has been signed VS-NfD compliant using the key <a href=\"%1\">"
+"%2</a>."
+msgstr ""
+"To sporočilo je bilo podpisano ustrezno z VS-NfD s ključem <a href=\"%1\">"
+"%2</a>."
+
+#: widgets/messagecontainerwidget.cpp:73
+#, kde-format
+msgctxt "@label"
+msgid "This message has been signed using the key <a href=\"%1\">%2</a>."
+msgstr "To sporočilo je bilo podpisano s ključem <a href=\"%1\">%2</a>."
+
+#: widgets/messagecontainerwidget.cpp:81
+#, kde-format
+msgctxt "@label"
+msgid "This message has been signed VS-NfD compliant by %1."
+msgstr "To sporočilo je bilo podpisano ustrezno z VS-NfD z %1."
+
+#: widgets/messagecontainerwidget.cpp:83
+#, kde-format
+msgctxt "@label"
+msgid "This message has been signed by %1."
+msgstr "To sporočilo je bilo podpisano s %1."
+
+#: widgets/messagecontainerwidget.cpp:86
+#, kde-format
+msgctxt "@label"
+msgid "The <a href=\"%1\">key</a> was revoked."
+msgstr "<a href=\"%1\">Ključ</a> je bil preklican."
+
+#: widgets/messagecontainerwidget.cpp:89
+#, kde-format
+msgctxt "@label"
+msgid "The <a href=\"%1\">key</a> was expired."
+msgstr "<a href=\"%1\">Ključ</a> je potekel."
+
+#: widgets/messagecontainerwidget.cpp:94
+#, kde-format
+msgctxt "@label"
+msgid ""
+"The signature is valid, but the <a href=\"%1\">key</a>'s validity is unknown."
+msgstr "Podpis je veljaven, a veljavnost <a href=\"%1\">ključa</a> ni znana."
+
+#: widgets/messagecontainerwidget.cpp:97
+#, kde-format
+msgctxt "@label"
+msgid ""
+"The signature is valid and the <a href=\"%1\">key</a> is marginally trusted."
+msgstr ""
+"Podpis je veljaven in <a href=\"%1\">ključ</a> je le malo zaupanja vreden."
+
+#: widgets/messagecontainerwidget.cpp:99
+#, kde-format
+msgctxt "@label"
+msgid "The signature is valid and the <a href=\"%1\">key</a> is fully trusted."
+msgstr ""
+"Podpis je veljaven in <a href=\"%1\">ključ</a> je polno zaupanja vreden."
+
+#: widgets/messagecontainerwidget.cpp:102
+#, kde-format
+msgctxt "@label"
+msgid ""
+"The signature is valid and the <a href=\"%1\">key</a> is ultimately trusted."
+msgstr ""
+"Podpis je veljaven in <a href=\"%1\">ključ</a> je končno vreden zaupanja."
+
+#: widgets/messagecontainerwidget.cpp:104
+#, kde-format
+msgctxt "@label"
+msgid "The signature is valid, but the <a href=\"%1\">key</a> is untrusted."
+msgstr "Podpis je veljaven, a <a href=\"%1\">ključ</a> ni vreden zaupanja."
+
+#: widgets/messagecontainerwidget.cpp:203
+#, kde-format
+msgid ""
+"This message is VS-NfD compliant encrypted but we don't have the key for it."
+msgstr ""
+"To sporočilo je šifrirano v skladu z VS-NfD, vendar nimamo ključa zanj."
+
+#: widgets/messagecontainerwidget.cpp:209
+#, kde-format
+msgid "This message is VS-NfD compliant encrypted."
+msgstr "To sporočilo je šifrirano v skladu z VS-NfD."
+
+#: widgets/messagecontainerwidget.cpp:211
+#, kde-format
+msgid "This message is encrypted."
+msgstr "To sporočilo je šifrirano."
+
+#: widgets/messagecontainerwidget.cpp:220
+#, kde-format
+msgid "The message is encrypted for the following keys:"
+msgstr "Sporočilo je šifrirano z naslednjimi ključi:"
+
+#: widgets/messagecontainerwidget.cpp:237
+#, kde-format
+msgid "Unknown key"
+msgstr "Neznan ključ"
+
+#: widgets/messageviewer.cpp:57
 #, kde-format
 msgid "&Save Attachment As..."
 msgstr "&Shrani priloge kot..."
 
-#: widgets/messageviewer.cpp:59
+#: widgets/messageviewer.cpp:62
 #, kde-format
 msgctxt "to open"
 msgid "Open"
 msgstr "Odpri"
 
-#: widgets/messageviewer.cpp:64
+#: widgets/messageviewer.cpp:67
 #, kde-format
 msgctxt "@action:inmenu"
 msgid "Import public key"
 msgstr "Uvozi javni ključ"
 
-#: widgets/messageviewer.cpp:192
+#: widgets/messageviewer.cpp:236
 #, kde-format
 msgid "Invitation"
 msgstr "Povabilo"
 
-#: widgets/messageviewer.cpp:195
+#: widgets/messageviewer.cpp:239
 #, kde-format
 msgid "&Summary:"
 msgstr "Povzetek:"
 
-#: widgets/messageviewer.cpp:196
+#: widgets/messageviewer.cpp:240
 #, kde-format
 msgid "&Organizer:"
 msgstr "&Organizator:"
 
-#: widgets/messageviewer.cpp:198
+#: widgets/messageviewer.cpp:242
 #, kde-format
 msgid "&Location:"
 msgstr "&Lokacija:"
 
-#: widgets/messageviewer.cpp:200
+#: widgets/messageviewer.cpp:244
 #, kde-format
 msgid "&Start date:"
 msgstr "Datum začetka:"
 
-#: widgets/messageviewer.cpp:202
+#: widgets/messageviewer.cpp:246
 #, kde-format
 msgid "&End date:"
 msgstr "Datum konca:"
 
-#: widgets/messageviewer.cpp:205
+#: widgets/messageviewer.cpp:249
 #, kde-format
 msgid "&Details:"
 msgstr "Po&drobnosti:"
 
-#: widgets/messageviewer.cpp:215
+#: widgets/messageviewer.cpp:271
 #, kde-format
 msgid "Encapsulated email"
 msgstr "Enkapsulirano e-poštno sporočilo"
 
-#: widgets/messageviewer.cpp:223
+#: widgets/messageviewer.cpp:279
 #, kde-format
 msgid "From:"
 msgstr "Od:"
 
-#: widgets/messageviewer.cpp:224
+#: widgets/messageviewer.cpp:280
 #, kde-format
 msgid "Date:"
 msgstr "Datum:"
 
-#: widgets/messageviewer.cpp:237
-#, kde-format
-msgid "Error: %1"
-msgstr "Napaka: %1"
-
-#: widgets/messageviewer.cpp:257
+#: widgets/messageviewer.cpp:315
 #, kde-format
 msgid "&Subject:"
 msgstr "Predmet:"
 
-#: widgets/messageviewer.cpp:260
+#: widgets/messageviewer.cpp:318
 #, kde-format
 msgid "&From:"
 msgstr "Od:"
 
-#: widgets/messageviewer.cpp:263
+#: widgets/messageviewer.cpp:321
 #, kde-format
 msgid "&To:"
 msgstr "Za:"
+
+#: widgets/urlhandler.cpp:37
+#, kde-format
+msgid "Could not start certificate manager. Please check your installation."
+msgstr ""
+"Ni bilo mogoče zagnati upravljalnika potrdil. Preverite vašo namestitev."
+
+#: widgets/urlhandler.cpp:39
+#, kde-format
+msgid "KMail Error"
+msgstr "Napaka KMaila"
+
+#~ msgctxt "@info"
+#~ msgid "Could not decrypt the data: no key found for recipients."
+#~ msgstr ""
+#~ "Podatkov ni bilo mogoče dešifrirati: ključ za prejemnike ni bil najden."
diff --git a/po/sv/mimetreeparser.po b/po/sv/mimetreeparser.po
index 2295826..71f874c 100644
--- a/po/sv/mimetreeparser.po
+++ b/po/sv/mimetreeparser.po
@@ -1,337 +1,459 @@
 # Copyright (C) YEAR This file is copyright:
 # This file is distributed under the same license as the mimetreeparser package.
 #
 # Stefan Asserhäll <stefan.asserhall@bredband.net>, 2023.
 msgid ""
 msgstr ""
 "Project-Id-Version: mimetreeparser\n"
 "Report-Msgid-Bugs-To: https://bugs.kde.org\n"
-"POT-Creation-Date: 2023-08-15 00:46+0000\n"
-"PO-Revision-Date: 2023-08-12 07:56+0200\n"
+"POT-Creation-Date: 2023-08-27 00:47+0000\n"
+"PO-Revision-Date: 2023-08-27 07:18+0200\n"
 "Last-Translator: Stefan Asserhäll <stefan.asserhall@bredband.net>\n"
 "Language-Team: Swedish <kde-i18n-doc@kde.org>\n"
 "Language: sv\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=n != 1;\n"
-"X-Generator: Lokalize 20.08.1\n"
+"X-Generator: Lokalize 23.04.3\n"
 
-#: core/attachmentmodel.cpp:88
+#: core/attachmentmodel.cpp:90
 #, kde-format
 msgctxt "@title:column"
 msgid "Name"
 msgstr "Namn"
 
-#: core/attachmentmodel.cpp:90
+#: core/attachmentmodel.cpp:92
 #, kde-format
 msgctxt "@title:column"
 msgid "Size"
 msgstr "Storlek"
 
-#: core/attachmentmodel.cpp:92
+#: core/attachmentmodel.cpp:94
 #, kde-format
 msgctxt "@title:column"
 msgid "Encrypted"
 msgstr "Krypterad"
 
-#: core/attachmentmodel.cpp:94
+#: core/attachmentmodel.cpp:96
 #, kde-format
 msgctxt "@title:column"
 msgid "Signed"
 msgstr "Signerad"
 
-#: core/attachmentmodel.cpp:196
+#: core/attachmentmodel.cpp:198
 #, kde-format
 msgctxt "@info"
 msgid "Failed to save attachment."
 msgstr "Misslyckades spara bilaga."
 
-#: core/attachmentmodel.cpp:228
+#: core/attachmentmodel.cpp:230
 #, kde-format
 msgctxt "@info"
 msgid "Saved the attachment to disk: %1"
 msgstr "Sparade bilaga på disk: %1"
 
-#: core/attachmentmodel.cpp:245
+#: core/attachmentmodel.cpp:247
 #, kde-format
 msgctxt "@info"
 msgid "Failed to open attachment."
 msgstr "Misslyckades öppna bilaga."
 
-#: core/attachmentmodel.cpp:250
+#: core/attachmentmodel.cpp:252
 #, kde-format
 msgctxt "@info"
 msgid "Failed to save attachment for opening."
 msgstr "Misslyckades spara bilaga för att öppna den."
 
-#: core/attachmentmodel.cpp:268
+#: core/attachmentmodel.cpp:273
 #, kde-format
 msgctxt "@info"
 msgid "No keys were found in this attachment"
 msgstr "Inga nycklar hittades i den här bilagan"
 
-#: core/attachmentmodel.cpp:271
+#: core/attachmentmodel.cpp:276
 #, kde-format
 msgctxt "@info"
 msgid "one key imported"
 msgid_plural "%1 keys imported"
 msgstr[0] "en nyckel importerad"
 msgstr[1] "%1 nycklar importerade"
 
-#: core/attachmentmodel.cpp:274
+#: core/attachmentmodel.cpp:279
 #, kde-format
 msgctxt "@info"
 msgid "one key was already imported"
 msgid_plural "%1 keys were already imported"
 msgstr[0] "en nyckel har redan importerats"
 msgstr[1] "%1 nycklar har redan importerats"
 
 #: core/mailtemplates.cpp:103
 #, kde-format
 msgid "fwd"
 msgstr "fwd"
 
 #: core/mailtemplates.cpp:132
 #, kde-format
 msgid "re"
 msgstr "sv"
 
 #: core/messageparser.cpp:158
 #, kde-format
 msgctxt "displayed when a mail has unknown sender, receiver or date"
 msgid "Unknown"
 msgstr "Okänd"
 
-#: core/messagepart.cpp:618 core/messagepart.cpp:709 core/messagepart.cpp:783
+#: core/messagepart.cpp:625 core/messagepart.cpp:660 core/messagepart.cpp:835
 #, kde-format
 msgctxt "@info:status"
 msgid "Wrong Crypto Plug-In."
 msgstr "Felaktigt kryptoinsticksprogram."
 
-#: core/messagepart.cpp:857
+#: core/messagepart.cpp:986
 #, kde-format
-msgctxt "@info"
-msgid "Could not decrypt the data: no key found for recipients."
-msgstr "Kunde inte dekryptera data. Ingen nyckel hittades för mottagarna."
+msgid "No appropriate crypto plug-in was found."
+msgstr "Inget lämpligt kryptoinsticksprogram hittades."
 
-#: core/messagepart.cpp:862
+#: core/messagepart.cpp:988
 #, kde-format
-msgctxt "@info"
-msgid "Could not decrypt the data."
-msgstr "Kunde inte dekryptera data."
+msgid "Crypto plug-in \"%1\" cannot decrypt messages."
+msgstr "Kryptoinsticksprogrammet \"%1\" kan inte dekryptera några brev."
+
+#: core/messagepart.cpp:990
+#, kde-format
+msgid "Crypto plug-in \"%1\" could not decrypt the data."
+msgstr "Kryptoinsticksprogrammet \"%1\" kunde inte dekryptera data."
 
-#: core/partmodel.cpp:483
+#: core/messagepart.cpp:991 widgets/messageviewer.cpp:295
+#, kde-format
+msgid "Error: %1"
+msgstr "Fel: %1"
+
+#: core/partmodel.cpp:487
 #, kde-format
 msgctxt "@info:status"
 msgid "No key available."
 msgstr "Ingen nyckel tillgänglig."
 
-#: core/partmodel.cpp:485
+#: core/partmodel.cpp:489
 #, kde-format
 msgctxt "@info:status"
 msgid "Wrong passphrase."
 msgstr "Felaktig lösenordsfras."
 
-#: quick/qml/MailViewer.qml:39
+#: quick/qml/MailViewer.qml:38
 #, kde-format
 msgctxt "@title:window"
 msgid "Message viewer"
 msgstr "Brevvisning"
 
-#: quick/qml/MailViewer.qml:76
+#: quick/qml/MailViewer.qml:75
 #, kde-format
 msgctxt "@label"
 msgid "Subject:"
 msgstr "Rubrik:"
 
-#: quick/qml/MailViewer.qml:98
+#: quick/qml/MailViewer.qml:96
 #, kde-format
 msgctxt "@label"
 msgid "From:"
 msgstr "Från:"
 
-#: quick/qml/MailViewer.qml:115
+#: quick/qml/MailViewer.qml:112
 #, kde-format
 msgctxt "@label"
 msgid "Sender:"
 msgstr "Avsändare:"
 
-#: quick/qml/MailViewer.qml:132
+#: quick/qml/MailViewer.qml:128
 #, kde-format
 msgctxt "@label"
 msgid "To:"
 msgstr "Till:"
 
-#: quick/qml/MailViewer.qml:190
+#: quick/qml/MailViewer.qml:185
 #, kde-format
 msgctxt "@action:button"
 msgid "Save attachment"
 msgstr "Spara bilaga"
 
 #: quick/qml/private/AttachmentDelegate.qml:56
 #, kde-format
 msgctxt "@action:button"
 msgid "Import key"
 msgstr "Importera nyckel"
 
 #: quick/qml/private/Banner.qml:188
 #, kde-format
 msgctxt "@action:button"
 msgid "Close"
 msgstr "Stäng"
 
 #: quick/qml/private/ErrorPart.qml:27
 #, kde-format
 msgid "An error occurred: %1"
 msgstr "Ett fel uppstod: %1"
 
-#: quick/qml/private/ICalPart.qml:28
+#: quick/qml/private/ICalPart.qml:27
 #, kde-format
 msgid "This mail contains an invitation"
 msgstr "Brevet innehåller en inbjudan"
 
-#: quick/qml/private/MailPart.qml:42
+#: quick/qml/private/MailPart.qml:41
 #, kde-format
 msgctxt "@info"
 msgid "sent by %1 on %2"
 msgstr "skickat av %1 %2"
 
-#: quick/qml/private/MailPartModel.qml:54
+#: quick/qml/private/MailPartModel.qml:55
 #, kde-format
 msgctxt "@label"
 msgid "This message has been signed using the key %1."
 msgstr "Brevet har signerats med användning av nyckel %1."
 
-#: quick/qml/private/MailPartModel.qml:55
+#: quick/qml/private/MailPartModel.qml:56 widgets/messagecontainerwidget.cpp:78
 #, kde-format
 msgctxt "@label"
 msgid "The key details are not available."
 msgstr "Nyckelinformationen är inte tillgänglig."
 
-#: quick/qml/private/MailPartModel.qml:57
+#: quick/qml/private/MailPartModel.qml:58
 #, kde-format
 msgctxt "@label"
 msgid "This message has been signed using the key %1 by %2."
 msgstr "Brevet har signerats med användning av nyckel %1 av %2."
 
-#: quick/qml/private/MailPartModel.qml:59
+#: quick/qml/private/MailPartModel.qml:60
 #, kde-format
 msgctxt "@label"
 msgid "The key was revoked."
 msgstr "Nyckeln har återkallats."
 
-#: quick/qml/private/MailPartModel.qml:62
+#: quick/qml/private/MailPartModel.qml:63
 #, kde-format
 msgctxt "@label"
 msgid "The key has expired."
 msgstr "Nyckeln har gått ut."
 
-#: quick/qml/private/MailPartModel.qml:65
+#: quick/qml/private/MailPartModel.qml:66
 #, kde-format
 msgctxt "@label"
 msgid "You are trusting this key."
 msgstr "Du litar på den här nyckeln."
 
-#: quick/qml/private/MailPartModel.qml:68
+#: quick/qml/private/MailPartModel.qml:69
+#: widgets/messagecontainerwidget.cpp:108
 #, kde-format
 msgctxt "@label"
 msgid "The signature is invalid."
 msgstr "Signaturen är ogiltig."
 
-#: quick/qml/private/MailPartModel.qml:97
+#: quick/qml/private/MailPartModel.qml:98
+#: widgets/messagecontainerwidget.cpp:205
 #, kde-format
 msgid "This message is encrypted but we don't have the key for it."
 msgstr "Brevet är krypterat men vi har inte nyckeln för det."
 
-#: quick/qml/private/MailPartModel.qml:97
+#: quick/qml/private/MailPartModel.qml:98
 #, kde-format
 msgid "This message is encrypted to the key: %1"
 msgstr "Brevet är krypterat med nyckeln: %1"
 
-#: widgets/messageviewer.cpp:54
+#: widgets/messagecontainerwidget.cpp:66
+#, kde-format
+msgctxt "@label"
+msgid ""
+"This message has been signed VS-NfD compliant using the key <a href=\"%1\">"
+"%2</a>."
+msgstr ""
+"Brevet har signerats VS-NfD kompatibelt med användning av nyckel <a href="
+"\"%1\">%2</a>."
+
+#: widgets/messagecontainerwidget.cpp:73
+#, kde-format
+msgctxt "@label"
+msgid "This message has been signed using the key <a href=\"%1\">%2</a>."
+msgstr "Brevet har signerats med användning av nyckel <a href=\"%1\">%2</a>."
+
+#: widgets/messagecontainerwidget.cpp:81
+#, kde-format
+msgctxt "@label"
+msgid "This message has been signed VS-NfD compliant by %1."
+msgstr "Brevet har signerats VS-NfD kompatibelt av %1."
+
+#: widgets/messagecontainerwidget.cpp:83
+#, kde-format
+msgctxt "@label"
+msgid "This message has been signed by %1."
+msgstr "Brevet har signerats av %1."
+
+#: widgets/messagecontainerwidget.cpp:86
+#, kde-format
+msgctxt "@label"
+msgid "The <a href=\"%1\">key</a> was revoked."
+msgstr "<a href=\"%1\">Nyckeln</a> har återkallats."
+
+#: widgets/messagecontainerwidget.cpp:89
+#, kde-format
+msgctxt "@label"
+msgid "The <a href=\"%1\">key</a> was expired."
+msgstr "<a href=\"%1\">Nyckeln</a> har gått ut."
+
+#: widgets/messagecontainerwidget.cpp:94
+#, kde-format
+msgctxt "@label"
+msgid ""
+"The signature is valid, but the <a href=\"%1\">key</a>'s validity is unknown."
+msgstr ""
+"Signaturen är giltig, men <a href=\"%1\">nyckelns</a> giltighet är okänd."
+
+#: widgets/messagecontainerwidget.cpp:97
+#, kde-format
+msgctxt "@label"
+msgid ""
+"The signature is valid and the <a href=\"%1\">key</a> is marginally trusted."
+msgstr ""
+"Signaturen är giltig och <a href=\"%1\">nyckeln</a> är möjligen pålitlig."
+
+#: widgets/messagecontainerwidget.cpp:99
+#, kde-format
+msgctxt "@label"
+msgid "The signature is valid and the <a href=\"%1\">key</a> is fully trusted."
+msgstr "Signaturen är giltig och <a href=\"%1\">nyckeln</a> är pålitlig."
+
+#: widgets/messagecontainerwidget.cpp:102
+#, kde-format
+msgctxt "@label"
+msgid ""
+"The signature is valid and the <a href=\"%1\">key</a> is ultimately trusted."
+msgstr ""
+"Signaturen är giltig och <a href=\"%1\">nyckeln</a> är fullständigt pålitlig."
+
+#: widgets/messagecontainerwidget.cpp:104
+#, kde-format
+msgctxt "@label"
+msgid "The signature is valid, but the <a href=\"%1\">key</a> is untrusted."
+msgstr "Signaturen är giltig, men <a href=\"%1\">nyckeln</a> är opålitlig."
+
+#: widgets/messagecontainerwidget.cpp:203
+#, kde-format
+msgid ""
+"This message is VS-NfD compliant encrypted but we don't have the key for it."
+msgstr ""
+"Brevet är krypterat VS-NfD kompatibelt men vi har inte nyckeln för det."
+
+#: widgets/messagecontainerwidget.cpp:209
+#, kde-format
+msgid "This message is VS-NfD compliant encrypted."
+msgstr "Brevet är krypterat VS-NfD kompatibelt."
+
+#: widgets/messagecontainerwidget.cpp:211
+#, kde-format
+msgid "This message is encrypted."
+msgstr "Brevet är krypterat."
+
+#: widgets/messagecontainerwidget.cpp:220
+#, kde-format
+msgid "The message is encrypted for the following keys:"
+msgstr "Brevet är krypterat för följande nycklar:"
+
+#: widgets/messagecontainerwidget.cpp:237
+#, kde-format
+msgid "Unknown key"
+msgstr "Okänd nyckel"
+
+#: widgets/messageviewer.cpp:57
 #, kde-format
 msgid "&Save Attachment As..."
 msgstr "&Spara bilaga som..."
 
-#: widgets/messageviewer.cpp:59
+#: widgets/messageviewer.cpp:62
 #, kde-format
 msgctxt "to open"
 msgid "Open"
 msgstr "Öppna"
 
-#: widgets/messageviewer.cpp:64
+#: widgets/messageviewer.cpp:67
 #, kde-format
 msgctxt "@action:inmenu"
 msgid "Import public key"
 msgstr "Importera öppen nyckel"
 
-#: widgets/messageviewer.cpp:192
+#: widgets/messageviewer.cpp:236
 #, kde-format
 msgid "Invitation"
 msgstr "Inbjudan"
 
-#: widgets/messageviewer.cpp:195
+#: widgets/messageviewer.cpp:239
 #, kde-format
 msgid "&Summary:"
 msgstr "&Sammanfattning:"
 
-#: widgets/messageviewer.cpp:196
+#: widgets/messageviewer.cpp:240
 #, kde-format
 msgid "&Organizer:"
 msgstr "&Organisatör:"
 
-#: widgets/messageviewer.cpp:198
+#: widgets/messageviewer.cpp:242
 #, kde-format
 msgid "&Location:"
 msgstr "&Plats:"
 
-#: widgets/messageviewer.cpp:200
+#: widgets/messageviewer.cpp:244
 #, kde-format
 msgid "&Start date:"
 msgstr "&Startdatum:"
 
-#: widgets/messageviewer.cpp:202
+#: widgets/messageviewer.cpp:246
 #, kde-format
 msgid "&End date:"
 msgstr "Slut&datum:"
 
-#: widgets/messageviewer.cpp:205
+#: widgets/messageviewer.cpp:249
 #, kde-format
 msgid "&Details:"
 msgstr "&Detaljinformation:"
 
-#: widgets/messageviewer.cpp:215
+#: widgets/messageviewer.cpp:271
 #, kde-format
 msgid "Encapsulated email"
 msgstr "Inbäddat brev"
 
-#: widgets/messageviewer.cpp:223
+#: widgets/messageviewer.cpp:279
 #, kde-format
 msgid "From:"
 msgstr "Från:"
 
-#: widgets/messageviewer.cpp:224
+#: widgets/messageviewer.cpp:280
 #, kde-format
 msgid "Date:"
 msgstr "Datum:"
 
-#: widgets/messageviewer.cpp:237
-#, kde-format
-msgid "Error: %1"
-msgstr "Fel: %1"
-
-#: widgets/messageviewer.cpp:257
+#: widgets/messageviewer.cpp:315
 #, kde-format
 msgid "&Subject:"
 msgstr "&Rubrik:"
 
-#: widgets/messageviewer.cpp:260
+#: widgets/messageviewer.cpp:318
 #, kde-format
 msgid "&From:"
 msgstr "&Från:"
 
-#: widgets/messageviewer.cpp:263
+#: widgets/messageviewer.cpp:321
 #, kde-format
 msgid "&To:"
 msgstr "&Till:"
+
+#: widgets/urlhandler.cpp:37
+#, kde-format
+msgid "Could not start certificate manager. Please check your installation."
+msgstr "Kunde inte starta certifikathanteraren. Kontrollera installationen."
+
+#: widgets/urlhandler.cpp:39
+#, kde-format
+msgid "KMail Error"
+msgstr "Kmail-fel"
+
+#~ msgctxt "@info"
+#~ msgid "Could not decrypt the data: no key found for recipients."
+#~ msgstr "Kunde inte dekryptera data. Ingen nyckel hittades för mottagarna."
diff --git a/po/tr/mimetreeparser.po b/po/tr/mimetreeparser.po
index 2b81d6a..5fb99a8 100644
--- a/po/tr/mimetreeparser.po
+++ b/po/tr/mimetreeparser.po
@@ -1,337 +1,458 @@
 # Copyright (C) YEAR This file is copyright:
 # This file is distributed under the same license as the mimetreeparser package.
 #
 # Emir SARI <emir_sari@icloud.com>, 2023.
 msgid ""
 msgstr ""
 "Project-Id-Version: mimetreeparser\n"
 "Report-Msgid-Bugs-To: https://bugs.kde.org\n"
-"POT-Creation-Date: 2023-08-15 00:46+0000\n"
-"PO-Revision-Date: 2023-08-12 12:08+0300\n"
+"POT-Creation-Date: 2023-08-27 00:47+0000\n"
+"PO-Revision-Date: 2023-08-27 21:49+0300\n"
 "Last-Translator: Emir SARI <emir_sari@icloud.com>\n"
 "Language-Team: Turkish <kde-l10n-tr@kde.org>\n"
 "Language: tr\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=(n > 1);\n"
 "X-Generator: Lokalize 23.11.70\n"
 
-#: core/attachmentmodel.cpp:88
+#: core/attachmentmodel.cpp:90
 #, kde-format
 msgctxt "@title:column"
 msgid "Name"
 msgstr "Ad"
 
-#: core/attachmentmodel.cpp:90
+#: core/attachmentmodel.cpp:92
 #, kde-format
 msgctxt "@title:column"
 msgid "Size"
 msgstr "Boyut"
 
-#: core/attachmentmodel.cpp:92
+#: core/attachmentmodel.cpp:94
 #, kde-format
 msgctxt "@title:column"
 msgid "Encrypted"
 msgstr "Şifrelenmiş"
 
-#: core/attachmentmodel.cpp:94
+#: core/attachmentmodel.cpp:96
 #, kde-format
 msgctxt "@title:column"
 msgid "Signed"
 msgstr "İmzalanmış"
 
-#: core/attachmentmodel.cpp:196
+#: core/attachmentmodel.cpp:198
 #, kde-format
 msgctxt "@info"
 msgid "Failed to save attachment."
 msgstr "Ek kaydedilemedi."
 
-#: core/attachmentmodel.cpp:228
+#: core/attachmentmodel.cpp:230
 #, kde-format
 msgctxt "@info"
 msgid "Saved the attachment to disk: %1"
 msgstr "Ek diske kaydedildi: %1"
 
-#: core/attachmentmodel.cpp:245
+#: core/attachmentmodel.cpp:247
 #, kde-format
 msgctxt "@info"
 msgid "Failed to open attachment."
 msgstr "Ek açılamadı."
 
-#: core/attachmentmodel.cpp:250
+#: core/attachmentmodel.cpp:252
 #, kde-format
 msgctxt "@info"
 msgid "Failed to save attachment for opening."
 msgstr "Ek, açmak üzere kaydedilemedi."
 
-#: core/attachmentmodel.cpp:268
+#: core/attachmentmodel.cpp:273
 #, kde-format
 msgctxt "@info"
 msgid "No keys were found in this attachment"
 msgstr "Bu ekte hiçbir anahtar bulunamadı."
 
-#: core/attachmentmodel.cpp:271
+#: core/attachmentmodel.cpp:276
 #, kde-format
 msgctxt "@info"
 msgid "one key imported"
 msgid_plural "%1 keys imported"
 msgstr[0] "1 anahtar içe aktarıldı"
 msgstr[1] "%1 anahtar içe aktarıldı"
 
-#: core/attachmentmodel.cpp:274
+#: core/attachmentmodel.cpp:279
 #, kde-format
 msgctxt "@info"
 msgid "one key was already imported"
 msgid_plural "%1 keys were already imported"
 msgstr[0] "1 anahtar halihazırda içe aktarılmıştı"
 msgstr[1] "%1 anahtar halihazırda içe aktarılmıştı"
 
 #: core/mailtemplates.cpp:103
 #, kde-format
 msgid "fwd"
 msgstr "ilt"
 
 #: core/mailtemplates.cpp:132
 #, kde-format
 msgid "re"
 msgstr "ynt"
 
 #: core/messageparser.cpp:158
 #, kde-format
 msgctxt "displayed when a mail has unknown sender, receiver or date"
 msgid "Unknown"
 msgstr "Bilinmeyen"
 
-#: core/messagepart.cpp:618 core/messagepart.cpp:709 core/messagepart.cpp:783
+#: core/messagepart.cpp:625 core/messagepart.cpp:660 core/messagepart.cpp:835
 #, kde-format
 msgctxt "@info:status"
 msgid "Wrong Crypto Plug-In."
 msgstr "Yanlış kripto eklentisi."
 
-#: core/messagepart.cpp:857
+#: core/messagepart.cpp:986
 #, kde-format
-msgctxt "@info"
-msgid "Could not decrypt the data: no key found for recipients."
-msgstr "Verinin şifresi çözülemedi: Alıcılar için hiçbir anahtar bulunamadı."
+msgid "No appropriate crypto plug-in was found."
+msgstr "Uygun kripto eklentisi bulunamadı."
 
-#: core/messagepart.cpp:862
+#: core/messagepart.cpp:988
 #, kde-format
-msgctxt "@info"
-msgid "Could not decrypt the data."
-msgstr "Verinin şifresi çözülemedi."
+msgid "Crypto plug-in \"%1\" cannot decrypt messages."
+msgstr "Kripto eklentisi \"%1\", iletilerin şifresini çözemez."
+
+#: core/messagepart.cpp:990
+#, kde-format
+msgid "Crypto plug-in \"%1\" could not decrypt the data."
+msgstr "Kripto eklentisi \"%1\", verinin şifresini çözemedi."
 
-#: core/partmodel.cpp:483
+#: core/messagepart.cpp:991 widgets/messageviewer.cpp:295
+#, kde-format
+msgid "Error: %1"
+msgstr "Hata: %1"
+
+#: core/partmodel.cpp:487
 #, kde-format
 msgctxt "@info:status"
 msgid "No key available."
 msgstr "Kullanılabilir anahtar yok."
 
-#: core/partmodel.cpp:485
+#: core/partmodel.cpp:489
 #, kde-format
 msgctxt "@info:status"
 msgid "Wrong passphrase."
 msgstr "Yanlış anahtar parolası."
 
-#: quick/qml/MailViewer.qml:39
+#: quick/qml/MailViewer.qml:38
 #, kde-format
 msgctxt "@title:window"
 msgid "Message viewer"
 msgstr "İleti Görüntüleyicisi"
 
-#: quick/qml/MailViewer.qml:76
+#: quick/qml/MailViewer.qml:75
 #, kde-format
 msgctxt "@label"
 msgid "Subject:"
 msgstr "Konu:"
 
-#: quick/qml/MailViewer.qml:98
+#: quick/qml/MailViewer.qml:96
 #, kde-format
 msgctxt "@label"
 msgid "From:"
 msgstr "Kimden:"
 
-#: quick/qml/MailViewer.qml:115
+#: quick/qml/MailViewer.qml:112
 #, kde-format
 msgctxt "@label"
 msgid "Sender:"
 msgstr "Gönderen:"
 
-#: quick/qml/MailViewer.qml:132
+#: quick/qml/MailViewer.qml:128
 #, kde-format
 msgctxt "@label"
 msgid "To:"
 msgstr "Kime:"
 
-#: quick/qml/MailViewer.qml:190
+#: quick/qml/MailViewer.qml:185
 #, kde-format
 msgctxt "@action:button"
 msgid "Save attachment"
 msgstr "Eki Kaydet"
 
 #: quick/qml/private/AttachmentDelegate.qml:56
 #, kde-format
 msgctxt "@action:button"
 msgid "Import key"
 msgstr "Anahtar İçe Aktar"
 
 #: quick/qml/private/Banner.qml:188
 #, kde-format
 msgctxt "@action:button"
 msgid "Close"
 msgstr "Kapat"
 
 #: quick/qml/private/ErrorPart.qml:27
 #, kde-format
 msgid "An error occurred: %1"
 msgstr "Bir hata oluştu: %1"
 
-#: quick/qml/private/ICalPart.qml:28
+#: quick/qml/private/ICalPart.qml:27
 #, kde-format
 msgid "This mail contains an invitation"
 msgstr "Bu ileti, bir davet içeriyor"
 
-#: quick/qml/private/MailPart.qml:42
+#: quick/qml/private/MailPart.qml:41
 #, kde-format
 msgctxt "@info"
 msgid "sent by %1 on %2"
 msgstr "%1 tarafından %2 tarihinde gönderildi"
 
-#: quick/qml/private/MailPartModel.qml:54
+#: quick/qml/private/MailPartModel.qml:55
 #, kde-format
 msgctxt "@label"
 msgid "This message has been signed using the key %1."
-msgstr "Bu ileti, %1 anahtarı kullanılarak imzalandı."
+msgstr "Bu ileti, %1 anahtarı kullanılarak imzalanmış."
 
-#: quick/qml/private/MailPartModel.qml:55
+#: quick/qml/private/MailPartModel.qml:56 widgets/messagecontainerwidget.cpp:78
 #, kde-format
 msgctxt "@label"
 msgid "The key details are not available."
 msgstr "Anahtar ayrıntıları kullanılabilir değil."
 
-#: quick/qml/private/MailPartModel.qml:57
+#: quick/qml/private/MailPartModel.qml:58
 #, kde-format
 msgctxt "@label"
 msgid "This message has been signed using the key %1 by %2."
-msgstr "Bu ileti, %2 tarafından %1 anahtarı kullanılarak imzalandı."
+msgstr "Bu ileti, %2 tarafından %1 anahtarı kullanılarak imzalanmış."
 
-#: quick/qml/private/MailPartModel.qml:59
+#: quick/qml/private/MailPartModel.qml:60
 #, kde-format
 msgctxt "@label"
 msgid "The key was revoked."
 msgstr "Anahtar yürürlükten kaldırılmış."
 
-#: quick/qml/private/MailPartModel.qml:62
+#: quick/qml/private/MailPartModel.qml:63
 #, kde-format
 msgctxt "@label"
 msgid "The key has expired."
 msgstr "Anahtarın süresi geçmiş."
 
-#: quick/qml/private/MailPartModel.qml:65
+#: quick/qml/private/MailPartModel.qml:66
 #, kde-format
 msgctxt "@label"
 msgid "You are trusting this key."
 msgstr "Bu anahtara güveniyorsunuz."
 
-#: quick/qml/private/MailPartModel.qml:68
+#: quick/qml/private/MailPartModel.qml:69
+#: widgets/messagecontainerwidget.cpp:108
 #, kde-format
 msgctxt "@label"
 msgid "The signature is invalid."
 msgstr "İmza geçersiz."
 
-#: quick/qml/private/MailPartModel.qml:97
+#: quick/qml/private/MailPartModel.qml:98
+#: widgets/messagecontainerwidget.cpp:205
 #, kde-format
 msgid "This message is encrypted but we don't have the key for it."
-msgstr "İleti şifrelenmiş; ancak şifresini çözmek için anahtarımız yok."
+msgstr "İleti şifrelenmiş; ancak bunun anahtarı bizde yok."
 
-#: quick/qml/private/MailPartModel.qml:97
+#: quick/qml/private/MailPartModel.qml:98
 #, kde-format
 msgid "This message is encrypted to the key: %1"
 msgstr "Bu ileti, şu anahtarla şifrelenmiş: %1"
 
-#: widgets/messageviewer.cpp:54
+#: widgets/messagecontainerwidget.cpp:66
+#, kde-format
+msgctxt "@label"
+msgid ""
+"This message has been signed VS-NfD compliant using the key <a href=\"%1\">"
+"%2</a>."
+msgstr ""
+"Bu ileti, <a href=\"%1\">%2</a> anahtarı kullanılarak VS-NfD uyumlu olarak "
+"imzalandı."
+
+#: widgets/messagecontainerwidget.cpp:73
+#, kde-format
+msgctxt "@label"
+msgid "This message has been signed using the key <a href=\"%1\">%2</a>."
+msgstr "Bu ileti, <a href=\"%1\">%2</a> anahtarı kullanılarak imzalandı."
+
+#: widgets/messagecontainerwidget.cpp:81
+#, kde-format
+msgctxt "@label"
+msgid "This message has been signed VS-NfD compliant by %1."
+msgstr "Bu ileti, %1 tarafından VS-NfD uyumlu olarak imzalandı."
+
+#: widgets/messagecontainerwidget.cpp:83
+#, kde-format
+msgctxt "@label"
+msgid "This message has been signed by %1."
+msgstr "Bu ileti, %1 tarafından imzalandı."
+
+#: widgets/messagecontainerwidget.cpp:86
+#, kde-format
+msgctxt "@label"
+msgid "The <a href=\"%1\">key</a> was revoked."
+msgstr "<a href=\"%1\">Anahtar</a> yürürlükten kaldırılmış."
+
+#: widgets/messagecontainerwidget.cpp:89
+#, kde-format
+msgctxt "@label"
+msgid "The <a href=\"%1\">key</a> was expired."
+msgstr "<a href=\"%1\">Anahtarın</a> süresi geçmiş."
+
+#: widgets/messagecontainerwidget.cpp:94
+#, kde-format
+msgctxt "@label"
+msgid ""
+"The signature is valid, but the <a href=\"%1\">key</a>'s validity is unknown."
+msgstr ""
+"İmza geçerli; ancak <a href=\"%1\">anahtarın</a> geçerliliği bilinmiyor."
+
+#: widgets/messagecontainerwidget.cpp:97
+#, kde-format
+msgctxt "@label"
+msgid ""
+"The signature is valid and the <a href=\"%1\">key</a> is marginally trusted."
+msgstr ""
+"İmza geçerli ve <a href=\"%1\">anahtara</a> marjinal olarak güveniliyor."
+
+#: widgets/messagecontainerwidget.cpp:99
+#, kde-format
+msgctxt "@label"
+msgid "The signature is valid and the <a href=\"%1\">key</a> is fully trusted."
+msgstr "İmza geçerli ve <a href=\"%1\">anahtara</a> tümüyle güveniliyor."
+
+#: widgets/messagecontainerwidget.cpp:102
+#, kde-format
+msgctxt "@label"
+msgid ""
+"The signature is valid and the <a href=\"%1\">key</a> is ultimately trusted."
+msgstr "İmza geçerli ve <a href=\"%1\">anahtara</a> sonuç olarak güveniliyor."
+
+#: widgets/messagecontainerwidget.cpp:104
+#, kde-format
+msgctxt "@label"
+msgid "The signature is valid, but the <a href=\"%1\">key</a> is untrusted."
+msgstr "İmza geçerli; ancak <a href=\"%1\">anahtara</a> güvenilmiyor."
+
+#: widgets/messagecontainerwidget.cpp:203
+#, kde-format
+msgid ""
+"This message is VS-NfD compliant encrypted but we don't have the key for it."
+msgstr "Bu ileti, VS-NfD uyumlu şifrelenmiş; ancak bunun anahtarı bizde yok."
+
+#: widgets/messagecontainerwidget.cpp:209
+#, kde-format
+msgid "This message is VS-NfD compliant encrypted."
+msgstr "Bu ileti, VS-NfD uyumlu şifrelenmiş."
+
+#: widgets/messagecontainerwidget.cpp:211
+#, kde-format
+msgid "This message is encrypted."
+msgstr "Bu ileti şifrelenmiş."
+
+#: widgets/messagecontainerwidget.cpp:220
+#, kde-format
+msgid "The message is encrypted for the following keys:"
+msgstr "Bu ileti, şu anahtarlar için şifrelenmiş:"
+
+#: widgets/messagecontainerwidget.cpp:237
+#, kde-format
+msgid "Unknown key"
+msgstr "Bilinmeyen anahtar"
+
+#: widgets/messageviewer.cpp:57
 #, kde-format
 msgid "&Save Attachment As..."
 msgstr "Eki Farklı &Kaydet..."
 
-#: widgets/messageviewer.cpp:59
+#: widgets/messageviewer.cpp:62
 #, kde-format
 msgctxt "to open"
 msgid "Open"
 msgstr "Aç"
 
-#: widgets/messageviewer.cpp:64
+#: widgets/messageviewer.cpp:67
 #, kde-format
 msgctxt "@action:inmenu"
 msgid "Import public key"
 msgstr "Genel Anahtar İçe Aktar"
 
-#: widgets/messageviewer.cpp:192
+#: widgets/messageviewer.cpp:236
 #, kde-format
 msgid "Invitation"
 msgstr "Davet"
 
-#: widgets/messageviewer.cpp:195
+#: widgets/messageviewer.cpp:239
 #, kde-format
 msgid "&Summary:"
 msgstr "&Özet:"
 
-#: widgets/messageviewer.cpp:196
+#: widgets/messageviewer.cpp:240
 #, kde-format
 msgid "&Organizer:"
 msgstr "&Organizatör:"
 
-#: widgets/messageviewer.cpp:198
+#: widgets/messageviewer.cpp:242
 #, kde-format
 msgid "&Location:"
 msgstr "&Konum:"
 
-#: widgets/messageviewer.cpp:200
+#: widgets/messageviewer.cpp:244
 #, kde-format
 msgid "&Start date:"
 msgstr "&Başlangıç tarihi:"
 
-#: widgets/messageviewer.cpp:202
+#: widgets/messageviewer.cpp:246
 #, kde-format
 msgid "&End date:"
 msgstr "B&itiş tarihi:"
 
-#: widgets/messageviewer.cpp:205
+#: widgets/messageviewer.cpp:249
 #, kde-format
 msgid "&Details:"
 msgstr "&Ayrıntılar:"
 
-#: widgets/messageviewer.cpp:215
+#: widgets/messageviewer.cpp:271
 #, kde-format
 msgid "Encapsulated email"
 msgstr "Kapsüllenmiş E-posta"
 
-#: widgets/messageviewer.cpp:223
+#: widgets/messageviewer.cpp:279
 #, kde-format
 msgid "From:"
 msgstr "Kimden:"
 
-#: widgets/messageviewer.cpp:224
+#: widgets/messageviewer.cpp:280
 #, kde-format
 msgid "Date:"
 msgstr "Tarih:"
 
-#: widgets/messageviewer.cpp:237
-#, kde-format
-msgid "Error: %1"
-msgstr "Hata: %1"
-
-#: widgets/messageviewer.cpp:257
+#: widgets/messageviewer.cpp:315
 #, kde-format
 msgid "&Subject:"
 msgstr "K&onu:"
 
-#: widgets/messageviewer.cpp:260
+#: widgets/messageviewer.cpp:318
 #, kde-format
 msgid "&From:"
 msgstr "&Kimden:"
 
-#: widgets/messageviewer.cpp:263
+#: widgets/messageviewer.cpp:321
 #, kde-format
 msgid "&To:"
 msgstr "Kim&e:"
+
+#: widgets/urlhandler.cpp:37
+#, kde-format
+msgid "Could not start certificate manager. Please check your installation."
+msgstr "Sertifika yöneticisi başlatılamadı. Lütfen kurulumunuzu denetleyin."
+
+#: widgets/urlhandler.cpp:39
+#, kde-format
+msgid "KMail Error"
+msgstr "K Posta Hatası"
+
+#~ msgctxt "@info"
+#~ msgid "Could not decrypt the data: no key found for recipients."
+#~ msgstr ""
+#~ "Verinin şifresi çözülemedi: Alıcılar için hiçbir anahtar bulunamadı."
diff --git a/po/uk/mimetreeparser.po b/po/uk/mimetreeparser.po
index 41d7881..778a88f 100644
--- a/po/uk/mimetreeparser.po
+++ b/po/uk/mimetreeparser.po
@@ -1,343 +1,466 @@
 # Copyright (C) YEAR This file is copyright:
 # This file is distributed under the same license as the mimetreeparser package.
 #
 # Yuri Chornoivan <yurchor@ukr.net>, 2023.
 msgid ""
 msgstr ""
 "Project-Id-Version: mimetreeparser\n"
 "Report-Msgid-Bugs-To: https://bugs.kde.org\n"
-"POT-Creation-Date: 2023-08-15 00:46+0000\n"
-"PO-Revision-Date: 2023-08-12 08:58+0300\n"
+"POT-Creation-Date: 2023-08-27 00:47+0000\n"
+"PO-Revision-Date: 2023-08-26 08:47+0300\n"
 "Last-Translator: Yuri Chornoivan <yurchor@ukr.net>\n"
 "Language-Team: Ukrainian <kde-i18n-uk@kde.org>\n"
 "Language: uk\n"
 "MIME-Version: 1.0\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=4; plural=n==1 ? 3 : n%10==1 && n%100!=11 ? 0 : n"
 "%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
 "X-Generator: Lokalize 20.12.0\n"
 
-#: core/attachmentmodel.cpp:88
+#: core/attachmentmodel.cpp:90
 #, kde-format
 msgctxt "@title:column"
 msgid "Name"
 msgstr "Назва"
 
-#: core/attachmentmodel.cpp:90
+#: core/attachmentmodel.cpp:92
 #, kde-format
 msgctxt "@title:column"
 msgid "Size"
 msgstr "Розмір"
 
-#: core/attachmentmodel.cpp:92
+#: core/attachmentmodel.cpp:94
 #, kde-format
 msgctxt "@title:column"
 msgid "Encrypted"
 msgstr "Зашифровано"
 
-#: core/attachmentmodel.cpp:94
+#: core/attachmentmodel.cpp:96
 #, kde-format
 msgctxt "@title:column"
 msgid "Signed"
 msgstr "Підписано"
 
-#: core/attachmentmodel.cpp:196
+#: core/attachmentmodel.cpp:198
 #, kde-format
 msgctxt "@info"
 msgid "Failed to save attachment."
 msgstr "Не вдалося зберегти долучення."
 
-#: core/attachmentmodel.cpp:228
+#: core/attachmentmodel.cpp:230
 #, kde-format
 msgctxt "@info"
 msgid "Saved the attachment to disk: %1"
 msgstr "Збереження долучення на диску: %1"
 
-#: core/attachmentmodel.cpp:245
+#: core/attachmentmodel.cpp:247
 #, kde-format
 msgctxt "@info"
 msgid "Failed to open attachment."
 msgstr "Не вдалося відкрити долучення."
 
-#: core/attachmentmodel.cpp:250
+#: core/attachmentmodel.cpp:252
 #, kde-format
 msgctxt "@info"
 msgid "Failed to save attachment for opening."
 msgstr "Не вдалося зберегти долучення для відкриття."
 
-#: core/attachmentmodel.cpp:268
+#: core/attachmentmodel.cpp:273
 #, kde-format
 msgctxt "@info"
 msgid "No keys were found in this attachment"
 msgstr "У цьому долученні не було знайдено ключів"
 
-#: core/attachmentmodel.cpp:271
+#: core/attachmentmodel.cpp:276
 #, kde-format
 msgctxt "@info"
 msgid "one key imported"
 msgid_plural "%1 keys imported"
 msgstr[0] "імпортовано %1 ключ"
 msgstr[1] "імпортовано %1 ключі"
 msgstr[2] "імпортовано %1 ключів"
 msgstr[3] "імпортовано %1 ключ"
 
-#: core/attachmentmodel.cpp:274
+#: core/attachmentmodel.cpp:279
 #, kde-format
 msgctxt "@info"
 msgid "one key was already imported"
 msgid_plural "%1 keys were already imported"
 msgstr[0] "%1 ключ вже імпортовано"
 msgstr[1] "%1 ключі вже імпортовано"
 msgstr[2] "%1 ключів вже імпортовано"
 msgstr[3] "%1 ключ вже імпортовано"
 
 #: core/mailtemplates.cpp:103
 #, kde-format
 msgid "fwd"
 msgstr "fwd"
 
 #: core/mailtemplates.cpp:132
 #, kde-format
 msgid "re"
 msgstr "re"
 
 #: core/messageparser.cpp:158
 #, kde-format
 msgctxt "displayed when a mail has unknown sender, receiver or date"
 msgid "Unknown"
 msgstr "Невідомо"
 
-#: core/messagepart.cpp:618 core/messagepart.cpp:709 core/messagepart.cpp:783
+#: core/messagepart.cpp:625 core/messagepart.cpp:660 core/messagepart.cpp:835
 #, kde-format
 msgctxt "@info:status"
 msgid "Wrong Crypto Plug-In."
 msgstr "Неправильний додаток шифрування."
 
-#: core/messagepart.cpp:857
+#: core/messagepart.cpp:986
 #, kde-format
-msgctxt "@info"
-msgid "Could not decrypt the data: no key found for recipients."
-msgstr "Не вдалося розшифрувати дані. Не знайдено ключа для отримувачів."
+msgid "No appropriate crypto plug-in was found."
+msgstr "Не знайдено відповідного додатка шифрування."
 
-#: core/messagepart.cpp:862
+#: core/messagepart.cpp:988
 #, kde-format
-msgctxt "@info"
-msgid "Could not decrypt the data."
-msgstr "Не вдалося розшифрувати дані."
+msgid "Crypto plug-in \"%1\" cannot decrypt messages."
+msgstr "Додатку шифрування «%1» не вдалося розшифрувати повідомлення."
+
+#: core/messagepart.cpp:990
+#, kde-format
+msgid "Crypto plug-in \"%1\" could not decrypt the data."
+msgstr "Додатку шифрування «%1» не вдалося розшифрувати дані."
 
-#: core/partmodel.cpp:483
+#: core/messagepart.cpp:991 widgets/messageviewer.cpp:295
+#, kde-format
+msgid "Error: %1"
+msgstr "Помилка: %1"
+
+#: core/partmodel.cpp:487
 #, kde-format
 msgctxt "@info:status"
 msgid "No key available."
 msgstr "Немає доступних ключів."
 
-#: core/partmodel.cpp:485
+#: core/partmodel.cpp:489
 #, kde-format
 msgctxt "@info:status"
 msgid "Wrong passphrase."
 msgstr "Неправильний пароль."
 
-#: quick/qml/MailViewer.qml:39
+#: quick/qml/MailViewer.qml:38
 #, kde-format
 msgctxt "@title:window"
 msgid "Message viewer"
 msgstr "Перегляд повідомлень"
 
-#: quick/qml/MailViewer.qml:76
+#: quick/qml/MailViewer.qml:75
 #, kde-format
 msgctxt "@label"
 msgid "Subject:"
 msgstr "Тема:"
 
-#: quick/qml/MailViewer.qml:98
+#: quick/qml/MailViewer.qml:96
 #, kde-format
 msgctxt "@label"
 msgid "From:"
 msgstr "Від:"
 
-#: quick/qml/MailViewer.qml:115
+#: quick/qml/MailViewer.qml:112
 #, kde-format
 msgctxt "@label"
 msgid "Sender:"
 msgstr "Відправник:"
 
-#: quick/qml/MailViewer.qml:132
+#: quick/qml/MailViewer.qml:128
 #, kde-format
 msgctxt "@label"
 msgid "To:"
 msgstr "Кому:"
 
-#: quick/qml/MailViewer.qml:190
+#: quick/qml/MailViewer.qml:185
 #, kde-format
 msgctxt "@action:button"
 msgid "Save attachment"
 msgstr "Зберегти долучення"
 
 #: quick/qml/private/AttachmentDelegate.qml:56
 #, kde-format
 msgctxt "@action:button"
 msgid "Import key"
 msgstr "Імпортувати ключ"
 
 #: quick/qml/private/Banner.qml:188
 #, kde-format
 msgctxt "@action:button"
 msgid "Close"
 msgstr "Закрити"
 
 #: quick/qml/private/ErrorPart.qml:27
 #, kde-format
 msgid "An error occurred: %1"
 msgstr "Сталася помилка: %1"
 
-#: quick/qml/private/ICalPart.qml:28
+#: quick/qml/private/ICalPart.qml:27
 #, kde-format
 msgid "This mail contains an invitation"
 msgstr "У цьому повідомленні міститься запрошення"
 
-#: quick/qml/private/MailPart.qml:42
+#: quick/qml/private/MailPart.qml:41
 #, kde-format
 msgctxt "@info"
 msgid "sent by %1 on %2"
 msgstr "надіслано %1, %2"
 
-#: quick/qml/private/MailPartModel.qml:54
+#: quick/qml/private/MailPartModel.qml:55
 #, kde-format
 msgctxt "@label"
 msgid "This message has been signed using the key %1."
 msgstr "Це повідомлення підписано за допомогою ключа %1."
 
-#: quick/qml/private/MailPartModel.qml:55
+#: quick/qml/private/MailPartModel.qml:56 widgets/messagecontainerwidget.cpp:78
 #, kde-format
 msgctxt "@label"
 msgid "The key details are not available."
 msgstr "Подробиці щодо ключа недоступні."
 
-#: quick/qml/private/MailPartModel.qml:57
+#: quick/qml/private/MailPartModel.qml:58
 #, kde-format
 msgctxt "@label"
 msgid "This message has been signed using the key %1 by %2."
 msgstr "Це повідомлення підписано за допомогою ключа %1 %2."
 
-#: quick/qml/private/MailPartModel.qml:59
+#: quick/qml/private/MailPartModel.qml:60
 #, kde-format
 msgctxt "@label"
 msgid "The key was revoked."
 msgstr "Ключ відкликано."
 
-#: quick/qml/private/MailPartModel.qml:62
+#: quick/qml/private/MailPartModel.qml:63
 #, kde-format
 msgctxt "@label"
 msgid "The key has expired."
 msgstr "Строк дії ключа вичерпано."
 
-#: quick/qml/private/MailPartModel.qml:65
+#: quick/qml/private/MailPartModel.qml:66
 #, kde-format
 msgctxt "@label"
 msgid "You are trusting this key."
 msgstr "Ви довіряєте цьому ключу."
 
-#: quick/qml/private/MailPartModel.qml:68
+#: quick/qml/private/MailPartModel.qml:69
+#: widgets/messagecontainerwidget.cpp:108
 #, kde-format
 msgctxt "@label"
 msgid "The signature is invalid."
 msgstr "Підпис є некоректним."
 
-#: quick/qml/private/MailPartModel.qml:97
+#: quick/qml/private/MailPartModel.qml:98
+#: widgets/messagecontainerwidget.cpp:205
 #, kde-format
 msgid "This message is encrypted but we don't have the key for it."
 msgstr ""
 "Це повідомлення зашифровано, але у нас немає ключа для його розшифровування."
 
-#: quick/qml/private/MailPartModel.qml:97
+#: quick/qml/private/MailPartModel.qml:98
 #, kde-format
 msgid "This message is encrypted to the key: %1"
 msgstr "Це повідомлення зашифровано таким ключем: %1"
 
-#: widgets/messageviewer.cpp:54
+#: widgets/messagecontainerwidget.cpp:66
+#, kde-format
+msgctxt "@label"
+msgid ""
+"This message has been signed VS-NfD compliant using the key <a href=\"%1\">"
+"%2</a>."
+msgstr ""
+"Це повідомлення підписано відповідно до VS-NfD за допомогою ключа <a href="
+"\"%1\">%2</a>."
+
+#: widgets/messagecontainerwidget.cpp:73
+#, kde-format
+msgctxt "@label"
+msgid "This message has been signed using the key <a href=\"%1\">%2</a>."
+msgstr "Це повідомлення підписано за допомогою ключа <a href=\"%1\">%2</a>."
+
+#: widgets/messagecontainerwidget.cpp:81
+#, kde-format
+msgctxt "@label"
+msgid "This message has been signed VS-NfD compliant by %1."
+msgstr "Це повідомлення підписано відповідно до VS-NfD %1."
+
+#: widgets/messagecontainerwidget.cpp:83
+#, kde-format
+msgctxt "@label"
+msgid "This message has been signed by %1."
+msgstr "Це повідомлення підписано %1."
+
+#: widgets/messagecontainerwidget.cpp:86
+#, kde-format
+msgctxt "@label"
+msgid "The <a href=\"%1\">key</a> was revoked."
+msgstr "<a href=\"%1\">Ключ</a> відкликано."
+
+#: widgets/messagecontainerwidget.cpp:89
+#, kde-format
+msgctxt "@label"
+msgid "The <a href=\"%1\">key</a> was expired."
+msgstr "Строк дії <a href=\"%1\">ключа</a> вичерпано."
+
+#: widgets/messagecontainerwidget.cpp:94
+#, kde-format
+msgctxt "@label"
+msgid ""
+"The signature is valid, but the <a href=\"%1\">key</a>'s validity is unknown."
+msgstr ""
+"Підпис коректний, але невідомо коректний <a href=\"%1\">ключ</a> чи ні."
+
+#: widgets/messagecontainerwidget.cpp:97
+#, kde-format
+msgctxt "@label"
+msgid ""
+"The signature is valid and the <a href=\"%1\">key</a> is marginally trusted."
+msgstr "Підпис коректний, надійність <a href=\"%1\">ключа</a> є обмеженою."
+
+#: widgets/messagecontainerwidget.cpp:99
+#, kde-format
+msgctxt "@label"
+msgid "The signature is valid and the <a href=\"%1\">key</a> is fully trusted."
+msgstr "Підпис коректний, надійність <a href=\"%1\">ключа</a> повна."
+
+#: widgets/messagecontainerwidget.cpp:102
+#, kde-format
+msgctxt "@label"
+msgid ""
+"The signature is valid and the <a href=\"%1\">key</a> is ultimately trusted."
+msgstr "Підпис коректний, надійність <a href=\"%1\">ключа</a> необмежена."
+
+#: widgets/messagecontainerwidget.cpp:104
+#, kde-format
+msgctxt "@label"
+msgid "The signature is valid, but the <a href=\"%1\">key</a> is untrusted."
+msgstr "Підпис коректний, але <a href=\"%1\">ключ</a> не є надійним."
+
+#: widgets/messagecontainerwidget.cpp:203
+#, kde-format
+msgid ""
+"This message is VS-NfD compliant encrypted but we don't have the key for it."
+msgstr ""
+"Це повідомлення зашифровано відповідно до VS-NfD, але у нас немає ключа для "
+"його розшифровування."
+
+#: widgets/messagecontainerwidget.cpp:209
+#, kde-format
+msgid "This message is VS-NfD compliant encrypted."
+msgstr "Це повідомлення зашифровано відповідно до VS-NfD."
+
+#: widgets/messagecontainerwidget.cpp:211
+#, kde-format
+msgid "This message is encrypted."
+msgstr "Це повідомлення зашифровано."
+
+#: widgets/messagecontainerwidget.cpp:220
+#, kde-format
+msgid "The message is encrypted for the following keys:"
+msgstr "Повідомлення зашифровано для таких ключів:"
+
+#: widgets/messagecontainerwidget.cpp:237
+#, kde-format
+msgid "Unknown key"
+msgstr "Невідомий ключ"
+
+#: widgets/messageviewer.cpp:57
 #, kde-format
 msgid "&Save Attachment As..."
 msgstr "&Зберегти долучення як…"
 
-#: widgets/messageviewer.cpp:59
+#: widgets/messageviewer.cpp:62
 #, kde-format
 msgctxt "to open"
 msgid "Open"
 msgstr "Відкрити"
 
-#: widgets/messageviewer.cpp:64
+#: widgets/messageviewer.cpp:67
 #, kde-format
 msgctxt "@action:inmenu"
 msgid "Import public key"
 msgstr "Імпортувати відкритий ключ"
 
-#: widgets/messageviewer.cpp:192
+#: widgets/messageviewer.cpp:236
 #, kde-format
 msgid "Invitation"
 msgstr "Запрошення"
 
-#: widgets/messageviewer.cpp:195
+#: widgets/messageviewer.cpp:239
 #, kde-format
 msgid "&Summary:"
 msgstr "&Резюме:"
 
-#: widgets/messageviewer.cpp:196
+#: widgets/messageviewer.cpp:240
 #, kde-format
 msgid "&Organizer:"
 msgstr "&Організатор:"
 
-#: widgets/messageviewer.cpp:198
+#: widgets/messageviewer.cpp:242
 #, kde-format
 msgid "&Location:"
 msgstr "&Розташування:"
 
-#: widgets/messageviewer.cpp:200
+#: widgets/messageviewer.cpp:244
 #, kde-format
 msgid "&Start date:"
 msgstr "Дата &початку:"
 
-#: widgets/messageviewer.cpp:202
+#: widgets/messageviewer.cpp:246
 #, kde-format
 msgid "&End date:"
 msgstr "Дата за&кінчення:"
 
-#: widgets/messageviewer.cpp:205
+#: widgets/messageviewer.cpp:249
 #, kde-format
 msgid "&Details:"
 msgstr "&Подробиці:"
 
-#: widgets/messageviewer.cpp:215
+#: widgets/messageviewer.cpp:271
 #, kde-format
 msgid "Encapsulated email"
 msgstr "Вбудовано повідомлення"
 
-#: widgets/messageviewer.cpp:223
+#: widgets/messageviewer.cpp:279
 #, kde-format
 msgid "From:"
 msgstr "Від:"
 
-#: widgets/messageviewer.cpp:224
+#: widgets/messageviewer.cpp:280
 #, kde-format
 msgid "Date:"
 msgstr "Дата:"
 
-#: widgets/messageviewer.cpp:237
-#, kde-format
-msgid "Error: %1"
-msgstr "Помилка: %1"
-
-#: widgets/messageviewer.cpp:257
+#: widgets/messageviewer.cpp:315
 #, kde-format
 msgid "&Subject:"
 msgstr "&Тема:"
 
-#: widgets/messageviewer.cpp:260
+#: widgets/messageviewer.cpp:318
 #, kde-format
 msgid "&From:"
 msgstr "&Від:"
 
-#: widgets/messageviewer.cpp:263
+#: widgets/messageviewer.cpp:321
 #, kde-format
 msgid "&To:"
 msgstr "&Кому:"
+
+#: widgets/urlhandler.cpp:37
+#, kde-format
+msgid "Could not start certificate manager. Please check your installation."
+msgstr ""
+"Не вдається запустити програму керування сертифікатами. Будь ласка, "
+"перевірте, чи належним чином встановлено програму."
+
+#: widgets/urlhandler.cpp:39
+#, kde-format
+msgid "KMail Error"
+msgstr "Помилка KMail"
+
+#~ msgctxt "@info"
+#~ msgid "Could not decrypt the data: no key found for recipients."
+#~ msgstr "Не вдалося розшифрувати дані. Не знайдено ключа для отримувачів."
diff --git a/po/zh_CN/mimetreeparser.po b/po/zh_CN/mimetreeparser.po
new file mode 100644
index 0000000..0963b98
--- /dev/null
+++ b/po/zh_CN/mimetreeparser.po
@@ -0,0 +1,449 @@
+msgid ""
+msgstr ""
+"Project-Id-Version: kdeorg\n"
+"Report-Msgid-Bugs-To: https://bugs.kde.org\n"
+"POT-Creation-Date: 2023-08-27 00:47+0000\n"
+"PO-Revision-Date: 2023-08-27 03:42\n"
+"Last-Translator: \n"
+"Language-Team: Chinese Simplified\n"
+"Language: zh_CN\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+"X-Crowdin-Project: kdeorg\n"
+"X-Crowdin-Project-ID: 269464\n"
+"X-Crowdin-Language: zh-CN\n"
+"X-Crowdin-File: /kf5-trunk/messages/mimetreeparser/mimetreeparser.pot\n"
+"X-Crowdin-File-ID: 44633\n"
+
+#: core/attachmentmodel.cpp:90
+#, kde-format
+msgctxt "@title:column"
+msgid "Name"
+msgstr "名称"
+
+#: core/attachmentmodel.cpp:92
+#, kde-format
+msgctxt "@title:column"
+msgid "Size"
+msgstr "大小"
+
+#: core/attachmentmodel.cpp:94
+#, kde-format
+msgctxt "@title:column"
+msgid "Encrypted"
+msgstr "已加密"
+
+#: core/attachmentmodel.cpp:96
+#, kde-format
+msgctxt "@title:column"
+msgid "Signed"
+msgstr "已签名"
+
+#: core/attachmentmodel.cpp:198
+#, kde-format
+msgctxt "@info"
+msgid "Failed to save attachment."
+msgstr "无法保存附件。"
+
+#: core/attachmentmodel.cpp:230
+#, kde-format
+msgctxt "@info"
+msgid "Saved the attachment to disk: %1"
+msgstr "已将附件保存到磁盘:%1"
+
+#: core/attachmentmodel.cpp:247
+#, kde-format
+msgctxt "@info"
+msgid "Failed to open attachment."
+msgstr "无法打开附件。"
+
+#: core/attachmentmodel.cpp:252
+#, kde-format
+msgctxt "@info"
+msgid "Failed to save attachment for opening."
+msgstr "无法保存以打开附件。"
+
+#: core/attachmentmodel.cpp:273
+#, kde-format
+msgctxt "@info"
+msgid "No keys were found in this attachment"
+msgstr "没有在此附件中找到密钥"
+
+#: core/attachmentmodel.cpp:276
+#, kde-format
+msgctxt "@info"
+msgid "one key imported"
+msgid_plural "%1 keys imported"
+msgstr[0] "已导入 %1 个密钥"
+
+#: core/attachmentmodel.cpp:279
+#, kde-format
+msgctxt "@info"
+msgid "one key was already imported"
+msgid_plural "%1 keys were already imported"
+msgstr[0] "%1 个密钥已经被导入过了"
+
+#: core/mailtemplates.cpp:103
+#, kde-format
+msgid "fwd"
+msgstr "转发"
+
+#: core/mailtemplates.cpp:132
+#, kde-format
+msgid "re"
+msgstr "回复"
+
+#: core/messageparser.cpp:158
+#, kde-format
+msgctxt "displayed when a mail has unknown sender, receiver or date"
+msgid "Unknown"
+msgstr "未知"
+
+#: core/messagepart.cpp:625 core/messagepart.cpp:660 core/messagepart.cpp:835
+#, kde-format
+msgctxt "@info:status"
+msgid "Wrong Crypto Plug-In."
+msgstr "错误的加密插件。"
+
+#: core/messagepart.cpp:986
+#, kde-format
+msgid "No appropriate crypto plug-in was found."
+msgstr "没有找到合适的加密算法插件。"
+
+#: core/messagepart.cpp:988
+#, kde-format
+msgid "Crypto plug-in \"%1\" cannot decrypt messages."
+msgstr "加密算法插件“%1”无法解密任何信件。"
+
+#: core/messagepart.cpp:990
+#, kde-format
+msgid "Crypto plug-in \"%1\" could not decrypt the data."
+msgstr "加密插件“%1”无法解密数据。"
+
+#: core/messagepart.cpp:991 widgets/messageviewer.cpp:295
+#, kde-format
+msgid "Error: %1"
+msgstr "错误:%1"
+
+#: core/partmodel.cpp:487
+#, kde-format
+msgctxt "@info:status"
+msgid "No key available."
+msgstr "没有可用的密钥。"
+
+#: core/partmodel.cpp:489
+#, kde-format
+msgctxt "@info:status"
+msgid "Wrong passphrase."
+msgstr "密码错误。"
+
+#: quick/qml/MailViewer.qml:38
+#, kde-format
+msgctxt "@title:window"
+msgid "Message viewer"
+msgstr "消息查看器"
+
+#: quick/qml/MailViewer.qml:75
+#, kde-format
+msgctxt "@label"
+msgid "Subject:"
+msgstr "主题:"
+
+#: quick/qml/MailViewer.qml:96
+#, kde-format
+msgctxt "@label"
+msgid "From:"
+msgstr "发件人:"
+
+#: quick/qml/MailViewer.qml:112
+#, kde-format
+msgctxt "@label"
+msgid "Sender:"
+msgstr "发送者:"
+
+#: quick/qml/MailViewer.qml:128
+#, kde-format
+msgctxt "@label"
+msgid "To:"
+msgstr "收件人:"
+
+#: quick/qml/MailViewer.qml:185
+#, kde-format
+msgctxt "@action:button"
+msgid "Save attachment"
+msgstr "保存附件"
+
+#: quick/qml/private/AttachmentDelegate.qml:56
+#, kde-format
+msgctxt "@action:button"
+msgid "Import key"
+msgstr "导入密钥"
+
+#: quick/qml/private/Banner.qml:188
+#, kde-format
+msgctxt "@action:button"
+msgid "Close"
+msgstr "关闭"
+
+#: quick/qml/private/ErrorPart.qml:27
+#, kde-format
+msgid "An error occurred: %1"
+msgstr "发生错误:%1"
+
+#: quick/qml/private/ICalPart.qml:27
+#, kde-format
+msgid "This mail contains an invitation"
+msgstr "此邮件包含一个邀请"
+
+#: quick/qml/private/MailPart.qml:41
+#, kde-format
+msgctxt "@info"
+msgid "sent by %1 on %2"
+msgstr "由 %1 发送于 %2"
+
+#: quick/qml/private/MailPartModel.qml:55
+#, kde-format
+msgctxt "@label"
+msgid "This message has been signed using the key %1."
+msgstr "此消息已使用密钥 %1 签名。"
+
+#: quick/qml/private/MailPartModel.qml:56 widgets/messagecontainerwidget.cpp:78
+#, kde-format
+msgctxt "@label"
+msgid "The key details are not available."
+msgstr "无法获取密钥详情。"
+
+#: quick/qml/private/MailPartModel.qml:58
+#, kde-format
+msgctxt "@label"
+msgid "This message has been signed using the key %1 by %2."
+msgstr "此消息已由 %2 使用密钥 %1 签名。"
+
+#: quick/qml/private/MailPartModel.qml:60
+#, kde-format
+msgctxt "@label"
+msgid "The key was revoked."
+msgstr "此密钥已被吊销。"
+
+#: quick/qml/private/MailPartModel.qml:63
+#, kde-format
+msgctxt "@label"
+msgid "The key has expired."
+msgstr "此密钥已过期。"
+
+#: quick/qml/private/MailPartModel.qml:66
+#, kde-format
+msgctxt "@label"
+msgid "You are trusting this key."
+msgstr "您信任此密钥。"
+
+#: quick/qml/private/MailPartModel.qml:69
+#: widgets/messagecontainerwidget.cpp:108
+#, kde-format
+msgctxt "@label"
+msgid "The signature is invalid."
+msgstr "此签名无效。"
+
+#: quick/qml/private/MailPartModel.qml:98
+#: widgets/messagecontainerwidget.cpp:205
+#, kde-format
+msgid "This message is encrypted but we don't have the key for it."
+msgstr "此消息已加密,但我们没有它的密钥。"
+
+#: quick/qml/private/MailPartModel.qml:98
+#, kde-format
+msgid "This message is encrypted to the key: %1"
+msgstr "此消息已使用密钥 %1 加密"
+
+#: widgets/messagecontainerwidget.cpp:66
+#, kde-format
+msgctxt "@label"
+msgid ""
+"This message has been signed VS-NfD compliant using the key <a href=\"%1\">"
+"%2</a>."
+msgstr ""
+
+#: widgets/messagecontainerwidget.cpp:73
+#, kde-format
+msgctxt "@label"
+msgid "This message has been signed using the key <a href=\"%1\">%2</a>."
+msgstr ""
+
+#: widgets/messagecontainerwidget.cpp:81
+#, kde-format
+msgctxt "@label"
+msgid "This message has been signed VS-NfD compliant by %1."
+msgstr ""
+
+#: widgets/messagecontainerwidget.cpp:83
+#, kde-format
+msgctxt "@label"
+msgid "This message has been signed by %1."
+msgstr ""
+
+#: widgets/messagecontainerwidget.cpp:86
+#, kde-format
+msgctxt "@label"
+msgid "The <a href=\"%1\">key</a> was revoked."
+msgstr ""
+
+#: widgets/messagecontainerwidget.cpp:89
+#, kde-format
+msgctxt "@label"
+msgid "The <a href=\"%1\">key</a> was expired."
+msgstr ""
+
+#: widgets/messagecontainerwidget.cpp:94
+#, kde-format
+msgctxt "@label"
+msgid ""
+"The signature is valid, but the <a href=\"%1\">key</a>'s validity is unknown."
+msgstr ""
+
+#: widgets/messagecontainerwidget.cpp:97
+#, kde-format
+msgctxt "@label"
+msgid ""
+"The signature is valid and the <a href=\"%1\">key</a> is marginally trusted."
+msgstr ""
+
+#: widgets/messagecontainerwidget.cpp:99
+#, kde-format
+msgctxt "@label"
+msgid "The signature is valid and the <a href=\"%1\">key</a> is fully trusted."
+msgstr ""
+
+#: widgets/messagecontainerwidget.cpp:102
+#, kde-format
+msgctxt "@label"
+msgid ""
+"The signature is valid and the <a href=\"%1\">key</a> is ultimately trusted."
+msgstr ""
+
+#: widgets/messagecontainerwidget.cpp:104
+#, kde-format
+msgctxt "@label"
+msgid "The signature is valid, but the <a href=\"%1\">key</a> is untrusted."
+msgstr ""
+
+#: widgets/messagecontainerwidget.cpp:203
+#, kde-format
+msgid ""
+"This message is VS-NfD compliant encrypted but we don't have the key for it."
+msgstr ""
+
+#: widgets/messagecontainerwidget.cpp:209
+#, kde-format
+msgid "This message is VS-NfD compliant encrypted."
+msgstr ""
+
+#: widgets/messagecontainerwidget.cpp:211
+#, kde-format
+msgid "This message is encrypted."
+msgstr ""
+
+#: widgets/messagecontainerwidget.cpp:220
+#, kde-format
+msgid "The message is encrypted for the following keys:"
+msgstr ""
+
+#: widgets/messagecontainerwidget.cpp:237
+#, fuzzy, kde-format
+#| msgctxt "displayed when a mail has unknown sender, receiver or date"
+#| msgid "Unknown"
+msgid "Unknown key"
+msgstr "未知"
+
+#: widgets/messageviewer.cpp:57
+#, kde-format
+msgid "&Save Attachment As..."
+msgstr "附件另存为(&S)..."
+
+#: widgets/messageviewer.cpp:62
+#, kde-format
+msgctxt "to open"
+msgid "Open"
+msgstr "打开"
+
+#: widgets/messageviewer.cpp:67
+#, kde-format
+msgctxt "@action:inmenu"
+msgid "Import public key"
+msgstr "导入公钥"
+
+#: widgets/messageviewer.cpp:236
+#, kde-format
+msgid "Invitation"
+msgstr "邀请"
+
+#: widgets/messageviewer.cpp:239
+#, kde-format
+msgid "&Summary:"
+msgstr "摘要(&S):"
+
+#: widgets/messageviewer.cpp:240
+#, kde-format
+msgid "&Organizer:"
+msgstr "组织者(&O):"
+
+#: widgets/messageviewer.cpp:242
+#, kde-format
+msgid "&Location:"
+msgstr "地点(&L):"
+
+#: widgets/messageviewer.cpp:244
+#, kde-format
+msgid "&Start date:"
+msgstr "开始日期(&S):"
+
+#: widgets/messageviewer.cpp:246
+#, kde-format
+msgid "&End date:"
+msgstr "结束日期(&E):"
+
+#: widgets/messageviewer.cpp:249
+#, kde-format
+msgid "&Details:"
+msgstr "详细信息(&D):"
+
+#: widgets/messageviewer.cpp:271
+#, kde-format
+msgid "Encapsulated email"
+msgstr "封装的信件"
+
+#: widgets/messageviewer.cpp:279
+#, kde-format
+msgid "From:"
+msgstr "发件人:"
+
+#: widgets/messageviewer.cpp:280
+#, kde-format
+msgid "Date:"
+msgstr "日期:"
+
+#: widgets/messageviewer.cpp:315
+#, kde-format
+msgid "&Subject:"
+msgstr "主题(&S):"
+
+#: widgets/messageviewer.cpp:318
+#, kde-format
+msgid "&From:"
+msgstr "发件人(&F):"
+
+#: widgets/messageviewer.cpp:321
+#, kde-format
+msgid "&To:"
+msgstr "收件人(&T):"
+
+#: widgets/urlhandler.cpp:37
+#, kde-format
+msgid "Could not start certificate manager. Please check your installation."
+msgstr ""
+
+#: widgets/urlhandler.cpp:39
+#, kde-format
+msgid "KMail Error"
+msgstr ""
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index 75b59ff..e98b2ec 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -1,200 +1,194 @@
 # SPDX-FileCopyrightText: 2023 Carl Schwan <carl.schwan@gnupg.com>
 # SPDX-License-Identifier: BSD-3-Clause
 
 ecm_setup_version(PROJECT
     VARIABLE_PREFIX MIMETREEPARSER_CORE
     VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/mimetreeparser_core_version.h"
     PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/KPim6MimeTreeParserCoreConfigVersion.cmake"
     SOVERSION 6
 )
 
 # private library
 
 add_library(mimetreeparserprivate STATIC)
 set_target_properties(mimetreeparserprivate PROPERTIES POSITION_INDEPENDENT_CODE ON)
 add_definitions(-DTRANSLATION_DOMAIN=\"mimetreeparser\")
 target_sources(mimetreeparserprivate PUBLIC
      messagepart.h
      messagepart.cpp
-     crypto.h
-     crypto.cpp
      cryptohelper.h
      cryptohelper.cpp
      utils.h
      utils.cpp
-     mailtemplates.h
-     mailtemplates.cpp
-     mailcrypto.h
-     mailcrypto.cpp
+     job/qgpgmejobexecutor.h
+     job/qgpgmejobexecutor.cpp
 )
 target_link_libraries(mimetreeparserprivate
 PUBLIC
     KPim6::Mime
     KF6::I18n
     Qt6::Gui
     KF6::Codecs
     Qt6::Core5Compat
     Gpgme::Gpgme
+    KPim${KF_MAJOR_VERSION}::Libkleo
 )
 
 ecm_qt_declare_logging_category(mimetreeparserprivate
     HEADER mimetreeparser_core_debug.h
     IDENTIFIER MIMETREEPARSER_CORE_LOG
     CATEGORY_NAME org.kde.pim.mimetreeparser.core
     DESCRIPTION "mimetreeparser (pim lib)"
     EXPORT MIMETREEPARSERNG
 )
 
 # public dynamic library
 
 
 add_library(KPim6MimeTreeParserCore)
 add_library(KPim6::MimeTreeParserCore
     ALIAS KPim6MimeTreeParserCore
 )
 
 target_sources(KPim6MimeTreeParserCore PRIVATE
      errors.h
      async.h
      attachmentmodel.h
      bodypartformatter.h
      bodypartformatterbasefactory.h
      bodypartformatterbasefactory_p.h
      enums.h
      htmlutils.h
      messageparser.h
      objecttreeparser.h
      partmetadata.h
      partmodel.h
      attachmentmodel.cpp
      bodypartformatter.cpp
      bodypartformatter_impl.cpp
      bodypartformatterbasefactory.cpp
      htmlutils.cpp
      messageparser.cpp
      objecttreeparser.cpp
      partmodel.cpp
 )
 
 if (COMPILE_WITH_UNITY_CMAKE_SUPPORT)
     set_target_properties(KPim6MimeTreeParserCore PROPERTIES UNITY_BUILD ON)
 endif()
 generate_export_header(KPim6MimeTreeParserCore BASE_NAME mimetreeparser_core)
 
 
 target_include_directories(KPim6MimeTreeParserCore
     INTERFACE "$<INSTALL_INTERFACE:${KDE_INSTALL_INCLUDEDIR}/KPim6/MimeTreeParserCore>")
 
 target_link_libraries(KPim6MimeTreeParserCore
 PUBLIC
     KPim6::Mime
     KF6::I18n
     Qt6::Gui
 PRIVATE
     mimetreeparserprivate
 )
 
 set_target_properties(KPim6MimeTreeParserCore PROPERTIES
     VERSION ${MIMETREEPARSERNG_VERSION}
     SOVERSION ${MIMETREEPARSERNG_SOVERSION}
     EXPORT_NAME MimeTreeParserCore
 )
 
 ecm_generate_pri_file(BASE_NAME MimeTreeParserCore
     LIB_NAME KPim6MimeTreeParserCore
     DEPS "MimeTreeParserCore" 
     FILENAME_VAR PRI_FILENAME
 )
 
 install(FILES ${PRI_FILENAME} DESTINATION ${ECM_MKSPECS_INSTALL_DIR})
 
 install(TARGETS
     KPim6MimeTreeParserCore
     EXPORT KPim6MimeTreeParserCoreTargets ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}
 )
 
 ecm_generate_headers(MimeTreeParserCore_CamelCase_HEADERS
     HEADER_NAMES
         AttachmentModel
         ObjectTreeParser
         MessageParser
         MessagePart
         PartModel
-        MailCrypto
-        Crypto
         Errors
         PartMetaData
-        MailTemplates
     REQUIRED_HEADERS MimeTreeParserCore_HEADERS
     PREFIX MimeTreeParserCore
 )
 
 install(FILES
     ${MimeTreeParserCore_CamelCase_HEADERS}
     DESTINATION ${KDE_INSTALL_INCLUDEDIR}/KPim6/MimeTreeParserCore/MimeTreeParserCore
     COMPONENT Devel
 )
 install(FILES
     ${CMAKE_CURRENT_BINARY_DIR}/mimetreeparser_core_export.h
     ${MimeTreeParserCore_HEADERS}
     DESTINATION ${KDE_INSTALL_INCLUDEDIR}/KPim6/MimeTreeParserCore/mimetreeparsercore
     COMPONENT Devel
 )
 
 if (BUILD_QCH)
     ecm_add_qch(
         KPim6MimeTreeParserCore_QCH
         NAME MimeTreeParserCore
         BASE_NAME KPim6MimeTreeParserCore
         VERSION ${PIM_VERSION}
         ORG_DOMAIN org.kde
         # using only public headers, to cover only public API
         SOURCES ${MimeTreeParserCore_HEADERS}
         MD_MAINPAGE "${CMAKE_SOURCE_DIR}/README.md"
         LINK_QCHS
             Qt6Core_QCH
         INCLUDE_DIRS
             ${CMAKE_CURRENT_BINARY_DIR}
         BLANK_MACROS
             MIMETREEPARSERCORE_EXPORT
         TAGFILE_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR}
         QCH_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR}
         COMPONENT Devel
     )
 endif()
 
 ########### CMake Config Files ###########
 set(CMAKECONFIG_INSTALL_DIR "${KDE_INSTALL_CMAKEPACKAGEDIR}/KPim6MimeTreeParserCore")
 
 if (BUILD_QCH)
     ecm_install_qch_export(
         TARGETS KPim6MimeTreeParserCore_QCH
         FILE KPim6MimeTreeParserCoreQchTargets.cmake
         DESTINATION "${CMAKECONFIG_INSTALL_DIR}"
         COMPONENT Devel
     )
     set(PACKAGE_INCLUDE_QCHTARGETS "include(\"\${CMAKE_CURRENT_LIST_DIR}/KPim6MimeTreeParserCoreQchTargets.cmake\")")
 endif()
 
 configure_package_config_file(
     "${CMAKE_CURRENT_SOURCE_DIR}/KPimMimeTreeParserCoreConfig.cmake.in"
     "${CMAKE_CURRENT_BINARY_DIR}/KPim6MimeTreeParserCoreConfig.cmake"
     INSTALL_DESTINATION  ${CMAKECONFIG_INSTALL_DIR}
 )
 
 install(FILES
     "${CMAKE_CURRENT_BINARY_DIR}/KPim6MimeTreeParserCoreConfig.cmake"
     "${CMAKE_CURRENT_BINARY_DIR}/KPim6MimeTreeParserCoreConfigVersion.cmake"
     DESTINATION "${CMAKECONFIG_INSTALL_DIR}"
     COMPONENT Devel
 )
 
 install(EXPORT KPim6MimeTreeParserCoreTargets
     DESTINATION "${CMAKECONFIG_INSTALL_DIR}"
     FILE KPim6MimeTreeParserCoreTargets.cmake
     NAMESPACE KPim6::
 )
 
 if (BUILD_TESTING)
     add_subdirectory(autotests)
 endif()
diff --git a/src/core/attachmentmodel.cpp b/src/core/attachmentmodel.cpp
index 6bdaebc..d808ac9 100644
--- a/src/core/attachmentmodel.cpp
+++ b/src/core/attachmentmodel.cpp
@@ -1,298 +1,304 @@
 // SPDX-FileCopyrightText: 2016 Sandro Knauß <knauss@kolabsys.com>
 // SPDX-FileCopyCopyright: 2017 Christian Mollekopf <mollekopf@kolabsys.com>
 // SPDX-License-Identifier: LGPL-2.0-or-later
 
 #include "attachmentmodel.h"
 
-#include <QString>
+#include "job/qgpgmejobexecutor.h"
+#include "objecttreeparser.h"
+
+#include <QGpgME/ImportJob>
+#include <QGpgME/Protocol>
 
 #include <KLocalizedString>
 #include <KMime/Content>
+
 #include <QDebug>
 #include <QDesktopServices>
 #include <QDir>
 #include <QFile>
 #include <QGuiApplication>
 #include <QIcon>
 #include <QMimeDatabase>
 #include <QStandardPaths>
 #include <QUrl>
 
-#include <QStringLiteral>
-
 QString sizeHuman(float size)
 {
     QStringList list;
     list << QStringLiteral("KB") << QStringLiteral("MB") << QStringLiteral("GB") << QStringLiteral("TB");
 
     QStringListIterator i(list);
     QString unit = QStringLiteral("Bytes");
 
     while (size >= 1024.0 && i.hasNext()) {
         unit = i.next();
         size /= 1024.0;
     }
 
     if (unit == QStringLiteral("Bytes")) {
         return QString().setNum(size) + QStringLiteral(" ") + unit;
     } else {
         return QString().setNum(size, 'f', 2) + QStringLiteral(" ") + unit;
     }
 }
 
 class AttachmentModelPrivate
 {
 public:
     AttachmentModelPrivate(AttachmentModel *q_ptr, const std::shared_ptr<MimeTreeParser::ObjectTreeParser> &parser);
 
     AttachmentModel *q;
     std::shared_ptr<MimeTreeParser::ObjectTreeParser> mParser;
     MimeTreeParser::MessagePart::List mAttachments;
 };
 
 AttachmentModelPrivate::AttachmentModelPrivate(AttachmentModel *q_ptr, const std::shared_ptr<MimeTreeParser::ObjectTreeParser> &parser)
     : q(q_ptr)
     , mParser(parser)
 {
     mAttachments = mParser->collectAttachmentParts();
 }
 
 AttachmentModel::AttachmentModel(std::shared_ptr<MimeTreeParser::ObjectTreeParser> parser)
     : QAbstractTableModel()
     , d(std::unique_ptr<AttachmentModelPrivate>(new AttachmentModelPrivate(this, parser)))
 {
 }
 
 AttachmentModel::~AttachmentModel()
 {
 }
 
 QHash<int, QByteArray> AttachmentModel::roleNames() const
 {
     return {
         {TypeRole, QByteArrayLiteral("type")},
         {NameRole, QByteArrayLiteral("name")},
         {SizeRole, QByteArrayLiteral("size")},
         {IconRole, QByteArrayLiteral("iconName")},
         {IsEncryptedRole, QByteArrayLiteral("encrypted")},
         {IsSignedRole, QByteArrayLiteral("signed")},
     };
 }
 
 QVariant AttachmentModel::headerData(int section, Qt::Orientation orientation, int role) const
 {
     if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
         switch (section) {
         case NameColumn:
             return i18ndc("mimetreeparser", "@title:column", "Name");
         case SizeColumn:
             return i18ndc("mimetreeparser", "@title:column", "Size");
         case IsEncryptedColumn:
             return i18ndc("mimetreeparser", "@title:column", "Encrypted");
         case IsSignedColumn:
             return i18ndc("mimetreeparser", "@title:column", "Signed");
         }
     }
     return {};
 }
 
 QVariant AttachmentModel::data(const QModelIndex &index, int role) const
 {
     const auto row = index.row();
     const auto column = index.column();
 
     const auto part = d->mAttachments.at(row);
     Q_ASSERT(part);
     auto node = part->node();
     if (!node) {
         qWarning() << "no content for attachment";
         return {};
     }
     QMimeDatabase mimeDb;
     const auto mimetype = mimeDb.mimeTypeForName(QString::fromLatin1(part->mimeType()));
     const auto content = node->encodedContent();
 
     switch (column) {
     case NameColumn:
         switch (role) {
         case TypeRole:
             return mimetype.name();
         case Qt::DisplayRole:
         case NameRole:
             return part->filename();
         case IconRole:
             return mimetype.iconName();
         case Qt::DecorationRole:
             return QIcon::fromTheme(mimetype.iconName());
         case SizeRole:
             return sizeHuman(content.size());
         case IsEncryptedRole:
             return part->encryptions().size() > 0;
         case IsSignedRole:
             return part->signatures().size() > 0;
         case AttachmentPartRole:
             return QVariant::fromValue(part);
         default:
             return {};
         }
     case SizeColumn:
         switch (role) {
         case Qt::DisplayRole:
             return sizeHuman(content.size());
         default:
             return {};
         }
     case IsEncryptedColumn:
         switch (role) {
         case Qt::CheckStateRole:
             return part->encryptions().size() > 0 ? Qt::Checked : Qt::Unchecked;
         default:
             return {};
         }
     case IsSignedColumn:
         switch (role) {
         case Qt::CheckStateRole:
             return part->signatures().size() > 0 ? Qt::Checked : Qt::Unchecked;
         default:
             return {};
         }
     default:
         return {};
     }
 }
 
 static QString internalSaveAttachmentToDisk(AttachmentModel *model, const MimeTreeParser::MessagePart::Ptr &part, const QString &path, bool readonly = false)
 {
     Q_ASSERT(part);
     auto node = part->node();
     auto data = node->decodedContent();
     // This is necessary to store messages embedded messages (EncapsulatedRfc822MessagePart)
     if (data.isEmpty()) {
         data = node->encodedContent();
     }
     if (part->isText()) {
         // convert CRLF to LF before writing text attachments to disk
         data = KMime::CRLFtoLF(data);
     }
     const auto name = part->filename();
     QString fname = path + name;
 
     // Fallback name should we end up with an empty name
     if (name.isEmpty()) {
         fname = path + QStringLiteral("unnamed");
         while (QFileInfo::exists(fname)) {
             fname = fname + QStringLiteral("_1");
         }
     }
 
     // A file with that name already exists, we assume it's the right file
     if (QFileInfo::exists(fname)) {
         return fname;
     }
     QFile f(fname);
     if (!f.open(QIODevice::ReadWrite)) {
         qWarning() << "Failed to write attachment to file:" << fname << " Error: " << f.errorString();
         Q_EMIT model->info(i18ndc("mimetreeparser", "@info", "Failed to save attachment."));
         return {};
     }
     f.write(data);
     if (readonly) {
         // make file read-only so that nobody gets the impression that he migh edit attached files
         f.setPermissions(QFileDevice::ReadUser);
     }
     f.close();
     qInfo() << "Wrote attachment to file: " << fname;
     return fname;
 }
 
 bool AttachmentModel::saveAttachmentToDisk(const int row)
 {
     const auto part = d->mAttachments.at(row);
     return saveAttachmentToDisk(part);
 }
 
 bool AttachmentModel::saveAttachmentToDisk(const MimeTreeParser::MessagePart::Ptr &message)
 {
     QString downloadDir = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);
     if (downloadDir.isEmpty()) {
         downloadDir = QStringLiteral("~");
     }
     downloadDir += QLatin1Char('/') + qGuiApp->applicationName();
     QDir{}.mkpath(downloadDir);
 
     auto path = internalSaveAttachmentToDisk(this, message, downloadDir);
     if (path.isEmpty()) {
         return false;
     }
     Q_EMIT info(i18ndc("mimetreeparser", "@info", "Saved the attachment to disk: %1", path));
     return true;
 }
 
 bool AttachmentModel::openAttachment(const int row)
 {
     const auto part = d->mAttachments.at(row);
     return openAttachment(part);
 }
 
 bool AttachmentModel::openAttachment(const MimeTreeParser::MessagePart::Ptr &message)
 {
     QString downloadDir = QStandardPaths::writableLocation(QStandardPaths::TempLocation) + QLatin1Char('/') + qGuiApp->applicationName();
     QDir{}.mkpath(downloadDir);
     const auto filePath = internalSaveAttachmentToDisk(this, message, downloadDir, true);
     if (!filePath.isEmpty()) {
         if (!QDesktopServices::openUrl(QUrl(QStringLiteral("file://") + filePath))) {
             Q_EMIT info(i18ndc("mimetreeparser", "@info", "Failed to open attachment."));
             return false;
         }
         return true;
     }
     Q_EMIT info(i18ndc("mimetreeparser", "@info", "Failed to save attachment for opening."));
     return false;
 }
 
 bool AttachmentModel::importPublicKey(const int row)
 {
     const auto part = d->mAttachments.at(row);
     return importPublicKey(part);
 }
 
 bool AttachmentModel::importPublicKey(const MimeTreeParser::MessagePart::Ptr &part)
 {
     Q_ASSERT(part);
-    auto result = Crypto::importKey(Crypto::OpenPGP, part->node()->decodedContent());
+    const QByteArray certData = part->node()->decodedContent();
+    QGpgME::ImportJob *import = QGpgME::openpgp()->importJob();
+    MimeTreeParser::QGpgMEJobExecutor executor;
+    auto result = executor.exec(import, certData);
 
     bool success = true;
     QString message;
-    if (result.considered == 0) {
+    if (result.numConsidered() == 0) {
         message = i18ndc("mimetreeparser", "@info", "No keys were found in this attachment");
         success = false;
     } else {
-        message = i18ndcp("mimetreeparser", "@info", "one key imported", "%1 keys imported", result.imported);
-        if (result.unchanged != 0) {
-            message +=
-                QStringLiteral("\n") + i18ndcp("mimetreeparser", "@info", "one key was already imported", "%1 keys were already imported", result.unchanged);
+        message = i18ndcp("mimetreeparser", "@info", "one key imported", "%1 keys imported", result.numImported());
+        if (result.numUnchanged() != 0) {
+            message += QStringLiteral("\n")
+                + i18ndcp("mimetreeparser", "@info", "one key was already imported", "%1 keys were already imported", result.numUnchanged());
         }
     }
 
     Q_EMIT info(message);
 
     return success;
 }
 
 int AttachmentModel::rowCount(const QModelIndex &parent) const
 {
     if (!parent.isValid()) {
         return d->mAttachments.size();
     }
     return 0;
 }
 
 int AttachmentModel::columnCount(const QModelIndex &parent) const
 {
     if (!parent.isValid()) {
         return ColumnCount;
     }
     return 0;
 }
 
 #include "moc_attachmentmodel.cpp"
diff --git a/src/core/autotests/CMakeLists.txt b/src/core/autotests/CMakeLists.txt
index 38239d4..ed6c39e 100644
--- a/src/core/autotests/CMakeLists.txt
+++ b/src/core/autotests/CMakeLists.txt
@@ -1,94 +1,76 @@
 # SPDX-FileCopyrightText: 2017 Christian Mollekopf <mollekopf@kolabsys.com>
 # SPDX-FileCopyrightText: 2022 Carl Schwan <carl@carlschwan.eu>
 # SPDX-License-Identifier: BSD-3-Clause
 
 set(AUTOMOC ON)
 set(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR})
 add_definitions(-DMAIL_DATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/../testdata" )
 add_definitions(-DMAILTEMPLATE_DATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/../templatetestdata" )
 
 
 include(ECMAddTests)
 include(${CMAKE_SOURCE_DIR}/cmake/modules/add_gpg_crypto_test.cmake)
 
 function(add_mimetreeparser_class_unittest _name _additionalSource)
     add_executable(${_name} ${_name}.cpp setupenv.cpp ${_additionalSource})
     target_link_libraries(${_name} PRIVATE
         Qt::Test
         mimetreeparserprivate
         KPim6::MimeTreeParserCore
     )
     target_include_directories(${_name} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/..)
     add_test(NAME ${_name} COMMAND $<TARGET_FILE:${_name}>)
 endfunction()
 
 function(add_mimetreeparser_crypto_unittest _name)
     add_executable(${_name} ${_name}.cpp setupenv.cpp)
     target_include_directories(${_name} PRIVATE
         ${CMAKE_CURRENT_SOURCE_DIR}/..
         ${GPGME_INCLUDE_DIRS}
     )
     target_link_libraries(${_name} PRIVATE
         Qt::Test
         mimetreeparserprivate
         KPim6::MimeTreeParserCore
     )
     add_gpg_crypto_test(${_name} mimetreeparser-${_name})
 endfunction()
 
 add_subdirectory(gnupg_home)
 add_mimetreeparser_crypto_unittest(attachmenttest)
 add_mimetreeparser_class_unittest(cryptohelpertest "../cryptohelper.cpp")
 
 add_definitions( -DMAIL_DATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/../testdata" )
 include(${CMAKE_SOURCE_DIR}/cmake/modules/add_gpg_crypto_test.cmake)
 include_directories(
     ${CMAKE_CURRENT_BINARY_DIR}
     ${CMAKE_CURRENT_SOURCE_DIR}/..
 )
 include_directories(${GPGME_INCLUDE_DIRS})
 
 include(ECMAddTests)
 
 add_executable(mimetreeparsertest mimetreeparsertest.cpp)
 add_gpg_crypto_test(mimetreeparsertest mimetreeparsertest)
 target_link_libraries(mimetreeparsertest PUBLIC
     Qt::Test
     mimetreeparserprivate
     KPim6::MimeTreeParserCore
 )
 
 add_executable(gpgerrortest gpgerrortest.cpp)
 target_link_libraries(gpgerrortest PUBLIC
     Qt::Test
     mimetreeparserprivate
     KPim6::MimeTreeParserCore
 )
 add_test(NAME gpgerrortest COMMAND $<TARGET_FILE:gpgerrortest>)
 
-## Crypto test
-add_executable(cryptotest cryptotest.cpp)
-add_gpg_crypto_test(cryptotest cryptotest)
-target_link_libraries(cryptotest
-    Qt::Test
-    mimetreeparserprivate
-    KPim6::MimeTreeParserCore
-)
-
-## MailTemplates test
-add_executable(mailtemplatetest mailtemplatetest.cpp)
-add_gpg_crypto_test(mailtemplatetest mailtemplatetest)
-target_link_libraries(mailtemplatetest
-    Qt::Test
-    mimetreeparserprivate
-    KPim6::MimeTreeParserCore
-)
-
 ## PartModel test
 add_executable(partmodeltest partmodeltest.cpp)
 add_gpg_crypto_test(partmodeltest partmodeltest)
 target_link_libraries(partmodeltest
     Qt::Test
     mimetreeparserprivate
     KPim6::MimeTreeParserCore
 )
diff --git a/src/core/autotests/cryptohelpertest.cpp b/src/core/autotests/cryptohelpertest.cpp
index c6f5887..588b9d4 100644
--- a/src/core/autotests/cryptohelpertest.cpp
+++ b/src/core/autotests/cryptohelpertest.cpp
@@ -1,135 +1,135 @@
-// Copyright 2015 Sandro Knauß <knauss@kolabsys.com>
+// SPDX-FileCopyrightText: 2015 Sandro Knauß <knauss@kolabsys.com>
 // SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
 
 #include "cryptohelpertest.h"
 
 #include "cryptohelper.h"
 
 #include <QTest>
 
 using namespace MimeTreeParser;
 
 void CryptoHelperTest::testPMFDEmpty()
 {
     QCOMPARE(prepareMessageForDecryption("").count(), 0);
 }
 
 void CryptoHelperTest::testPMFDWithNoPGPBlock()
 {
     const QByteArray text = "testblabla";
-    const QList<Block> blocks = prepareMessageForDecryption(text);
+    const QVector<Block> blocks = prepareMessageForDecryption(text);
     QCOMPARE(blocks.count(), 1);
     QCOMPARE(blocks[0].text(), text);
     QCOMPARE(blocks[0].type(), NoPgpBlock);
 }
 
 void CryptoHelperTest::testPGPBlockType()
 {
     const QString blockText = QStringLiteral("text");
     const QString preString = QStringLiteral("before\n");
     for (int i = 1; i <= PrivateKeyBlock; ++i) {
         QString name;
         switch (i) {
         case PgpMessageBlock:
             name = QStringLiteral("MESSAGE");
             break;
         case MultiPgpMessageBlock:
             name = QStringLiteral("MESSAGE PART");
             break;
         case SignatureBlock:
             name = QStringLiteral("SIGNATURE");
             break;
         case ClearsignedBlock:
             name = QStringLiteral("SIGNED MESSAGE");
             break;
         case PublicKeyBlock:
             name = QStringLiteral("PUBLIC KEY BLOCK");
             break;
         case PrivateKeyBlock:
             name = QStringLiteral("PRIVATE KEY BLOCK");
             break;
         }
-        QString text = QLatin1String("-----BEGIN PGP ") + name + QLatin1String("\n") + blockText;
-        QList<Block> blocks = prepareMessageForDecryption(preString.toLatin1() + text.toLatin1());
+        QString text = QLatin1String("-----BEGIN PGP ") + name + QLatin1Char('\n') + blockText;
+        QVector<Block> blocks = prepareMessageForDecryption(preString.toLatin1() + text.toLatin1());
         QCOMPARE(blocks.count(), 1);
         QCOMPARE(blocks[0].type(), UnknownBlock);
 
-        text += QLatin1String("\n-----END PGP ") + name + QLatin1String("\n");
+        text += QLatin1String("\n-----END PGP ") + name + QLatin1Char('\n');
         blocks = prepareMessageForDecryption(preString.toLatin1() + text.toLatin1());
         QCOMPARE(blocks.count(), 2);
         QCOMPARE(blocks[1].text(), text.toLatin1());
         QCOMPARE(blocks[1].type(), static_cast<PGPBlockType>(i));
     }
 }
 
 void CryptoHelperTest::testDeterminePGPBlockType()
 {
     const QString blockText = QStringLiteral("text");
     for (int i = 1; i <= PrivateKeyBlock; ++i) {
         QString name;
         switch (i) {
         case PgpMessageBlock:
             name = QStringLiteral("MESSAGE");
             break;
         case MultiPgpMessageBlock:
             name = QStringLiteral("MESSAGE PART");
             break;
         case SignatureBlock:
             name = QStringLiteral("SIGNATURE");
             break;
         case ClearsignedBlock:
             name = QStringLiteral("SIGNED MESSAGE");
             break;
         case PublicKeyBlock:
             name = QStringLiteral("PUBLIC KEY BLOCK");
             break;
         case PrivateKeyBlock:
             name = QStringLiteral("PRIVATE KEY BLOCK");
             break;
         }
-        const QString text = QLatin1String("-----BEGIN PGP ") + name + QLatin1String("\n") + blockText + QLatin1String("\n");
+        const QString text = QLatin1String("-----BEGIN PGP ") + name + QLatin1Char('\n') + blockText + QLatin1Char('\n');
         const Block block = Block(text.toLatin1());
         QCOMPARE(block.text(), text.toLatin1());
         QCOMPARE(block.type(), static_cast<PGPBlockType>(i));
     }
 }
 
 void CryptoHelperTest::testEmbededPGPBlock()
 {
     const QByteArray text = QByteArray("before\n-----BEGIN PGP MESSAGE-----\ncrypted - you see :)\n-----END PGP MESSAGE-----\nafter");
-    const QList<Block> blocks = prepareMessageForDecryption(text);
+    const QVector<Block> blocks = prepareMessageForDecryption(text);
     QCOMPARE(blocks.count(), 3);
     QCOMPARE(blocks[0].text(), QByteArray("before\n"));
     QCOMPARE(blocks[1].text(), QByteArray("-----BEGIN PGP MESSAGE-----\ncrypted - you see :)\n-----END PGP MESSAGE-----\n"));
     QCOMPARE(blocks[2].text(), QByteArray("after"));
 }
 
 void CryptoHelperTest::testClearSignedMessage()
 {
     const QByteArray text = QByteArray(
         "before\n-----BEGIN PGP SIGNED MESSAGE-----\nsigned content\n-----BEGIN PGP SIGNATURE-----\nfancy signature\n-----END PGP SIGNATURE-----\nafter");
-    const QList<Block> blocks = prepareMessageForDecryption(text);
+    const QVector<Block> blocks = prepareMessageForDecryption(text);
     QCOMPARE(blocks.count(), 3);
     QCOMPARE(blocks[0].text(), QByteArray("before\n"));
     QCOMPARE(blocks[1].text(),
              QByteArray("-----BEGIN PGP SIGNED MESSAGE-----\nsigned content\n-----BEGIN PGP SIGNATURE-----\nfancy signature\n-----END PGP SIGNATURE-----\n"));
     QCOMPARE(blocks[2].text(), QByteArray("after"));
 }
 
 void CryptoHelperTest::testMultipleBlockMessage()
 {
     const QByteArray text = QByteArray(
         "before\n-----BEGIN PGP SIGNED MESSAGE-----\nsigned content\n-----BEGIN PGP SIGNATURE-----\nfancy signature\n-----END PGP "
         "SIGNATURE-----\nafter\n-----BEGIN PGP MESSAGE-----\ncrypted - you see :)\n-----END PGP MESSAGE-----\n");
-    const QList<Block> blocks = prepareMessageForDecryption(text);
+    const QVector<Block> blocks = prepareMessageForDecryption(text);
     QCOMPARE(blocks.count(), 4);
     QCOMPARE(blocks[0].text(), QByteArray("before\n"));
     QCOMPARE(blocks[1].text(),
              QByteArray("-----BEGIN PGP SIGNED MESSAGE-----\nsigned content\n-----BEGIN PGP SIGNATURE-----\nfancy signature\n-----END PGP SIGNATURE-----\n"));
     QCOMPARE(blocks[2].text(), QByteArray("after\n"));
     QCOMPARE(blocks[3].text(), QByteArray("-----BEGIN PGP MESSAGE-----\ncrypted - you see :)\n-----END PGP MESSAGE-----\n"));
 }
 
 QTEST_APPLESS_MAIN(CryptoHelperTest)
 
 #include "moc_cryptohelpertest.cpp"
diff --git a/src/core/autotests/cryptohelpertest.h b/src/core/autotests/cryptohelpertest.h
index 8d45c59..e6faf2e 100644
--- a/src/core/autotests/cryptohelpertest.h
+++ b/src/core/autotests/cryptohelpertest.h
@@ -1,25 +1,23 @@
-// Copyright 2009 Thomas McGuire <mcguire@kde.org>
+// SPDX-FileCopyrightText: 2009 Thomas McGuire <mcguire@kde.org>
 // SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
 
 #pragma once
 
 #include <QObject>
 
 namespace MimeTreeParser
 {
-
 class CryptoHelperTest : public QObject
 {
     Q_OBJECT
 
 private Q_SLOTS:
     void testPMFDEmpty();
     void testPMFDWithNoPGPBlock();
     void testPGPBlockType();
     void testDeterminePGPBlockType();
     void testEmbededPGPBlock();
     void testClearSignedMessage();
     void testMultipleBlockMessage();
 };
-
 }
diff --git a/src/core/autotests/cryptotest.cpp b/src/core/autotests/cryptotest.cpp
deleted file mode 100644
index 96ca9b4..0000000
--- a/src/core/autotests/cryptotest.cpp
+++ /dev/null
@@ -1,233 +0,0 @@
-// SPDX-FileCopyrightText: 2022 Christian Mollekopf <christian@mkpf.ch>
-// SPDX-License-Identifier: LGPL-2.0-or-later
-
-#include <crypto.h>
-
-#include <QDebug>
-#include <QTest>
-
-class CryptoTest : public QObject
-{
-    Q_OBJECT
-
-private Q_SLOTS:
-
-    void testDecrypt()
-    {
-        QByteArray input =
-            "-----BEGIN PGP "
-            "MESSAGE-----\n\nhQEMAwzOQ1qnzNo7AQgA3kD1WyRdQawpduoJ3J9h3SpSC7YiNqU7aiyTMUGAdbGO\nBMhIzPdEkai9P486Wpg5h+ywmQrk3KoH/GioRjwvIaeNZY/cmxetT0/"
-            "ig5rrnqxM\nj63vFFbCbE6kSeDbvYqF5mL3XH+TqpZRW5ApPSgkr7jMDOK7k1eF5A5ey84LYFny\nKy63LGy5KEQk7E1cMLZOHAZnorcm7Lh3RVWgPj+"
-            "DRDowMn3yVdFOpT5bQ66zAIkc\nBs9IWuq0lMxGsdfRv5wlzUqZJGge3oT7tkZhI6D56MLIjqg7SurQMiWrn6wh51Sr\nR7W9N6lHyrKrffP2VjFwPPK1/"
-            "Vjd0Am4gTPkf+GcJ4UBDAPKpRg2CPD7UAEIAJuC\n8s2uGAGF9zgoQdrmL6bInA5JCQiZI+B5Jgg9wQ/dW3idJN9esr1Ff7/"
-            "d8DVuzf1V\nbFydMBqQk5Zkp5FuDhJsfGWK+NPJBUaOKGlGqRPZX+SjP2k1SuDoxvdxvtWYBVOt\nZhq03zczRWo4dxJ++WYqxu6gMlBCO+z84kfhknWyBNeN7+8mmYGNWDo/"
-            "ARWhspO0\nCIOfBCFeqwCpCZLIiCTBjGg98Ed+SGIdjQwq97suh3nANlKFpiNp2+w02H+"
-            "rarMj\nIUkaVrKIGqaKw3X7JxuBcD8gzW2nyw6MKrm4q2iTCXYQb8lpUuvITJmNJFIkTmpt\nDjlDEZdJiNhs0IOIepbS6QETyg97HVDWmL4frclu4QAeF28007HHlg805IAjAZ/x\nwU9P3/"
-            "VH74zZJinBLmXaIe3XidksWHES3H8ay+UtsUafC+4icSZRwplW9MexBNsl\nmJ4pfrHtAf+t+Tk0/BbuanSbL6OGA1wG7GVEfj39rsc4vYgSS/"
-            "NLI5njq55AXFVG\nsyeplNt2FNw3Ii6V3NnEkvlKcmj8sVnGCAIIeaG9yiAJ5qOsHP37sk3TAduKRGSE\nd8Ldty4mBftkTPyOG8eMr28XCldnhqnNWbcP9t2maKAyQ4bjv15Erx+"
-            "1AfXXGtVq\n3PsVsUN2YQIib7VLBjOYzW9jysQWFFuE26sE2oH4of34E0jD6GV3d6Ng8gTtpIhO\nBWePihWtHdBGsNNoYrp19IXX594hayaF+"
-            "WV8rpaBS0KxVoEHZhFusyvxcDiQnsO7\n3QPVu3btkkte8Hq9KtoFVeFm91M9fii3m3vFBsPu7weMm2zBCxTTRdLd5X+"
-            "yEYah\nn8tKnUUFHFIcgdZ3FIoQmIJrrYtcjLnvXeDdB2F6HX7z9KMQ5RzKBZcCMnVViKxl\nPIF3bikUzhtg55BGNyiAu2sz1wLzOoERsb38GN3UK4qinFVXLHxXhcdpEXKocb+"
-            "k\nPyRTykAunux+J5+klASl/k85s8gNvMH1CijdNseQEqLlsISERu+zxyFPPit6/"
-            "tP6\nwDyvhjHcGV2Jpw5T01n5aJpKklbGb9qUBkzlfayc03ebtqPzBTwG8NEzu9rlZQpr\nldbYUvXYBHSOqk2Z403I0PWR5DlcasFgciHFQw9PODRNK+"
-            "OVY46btfyBXABlzYTH\nOMfnHd8HoCOKCp29+ugK2G8y91JNd4M8B2xI1zACFB3hlDoc7K4h85Yi0cCYZahX\nOUWZxSZjfl8X793RT5h+2BE+0bRGLOJIGhAxe+JKUC7O5njXBc3O5/"
-            "ocfLif2t4y\neYJmu/w46hJUPxYk4Poe3Oppcim1hKHI7DVSJeJydBRqJgDzRzQ/1u7dD15z69/0\nrbexuTVmwmXN695s/V0=\n=1o3V\n-----END PGP MESSAGE-----\n";
-
-        QByteArray output;
-        const auto decryptResult = Crypto::decrypt(Crypto::OpenPGP, input, output);
-
-        const QByteArray expectedOutput =
-            "Content-Type: multipart/mixed; boundary=\"HKEnEQX5LVRrGxcPEIzBMsyPh2G25GeZZ\";\n protected-headers=\"v1\"\nFrom: test1 <test1@kolab.org>\nTo: "
-            "test@kolab.org\nMessage-ID: <a85660b4-6fdf-9d74-ad1c-e6899f57e4b0@kolab.org>\nSubject: "
-            "enc+signed\n\n--HKEnEQX5LVRrGxcPEIzBMsyPh2G25GeZZ\nContent-Type: text/rfc822-headers; protected-headers=\"v1\"\nContent-Disposition: "
-            "inline\n\nFrom: test1 <test1@kolab.org>\nTo: test@kolab.org\nSubject: enc+signed\n\n--HKEnEQX5LVRrGxcPEIzBMsyPh2G25GeZZ\nContent-Type: "
-            "text/plain; charset=utf-8\nContent-Transfer-Encoding: quoted-printable\nContent-Language: en-US\n\ntest\n\n--=20\nThis is a HTML "
-            "signature.\n\n\n--HKEnEQX5LVRrGxcPEIzBMsyPh2G25GeZZ--\n";
-        QCOMPARE(output, expectedOutput);
-        QCOMPARE(decryptResult.recipients.size(), 2);
-        QCOMPARE(decryptResult.recipients[0].keyId, QByteArray{"0CCE435AA7CCDA3B"});
-        QCOMPARE(decryptResult.recipients[1].keyId, QByteArray{"CAA5183608F0FB50"});
-    }
-
-    void testDecryptAndVerify()
-    {
-        QByteArray input =
-            "-----BEGIN PGP "
-            "MESSAGE-----\n\nhQEMAwzOQ1qnzNo7AQgA3kD1WyRdQawpduoJ3J9h3SpSC7YiNqU7aiyTMUGAdbGO\nBMhIzPdEkai9P486Wpg5h+ywmQrk3KoH/GioRjwvIaeNZY/cmxetT0/"
-            "ig5rrnqxM\nj63vFFbCbE6kSeDbvYqF5mL3XH+TqpZRW5ApPSgkr7jMDOK7k1eF5A5ey84LYFny\nKy63LGy5KEQk7E1cMLZOHAZnorcm7Lh3RVWgPj+"
-            "DRDowMn3yVdFOpT5bQ66zAIkc\nBs9IWuq0lMxGsdfRv5wlzUqZJGge3oT7tkZhI6D56MLIjqg7SurQMiWrn6wh51Sr\nR7W9N6lHyrKrffP2VjFwPPK1/"
-            "Vjd0Am4gTPkf+GcJ4UBDAPKpRg2CPD7UAEIAJuC\n8s2uGAGF9zgoQdrmL6bInA5JCQiZI+B5Jgg9wQ/dW3idJN9esr1Ff7/"
-            "d8DVuzf1V\nbFydMBqQk5Zkp5FuDhJsfGWK+NPJBUaOKGlGqRPZX+SjP2k1SuDoxvdxvtWYBVOt\nZhq03zczRWo4dxJ++WYqxu6gMlBCO+z84kfhknWyBNeN7+8mmYGNWDo/"
-            "ARWhspO0\nCIOfBCFeqwCpCZLIiCTBjGg98Ed+SGIdjQwq97suh3nANlKFpiNp2+w02H+"
-            "rarMj\nIUkaVrKIGqaKw3X7JxuBcD8gzW2nyw6MKrm4q2iTCXYQb8lpUuvITJmNJFIkTmpt\nDjlDEZdJiNhs0IOIepbS6QETyg97HVDWmL4frclu4QAeF28007HHlg805IAjAZ/x\nwU9P3/"
-            "VH74zZJinBLmXaIe3XidksWHES3H8ay+UtsUafC+4icSZRwplW9MexBNsl\nmJ4pfrHtAf+t+Tk0/BbuanSbL6OGA1wG7GVEfj39rsc4vYgSS/"
-            "NLI5njq55AXFVG\nsyeplNt2FNw3Ii6V3NnEkvlKcmj8sVnGCAIIeaG9yiAJ5qOsHP37sk3TAduKRGSE\nd8Ldty4mBftkTPyOG8eMr28XCldnhqnNWbcP9t2maKAyQ4bjv15Erx+"
-            "1AfXXGtVq\n3PsVsUN2YQIib7VLBjOYzW9jysQWFFuE26sE2oH4of34E0jD6GV3d6Ng8gTtpIhO\nBWePihWtHdBGsNNoYrp19IXX594hayaF+"
-            "WV8rpaBS0KxVoEHZhFusyvxcDiQnsO7\n3QPVu3btkkte8Hq9KtoFVeFm91M9fii3m3vFBsPu7weMm2zBCxTTRdLd5X+"
-            "yEYah\nn8tKnUUFHFIcgdZ3FIoQmIJrrYtcjLnvXeDdB2F6HX7z9KMQ5RzKBZcCMnVViKxl\nPIF3bikUzhtg55BGNyiAu2sz1wLzOoERsb38GN3UK4qinFVXLHxXhcdpEXKocb+"
-            "k\nPyRTykAunux+J5+klASl/k85s8gNvMH1CijdNseQEqLlsISERu+zxyFPPit6/"
-            "tP6\nwDyvhjHcGV2Jpw5T01n5aJpKklbGb9qUBkzlfayc03ebtqPzBTwG8NEzu9rlZQpr\nldbYUvXYBHSOqk2Z403I0PWR5DlcasFgciHFQw9PODRNK+"
-            "OVY46btfyBXABlzYTH\nOMfnHd8HoCOKCp29+ugK2G8y91JNd4M8B2xI1zACFB3hlDoc7K4h85Yi0cCYZahX\nOUWZxSZjfl8X793RT5h+2BE+0bRGLOJIGhAxe+JKUC7O5njXBc3O5/"
-            "ocfLif2t4y\neYJmu/w46hJUPxYk4Poe3Oppcim1hKHI7DVSJeJydBRqJgDzRzQ/1u7dD15z69/0\nrbexuTVmwmXN695s/V0=\n=1o3V\n-----END PGP MESSAGE-----\n";
-
-        QByteArray output;
-
-        Crypto::DecryptionResult decryptResult;
-        Crypto::VerificationResult verifyResult;
-        std::tie(decryptResult, verifyResult) = Crypto::decryptAndVerify(Crypto::OpenPGP, input, output);
-
-        const QByteArray expectedOutput =
-            "Content-Type: multipart/mixed; boundary=\"HKEnEQX5LVRrGxcPEIzBMsyPh2G25GeZZ\";\n protected-headers=\"v1\"\nFrom: test1 <test1@kolab.org>\nTo: "
-            "test@kolab.org\nMessage-ID: <a85660b4-6fdf-9d74-ad1c-e6899f57e4b0@kolab.org>\nSubject: "
-            "enc+signed\n\n--HKEnEQX5LVRrGxcPEIzBMsyPh2G25GeZZ\nContent-Type: text/rfc822-headers; protected-headers=\"v1\"\nContent-Disposition: "
-            "inline\n\nFrom: test1 <test1@kolab.org>\nTo: test@kolab.org\nSubject: enc+signed\n\n--HKEnEQX5LVRrGxcPEIzBMsyPh2G25GeZZ\nContent-Type: "
-            "text/plain; charset=utf-8\nContent-Transfer-Encoding: quoted-printable\nContent-Language: en-US\n\ntest\n\n--=20\nThis is a HTML "
-            "signature.\n\n\n--HKEnEQX5LVRrGxcPEIzBMsyPh2G25GeZZ--\n";
-
-        QCOMPARE(output, expectedOutput);
-        QCOMPARE(verifyResult.signatures.size(), 1);
-        // QCOMPARE(verifyResult.signatures[0].fingerprint, QByteArray{"CBD116485DB9560CA3CD91E02E3B7787B1B75920"});
-        QVERIFY(verifyResult.signatures[0].fingerprint.contains(QByteArray{"2E3B7787B1B75920"}));
-        QCOMPARE(decryptResult.recipients.size(), 2);
-        QCOMPARE(decryptResult.recipients[0].keyId, QByteArray{"0CCE435AA7CCDA3B"});
-        QCOMPARE(decryptResult.recipients[1].keyId, QByteArray{"CAA5183608F0FB50"});
-    }
-
-    void testDecryptAndVerifyKeyMissing()
-    {
-        QByteArray input =
-            "-----BEGIN PGP "
-            "MESSAGE-----\n\nhQEMAwzOQ1qnzNo7AQgA3kD1WyRdQawpduoJ3J9h3SpSC7YiNqU7aiyTMUGAdbGO\nBMhIzPdEkai9P486Wpg5h+ywmQrk3KoH/GioRjwvIaeNZY/cmxetT0/"
-            "ig5rrnqxM\nj63vFFbCbE6kSeDbvYqF5mL3XH+TqpZRW5ApPSgkr7jMDOK7k1eF5A5ey84LYFny\nKy63LGy5KEQk7E1cMLZOHAZnorcm7Lh3RVWgPj+"
-            "DRDowMn3yVdFOpT5bQ66zAIkc\nBs9IWuq0lMxGsdfRv5wlzUqZJGge3oT7tkZhI6D56MLIjqg7SurQMiWrn6wh51Sr\nR7W9N6lHyrKrffP2VjFwPPK1/"
-            "Vjd0Am4gTPkf+GcJ4UBDAPKpRg2CPD7UAEIAJuC\n8s2uGAGF9zgoQdrmL6bInA5JCQiZI+B5Jgg9wQ/dW3idJN9esr1Ff7/"
-            "d8DVuzf1V\nbFydMBqQk5Zkp5FuDhJsfGWK+NPJBUaOKGlGqRPZX+SjP2k1SuDoxvdxvtWYBVOt\nZhq03zczRWo4dxJ++WYqxu6gMlBCO+z84kfhknWyBNeN7+8mmYGNWDo/"
-            "ARWhspO0\nCIOfBCFeqwCpCZLIiCTBjGg98Ed+SGIdjQwq97suh3nANlKFpiNp2+w02H+"
-            "rarMj\nIUkaVrKIGqaKw3X7JxuBcD8gzW2nyw6MKrm4q2iTCXYQb8lpUuvITJmNJFIkTmpt\nDjlDEZdJiNhs0IOIepbS6QETyg97HVDWmL4frclu4QAeF28007HHlg805IAjAZ/x\nwU9P3/"
-            "VH74zZJinBLmXaIe3XidksWHES3H8ay+UtsUafC+4icSZRwplW9MexBNsl\nmJ4pfrHtAf+t+Tk0/BbuanSbL6OGA1wG7GVEfj39rsc4vYgSS/"
-            "NLI5njq55AXFVG\nsyeplNt2FNw3Ii6V3NnEkvlKcmj8sVnGCAIIeaG9yiAJ5qOsHP37sk3TAduKRGSE\nd8Ldty4mBftkTPyOG8eMr28XCldnhqnNWbcP9t2maKAyQ4bjv15Erx+"
-            "1AfXXGtVq\n3PsVsUN2YQIib7VLBjOYzW9jysQWFFuE26sE2oH4of34E0jD6GV3d6Ng8gTtpIhO\nBWePihWtHdBGsNNoYrp19IXX594hayaF+"
-            "WV8rpaBS0KxVoEHZhFusyvxcDiQnsO7\n3QPVu3btkkte8Hq9KtoFVeFm91M9fii3m3vFBsPu7weMm2zBCxTTRdLd5X+"
-            "yEYah\nn8tKnUUFHFIcgdZ3FIoQmIJrrYtcjLnvXeDdB2F6HX7z9KMQ5RzKBZcCMnVViKxl\nPIF3bikUzhtg55BGNyiAu2sz1wLzOoERsb38GN3UK4qinFVXLHxXhcdpEXKocb+"
-            "k\nPyRTykAunux+J5+klASl/k85s8gNvMH1CijdNseQEqLlsISERu+zxyFPPit6/"
-            "tP6\nwDyvhjHcGV2Jpw5T01n5aJpKklbGb9qUBkzlfayc03ebtqPzBTwG8NEzu9rlZQpr\nldbYUvXYBHSOqk2Z403I0PWR5DlcasFgciHFQw9PODRNK+"
-            "OVY46btfyBXABlzYTH\nOMfnHd8HoCOKCp29+ugK2G8y91JNd4M8B2xI1zACFB3hlDoc7K4h85Yi0cCYZahX\nOUWZxSZjfl8X793RT5h+2BE+0bRGLOJIGhAxe+JKUC7O5njXBc3O5/"
-            "ocfLif2t4y\neYJmu/w46hJUPxYk4Poe3Oppcim1hKHI7DVSJeJydBRqJgDzRzQ/1u7dD15z69/0\nrbexuTVmwmXN695s/V0=\n=1o3V\n-----END PGP MESSAGE-----\n";
-
-        QByteArray output;
-
-        Crypto::DecryptionResult decryptResult;
-        Crypto::VerificationResult verifyResult;
-        std::tie(decryptResult, verifyResult) = Crypto::decryptAndVerify(Crypto::OpenPGP, input, output);
-
-        const QByteArray expectedOutput =
-            "Content-Type: multipart/mixed; boundary=\"HKEnEQX5LVRrGxcPEIzBMsyPh2G25GeZZ\";\n protected-headers=\"v1\"\nFrom: test1 <test1@kolab.org>\nTo: "
-            "test@kolab.org\nMessage-ID: <a85660b4-6fdf-9d74-ad1c-e6899f57e4b0@kolab.org>\nSubject: "
-            "enc+signed\n\n--HKEnEQX5LVRrGxcPEIzBMsyPh2G25GeZZ\nContent-Type: text/rfc822-headers; protected-headers=\"v1\"\nContent-Disposition: "
-            "inline\n\nFrom: test1 <test1@kolab.org>\nTo: test@kolab.org\nSubject: enc+signed\n\n--HKEnEQX5LVRrGxcPEIzBMsyPh2G25GeZZ\nContent-Type: "
-            "text/plain; charset=utf-8\nContent-Transfer-Encoding: quoted-printable\nContent-Language: en-US\n\ntest\n\n--=20\nThis is a HTML "
-            "signature.\n\n\n--HKEnEQX5LVRrGxcPEIzBMsyPh2G25GeZZ--\n";
-
-        QCOMPARE(output, expectedOutput);
-        QCOMPARE(verifyResult.signatures.size(), 1);
-        QCOMPARE(verifyResult.signatures[0].result, Crypto::Signature::KeyNotFound);
-    }
-
-    void testDecryptAndVerifyCRLFEncryptedWithSignature()
-    {
-        // This was the original input that seems to have some padding issues (which could stem from some crlf "normalization" we're doing.
-        //  QByteArray input = "-----BEGIN PGP
-        //  MESSAGE-----\n\nhQEMAwzOQ1qnzNo7AQgAgXLGaohf4ZPJVDkpmpNsyXL/nccd+SyY3/deHQ8d8vp9\nYe9Hr30Yz65+CAI7JCHKjIOaXjw1Nf1qqEDSyghKR0c16dLBGK37GlqOLaqScifZ\n/bC5WQu4V1a+dv1qnNOh3JNp5ynVpg22b5XaBggpAGCxCSrUsWWkRxTR+kBuPdn8\nEPMOlN3xKU1LFQfI+a3HMGpWo3PokVb4nrtuuwi261woSgKUSYjG86MJF1E28y+g\nbMC6rmRV+Jp0wpEmr7aogx4gELe17tglD41oLvNL9yZeEh/V8cMnnEDbO/oG+xba\nUjyM73V+TO2kXk0CTqItVbx6Q7kNWR/DfaJqGnzcy9KMAT1B8EpVCK/nN81mF8ia\n4KFKQ1OGHhcQ2tT5ZXs6m1vJ5/sz/6g0n0CMtSsWUSvPzpM5F+LK+B7dzOeJEQee\n3/S0wUFYpbAh1PyMPYobNLsEQCGtQ1PhsUXM7t1ai6jfM4k/lvbnIzUM11Q6vsnR\nLWS77CELfcW9WlQ755sNcwo3a/WySyU1C1gZdLw=Fijx\n-----END
-        //  PGP MESSAGE-----\n";
-
-        // This is what I got from reencrypting the output with gpgme.
-        QByteArray input =
-            "-----BEGIN PGP "
-            "MESSAGE-----\n\nhQEMAwzOQ1qnzNo7AQf/e+AvN9SH/nCo8VNWEhw8w9zMFrC+LK0HmXGedmwVDS+p\nX/"
-            "4dBFC4ftR0jvzKyiBpjtAisD7zfNIcPd1rkfjtjf1kvksuDej+"
-            "rVBZ8Mupa7Rl\ng9tCGSwxNmKU9v3BzSVXV9z4P2PHYpOn68WOnDZZqUVdvbZ6WuowAzBjiECo59cZ\nZmOp5kO4DGEE7vJ3QSR58VUYhM7kUcUYOf68syZiGOO7gC075ZLXIc3l+"
-            "rD4fYtJ\nAsGsfVXmbxB58aVuNZllK/CshfjddPbVRladc4xJ1KsnC/M+mOClUM2WJ7Ghw+V6\nkta1xPC3cjc1j7rPm1LH2ESzqvhkhay6ATaBNJ3rd9J/AYU0/"
-            "WFF+HQryulNxiCg\nzkN0kfC0lJ8GOKtlwgIcY6AsyStPR13fkXT0xRbB+zvms5nHmbtUvtsjeOsw7BJL\ndLBq5P+0xiCZR+/gBhkx/"
-            "OqBFtel3qktopoDRi3rnVMJGMVcKiAFmf3612HU5tYb\n8iolmHNdKSksNbDKYgzLnQ==\n=YzFH\n-----END PGP MESSAGE-----\n";
-
-        QByteArray output;
-
-        Crypto::DecryptionResult decryptResult;
-        Crypto::VerificationResult verifyResult;
-        std::tie(decryptResult, verifyResult) = Crypto::decryptAndVerify(Crypto::OpenPGP, input, output);
-
-        const QByteArray expectedOutput = "CRLF file\r\n\r\n-- \r\nThis is a signature\r\nWith two lines\r\n\r\nAand another line\r\n";
-
-        // QEXPECT_FAIL("", "either librnp or gpgme fails to handle CRLF properly", Continue);
-        QCOMPARE(output, expectedOutput);
-        QCOMPARE(verifyResult.signatures.size(), 0);
-        QCOMPARE(decryptResult.recipients.size(), 1);
-
-        // qWarning() << decryptResult.recipients[0].keyId;
-        // const auto keys = Crypto::findKeys({{decryptResult.recipients[0].keyId}});
-        // QCOMPARE(keys.size(), 1);
-        // auto result =  Crypto::signAndEncrypt(expectedOutput, keys, {});
-        // qWarning() << result.value();
-    }
-
-    void testVerifyDetachedSignature()
-    {
-        QByteArray signature =
-            "-----BEGIN PGP SIGNATURE-----\nVersion: GnuPG v2.0.15 "
-            "(GNU/Linux)\n\niQEcBAABAgAGBQJMh7F5AAoJEI2YYMWPJG3mOB0IALeHfwg8u7wK0tDKtKqxQSqC\n2Bbk4pTLuLw/VniQNauDG+kc1eUc5RJk/"
-            "R31aB1ysiQCV5Q8ucI8c9vCDDMbd+s4\nt2bZUEzMpXrw/"
-            "aFiHgYGXFAY+tpqZqDGNVRNHWsPYJKtx8cci9E5DLnBJcVXVqib\n3iiHlr9AQOok3PUmpPk1a61q2L0kk8wqRenC0yZXNw5qFn5WW/"
-            "hFeCOfYB+t+s5N\nIuE6ihFCJIlvGborrvl6VgPJTCyUQ3XgI90vS6ABN8TFlCNr3grXOWUePc2a20or\nxFgh38cnSR64WJajU5K1nUL9/RgfIcs+PvyHuJaRf/"
-            "iUFkj1jiMEuaSi9jVFco0=\n=bArV\n-----END PGP SIGNATURE-----\n";
-        QByteArray signedData =
-            "Content-Type: multipart/mixed; boundary=\"nextPart1512490.WQBKYaOrt8\"\r\nContent-Transfer-Encoding: "
-            "7Bit\r\n\r\n\r\n--nextPart1512490.WQBKYaOrt8\r\nContent-Transfer-Encoding: 7Bit\r\nContent-Type: text/plain; charset=\"us-ascii\"\r\n\r\nbla bla "
-            "bla\r\n--nextPart1512490.WQBKYaOrt8\r\nContent-Type: message/rfc822\r\nContent-Disposition: inline; filename=\"forwarded "
-            "message\"\r\nContent-Description: OpenPGP Test <test@kolab.org>: OpenPGP signed and encrypted\r\n\r\nFrom: OpenPGP Test <test@kolab.org>\r\nTo: "
-            "test@kolab.org\r\nSubject: OpenPGP signed and encrypted\r\nDate: Tue, 07 Sep 2010 18:08:44 +0200\r\nUser-Agent: KMail/4.6 pre "
-            "(Linux/2.6.34-rc2-2-default; KDE/4.5.60; x86_64; ; )\r\nMIME-Version: 1.0\r\nContent-Type: multipart/encrypted; "
-            "boundary=\"nextPart25203163.0xtB501Z4V\"; protocol=\"application/pgp-encrypted\"\r\nContent-Transfer-Encoding: "
-            "7Bit\r\n\r\n\r\n--nextPart25203163.0xtB501Z4V\r\nContent-Type: application/pgp-encrypted\r\nContent-Disposition: attachment\r\n\r\nVersion: "
-            "1\r\n--nextPart25203163.0xtB501Z4V\r\nContent-Type: application/octet-stream\r\nContent-Disposition: inline; "
-            "filename=\"msg.asc\"\r\n\r\n-----BEGIN PGP MESSAGE-----\r\nVersion: GnuPG v2.0.15 "
-            "(GNU/Linux)\r\n\r\nhQEMAwzOQ1qnzNo7AQf7BFYWaGiCTGtXY59bSh3LCXNnWZejblYALxIUNXOFEXbm\r\ny/YA95FmQsy3U5HRCAJV/"
-            "DY1PEaJz1RTm9bcdIpDC3Ab2YzSwmOwV5fcoUOB2df4\r\nKjX19Q+2F3JxpPQ0N1gHf4dKfIu19LH+CKeFzUN13aJs5J4A5wlj+"
-            "NjJikxzmxDS\r\nkDtNYndynPmo9DJQcsUFw3gpvx5HaHvx1cT4mAB2M5cd2l+vN1jYbaWb0x5Zq41z\r\nmRNI89aPieC3rcM2289m68fGloNbYvi8mZJu5RrI4Tbi/"
-            "D7Rjm1y63lHgVV6AN88\r\nXAzRiedOeF99LoTBulrJdtT8AAgCs8nCetcWpIffdtLpAZiZkzHmYOU7nqGxqpRk\r\nOVeUTrCn9DW2SMmHjaP4IiKnMvzEycu5F4a72+V1LeMIhMSjTRTq+"
-            "ZE2PTaqH59z\r\nQsMn7Nb6GlOICbTptRKNNtyJKO7xXlpT7YtvNKnCyEOkH2XrYH7GvpYCiuQ0/o+7\r\nSxV436ZejiYIg6DQDXJCoa2DXimGp0C10Jh0HwX0BixpoNtwEjkGRYcX6P/"
-            "JzkH0\r\noBood4Ly+Tiu6iVDisrK3AVGYpIzCrKkE9qULTw4R/"
-            "jFKR2tcCqGb7Fxtk2LV7Md\r\n3S+DyOKrvKQ5GNwbp9OE97pwk+Lr1JS3UAvj5f6BR+1PVNcC0i0wWkgwDjPh1eGD\r\nenMQmorE6+N0uHtH2F4fOxo/TbbA3+zhI25kVW3bO03xyUl/"
-            "cmQZeb52nvfOvtOo\r\ngSb2j6bPkzljDMPEzrtJjbFtGHJbPfUQYJgZv9OE2EQIqpg6goIw279alBq6GLIX\r\npkO+dRmztzjcDyhcLxMuQ4cTizel/0J/"
-            "bU7U6lvwHSyZVbT4Ev+opG5K70Hbqbwr\r\nNZcgdWXbSeesxGM/oQaMeSurOevxVl+/"
-            "zrTVAek61aRRd1baAYqgi2pf2V7y4oK3\r\nqkdxzmoFpRdNlfrQW65NZWnHOi9rC9XxANIwnVn3kRcDf+t2K4PrFluI157lXM/"
-            "o\r\nwX91j88fazysbJlQ6TjsApO9ETiPOFEBqouxCTtCZzlUgyVG8jpIjdHWFnagHeXH\r\n+"
-            "lXNdYjxnTWTjTxMOZC9ySMpXkjWdFI1ecxVwu6Ik6RX51rvBJAAXWP75yUjPKJ4\r\nrRi5oQl/VLl0QznO7lvgMPtUwgDVNWO/r7Kn9B387h9fAJZ/"
-            "kWFAEDW2yhAzABqO\r\nrCNKDzBPgfAwCnikCpMoCbOL7SU8BdbzQHD8/Lkv4m0pzliHQ/KkGF710koBzTmF\r\nN7+wk9pwIuvcrEBQj567\r\n=GV0c\r\n-----END PGP "
-            "MESSAGE-----\r\n\r\n--nextPart25203163.0xtB501Z4V--\r\n\r\n--nextPart1512490.WQBKYaOrt8--\r\n";
-
-        const auto result = Crypto::verifyDetachedSignature(Crypto::OpenPGP, signature, signedData);
-        QCOMPARE(result.signatures.size(), 1);
-        QVERIFY(result.signatures[0].fingerprint.contains(QByteArray{"8D9860C58F246DE6"}));
-        QCOMPARE(result.signatures[0].result, Crypto::Signature::Ok);
-    }
-
-    void testVerifyOpaqueSignature()
-    {
-        QByteArray signedData =
-            "-----BEGIN PGP SIGNED MESSAGE-----\nHash: SHA256\n\nohno \xF6\xE4\xFC\n-----BEGIN PGP SIGNATURE-----\nVersion: GnuPG "
-            "v2\n\niQEcBAEBCAAGBQJV3H/vAAoJEI2YYMWPJG3mEZQH/2mbCDa60risTUsomEecasc7\nkIc8Ch+OjZwlEQWKEiFbpLCMVjMwf0oGFcpc/"
-            "dqnIyIqeVvF6Em+v7iqKuyAaihu\n7ZxxC816tDDI7UIpmyWu39McqGB/2hoA/"
-            "q+"
-            "QAMgBiaIuMwYJK9Aw08hXzoCds6O7\nUor2Y6kMSwEiRnTSYvQHdoaZY3F9SFTLPgjvwfSu7scvp7xvH7bAVIqGGfkLjXpP\nOFkDhEqUI7ORwD5cvvzEu57XmbGB7Nj5LRCGcTq6IlaGeN6Pw"
-            "5+hOdd6MQ4iISwy\n870msP9NvktURnfXYC3fYnJaK/eUln7LYCBl/k04Z/3Um6dMYyQGh63oGv/2qxQ=\n=4ctb\n-----END PGP SIGNATURE-----";
-        QByteArray outdata;
-        const auto result = Crypto::verifyOpaqueSignature(Crypto::OpenPGP, signedData, outdata);
-        QCOMPARE(result.signatures.size(), 1);
-        QVERIFY(result.signatures[0].fingerprint.contains(QByteArray{"8D9860C58F246DE6"}));
-        QCOMPARE(result.signatures[0].result, Crypto::Signature::Ok);
-        QCOMPARE(outdata, QByteArray{"ohno \xF6\xE4\xFC\n"});
-    }
-};
-
-QTEST_GUILESS_MAIN(CryptoTest)
-#include "cryptotest.moc"
diff --git a/src/core/autotests/gpgerrortest.cpp b/src/core/autotests/gpgerrortest.cpp
index d55ef3e..6327c8f 100644
--- a/src/core/autotests/gpgerrortest.cpp
+++ b/src/core/autotests/gpgerrortest.cpp
@@ -1,200 +1,200 @@
 // SPDX-FileCopyrightText: 2016 Sandro Knauß <knauss@kolabsystems.com>
 // SPDX-License-Identifier: LGPL-2.0-or-later
 
 #include <MimeTreeParserCore/ObjectTreeParser>
 
 #include <gpgme.h>
 
 #include <QProcess>
 #include <QTest>
 
 QByteArray readMailFromFile(const QString &mailFile)
 {
     QFile file(QLatin1String(MAIL_DATA_DIR) + QLatin1Char('/') + mailFile);
     file.open(QIODevice::ReadOnly);
     Q_ASSERT(file.isOpen());
     return file.readAll();
 }
 
 void killAgent(const QString &dir)
 {
     QProcess proc;
     proc.setProgram(QStringLiteral("gpg-connect-agent"));
     QStringList arguments;
     arguments << QStringLiteral("-S ") << dir + QStringLiteral("/S.gpg-agent");
     proc.start();
     proc.waitForStarted();
     proc.write("KILLAGENT\n");
     proc.write("BYE\n");
     proc.closeWriteChannel();
     proc.waitForFinished();
 }
 
 class GpgErrorTest : public QObject
 {
     Q_OBJECT
 
 private Q_SLOTS:
 
     void testGpgConfiguredCorrectly()
     {
         setEnv("GNUPGHOME", GNUPGHOME);
 
         MimeTreeParser::ObjectTreeParser otp;
         otp.parseObjectTree(readMailFromFile(QStringLiteral("openpgp-inline-charset-encrypted.mbox")));
         otp.print();
         otp.decryptParts();
         otp.print();
         auto partList = otp.collectContentParts();
         QCOMPARE(partList.size(), 1);
         auto part = partList[0];
         QVERIFY(bool(part));
 
         QVERIFY(part->text().startsWith(QStringLiteral("asdasd")));
         QCOMPARE(part->encryptions().size(), 1);
         auto enc = part->encryptions()[0];
         QCOMPARE(enc->error(), MimeTreeParser::MessagePart::NoError);
         // QCOMPARE((int) enc->recipients().size(), 2);
     }
 
     void testNoGPGInstalled_data()
     {
         QTest::addColumn<QString>("mailFileName");
 
         QTest::newRow("openpgp-inline-charset-encrypted") << "openpgp-inline-charset-encrypted.mbox";
         QTest::newRow("openpgp-encrypted-attachment-and-non-encrypted-attachment") << "openpgp-encrypted-attachment-and-non-encrypted-attachment.mbox";
         QTest::newRow("smime-encrypted") << "smime-encrypted.mbox";
     }
 
     void testNoGPGInstalled()
     {
         QFETCH(QString, mailFileName);
 
         setEnv("PATH", "/nonexististing");
         setGpgMEfname("/nonexisting/gpg", "");
 
         MimeTreeParser::ObjectTreeParser otp;
         otp.parseObjectTree(readMailFromFile(mailFileName));
         otp.print();
         otp.decryptParts();
         otp.print();
         auto partList = otp.collectContentParts();
         QCOMPARE(partList.size(), 1);
         auto part = partList[0].dynamicCast<MimeTreeParser::MessagePart>();
         QVERIFY(bool(part));
 
         QCOMPARE(part->encryptions().size(), 1);
         QVERIFY(part->text().isEmpty());
         auto enc = part->encryptions()[0];
         QCOMPARE(enc->error(), MimeTreeParser::MessagePart::NoKeyError);
     }
 
     void testGpgIncorrectGPGHOME_data()
     {
         QTest::addColumn<QString>("mailFileName");
 
         QTest::newRow("openpgp-inline-charset-encrypted") << "openpgp-inline-charset-encrypted.mbox";
         QTest::newRow("openpgp-encrypted-attachment-and-non-encrypted-attachment") << "openpgp-encrypted-attachment-and-non-encrypted-attachment.mbox";
         QTest::newRow("smime-encrypted") << "smime-encrypted.mbox";
     }
 
     void testGpgIncorrectGPGHOME()
     {
         QFETCH(QString, mailFileName);
         setEnv("GNUPGHOME", QByteArray(GNUPGHOME) + QByteArray("noexist"));
 
         MimeTreeParser::ObjectTreeParser otp;
         otp.parseObjectTree(readMailFromFile(mailFileName));
         otp.print();
         otp.decryptParts();
         otp.print();
         auto partList = otp.collectContentParts();
         QCOMPARE(partList.size(), 1);
         auto part = partList[0].dynamicCast<MimeTreeParser::MessagePart>();
         QVERIFY(bool(part));
 
         QCOMPARE(part->encryptions().size(), 1);
         QCOMPARE(part->signatures().size(), 0);
         QVERIFY(part->text().isEmpty());
         auto enc = part->encryptions()[0];
-        QCOMPARE(enc->error(), MimeTreeParser::MessagePart::NoKeyError);
+        QVERIFY(enc->isNoSecKey());
         // QCOMPARE((int) enc->recipients().size(), 2);
     }
 
 public Q_SLOTS:
     void init()
     {
         mResetGpgmeEngine = false;
         mModifiedEnv.clear();
         {
             gpgme_check_version(nullptr);
             gpgme_ctx_t ctx = nullptr;
             gpgme_new(&ctx);
             gpgme_set_protocol(ctx, GPGME_PROTOCOL_OpenPGP);
             gpgme_engine_info_t info = gpgme_ctx_get_engine_info(ctx);
             mGpgmeEngine_fname = info->file_name;
             gpgme_release(ctx);
         }
         mEnv = QProcessEnvironment::systemEnvironment();
         unsetEnv("GNUPGHOME");
     }
 
     void cleanup()
     {
         QCoreApplication::sendPostedEvents();
 
         const QString &gnupghome = QString::fromUtf8(qgetenv("GNUPGHOME"));
         if (!gnupghome.isEmpty()) {
             killAgent(gnupghome);
         }
 
         resetGpgMfname();
         resetEnv();
     }
 
 private:
     void unsetEnv(const QByteArray &name)
     {
         mModifiedEnv << name;
         qunsetenv(name.data());
     }
 
     void setEnv(const QByteArray &name, const QByteArray &value)
     {
         mModifiedEnv << name;
         qputenv(name.data(), value);
     }
 
     void resetEnv()
     {
         for (const auto &i : std::as_const(mModifiedEnv)) {
             const auto env = i.data();
             if (mEnv.contains(QString::fromUtf8(i))) {
                 qputenv(env, mEnv.value(QString::fromUtf8(i)).toUtf8());
             } else {
                 qunsetenv(env);
             }
         }
     }
 
     void resetGpgMfname()
     {
         if (mResetGpgmeEngine) {
             gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP, mGpgmeEngine_fname.data(), nullptr);
         }
     }
 
     void setGpgMEfname(const QByteArray &fname, const QByteArray &homedir)
     {
         mResetGpgmeEngine = true;
         gpgme_set_engine_info(GPGME_PROTOCOL_OpenPGP, fname.data(), homedir.data());
     }
 
     QSet<QByteArray> mModifiedEnv;
     QProcessEnvironment mEnv;
     bool mResetGpgmeEngine;
     QByteArray mGpgmeEngine_fname;
 };
 
 QTEST_GUILESS_MAIN(GpgErrorTest)
 #include "gpgerrortest.moc"
diff --git a/src/core/autotests/mailtemplatetest.cpp b/src/core/autotests/mailtemplatetest.cpp
deleted file mode 100644
index cef3005..0000000
--- a/src/core/autotests/mailtemplatetest.cpp
+++ /dev/null
@@ -1,669 +0,0 @@
-// SPDX-FileCopyrightText: 2017 Christian Mollekopf <mollekopf@kolabsys.com>
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-#include <QDebug>
-#include <QStandardPaths>
-#include <QTest>
-#include <functional>
-
-#include <MimeTreeParserCore/MailCrypto>
-#include <MimeTreeParserCore/MailTemplates>
-
-static KMime::Content *getSubpart(KMime::Content *msg, const QByteArray &mimeType)
-{
-    const auto contents = msg->contents();
-    for (const auto c : contents) {
-        if (c->contentType(false)->mimeType() == mimeType) {
-            return c;
-        }
-    }
-    return nullptr;
-}
-
-static QByteArray readMailFromFile(const QString &mailFile)
-{
-    Q_ASSERT(!QString::fromLatin1(MAILTEMPLATE_DATA_DIR).isEmpty());
-    QFile file(QLatin1String(MAILTEMPLATE_DATA_DIR) + QLatin1Char('/') + mailFile);
-    file.open(QIODevice::ReadOnly);
-    Q_ASSERT(file.isOpen());
-    return file.readAll();
-}
-
-static KMime::Message::Ptr readMail(const QString &mailFile)
-{
-    auto msg = KMime::Message::Ptr::create();
-    msg->setContent(readMailFromFile(mailFile));
-    msg->parse();
-    return msg;
-}
-
-static QString removeFirstLine(const QString &s)
-{
-    return s.mid(s.indexOf(QLatin1String("\n")) + 1);
-}
-
-static QString normalize(const QString &s)
-{
-    auto text = s;
-    text.replace(QLatin1String(">"), QString());
-    text.replace(QLatin1String("\n"), QString());
-    text.replace(QLatin1String("="), QString());
-    text.replace(QLatin1String(" "), QString());
-    return text;
-}
-
-static QString unquote(const QString &s)
-{
-    auto text = s;
-    text.replace(QLatin1String("> "), QString());
-    return text;
-}
-
-class MailTemplateTest : public QObject
-{
-    Q_OBJECT
-
-    bool validate(KMime::Message::Ptr msg)
-    {
-        const auto data = msg->encodedContent();
-        // IMAP compat: The ASCII NUL character, %x00, MUST NOT be used at any time.
-        if (data.contains('\0')) {
-            return false;
-        }
-        return true;
-    }
-
-private Q_SLOTS:
-
-    // Ensures we don't crash on garbage
-    void testEmpty()
-    {
-        MailTemplates::reply(KMime::Message::Ptr::create(), [&](const KMime::Message::Ptr &) {});
-    }
-
-    void testPlainReply()
-    {
-        auto msg = readMail(QLatin1String("plaintext.mbox"));
-        KMime::Message::Ptr result;
-        MailTemplates::reply(msg, [&](const KMime::Message::Ptr &r) {
-            result = r;
-        });
-        QTRY_VERIFY(result);
-        QCOMPARE(normalize(removeFirstLine(QString::fromUtf8(result->body()))), normalize(QString::fromUtf8(msg->body())));
-        QCOMPARE(result->to()->addresses(), QVector<QByteArray>{{"konqi@example.org"}});
-        QCOMPARE(result->subject()->asUnicodeString(), QLatin1String{"RE: A random subject with alternative contenttype"});
-    }
-
-    void testHtmlReply()
-    {
-        auto msg = readMail(QLatin1String("html.mbox"));
-        KMime::Message::Ptr result;
-        MailTemplates::reply(msg, [&](const KMime::Message::Ptr &r) {
-            result = r;
-        });
-        QTRY_VERIFY(result);
-        QCOMPARE(unquote(removeFirstLine(QString::fromUtf8(result->body()))), QLatin1String("HTML text"));
-    }
-
-    void testStripSignatureReply()
-    {
-        auto msg = readMail(QLatin1String("plaintext-with-signature.mbox"));
-        KMime::Message::Ptr result;
-        MailTemplates::reply(msg, [&](const KMime::Message::Ptr &r) {
-            result = r;
-        });
-        QTRY_VERIFY(result);
-        QVERIFY(!result->body().contains("This is a signature"));
-    }
-
-    void testStripSignatureHtmlReply()
-    {
-        auto msg = readMail(QLatin1String("html-with-signature.mbox"));
-        KMime::Message::Ptr result;
-        MailTemplates::reply(msg, [&](const KMime::Message::Ptr &r) {
-            result = r;
-        });
-        QTRY_VERIFY(result);
-        QVERIFY(!result->body().contains("This is a signature"));
-    }
-
-    // We can't test this because we can't commit a CRLF file due to commit hooks.
-    //  void testStripSignatureCrlfReply()
-    //  {
-    //      auto msg = readMail("crlf-with-signature.mbox");
-    //      KMime::Message::Ptr result;
-    //      MailTemplates::reply(msg, [&] (const KMime::Message::Ptr &r) {
-    //          result = r;
-    //      });
-    //      QTRY_VERIFY(result);
-    //      QVERIFY(!result->body().contains("This is a signature"));
-    //  }
-
-    void testStripEncryptedCRLFReply()
-    {
-        auto msg = readMail(QLatin1String("crlf-encrypted-with-signature.mbox"));
-        KMime::Message::Ptr result;
-        MailTemplates::reply(msg, [&](const KMime::Message::Ptr &r) {
-            result = r;
-        });
-        QTRY_VERIFY(result);
-        QVERIFY(!result->body().contains("This is a signature"));
-    }
-
-    void testHtml8BitEncodedReply()
-    {
-        auto msg = readMail(QLatin1String("8bitencoded.mbox"));
-        KMime::Message::Ptr result;
-        MailTemplates::reply(msg, [&](const KMime::Message::Ptr &r) {
-            result = r;
-        });
-        QTRY_VERIFY(result);
-        QVERIFY(MailTemplates::plaintextContent(result).contains(QString::fromUtf8("Why Pisa’s Tower")));
-    }
-
-    void testMultipartSignedReply()
-    {
-        auto msg = readMail(QLatin1String("openpgp-signed-mailinglist.mbox"));
-        KMime::Message::Ptr result;
-        MailTemplates::reply(msg, [&](const KMime::Message::Ptr &r) {
-            result = r;
-        });
-        QTRY_VERIFY(result);
-        auto content = removeFirstLine(QString::fromUtf8(result->body()));
-        QVERIFY(!content.isEmpty());
-        QVERIFY(content.contains(QLatin1String("i noticed a new branch")));
-    }
-
-    void testMultipartAlternativeReply()
-    {
-        auto msg = readMail(QLatin1String("alternative.mbox"));
-        KMime::Message::Ptr result;
-        MailTemplates::reply(msg, [&](const KMime::Message::Ptr &r) {
-            result = r;
-        });
-        QTRY_VERIFY(result);
-        auto content = removeFirstLine(QString::fromUtf8(result->body()));
-        QVERIFY(!content.isEmpty());
-        QCOMPARE(unquote(content),
-                 QLatin1String("If you can see this text it means that your email client couldn't display our newsletter properly.\nPlease visit this link to "
-                               "view the newsletter on our website: http://www.gog.com/newsletter/\n"));
-    }
-
-    void testAttachmentReply()
-    {
-        auto msg = readMail(QLatin1String("plaintextattachment.mbox"));
-        KMime::Message::Ptr result;
-        MailTemplates::reply(msg, [&](const KMime::Message::Ptr &r) {
-            result = r;
-        });
-        QTRY_VERIFY(result);
-        auto content = removeFirstLine(QString::fromUtf8(result->body()));
-        QVERIFY(!content.isEmpty());
-        QCOMPARE(unquote(content), QLatin1String("sdlkjsdjf"));
-    }
-
-    void testMultiRecipientReply()
-    {
-        auto msg = readMail(QLatin1String("multirecipients.mbox"));
-        KMime::Message::Ptr result;
-        MailTemplates::reply(msg, [&](const KMime::Message::Ptr &r) {
-            result = r;
-        });
-        QTRY_VERIFY(result);
-        auto content = removeFirstLine(QString::fromUtf8(result->body()));
-        QVERIFY(!content.isEmpty());
-        QCOMPARE(unquote(content), QLatin1String("test"));
-        QCOMPARE(result->to()->addresses(), QVector<QByteArray>{{"from@example.org"}});
-        auto l = QVector<QByteArray>{{"to1@example.org"}, {"to2@example.org"}, {"cc1@example.org"}, {"cc2@example.org"}};
-        QCOMPARE(result->cc()->addresses(), l);
-    }
-
-    void testMultiRecipientReplyFilteringMe()
-    {
-        KMime::Types::AddrSpecList me;
-        KMime::Types::Mailbox mb;
-        mb.setAddress("to1@example.org");
-        me << mb.addrSpec();
-
-        auto msg = readMail(QLatin1String("multirecipients.mbox"));
-        KMime::Message::Ptr result;
-        MailTemplates::reply(
-            msg,
-            [&](const KMime::Message::Ptr &r) {
-                result = r;
-            },
-            me);
-        QTRY_VERIFY(result);
-        QCOMPARE(result->to()->addresses(), QVector<QByteArray>{{"from@example.org"}});
-        auto l = QVector<QByteArray>{{"to2@example.org"}, {"cc1@example.org"}, {"cc2@example.org"}};
-        QCOMPARE(result->cc()->addresses(), l);
-    }
-
-    void testMultiRecipientReplyOwnMessage()
-    {
-        KMime::Types::AddrSpecList me;
-        KMime::Types::Mailbox mb;
-        mb.setAddress("from@example.org");
-        me << mb.addrSpec();
-
-        auto msg = readMail(QLatin1String("multirecipients.mbox"));
-        KMime::Message::Ptr result;
-        MailTemplates::reply(
-            msg,
-            [&](const KMime::Message::Ptr &r) {
-                result = r;
-            },
-            me);
-        QTRY_VERIFY(result);
-
-        auto to = QVector<QByteArray>{{"to1@example.org"}, {"to2@example.org"}};
-        QCOMPARE(result->to()->addresses(), to);
-        auto cc = QVector<QByteArray>{{"cc1@example.org"}, {"cc2@example.org"}};
-        QCOMPARE(result->cc()->addresses(), cc);
-    }
-
-    void testReplyList()
-    {
-        KMime::Types::AddrSpecList me;
-        KMime::Types::Mailbox mb;
-        mb.setAddress("me@example.org");
-        me << mb.addrSpec();
-
-        auto msg = readMail(QLatin1String("listmessage.mbox"));
-        KMime::Message::Ptr result;
-        MailTemplates::reply(
-            msg,
-            [&](const KMime::Message::Ptr &r) {
-                result = r;
-            },
-            me);
-        QTRY_VERIFY(result);
-
-        auto to = QVector<QByteArray>{{"list@example.org"}};
-        QCOMPARE(result->to()->addresses(), to);
-        auto cc = QVector<QByteArray>{{"to@example.org"}, {"cc1@example.org"}};
-        QCOMPARE(result->cc()->addresses(), cc);
-    }
-
-    void testForwardAsAttachment()
-    {
-        auto msg = readMail(QString::fromUtf8("plaintext.mbox"));
-        KMime::Message::Ptr result;
-        MailTemplates::forward(msg, [&](const KMime::Message::Ptr &r) {
-            result = r;
-        });
-        QTRY_VERIFY(result);
-        QCOMPARE(result->subject(false)->asUnicodeString(), QLatin1String{"FW: A random subject with alternative contenttype"});
-        QCOMPARE(result->to()->addresses(), {});
-        QCOMPARE(result->cc()->addresses(), {});
-        QCOMPARE(result->references()->identifiers(), {"1505824.VT1nqpAGu0@vkpc5"});
-        QCOMPARE(result->inReplyTo()->identifiers(), {});
-
-        auto attachments = result->attachments();
-        QCOMPARE(attachments.size(), 1);
-        auto attachment = attachments[0];
-        QCOMPARE(attachment->contentDisposition(false)->disposition(), KMime::Headers::CDinline);
-        QCOMPARE(attachment->contentDisposition(false)->filename(), QLatin1String{"A random subject with alternative contenttype.eml"});
-        QVERIFY(attachment->bodyIsMessage());
-
-        attachment->parse();
-        auto origMsg = attachment->bodyAsMessage();
-        QCOMPARE(origMsg->subject(false)->asUnicodeString(), QLatin1String{"A random subject with alternative contenttype"});
-    }
-
-    void testEncryptedForwardAsAttachment()
-    {
-        auto msg = readMail(QLatin1String("openpgp-encrypted.mbox"));
-        KMime::Message::Ptr result;
-        MailTemplates::forward(msg, [&](const KMime::Message::Ptr &r) {
-            result = r;
-        });
-        QTRY_VERIFY(result);
-        QCOMPARE(result->subject(false)->asUnicodeString(), QLatin1String{"FW: OpenPGP encrypted"});
-        QCOMPARE(result->to()->addresses(), {});
-        QCOMPARE(result->cc()->addresses(), {});
-        QCOMPARE(result->references()->identifiers(), {"1505824.VT2nqpAGu0@vkpc5"});
-        QCOMPARE(result->inReplyTo()->identifiers(), {});
-
-        auto attachments = result->attachments();
-        QCOMPARE(attachments.size(), 1);
-        auto attachment = attachments[0];
-        QCOMPARE(attachment->contentDisposition(false)->disposition(), KMime::Headers::CDinline);
-        QCOMPARE(attachment->contentDisposition(false)->filename(), QLatin1String{"OpenPGP encrypted.eml"});
-        QVERIFY(attachment->bodyIsMessage());
-
-        attachment->parse();
-        auto origMsg = attachment->bodyAsMessage();
-        QCOMPARE(origMsg->subject(false)->asUnicodeString(), QLatin1String{"OpenPGP encrypted"});
-    }
-
-    void testEncryptedWithAttachmentsForwardAsAttachment()
-    {
-        auto msg = readMail(QLatin1String("openpgp-encrypted-two-attachments.mbox"));
-        KMime::Message::Ptr result;
-        MailTemplates::forward(msg, [&](const KMime::Message::Ptr &r) {
-            result = r;
-        });
-        QTRY_VERIFY(result);
-        QCOMPARE(result->subject(false)->asUnicodeString(), QLatin1String{"FW: OpenPGP encrypted with 2 text attachments"});
-        QCOMPARE(result->to()->addresses(), {});
-        QCOMPARE(result->cc()->addresses(), {});
-        QCOMPARE(result->references()->identifiers(), {"1505824.VT0nqpAGu0@vkpc5"});
-        QCOMPARE(result->inReplyTo()->identifiers(), {});
-
-        auto attachments = result->attachments();
-        QCOMPARE(attachments.size(), 1);
-        auto attachment = attachments[0];
-        QCOMPARE(attachment->contentDisposition(false)->disposition(), KMime::Headers::CDinline);
-        QCOMPARE(attachment->contentDisposition(false)->filename(), QLatin1String{"OpenPGP encrypted with 2 text attachments.eml"});
-        QVERIFY(attachment->bodyIsMessage());
-
-        attachment->parse();
-        auto origMsg = attachment->bodyAsMessage();
-        QCOMPARE(origMsg->subject(false)->asUnicodeString(), QLatin1String{"OpenPGP encrypted with 2 text attachments"});
-
-        auto attattachments = origMsg->attachments();
-        QCOMPARE(attattachments.size(), 2);
-        QCOMPARE(attattachments[0]->contentDisposition(false)->filename(), QLatin1String{"attachment1.txt"});
-        QCOMPARE(attattachments[1]->contentDisposition(false)->filename(), QLatin1String{"attachment2.txt"});
-    }
-
-    void testForwardAlreadyForwarded()
-    {
-        auto msg = readMail(QLatin1String("cid-links-forwarded-inline.mbox"));
-        KMime::Message::Ptr result;
-        MailTemplates::forward(msg, [&](const KMime::Message::Ptr &r) {
-            result = r;
-        });
-        QTRY_VERIFY(result);
-        QCOMPARE(result->subject(false)->asUnicodeString(), QLatin1String{"FW: Html Hello (inlin)"});
-        QCOMPARE(result->to()->addresses(), {});
-        QCOMPARE(result->cc()->addresses(), {});
-        const QVector<QByteArray> references{QByteArray{"a1777ec781546ccc5dcd4918a5e4e03d@info"}, QByteArray{"46b164308eb6056361c866932a740a3c@info"}};
-        QCOMPARE(result->references()->identifiers(), references);
-        QCOMPARE(result->inReplyTo()->identifiers(), {});
-    }
-
-    void testCreatePlainMail()
-    {
-        QStringList to = {{QLatin1String("to@example.org")}};
-        QStringList cc = {{QLatin1String("cc@example.org")}};
-        QStringList bcc = {{QLatin1String("bcc@example.org")}};
-
-        KMime::Types::Mailbox from;
-        from.fromUnicodeString(QLatin1String("from@example.org"));
-        QString subject = QLatin1String("subject");
-        QString body = QLatin1String("body");
-        QList<Attachment> attachments;
-
-        auto result = MailTemplates::createMessage({}, to, cc, bcc, from, subject, body, false, attachments);
-
-        QVERIFY(result);
-        QVERIFY(validate(result));
-        QCOMPARE(result->subject()->asUnicodeString(), subject);
-        QCOMPARE(result->body(), body.toUtf8());
-        QVERIFY(result->date(false)->dateTime().isValid());
-        QVERIFY(result->contentType()->isMimeType("text/plain"));
-        QVERIFY(result->messageID(false) && !result->messageID(false)->isEmpty());
-    }
-
-    void testCreateHtmlMail()
-    {
-        QStringList to = {{QLatin1String("to@example.org")}};
-        QStringList cc = {{QLatin1String("cc@example.org")}};
-        QStringList bcc = {{QLatin1String("bcc@example.org")}};
-
-        KMime::Types::Mailbox from;
-        from.fromUnicodeString(QLatin1String("from@example.org"));
-        QString subject = QLatin1String("subject");
-        QString body = QLatin1String("body");
-        QList<Attachment> attachments;
-
-        auto result = MailTemplates::createMessage({}, to, cc, bcc, from, subject, body, true, attachments);
-
-        QVERIFY(result);
-        QVERIFY(validate(result));
-        QCOMPARE(result->subject()->asUnicodeString(), subject);
-        QVERIFY(result->date(false)->dateTime().isValid());
-        QVERIFY(result->contentType()->isMimeType("multipart/alternative"));
-        const auto contents = result->contents();
-        // 1 Plain + 1 Html
-        QCOMPARE(contents.size(), 2);
-    }
-
-    void testCreatePlainMailWithAttachments()
-    {
-        QStringList to = {{QLatin1String("to@example.org")}};
-        QStringList cc = {{QLatin1String("cc@example.org")}};
-        QStringList bcc = {{QLatin1String("bcc@example.org")}};
-
-        KMime::Types::Mailbox from;
-        from.fromUnicodeString(QLatin1String("from@example.org"));
-        QString subject = QLatin1String("subject");
-        QString body = QLatin1String("body");
-        QList<Attachment> attachments = {{QLatin1String("name"), QLatin1String("filename"), "mimetype", true, "inlineAttachment"},
-                                         {QLatin1String("name"), QLatin1String("filename"), "mimetype", false, "nonInlineAttachment"}};
-
-        auto result = MailTemplates::createMessage({}, to, cc, bcc, from, subject, body, false, attachments);
-
-        QVERIFY(result);
-        QVERIFY(validate(result));
-        QCOMPARE(result->subject()->asUnicodeString(), subject);
-        QVERIFY(result->contentType()->isMimeType("multipart/mixed"));
-        QVERIFY(result->date(false)->dateTime().isValid());
-        const auto contents = result->contents();
-        // 1 Plain + 2 Attachments
-        QCOMPARE(contents.size(), 3);
-        auto p = getSubpart(result.data(), "text/plain");
-        QVERIFY(p);
-    }
-
-    void testCreateHtmlMailWithAttachments()
-    {
-        QStringList to = {{QLatin1String("to@example.org")}};
-        QStringList cc = {{QLatin1String("cc@example.org")}};
-        QStringList bcc = {{QLatin1String("bcc@example.org")}};
-
-        KMime::Types::Mailbox from;
-        from.fromUnicodeString(QLatin1String("from@example.org"));
-        QString subject = QLatin1String("subject");
-        QString body = QLatin1String("body");
-        QList<Attachment> attachments = {
-            {QLatin1String("name"), QLatin1String("filename"), "mimetype", true, "inlineAttachment"},
-            {QLatin1String("name"), QLatin1String("filename"), "mimetype", false, "nonInlineAttachment"},
-        };
-
-        auto result = MailTemplates::createMessage({}, to, cc, bcc, from, subject, body, true, attachments);
-
-        QVERIFY(result);
-        QVERIFY(validate(result));
-        QCOMPARE(result->subject()->asUnicodeString(), subject);
-        QVERIFY(result->contentType()->isMimeType("multipart/mixed"));
-        QVERIFY(result->date(false)->dateTime().isValid());
-        const auto contents = result->contents();
-        // 1 alternative + 2 Attachments
-        QCOMPARE(contents.size(), 3);
-        auto p = getSubpart(result.data(), "multipart/alternative");
-        QVERIFY(p);
-    }
-
-    void testCreatePlainMailSigned()
-    {
-        QStringList to = {{QLatin1String("to@example.org")}};
-        QStringList cc = {{QLatin1String("cc@example.org")}};
-        QStringList bcc = {{QLatin1String("bcc@example.org")}};
-
-        KMime::Types::Mailbox from;
-        from.fromUnicodeString(QLatin1String("from@example.org"));
-        QString subject = QLatin1String("subject");
-        QString body = QLatin1String("body");
-        QList<Attachment> attachments;
-
-        auto keys = Crypto::findKeys({}, true, false);
-        auto result = MailTemplates::createMessage({}, to, cc, bcc, from, subject, body, false, attachments, keys, {}, keys[0]);
-
-        QVERIFY(result);
-        QVERIFY(validate(result));
-        // qWarning() << "---------------------------------";
-        // qWarning().noquote() << result->encodedContent();
-        // qWarning() << "---------------------------------";
-        QCOMPARE(result->subject()->asUnicodeString(), subject);
-        QVERIFY(result->date(false)->dateTime().isValid());
-
-        QCOMPARE(result->contentType()->mimeType(), QByteArray{"multipart/signed"});
-        QCOMPARE(result->attachments().size(), 1);
-        QCOMPARE(result->attachments()[0]->contentDisposition()->filename(), QLatin1String{"0x8F246DE6.asc"});
-        QCOMPARE(result->contents().size(), 2);
-
-        auto signedMessage = result->contents()[0];
-        QVERIFY(signedMessage->contentType()->isMimeType("multipart/mixed"));
-        const auto contents = signedMessage->contents();
-        QCOMPARE(contents.size(), 2);
-        QCOMPARE(contents[0]->contentType()->mimeType(), QByteArray{"text/plain"});
-        QCOMPARE(contents[1]->contentType()->mimeType(), QByteArray{"application/pgp-keys"});
-        QCOMPARE(contents[1]->contentDisposition()->filename(), QLatin1String{"0x8F246DE6.asc"});
-
-        auto signature = result->contents()[1];
-        QCOMPARE(signature->contentDisposition()->filename(), QLatin1String{"signature.asc"});
-        QVERIFY(signature->contentType()->isMimeType("application/pgp-signature"));
-    }
-
-    void testCreatePlainMailWithAttachmentsSigned()
-    {
-        QStringList to = {{QLatin1String("to@example.org")}};
-        QStringList cc = {{QLatin1String("cc@example.org")}};
-        QStringList bcc = {{QLatin1String("bcc@example.org")}};
-
-        KMime::Types::Mailbox from;
-        from.fromUnicodeString(QLatin1String("from@example.org"));
-        QString subject = QLatin1String("subject");
-        QString body = QLatin1String("body");
-        QList<Attachment> attachments = {
-            {QLatin1String("name"), QLatin1String("filename1"), "mimetype1", true, "inlineAttachment"},
-            {QLatin1String("name"), QLatin1String("filename2"), "mimetype2", false, "nonInlineAttachment"},
-        };
-
-        auto signingKeys = Crypto::findKeys({}, true, false);
-        auto result = MailTemplates::createMessage({}, to, cc, bcc, from, subject, body, false, attachments, signingKeys, {}, signingKeys[0]);
-
-        QVERIFY(result);
-        QVERIFY(validate(result));
-        qWarning() << "---------------------------------";
-        qWarning().noquote() << result->encodedContent();
-        qWarning() << "---------------------------------";
-        QCOMPARE(result->subject()->asUnicodeString(), subject);
-        QVERIFY(result->date(false)->dateTime().isValid());
-
-        QCOMPARE(result->contentType()->mimeType(), QByteArray{"multipart/signed"});
-        QCOMPARE(result->attachments().size(), 3);
-        QCOMPARE(result->attachments()[0]->contentDisposition()->filename(), QLatin1String{"filename1"});
-        QCOMPARE(result->attachments()[1]->contentDisposition()->filename(), QLatin1String{"filename2"});
-        QCOMPARE(result->attachments()[2]->contentDisposition()->filename(), QLatin1String{"0x8F246DE6.asc"});
-
-        QCOMPARE(result->contents().size(), 2);
-
-        auto signedMessage = result->contents()[0];
-        QVERIFY(signedMessage->contentType()->isMimeType("multipart/mixed"));
-        const auto contents = signedMessage->contents();
-        QCOMPARE(contents.size(), 4);
-        QCOMPARE(contents[0]->contentType()->mimeType(), QByteArray{"text/plain"});
-        QCOMPARE(contents[1]->contentDisposition()->filename(), QLatin1String{"filename1"});
-        QCOMPARE(contents[2]->contentDisposition()->filename(), QLatin1String{"filename2"});
-        QCOMPARE(contents[3]->contentType()->mimeType(), QByteArray{"application/pgp-keys"});
-        QCOMPARE(contents[3]->contentDisposition()->filename(), QLatin1String{"0x8F246DE6.asc"});
-    }
-
-    void testCreateIMipMessage()
-    {
-        QStringList to = {{QLatin1String("to@example.org")}};
-        QStringList cc = {{QLatin1String("cc@example.org")}};
-        QStringList bcc = {{QLatin1String("bcc@example.org")}};
-        QString from = {QLatin1String("from@example.org")};
-        QString subject = QLatin1String("subject");
-        QString body = QLatin1String("body");
-
-        QString ical = QLatin1String("ical");
-
-        auto result = MailTemplates::createIMipMessage(from, {to, cc, bcc}, subject, body, ical);
-
-        QVERIFY(result);
-        QVERIFY(validate(result));
-        qWarning() << "---------------------------------";
-        qWarning().noquote() << result->encodedContent();
-        qWarning() << "---------------------------------";
-
-        QCOMPARE(result->contentType()->mimeType(), QByteArray{"multipart/alternative"});
-
-        QCOMPARE(result->contents().size(), 2);
-        QVERIFY(result->contents()[0]->contentType()->isMimeType("text/plain"));
-        QVERIFY(result->contents()[1]->contentType()->isMimeType("text/calendar"));
-        QCOMPARE(result->contents()[1]->contentType()->name(), QLatin1String{"event.ics"});
-
-        QVERIFY(KMime::isAttachment(result->contents()[1]));
-        // KMime should return 1 attachment for this multipart but it doesn't sometimes(?)
-        //
-        // It is an attachment as the ical part:
-        // A) Has a name
-        // B) Has a content disposition of CDattachment
-        //
-        // Check the result of QVERIFY(KMime::isAttachment(result->contents()[1])); if
-        // it is true then this confirms that result->attachments() is misreporting
-        QCOMPARE(result->attachments().count(), 1);
-    }
-
-    void testEncryptedWithProtectedHeadersReply()
-    {
-        KMime::Types::AddrSpecList me;
-        KMime::Types::Mailbox mb;
-        mb.setAddress("to1@example.org");
-        me << mb.addrSpec();
-
-        auto msg = readMail(QLatin1String("openpgp-encrypted-memoryhole2.mbox"));
-        KMime::Message::Ptr result;
-        MailTemplates::reply(
-            msg,
-            [&](const KMime::Message::Ptr &r) {
-                result = r;
-            },
-            me);
-        QTRY_VERIFY(result);
-        QCOMPARE(result->subject()->asUnicodeString(), QLatin1String{"RE: This is the subject"});
-        QCOMPARE(result->to()->addresses(), QVector<QByteArray>{{"jane@doe.com"}});
-        QCOMPARE(result->cc()->addresses(), QVector<QByteArray>{{"john@doe.com"}});
-        QCOMPARE(result->inReplyTo()->asUnicodeString(), QLatin1String{"<03db3530-0000-0000-95a2-8a148f00000@example.com>"});
-        QCOMPARE(result->references()->asUnicodeString(),
-                 QLatin1String{"<dbe9d22b-0a3f-cb1e-e883-8a148f00000@example.com> <03db3530-0000-0000-95a2-8a148f00000@example.com>"});
-        QCOMPARE(normalize(removeFirstLine(QString::fromUtf8(result->body()))),
-                 QLatin1String{"FsdflkjdslfjHappyMonday!Belowyouwillfindaquickoverviewofthecurrenton-goings.Remember"});
-    }
-
-    void testEncryptedWithProtectedHeadersForwardAsAttachment()
-    {
-        auto msg = readMail(QLatin1String("openpgp-encrypted-memoryhole2.mbox"));
-        KMime::Message::Ptr result;
-        MailTemplates::forward(msg, [&](const KMime::Message::Ptr &r) {
-            result = r;
-        });
-        QTRY_VERIFY(result);
-        QCOMPARE(result->subject()->asUnicodeString(), QLatin1String{"FW: This is the subject"});
-        QCOMPARE(result->to()->addresses(), {});
-        QCOMPARE(result->cc()->addresses(), {});
-        QCOMPARE(result->references()->asUnicodeString(),
-                 QLatin1String{"<dbe9d22b-0a3f-cb1e-e883-8a148f00000@example.com> <03db3530-0000-0000-95a2-8a148f00000@example.com>"});
-        QCOMPARE(result->inReplyTo()->identifiers(), {});
-
-        auto attachments = result->attachments();
-        QCOMPARE(attachments.size(), 1);
-        auto attachment = attachments[0];
-        QCOMPARE(attachment->contentDisposition(false)->disposition(), KMime::Headers::CDinline);
-        QCOMPARE(attachment->contentDisposition(false)->filename(), QLatin1String{"This is the subject.eml"});
-        QVERIFY(attachment->bodyIsMessage());
-
-        attachment->parse();
-        auto origMsg = attachment->bodyAsMessage();
-        QCOMPARE(origMsg->subject(false)->asUnicodeString(), QLatin1String{"..."});
-    }
-};
-
-QTEST_MAIN(MailTemplateTest)
-#include "mailtemplatetest.moc"
diff --git a/src/core/autotests/mimetreeparsertest.cpp b/src/core/autotests/mimetreeparsertest.cpp
index bdcf5e1..6362c63 100644
--- a/src/core/autotests/mimetreeparsertest.cpp
+++ b/src/core/autotests/mimetreeparsertest.cpp
@@ -1,777 +1,777 @@
 // SPDX-FileCopyrightText: 2016 Sandro Knauß <knauss@kolabsystems.com>
 // SPDX-License-Identifier: LGPL-2.0-or-later
 
 #include <MimeTreeParserCore/ObjectTreeParser>
 
 #include <QDebug>
 #include <QTest>
 #include <QTimeZone>
 
 QByteArray readMailFromFile(const QString &mailFile)
 {
     QFile file(QLatin1String(MAIL_DATA_DIR) + QLatin1Char('/') + mailFile);
     file.open(QIODevice::ReadOnly);
     Q_ASSERT(file.isOpen());
     return file.readAll();
 }
 
 class MimeTreeParserTest : public QObject
 {
     Q_OBJECT
 
 private Q_SLOTS:
     void testTextMail()
     {
         const auto expectedText = QStringLiteral(
             "If you can see this text it means that your email client couldn't display our newsletter properly.\nPlease visit this link to view the newsletter "
             "on our website: http://www.gog.com/newsletter/");
         MimeTreeParser::ObjectTreeParser otp;
         otp.parseObjectTree(readMailFromFile(QLatin1String("plaintext.mbox")));
         auto partList = otp.collectContentParts();
         QCOMPARE(partList.size(), 1);
         auto part = partList[0].dynamicCast<MimeTreeParser::MessagePart>();
         QCOMPARE(part->text(), expectedText);
         QCOMPARE(part->charset(), QStringLiteral("utf-8").toLocal8Bit());
 
         QCOMPARE(part->encryptions().size(), 0);
         QCOMPARE(part->signatures().size(), 0);
 
         QCOMPARE(otp.collectAttachmentParts().size(), 0);
 
         QCOMPARE(otp.plainTextContent(), expectedText);
         QVERIFY(otp.htmlContent().isEmpty());
     }
 
     void testAlternative()
     {
         MimeTreeParser::ObjectTreeParser otp;
         otp.parseObjectTree(readMailFromFile(QLatin1String("alternative.mbox")));
         auto partList = otp.collectContentParts();
         QCOMPARE(partList.size(), 1);
         auto part = partList[0].dynamicCast<MimeTreeParser::AlternativeMessagePart>();
         QVERIFY(bool(part));
         QCOMPARE(part->plaintextContent(),
                  QStringLiteral("If you can see this text it means that your email client couldn't display our newsletter properly.\nPlease visit this link to "
                                 "view the newsletter on our website: http://www.gog.com/newsletter/\n"));
         QCOMPARE(part->charset(), QStringLiteral("us-ascii").toLocal8Bit());
         QCOMPARE(part->htmlContent(), QStringLiteral("<html><body><p><span>HTML</span> text</p></body></html>\n\n"));
         QCOMPARE(otp.collectAttachmentParts().size(), 0);
         QCOMPARE(part->encryptions().size(), 0);
         QCOMPARE(part->signatures().size(), 0);
     }
 
     void testTextHtml()
     {
         auto expectedText = QStringLiteral("<html><body><p><span>HTML</span> text</p></body></html>");
         MimeTreeParser::ObjectTreeParser otp;
         otp.parseObjectTree(readMailFromFile(QLatin1String("html.mbox")));
         otp.print();
         auto partList = otp.collectContentParts();
         QCOMPARE(partList.size(), 1);
         auto part = partList[0].dynamicCast<MimeTreeParser::HtmlMessagePart>();
         QVERIFY(bool(part));
         QCOMPARE(part->htmlContent(), expectedText);
         QCOMPARE(part->charset(), QStringLiteral("windows-1252").toLocal8Bit());
         QCOMPARE(part->encryptions().size(), 0);
         QCOMPARE(part->signatures().size(), 0);
         auto contentAttachmentList = otp.collectAttachmentParts();
         QCOMPARE(contentAttachmentList.size(), 0);
 
         QCOMPARE(otp.htmlContent(), expectedText);
         QVERIFY(otp.plainTextContent().isEmpty());
     }
 
     void testSMimeEncrypted()
     {
         MimeTreeParser::ObjectTreeParser otp;
         otp.parseObjectTree(readMailFromFile(QLatin1String("smime-encrypted.mbox")));
         otp.print();
         otp.decryptParts();
         otp.print();
         auto partList = otp.collectContentParts();
         QCOMPARE(partList.size(), 1);
         auto part = partList[0].dynamicCast<MimeTreeParser::MessagePart>();
         QVERIFY(bool(part));
         QCOMPARE(part->text(), QStringLiteral("The quick brown fox jumped over the lazy dog."));
         QCOMPARE(part->charset(), QStringLiteral("us-ascii").toLocal8Bit());
         QCOMPARE(part->encryptions().size(), 1);
         QCOMPARE(part->signatures().size(), 0);
         auto contentAttachmentList = otp.collectAttachmentParts();
         QCOMPARE(contentAttachmentList.size(), 0);
     }
 
     void testOpenPGPEncryptedAttachment()
     {
         MimeTreeParser::ObjectTreeParser otp;
         otp.parseObjectTree(readMailFromFile(QLatin1String("openpgp-encrypted-attachment-and-non-encrypted-attachment.mbox")));
         otp.print();
         otp.decryptParts();
         otp.print();
         auto partList = otp.collectContentParts();
         QCOMPARE(partList.size(), 1);
         auto part = partList[0].dynamicCast<MimeTreeParser::MessagePart>();
         QVERIFY(bool(part));
         QCOMPARE(part->text(), QStringLiteral("test text"));
         QCOMPARE(part->charset(), QStringLiteral("us-ascii").toLocal8Bit());
         QCOMPARE(part->encryptions().size(), 1);
         QCOMPARE(part->signatures().size(), 1);
         QCOMPARE(part->encryptionState(), MimeTreeParser::KMMsgFullyEncrypted);
         QCOMPARE(part->signatureState(), MimeTreeParser::KMMsgFullySigned);
         auto contentAttachmentList = otp.collectAttachmentParts();
         QCOMPARE(contentAttachmentList.size(), 2);
         //     QCOMPARE(contentAttachmentList[0]->availableContents(), QVector<QByteArray>() << "text/plain");
         // QCOMPARE(contentAttachmentList[0]->content().size(), 1);
         QCOMPARE(contentAttachmentList[0]->encryptions().size(), 1);
         QCOMPARE(contentAttachmentList[0]->signatures().size(), 1);
         QCOMPARE(contentAttachmentList[0]->encryptionState(), MimeTreeParser::KMMsgFullyEncrypted);
         QCOMPARE(contentAttachmentList[0]->signatureState(), MimeTreeParser::KMMsgFullySigned);
         //     QCOMPARE(contentAttachmentList[1]->availableContents(), QVector<QByteArray>() << "image/png");
         //     QCOMPARE(contentAttachmentList[1]->content().size(), 1);
         QCOMPARE(contentAttachmentList[1]->encryptions().size(), 0);
         QCOMPARE(contentAttachmentList[1]->signatures().size(), 0);
         QCOMPARE(contentAttachmentList[1]->encryptionState(), MimeTreeParser::KMMsgNotEncrypted);
         QCOMPARE(contentAttachmentList[1]->signatureState(), MimeTreeParser::KMMsgNotSigned);
     }
 
     void testOpenPGPInline()
     {
         MimeTreeParser::ObjectTreeParser otp;
         otp.parseObjectTree(readMailFromFile(QLatin1String("openpgp-inline-charset-encrypted.mbox")));
         otp.print();
         otp.decryptParts();
         otp.print();
         auto partList = otp.collectContentParts();
         QCOMPARE(partList.size(), 1);
         auto part = partList[0].dynamicCast<MimeTreeParser::MessagePart>();
         QVERIFY(bool(part));
         QCOMPARE(part->charset(), QStringLiteral("ISO-8859-15").toLocal8Bit());
         QCOMPARE(part->text(), QString::fromUtf8("asdasd asd asd asdf sadf sdaf sadf öäü"));
 
         QCOMPARE(part->encryptions().size(), 1);
         QCOMPARE(part->signatures().size(), 1);
         QCOMPARE(otp.collectAttachmentParts().size(), 0);
     }
 
     void testOpenPPGInlineWithNonEncText()
     {
         MimeTreeParser::ObjectTreeParser otp;
         otp.parseObjectTree(readMailFromFile(QLatin1String("openpgp-inline-encrypted+nonenc.mbox")));
         otp.print();
         otp.decryptParts();
         otp.print();
         auto partList = otp.collectContentParts();
         QCOMPARE(partList.size(), 1);
         auto part1 = partList[0].dynamicCast<MimeTreeParser::MessagePart>();
         QVERIFY(bool(part1));
         QCOMPARE(part1->text(), QStringLiteral("Not encrypted not signed :(\n\nsome random text"));
         // TODO test if we get the proper subparts with the appropriate encryptions
         QCOMPARE(part1->charset(), QStringLiteral("us-ascii").toLocal8Bit());
 
         QCOMPARE(part1->encryptionState(), MimeTreeParser::KMMsgPartiallyEncrypted);
         QCOMPARE(part1->signatureState(), MimeTreeParser::KMMsgNotSigned);
 
         // QCOMPARE(part1->text(), QStringLiteral("Not encrypted not signed :(\n\n"));
         // QCOMPARE(part1->charset(), QStringLiteral("us-ascii").toLocal8Bit());
         // QCOMPARE(contentList[1]->content(), QStringLiteral("some random text").toLocal8Bit());
         // QCOMPARE(contentList[1]->charset(), QStringLiteral("us-ascii").toLocal8Bit());
         // QCOMPARE(contentList[1]->encryptions().size(), 1);
         // QCOMPARE(contentList[1]->signatures().size(), 0);
         QCOMPARE(otp.collectAttachmentParts().size(), 0);
     }
 
     void testEncryptionBlock()
     {
         MimeTreeParser::ObjectTreeParser otp;
         otp.parseObjectTree(readMailFromFile(QLatin1String("openpgp-encrypted-attachment-and-non-encrypted-attachment.mbox")));
         otp.print();
         otp.decryptParts();
         otp.print();
         auto partList = otp.collectContentParts();
         QCOMPARE(partList.size(), 1);
         auto part1 = partList[0].dynamicCast<MimeTreeParser::MessagePart>();
         QVERIFY(bool(part1));
         QCOMPARE(part1->encryptions().size(), 1);
         //     auto enc = contentList[0]->encryptions()[0];
         //     QCOMPARE((int) enc->recipients().size(), 2);
 
         //     auto r = enc->recipients()[0];
         //     QCOMPARE(r->keyid(),QStringLiteral("14B79E26050467AA"));
         //     QCOMPARE(r->name(),QStringLiteral("kdetest"));
         //     QCOMPARE(r->email(),QStringLiteral("you@you.com"));
         //     QCOMPARE(r->comment(),QStringLiteral(""));
 
         //     r = enc->recipients()[1];
         //     QCOMPARE(r->keyid(),QStringLiteral("8D9860C58F246DE6"));
         //     QCOMPARE(r->name(),QStringLiteral("unittest key"));
         //     QCOMPARE(r->email(),QStringLiteral("test@kolab.org"));
         //     QCOMPARE(r->comment(),QStringLiteral("no password"));
         auto attachmentList = otp.collectAttachmentParts();
         QCOMPARE(attachmentList.size(), 2);
         auto attachment1 = attachmentList[0];
         QVERIFY(attachment1->node());
         QCOMPARE(attachment1->filename(), QStringLiteral("file.txt"));
         auto attachment2 = attachmentList[1];
         QVERIFY(attachment2->node());
         QCOMPARE(attachment2->filename(), QStringLiteral("image.png"));
     }
 
     void testSignatureBlock()
     {
         MimeTreeParser::ObjectTreeParser otp;
         otp.parseObjectTree(readMailFromFile(QLatin1String("openpgp-encrypted-attachment-and-non-encrypted-attachment.mbox")));
         otp.print();
         otp.decryptParts();
         otp.print();
         auto partList = otp.collectContentParts();
         QCOMPARE(partList.size(), 1);
         auto part = partList[0].dynamicCast<MimeTreeParser::MessagePart>();
         QVERIFY(bool(part));
 
         // QCOMPARE(contentList[0]->signatures().size(), 1);
         // auto sig = contentList[0]->signatures()[0];
         // QCOMPARE(sig->creationDateTime(), QDateTime(QDate(2015,05,01),QTime(15,12,47)));
         // QCOMPARE(sig->expirationDateTime(), QDateTime());
         // QCOMPARE(sig->neverExpires(), true);
 
         // auto key = sig->key();
         // QCOMPARE(key->keyid(),QStringLiteral("8D9860C58F246DE6"));
         // QCOMPARE(key->name(),QStringLiteral("unittest key"));
         // QCOMPARE(key->email(),QStringLiteral("test@kolab.org"));
         // QCOMPARE(key->comment(),QStringLiteral("no password"));
     }
 
     void testRelatedAlternative()
     {
         MimeTreeParser::ObjectTreeParser otp;
         otp.parseObjectTree(readMailFromFile(QLatin1String("cid-links.mbox")));
         otp.print();
         auto partList = otp.collectContentParts();
         QCOMPARE(partList.size(), 1);
         auto part = partList[0].dynamicCast<MimeTreeParser::MessagePart>();
         QVERIFY(bool(part));
         QCOMPARE(part->encryptions().size(), 0);
         QCOMPARE(part->signatures().size(), 0);
         QCOMPARE(otp.collectAttachmentParts().size(), 1);
     }
 
     void testAttachmentPart()
     {
         MimeTreeParser::ObjectTreeParser otp;
         otp.parseObjectTree(readMailFromFile(QLatin1String("attachment.mbox")));
         otp.print();
         auto partList = otp.collectAttachmentParts();
         QCOMPARE(partList.size(), 1);
         auto part = partList[0].dynamicCast<MimeTreeParser::MessagePart>();
         QVERIFY(bool(part));
         QCOMPARE(part->mimeType(), "image/jpeg");
         QCOMPARE(part->filename(), QStringLiteral("aqnaozisxya.jpeg"));
     }
 
     void testAttachment2Part()
     {
         MimeTreeParser::ObjectTreeParser otp;
         otp.parseObjectTree(readMailFromFile(QLatin1String("attachment2.mbox")));
         otp.print();
         auto partList = otp.collectAttachmentParts();
         QCOMPARE(partList.size(), 1);
         auto part = partList[0].dynamicCast<MimeTreeParser::MessagePart>();
         QVERIFY(bool(part));
         QCOMPARE(part->mimeType(), "image/jpeg");
         QCOMPARE(part->filename(), QStringLiteral("aqnaozisxya.jpeg"));
     }
 
     void testCidLink()
     {
         MimeTreeParser::ObjectTreeParser otp;
         otp.parseObjectTree(readMailFromFile(QLatin1String("cid-links.mbox")));
         otp.print();
         auto partList = otp.collectContentParts();
         QCOMPARE(partList.size(), 1);
         auto part = partList[0].dynamicCast<MimeTreeParser::AlternativeMessagePart>();
         QVERIFY(bool(part));
         auto resolvedContent = otp.resolveCidLinks(part->htmlContent());
         QVERIFY(!resolvedContent.contains(QLatin1String("cid:")));
     }
 
     void testCidLinkInForwardedInline()
     {
         MimeTreeParser::ObjectTreeParser otp;
         otp.parseObjectTree(readMailFromFile(QLatin1String("cid-links-forwarded-inline.mbox")));
         otp.print();
         auto partList = otp.collectContentParts();
         QCOMPARE(partList.size(), 1);
         auto part = partList[0].dynamicCast<MimeTreeParser::AlternativeMessagePart>();
         QVERIFY(bool(part));
         auto resolvedContent = otp.resolveCidLinks(part->htmlContent());
         QVERIFY(!resolvedContent.contains(QLatin1String("cid:")));
     }
 
     void testOpenPGPInlineError()
     {
         MimeTreeParser::ObjectTreeParser otp;
         otp.parseObjectTree(readMailFromFile(QLatin1String("inlinepgpgencrypted-error.mbox")));
         otp.print();
         otp.decryptParts();
         otp.print();
         auto partList = otp.collectContentParts();
         QCOMPARE(partList.size(), 1);
         auto part = partList[0].dynamicCast<MimeTreeParser::EncryptedMessagePart>();
         QVERIFY(bool(part));
         QVERIFY(part->error());
     }
 
     void testEncapsulated()
     {
         MimeTreeParser::ObjectTreeParser otp;
         otp.parseObjectTree(readMailFromFile(QLatin1String("encapsulated-with-attachment.mbox")));
         otp.decryptParts();
         auto partList = otp.collectContentParts();
         QCOMPARE(partList.size(), 2);
         auto part = partList[1].dynamicCast<MimeTreeParser::EncapsulatedRfc822MessagePart>();
         QVERIFY(bool(part));
         QCOMPARE(part->from(), QLatin1String("Thomas McGuire <dontspamme@gmx.net>"));
         QCOMPARE(part->date().toString(), QLatin1String("Wed Aug 5 10:57:58 2009 GMT+0200"));
         auto subPartList = otp.collectContentParts(part);
         QCOMPARE(subPartList.size(), 1);
         qWarning() << subPartList[0]->metaObject()->className();
         auto subPart = subPartList[0].dynamicCast<MimeTreeParser::TextMessagePart>();
         QVERIFY(bool(subPart));
     }
 
     void test8bitEncodedInPlaintext()
     {
         MimeTreeParser::ObjectTreeParser otp;
         otp.parseObjectTree(readMailFromFile(QLatin1String("8bitencoded.mbox")));
         QVERIFY(otp.plainTextContent().contains(QString::fromUtf8("Why Pisa’s Tower")));
         QVERIFY(otp.htmlContent().contains(QString::fromUtf8("Why Pisa’s Tower")));
     }
 
     void testInlineSigned()
     {
         MimeTreeParser::ObjectTreeParser otp;
         otp.parseObjectTree(readMailFromFile(QLatin1String("openpgp-inline-signed.mbox")));
         otp.decryptParts();
         auto partList = otp.collectContentParts();
         QCOMPARE(partList.size(), 1);
         auto part = partList[0].dynamicCast<MimeTreeParser::MessagePart>();
         QCOMPARE(part->signatures().size(), 1);
         QCOMPARE(part->encryptionState(), MimeTreeParser::KMMsgNotEncrypted);
         QCOMPARE(part->signatureState(), MimeTreeParser::KMMsgFullySigned);
         QCOMPARE(part->text(), QString::fromUtf8("ohno öäü\n"));
 
         QVERIFY(otp.plainTextContent().contains(QString::fromUtf8("ohno öäü")));
 
         auto signaturePart = part->signatures().first();
         QCOMPARE(signaturePart->partMetaData()->isGoodSignature, true);
-        QCOMPARE(signaturePart->partMetaData()->keyIsTrusted, true);
-        QCOMPARE(signaturePart->partMetaData()->keyMissing, false);
-        QCOMPARE(signaturePart->partMetaData()->keyExpired, false);
-        QCOMPARE(signaturePart->partMetaData()->keyRevoked, false);
-        QCOMPARE(signaturePart->partMetaData()->sigExpired, false);
-        QCOMPARE(signaturePart->partMetaData()->crlMissing, false);
-        QCOMPARE(signaturePart->partMetaData()->crlTooOld, false);
+        QCOMPARE(signaturePart->partMetaData()->isTrusted(), true);
+        QCOMPARE(signaturePart->partMetaData()->sigSummary & GpgME::Signature::KeyMissing, false);
+        QCOMPARE(signaturePart->partMetaData()->sigSummary & GpgME::Signature::KeyExpired, false);
+        QCOMPARE(signaturePart->partMetaData()->sigSummary & GpgME::Signature::KeyRevoked, false);
+        QCOMPARE(signaturePart->partMetaData()->sigSummary & GpgME::Signature::SigExpired, false);
+        QCOMPARE(signaturePart->partMetaData()->sigSummary & GpgME::Signature::CrlMissing, false);
+        QCOMPARE(signaturePart->partMetaData()->sigSummary & GpgME::Signature::CrlTooOld, false);
         QCOMPARE(signaturePart->partMetaData()->keyId, QByteArray{"8D9860C58F246DE6"});
         QCOMPARE(signaturePart->partMetaData()->signer, QLatin1String{"unittest key (no password) <test@kolab.org>"});
         QCOMPARE(signaturePart->partMetaData()->signerMailAddresses, QStringList{{QStringLiteral("test@kolab.org")}});
     }
 
     void testEncryptedAndSigned()
     {
         MimeTreeParser::ObjectTreeParser otp;
         otp.parseObjectTree(readMailFromFile(QLatin1String("openpgp-encrypted+signed.mbox")));
         otp.decryptParts();
         auto partList = otp.collectContentParts();
         QCOMPARE(partList.size(), 1);
         auto part = partList[0].dynamicCast<MimeTreeParser::MessagePart>();
         QCOMPARE(part->signatures().size(), 1);
         QCOMPARE(part->encryptions().size(), 1);
         QCOMPARE(part->encryptionState(), MimeTreeParser::KMMsgFullyEncrypted);
         QCOMPARE(part->signatureState(), MimeTreeParser::KMMsgFullySigned);
         QVERIFY(otp.plainTextContent().contains(QString::fromUtf8("encrypted message text")));
 
         auto signaturePart = part->signatures().first();
         QCOMPARE(signaturePart->partMetaData()->keyId, QByteArray{"8D9860C58F246DE6"});
         QCOMPARE(signaturePart->partMetaData()->isGoodSignature, true);
     }
 
     void testOpenpgpMultipartEmbedded()
     {
         MimeTreeParser::ObjectTreeParser otp;
         otp.parseObjectTree(readMailFromFile(QLatin1String("openpgp-multipart-embedded.mbox")));
         otp.print();
         otp.decryptParts();
         otp.print();
         auto partList = otp.collectContentParts();
         QCOMPARE(partList.size(), 1);
         auto part = partList[0].dynamicCast<MimeTreeParser::MessagePart>();
         QCOMPARE(part->encryptions().size(), 1);
         QCOMPARE(part->encryptionState(), MimeTreeParser::KMMsgFullyEncrypted);
         QCOMPARE(otp.plainTextContent(), QString::fromUtf8("sdflskjsdf\n\n-- \nThis is a HTML signature.\n"));
     }
 
     void testOpenpgpMultipartEmbeddedSigned()
     {
         MimeTreeParser::ObjectTreeParser otp;
         otp.parseObjectTree(readMailFromFile(QLatin1String("openpgp-multipart-embedded-signed.mbox")));
         otp.decryptParts();
         auto partList = otp.collectContentParts();
         QCOMPARE(partList.size(), 1);
         auto part = partList[0].dynamicCast<MimeTreeParser::MessagePart>();
         QCOMPARE(part->encryptions().size(), 1);
         QCOMPARE(part->signatures().size(), 1);
         QCOMPARE(part->encryptionState(), MimeTreeParser::KMMsgFullyEncrypted);
         QCOMPARE(part->signatureState(), MimeTreeParser::KMMsgFullySigned);
         QCOMPARE(otp.plainTextContent(), QString::fromUtf8("test\n\n-- \nThis is a HTML signature.\n"));
 
         auto signaturePart = part->signatures().first();
         QVERIFY(signaturePart->partMetaData()->keyId.endsWith(QByteArray{"2E3B7787B1B75920"}));
         // We lack the public key for this message
-        QCOMPARE(signaturePart->partMetaData()->isGoodSignature, false);
-        QCOMPARE(signaturePart->partMetaData()->keyMissing, true);
+        QCOMPARE(false, signaturePart->partMetaData()->isGoodSignature);
+        QCOMPARE(GpgME::Signature::KeyMissing, signaturePart->partMetaData()->sigSummary);
     }
 
     void testAppleHtmlWithAttachments()
     {
         MimeTreeParser::ObjectTreeParser otp;
         otp.parseObjectTree(readMailFromFile(QLatin1String("applehtmlwithattachments.mbox")));
         otp.decryptParts();
         auto partList = otp.collectContentParts();
         QCOMPARE(partList.size(), 1);
         auto part = partList[0].dynamicCast<MimeTreeParser::AlternativeMessagePart>();
         QVERIFY(part);
         QCOMPARE(part->encryptions().size(), 0);
         QCOMPARE(part->signatures().size(), 0);
         QVERIFY(part->isHtml());
         QCOMPARE(otp.plainTextContent(), QString::fromUtf8("Hi,\n\nThis is an HTML message with attachments.\n\nCheers,\nChristian"));
         QCOMPARE(otp.htmlContent(),
                  QString::fromUtf8(
                      "<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=us-ascii\"></head><body style=\"word-wrap: break-word; "
                      "-webkit-nbsp-mode: space; line-break: after-white-space;\" class=\"\"><meta http-equiv=\"Content-Type\" content=\"text/html; "
                      "charset=us-ascii\" class=\"\"><div style=\"word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;\" "
                      "class=\"\">Hi,<div class=\"\"><br class=\"\"></div><blockquote style=\"margin: 0 0 0 40px; border: none; padding: 0px;\" class=\"\"><div "
                      "class=\"\">This is an <b class=\"\">HTML</b> message with attachments.</div></blockquote><div class=\"\"><br class=\"\"></div><div "
                      "class=\"\">Cheers,</div><div class=\"\">Christian<img apple-inline=\"yes\" id=\"B9EE68A9-83CA-41CD-A3E4-E5BA301F797A\" class=\"\" "
                      "src=\"cid:F5B62D1D-E4EC-4C59-AA5A-708525C2AC3C\"></div></div></body></html>"));
 
         auto attachments = otp.collectAttachmentParts();
         QCOMPARE(attachments.size(), 1);
     }
 
     void testAppleHtmlWithAttachmentsMixed()
     {
         MimeTreeParser::ObjectTreeParser otp;
         otp.parseObjectTree(readMailFromFile(QLatin1String("applehtmlwithattachmentsmixed.mbox")));
         otp.decryptParts();
         otp.print();
         auto partList = otp.collectContentParts();
         QCOMPARE(partList.size(), 1);
         auto part = partList[0].dynamicCast<MimeTreeParser::AlternativeMessagePart>();
         QVERIFY(part);
         QCOMPARE(part->encryptions().size(), 0);
         QCOMPARE(part->signatures().size(), 0);
         QVERIFY(part->isHtml());
         QCOMPARE(otp.plainTextContent(), QString::fromUtf8("Hello\n\n\n\nRegards\n\nFsdfsdf"));
         QCOMPARE(otp.htmlContent(),
                  QString::fromUtf8(
                      "<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=us-ascii\"></head><body style=\"word-wrap: break-word; "
                      "-webkit-nbsp-mode: space; line-break: after-white-space;\" class=\"\"><strike class=\"\">Hello</strike><div class=\"\"><br "
                      "class=\"\"></div><div class=\"\"></div></body></html><html><head><meta http-equiv=\"Content-Type\" content=\"text/html; "
                      "charset=us-ascii\"></head><body style=\"word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;\" "
                      "class=\"\"><div class=\"\"></div><div class=\"\"><br class=\"\"></div><div class=\"\"><b class=\"\">Regards</b></div><div class=\"\"><b "
                      "class=\"\"><br class=\"\"></b></div><div class=\"\">Fsdfsdf</div></body></html>"));
 
         auto attachments = otp.collectAttachmentParts();
         QCOMPARE(attachments.size(), 1);
     }
 
     void testInvitation()
     {
         MimeTreeParser::ObjectTreeParser otp;
         otp.parseObjectTree(readMailFromFile(QLatin1String("invitation.mbox")));
         otp.decryptParts();
         otp.print();
         auto partList = otp.collectContentParts();
         QCOMPARE(partList.size(), 1);
         auto part = partList[0].dynamicCast<MimeTreeParser::AlternativeMessagePart>();
         QVERIFY(part);
         QCOMPARE(part->encryptions().size(), 0);
         QCOMPARE(part->signatures().size(), 0);
         QVERIFY(!part->isHtml());
         QVERIFY(part->availableModes().contains(MimeTreeParser::AlternativeMessagePart::MultipartIcal));
 
         auto attachments = otp.collectAttachmentParts();
         QCOMPARE(attachments.size(), 0);
     }
 
     void testGmailInvitation()
     {
         MimeTreeParser::ObjectTreeParser otp;
         otp.parseObjectTree(readMailFromFile(QLatin1String("gmail-invitation.mbox")));
         otp.decryptParts();
         otp.print();
         auto partList = otp.collectContentParts();
         QCOMPARE(partList.size(), 1);
         auto part = partList[0].dynamicCast<MimeTreeParser::AlternativeMessagePart>();
         QVERIFY(part);
         QCOMPARE(part->encryptions().size(), 0);
         qWarning() << part;
         QCOMPARE(part->signatures().size(), 0);
         QVERIFY(part->isHtml());
         QVERIFY(part->availableModes().contains(MimeTreeParser::AlternativeMessagePart::MultipartIcal));
 
         auto attachments = otp.collectAttachmentParts();
         QCOMPARE(attachments.size(), 1);
     }
 
     void testMemoryHole()
     {
         MimeTreeParser::ObjectTreeParser otp;
         otp.parseObjectTree(readMailFromFile(QLatin1String("openpgp-encrypted-memoryhole.mbox")));
         otp.decryptParts();
         otp.print();
 
         auto partList = otp.collectContentParts();
         QCOMPARE(partList.size(), 1);
         auto part = partList[0].dynamicCast<MimeTreeParser::MessagePart>();
         QVERIFY(bool(part));
 
         QCOMPARE(part->text(), QStringLiteral("very secret mesage\n"));
 
         QCOMPARE(part->header("subject")->asUnicodeString(), QStringLiteral("hidden subject"));
         QCOMPARE(part->header("from")->asUnicodeString(), QStringLiteral("you@example.com"));
         QCOMPARE(part->header("to")->asUnicodeString(), QStringLiteral("me@example.com"));
         QCOMPARE(part->header("cc")->asUnicodeString(), QStringLiteral("cc@example.com"));
         QCOMPARE(part->header("message-id")->asUnicodeString(), QStringLiteral("<myhiddenreference@me>"));
         QCOMPARE(part->header("references")->asUnicodeString(), QStringLiteral("<hiddenreference@hidden>"));
         QCOMPARE(part->header("in-reply-to")->asUnicodeString(), QStringLiteral("<hiddenreference@hidden>"));
         QCOMPARE(static_cast<const KMime::Headers::Date *>(part->header("date"))->dateTime(), QDateTime(QDate(2018, 1, 2), QTime(3, 4, 5), QTimeZone::utc()));
     }
 
     /**
      * Required special handling because the list replaces the toplevel part.
      */
     void testMemoryHoleWithList()
     {
         MimeTreeParser::ObjectTreeParser otp;
         otp.parseObjectTree(readMailFromFile(QLatin1String("cid-links-forwarded-inline.mbox")));
         const auto parts = otp.collectContentParts();
         auto part = parts[0];
         QVERIFY(part->header("references"));
         QCOMPARE(part->header("references")->asUnicodeString(), QStringLiteral("<a1777ec781546ccc5dcd4918a5e4e03d@info>"));
     }
 
     void testMemoryHoleMultipartMixed()
     {
         MimeTreeParser::ObjectTreeParser otp;
         otp.parseObjectTree(readMailFromFile(QLatin1String("openpgp-encrypted-memoryhole2.mbox")));
         otp.decryptParts();
         otp.print();
 
         auto partList = otp.collectContentParts();
         QCOMPARE(partList.size(), 1);
         auto part = partList[0].dynamicCast<MimeTreeParser::MessagePart>();
         QVERIFY(bool(part));
 
         QCOMPARE(part->text(),
                  QStringLiteral("\n\n  Fsdflkjdslfj\n\n\nHappy Monday!\n\nBelow you will find a quick overview of the current on-goings. Remember\n"));
 
         QCOMPARE(part->header("subject")->asUnicodeString(), QStringLiteral("This is the subject"));
     }
 
     void testMIMESignature()
     {
         MimeTreeParser::ObjectTreeParser otp;
         otp.parseObjectTree(readMailFromFile(QLatin1String("text+html-maillinglist.mbox")));
         otp.decryptParts();
         otp.print();
 
         auto partList = otp.collectContentParts();
         for (const auto &part : partList) {
             qWarning() << "found part " << part->metaObject()->className();
         }
         QCOMPARE(partList.size(), 2);
         // The actual content
         {
             auto part = partList[0].dynamicCast<MimeTreeParser::AlternativeMessagePart>();
             QVERIFY(bool(part));
         }
 
         // The signature
         {
             auto part = partList[1].dynamicCast<MimeTreeParser::TextMessagePart>();
             QVERIFY(bool(part));
             QVERIFY(part->text().contains(QStringLiteral("bugzilla mailing list")));
         }
     }
 
     void testCRLFEncryptedWithSignature()
     {
         MimeTreeParser::ObjectTreeParser otp;
         otp.parseObjectTree(readMailFromFile(QLatin1String("crlf-encrypted-with-signature.mbox")));
         otp.decryptParts();
         otp.print();
 
         QCOMPARE(otp.plainTextContent(), QStringLiteral("CRLF file\n\n-- \nThis is a signature\nWith two lines\n\nAand another line\n"));
     }
 
     void testCRLFEncryptedWithSignatureMultipart()
     {
         MimeTreeParser::ObjectTreeParser otp;
         otp.parseObjectTree(readMailFromFile(QLatin1String("crlf-encrypted-with-signature-multipart.mbox")));
         otp.decryptParts();
         otp.print();
 
         // QEXPECT_FAIL("", "because MessagePart::parseInternal uses \n\n to detect encapsulated messages (so 'CRLF file' ends up as header)", Continue);
         // QCOMPARE(otp.plainTextContent(), QStringLiteral("CRLF file\n\n-- \nThis is a signature\nWith two lines\n\nAand another line\n"));
         // QVERIFY(!otp.htmlContent().contains(QStringLiteral("\r\n")));
     }
 
     void testCRLFOutlook()
     {
         MimeTreeParser::ObjectTreeParser otp;
         otp.parseObjectTree(readMailFromFile(QLatin1String("outlook.mbox")));
         otp.decryptParts();
         otp.print();
 
         qWarning() << otp.plainTextContent();
         QVERIFY(otp.plainTextContent().startsWith(QStringLiteral("Hi Christian,\n\nhabs gerade getestet:\n\n«This is a test")));
         QVERIFY(!otp.htmlContent().contains(QLatin1String("\r\n")));
     }
 
     void testOpenPGPEncryptedSignedThunderbird()
     {
         MimeTreeParser::ObjectTreeParser otp;
         otp.parseObjectTree(readMailFromFile(QLatin1String("openpgp-encrypted-signed-thunderbird.mbox")));
         otp.print();
         otp.decryptParts();
         otp.print();
         auto partList = otp.collectContentParts();
         QCOMPARE(partList.size(), 1);
         auto part = partList[0].dynamicCast<MimeTreeParser::MessagePart>();
         QVERIFY(bool(part));
         QCOMPARE(part->text(), QStringLiteral("sdfsdf\n"));
         QCOMPARE(part->charset(), QStringLiteral("utf-8").toLocal8Bit());
         QCOMPARE(part->encryptions().size(), 1);
         QCOMPARE(part->signatures().size(), 1);
         QCOMPARE(part->signatures()[0]->partMetaData()->isGoodSignature, true);
         QCOMPARE(part->encryptionState(), MimeTreeParser::KMMsgFullyEncrypted);
         QCOMPARE(part->signatureState(), MimeTreeParser::KMMsgFullySigned);
         auto contentAttachmentList = otp.collectAttachmentParts();
         QCOMPARE(contentAttachmentList.size(), 1);
         // QCOMPARE(contentAttachmentList[0]->content().size(), 1);
         QCOMPARE(contentAttachmentList[0]->encryptions().size(), 1);
         QCOMPARE(contentAttachmentList[0]->signatures().size(), 1);
         QCOMPARE(contentAttachmentList[0]->encryptionState(), MimeTreeParser::KMMsgFullyEncrypted);
         QCOMPARE(contentAttachmentList[0]->signatureState(), MimeTreeParser::KMMsgFullySigned);
     }
 
     void testSignedForwardOpenpgpSignedEncrypted()
     {
         MimeTreeParser::ObjectTreeParser otp;
         otp.parseObjectTree(readMailFromFile(QLatin1String("signed-forward-openpgp-signed-encrypted.mbox")));
         otp.print();
         otp.decryptParts();
         otp.print();
         auto partList = otp.collectContentParts();
         QCOMPARE(partList.size(), 2);
         auto part = partList[0].dynamicCast<MimeTreeParser::MessagePart>();
         QVERIFY(bool(part));
         QCOMPARE(part->text(), QStringLiteral("bla bla bla"));
 
         part = partList[1].dynamicCast<MimeTreeParser::MessagePart>();
         QVERIFY(bool(part));
         QCOMPARE(part->text(), QString());
         QCOMPARE(part->charset(), QStringLiteral("ISO-8859-1").toLocal8Bit());
         QCOMPARE(part->signatures().size(), 1);
         QCOMPARE(part->signatures()[0]->partMetaData()->isGoodSignature, true);
         auto contentAttachmentList = otp.collectAttachmentParts();
         QCOMPARE(contentAttachmentList.size(), 1);
     }
 
     void testSmimeOpaqueSign()
     {
         MimeTreeParser::ObjectTreeParser otp;
         otp.parseObjectTree(readMailFromFile(QLatin1String("smime-opaque-sign.mbox")));
         otp.print();
         otp.decryptParts();
         otp.print();
         auto partList = otp.collectContentParts();
         QCOMPARE(partList.size(), 1);
         auto part = partList[0].dynamicCast<MimeTreeParser::MessagePart>();
         QVERIFY(bool(part));
         QCOMPARE(part->text(), QStringLiteral("A simple signed only test."));
     }
 
     void testSmimeEncrypted()
     {
         MimeTreeParser::ObjectTreeParser otp;
         otp.parseObjectTree(readMailFromFile(QLatin1String("smime-encrypted.mbox")));
         otp.print();
         otp.decryptParts();
         otp.print();
         auto partList = otp.collectContentParts();
         QCOMPARE(partList.size(), 1);
         auto part = partList[0].dynamicCast<MimeTreeParser::MessagePart>();
         QVERIFY(bool(part));
         QCOMPARE(part->text(), QStringLiteral("The quick brown fox jumped over the lazy dog."));
     }
 
     void testSmimeSignedApple()
     {
         MimeTreeParser::ObjectTreeParser otp;
         otp.parseObjectTree(readMailFromFile(QLatin1String("smime-signed-apple.mbox")));
         otp.print();
         otp.decryptParts();
         otp.print();
         auto partList = otp.collectContentParts();
         QCOMPARE(partList.size(), 1);
         auto part = partList[0].dynamicCast<MimeTreeParser::MessagePart>();
         QVERIFY(bool(part));
         // QCOMPARE(part->text(), QStringLiteral("A simple signed only test."));
     }
 
     void testSmimeEncryptedOctetStream()
     {
         MimeTreeParser::ObjectTreeParser otp;
         otp.parseObjectTree(readMailFromFile(QLatin1String("smime-encrypted-octet-stream.mbox")));
         otp.print();
         otp.decryptParts();
         otp.print();
         auto partList = otp.collectContentParts();
         QCOMPARE(partList.size(), 1);
         auto part = partList[0].dynamicCast<MimeTreeParser::MessagePart>();
         QVERIFY(bool(part));
         QCOMPARE(part->text(), QStringLiteral("The quick brown fox jumped over the lazy dog."));
     }
 
     void testSmimeOpaqueSignedEncryptedAttachment()
     {
         MimeTreeParser::ObjectTreeParser otp;
         otp.parseObjectTree(readMailFromFile(QLatin1String("smime-opaque-signed-encrypted-attachment.mbox")));
         otp.print();
         otp.decryptParts();
         otp.print();
         auto partList = otp.collectContentParts();
         QCOMPARE(partList.size(), 1);
         auto part = partList[0].dynamicCast<MimeTreeParser::MessagePart>();
         QVERIFY(bool(part));
         QCOMPARE(part->text(), QStringLiteral("This is an Opaque S/MIME encrypted and signed message with attachment"));
     }
 
     void testSmimeOpaqueEncSign()
     {
         MimeTreeParser::ObjectTreeParser otp;
         otp.parseObjectTree(readMailFromFile(QLatin1String("smime-opaque-enc+sign.mbox")));
         otp.print();
         otp.decryptParts();
         otp.print();
         auto partList = otp.collectContentParts();
         QCOMPARE(partList.size(), 1);
         auto part = partList[0].dynamicCast<MimeTreeParser::MessagePart>();
         QVERIFY(bool(part));
         QCOMPARE(part->text(), QStringLiteral("Encrypted and signed mail."));
     }
 };
 
 QTEST_GUILESS_MAIN(MimeTreeParserTest)
 #include "mimetreeparsertest.moc"
diff --git a/src/core/bodypartformatter_impl.cpp b/src/core/bodypartformatter_impl.cpp
index 874a2a5..c8945ae 100644
--- a/src/core/bodypartformatter_impl.cpp
+++ b/src/core/bodypartformatter_impl.cpp
@@ -1,359 +1,360 @@
 // SPDX-FileCopyrightText: 2003 Marc Mutz <mutz@kde.org>
 // SPDX-License-Identifier: GPL-2.0-only
 
 #include "mimetreeparser_core_debug.h"
 
 #include "bodypartformatter.h"
 
 #include "bodypartformatterbasefactory.h"
 #include "bodypartformatterbasefactory_p.h"
 
 #include "messagepart.h"
 #include "objecttreeparser.h"
 #include "utils.h"
 
 #include <KMime/Content>
+#include <QGpgME/Protocol>
 
 using namespace MimeTreeParser;
 using namespace MimeTreeParser::Interface;
 
 namespace MimeTreeParser
 {
 class AnyTypeBodyPartFormatter : public MimeTreeParser::Interface::BodyPartFormatter
 {
 };
 
 class MessageRfc822BodyPartFormatter : public MimeTreeParser::Interface::BodyPartFormatter
 {
 public:
     MessagePart::Ptr process(ObjectTreeParser *objectTreeParser, KMime::Content *node) const Q_DECL_OVERRIDE
     {
         return MessagePart::Ptr(new EncapsulatedRfc822MessagePart(objectTreeParser, node, node->bodyAsMessage()));
     }
 };
 
 class HeadersBodyPartFormatter : public MimeTreeParser::Interface::BodyPartFormatter
 {
 public:
     MessagePart::Ptr process(ObjectTreeParser *objectTreeParser, KMime::Content *node) const Q_DECL_OVERRIDE
     {
         return MessagePart::Ptr(new HeadersPart(objectTreeParser, node));
     }
 };
 
 class MultiPartRelatedBodyPartFormatter : public MimeTreeParser::Interface::BodyPartFormatter
 {
 public:
     QVector<MessagePart::Ptr> processList(ObjectTreeParser *objectTreeParser, KMime::Content *node) const Q_DECL_OVERRIDE
     {
         if (node->contents().isEmpty()) {
             return {};
         }
         // We rely on the order of the parts.
         // Theoretically there could also be a Start parameter which would break this..
         // https://tools.ietf.org/html/rfc2387#section-4
 
         // We want to display attachments even if displayed inline.
         QVector<MessagePart::Ptr> list;
         list.append(MimeMessagePart::Ptr(new MimeMessagePart(objectTreeParser, node->contents().at(0), true)));
         for (int i = 1; i < node->contents().size(); i++) {
             auto p = node->contents().at(i);
             if (KMime::isAttachment(p)) {
                 list.append(MimeMessagePart::Ptr(new MimeMessagePart(objectTreeParser, p, true)));
             }
         }
         return list;
     }
 };
 
 class MultiPartMixedBodyPartFormatter : public MimeTreeParser::Interface::BodyPartFormatter
 {
 public:
     MessagePart::Ptr process(ObjectTreeParser *objectTreeParser, KMime::Content *node) const Q_DECL_OVERRIDE
     {
         const auto contents = node->contents();
         if (contents.isEmpty()) {
             return {};
         }
 
         // we need the intermediate part to preserve the headers (necessary for with protected headers using multipart mixed)
         auto part = MessagePart::Ptr(new MessagePart(objectTreeParser, {}, node));
         part->appendSubPart(MimeMessagePart::Ptr(new MimeMessagePart(objectTreeParser, contents.at(0), false)));
         return part;
     }
 };
 
 class ApplicationPGPEncryptedBodyPartFormatter : public MimeTreeParser::Interface::BodyPartFormatter
 {
 public:
     MessagePart::Ptr process(ObjectTreeParser *objectTreeParser, KMime::Content *node) const Q_DECL_OVERRIDE
     {
         if (node->decodedContent().trimmed() != "Version: 1") {
             qCWarning(MIMETREEPARSER_CORE_LOG) << "Unknown PGP Version String:" << node->decodedContent().trimmed();
         }
 
         if (!node->parent()) {
             return MessagePart::Ptr();
         }
 
         KMime::Content *data = findTypeInDirectChildren(node->parent(), "application/octet-stream");
 
         if (!data) {
             return MessagePart::Ptr(); // new MimeMessagePart(objectTreeParser, node));
         }
 
-        EncryptedMessagePart::Ptr mp(new EncryptedMessagePart(objectTreeParser, data->decodedText(), OpenPGP, node, data));
+        EncryptedMessagePart::Ptr mp(new EncryptedMessagePart(objectTreeParser, data->decodedText(), QGpgME::openpgp(), node, data));
         mp->setIsEncrypted(true);
         return mp;
     }
 };
 
 class ApplicationPkcs7MimeBodyPartFormatter : public MimeTreeParser::Interface::BodyPartFormatter
 {
 public:
     MessagePart::Ptr process(ObjectTreeParser *objectTreeParser, KMime::Content *node) const Q_DECL_OVERRIDE
     {
         if (node->head().isEmpty()) {
             return MessagePart::Ptr();
         }
 
         const QString smimeType = node->contentType()->parameter(QStringLiteral("smime-type")).toLower();
 
         if (smimeType == QLatin1String("certs-only")) {
-            return CertMessagePart::Ptr(new CertMessagePart(objectTreeParser, node, CMS));
+            return CertMessagePart::Ptr(new CertMessagePart(objectTreeParser, node, QGpgME::smime()));
         }
 
         bool isSigned = (smimeType == QLatin1String("signed-data"));
         bool isEncrypted = (smimeType == QLatin1String("enveloped-data"));
 
         // Analyze "signTestNode" node to find/verify a signature.
         // If zero part.objectTreeParser verification was successfully done after
         // decrypting via recursion by insertAndParseNewChildNode().
         KMime::Content *signTestNode = isEncrypted ? nullptr : node;
 
         // We try decrypting the content
         // if we either *know* that it is an encrypted message part
         // or there is neither signed nor encrypted parameter.
         MessagePart::Ptr mp;
         if (!isSigned) {
             if (isEncrypted) {
                 qCDebug(MIMETREEPARSER_CORE_LOG) << "pkcs7 mime     ==      S/MIME TYPE: enveloped (encrypted) data";
             } else {
                 qCDebug(MIMETREEPARSER_CORE_LOG) << "pkcs7 mime  -  type unknown  -  enveloped (encrypted) data ?";
             }
 
-            auto _mp = EncryptedMessagePart::Ptr(new EncryptedMessagePart(objectTreeParser, node->decodedText(), CMS, node));
+            auto _mp = EncryptedMessagePart::Ptr(new EncryptedMessagePart(objectTreeParser, node->decodedText(), QGpgME::smime(), node));
             mp = _mp;
             _mp->setIsEncrypted(true);
             // PartMetaData *messagePart(_mp->partMetaData());
             // if (!part.source()->decryptMessage()) {
             // isEncrypted = true;
             signTestNode = nullptr; // PENDING(marc) to be abs. sure, we'd need to have to look at the content
             // } else {
             //     _mp->startDecryption();
             //     if (messagePart->isDecryptable) {
             //         qCDebug(MIMETREEPARSER_CORE_LOG) << "pkcs7 mime  -  encryption found  -  enveloped (encrypted) data !";
             //         isEncrypted = true;
             //         part.nodeHelper()->setEncryptionState(node, KMMsgFullyEncrypted);
             //         signTestNode = nullptr;
 
             //     } else {
             //         // decryption failed, which could be because the part was encrypted but
             //         // decryption failed, or because we didn't know if it was encrypted, tried,
             //         // and failed. If the message was not actually encrypted, we continue
             //         // assuming it's signed
             //         if (_mp->passphraseError() || (smimeType.isEmpty() && messagePart->isEncrypted)) {
             //             isEncrypted = true;
             //             signTestNode = nullptr;
             //         }
 
             //         if (isEncrypted) {
             //             qCDebug(MIMETREEPARSER_CORE_LOG) << "pkcs7 mime  -  ERROR: COULD NOT DECRYPT enveloped data !";
             //         } else {
             //             qCDebug(MIMETREEPARSER_CORE_LOG) << "pkcs7 mime  -  NO encryption found";
             //         }
             //     }
             // }
         }
 
         // We now try signature verification if necessarry.
         if (signTestNode) {
             if (isSigned) {
                 qCDebug(MIMETREEPARSER_CORE_LOG) << "pkcs7 mime     ==      S/MIME TYPE: opaque signed data";
             } else {
                 qCDebug(MIMETREEPARSER_CORE_LOG) << "pkcs7 mime  -  type unknown  -  opaque signed data ?";
             }
 
-            return SignedMessagePart::Ptr(new SignedMessagePart(objectTreeParser, CMS, nullptr, signTestNode));
+            return SignedMessagePart::Ptr(new SignedMessagePart(objectTreeParser, QGpgME::smime(), nullptr, signTestNode));
         }
         return mp;
     }
 };
 
 class MultiPartAlternativeBodyPartFormatter : public MimeTreeParser::Interface::BodyPartFormatter
 {
 public:
     MessagePart::Ptr process(ObjectTreeParser *objectTreeParser, KMime::Content *node) const Q_DECL_OVERRIDE
     {
         if (node->contents().isEmpty()) {
             return MessagePart::Ptr();
         }
 
         AlternativeMessagePart::Ptr mp(new AlternativeMessagePart(objectTreeParser, node));
         if (mp->mChildParts.isEmpty()) {
             return MimeMessagePart::Ptr(new MimeMessagePart(objectTreeParser, node->contents().at(0)));
         }
         return mp;
     }
 };
 
 class MultiPartEncryptedBodyPartFormatter : public MimeTreeParser::Interface::BodyPartFormatter
 {
 public:
     MessagePart::Ptr process(ObjectTreeParser *objectTreeParser, KMime::Content *node) const Q_DECL_OVERRIDE
     {
         if (node->contents().isEmpty()) {
             Q_ASSERT(false);
             return MessagePart::Ptr();
         }
 
-        CryptoProtocol useThisCryptProto = UnknownProtocol;
+        const QGpgME::Protocol *protocol = nullptr;
 
         /*
         ATTENTION: This code is to be replaced by the new 'auto-detect' feature. --------------------------------------
         */
         KMime::Content *data = findTypeInDirectChildren(node, "application/octet-stream");
         if (data) {
-            useThisCryptProto = OpenPGP;
+            protocol = QGpgME::openpgp();
         } else {
             data = findTypeInDirectChildren(node, "application/pkcs7-mime");
             if (data) {
-                useThisCryptProto = CMS;
+                protocol = QGpgME::smime();
             }
         }
         /*
         ---------------------------------------------------------------------------------------------------------------
         */
 
         if (!data) {
             return MessagePart::Ptr(new MimeMessagePart(objectTreeParser, node->contents().at(0)));
         }
 
-        EncryptedMessagePart::Ptr mp(new EncryptedMessagePart(objectTreeParser, data->decodedText(), useThisCryptProto, node, data));
+        EncryptedMessagePart::Ptr mp(new EncryptedMessagePart(objectTreeParser, data->decodedText(), protocol, node, data));
         mp->setIsEncrypted(true);
         return mp;
     }
 };
 
 class MultiPartSignedBodyPartFormatter : public MimeTreeParser::Interface::BodyPartFormatter
 {
 public:
-    static CryptoProtocol detectProtocol(const QString &protocolContentType_, const QString &signatureContentType)
+    static const QGpgME::Protocol *detectProtocol(const QString &protocolContentType_, const QString &signatureContentType)
     {
         auto protocolContentType = protocolContentType_;
         if (protocolContentType.isEmpty()) {
             qCWarning(MIMETREEPARSER_CORE_LOG) << "Message doesn't set the protocol for the multipart/signed content-type, "
                                                   "using content-type of the signature:"
                                                << signatureContentType;
             protocolContentType = signatureContentType;
         }
 
-        CryptoProtocol protocol = UnknownProtocol;
+        const QGpgME::Protocol *protocol = nullptr;
         if (protocolContentType == QLatin1String("application/pkcs7-signature") || protocolContentType == QLatin1String("application/x-pkcs7-signature")) {
-            protocol = CMS;
+            protocol = QGpgME::smime();
         } else if (protocolContentType == QLatin1String("application/pgp-signature") || protocolContentType == QLatin1String("application/x-pgp-signature")) {
-            protocol = OpenPGP;
+            protocol = QGpgME::openpgp();
         }
         return protocol;
     }
 
     MessagePart::Ptr process(ObjectTreeParser *objectTreeParser, KMime::Content *node) const Q_DECL_OVERRIDE
     {
         if (node->contents().size() != 2) {
             qCDebug(MIMETREEPARSER_CORE_LOG) << "mulitpart/signed must have exactly two child parts!" << Qt::endl << "processing as multipart/mixed";
             if (!node->contents().isEmpty()) {
                 return MessagePart::Ptr(new MimeMessagePart(objectTreeParser, node->contents().at(0)));
             } else {
                 return MessagePart::Ptr();
             }
         }
 
         KMime::Content *signedData = node->contents().at(0);
         KMime::Content *signature = node->contents().at(1);
         Q_ASSERT(signedData);
         Q_ASSERT(signature);
 
         auto protocol =
             detectProtocol(node->contentType()->parameter(QStringLiteral("protocol")).toLower(), QLatin1String(signature->contentType()->mimeType().toLower()));
 
-        if (protocol == UnknownProtocol) {
+        if (!protocol) {
             return MessagePart::Ptr(new MimeMessagePart(objectTreeParser, signedData));
         }
 
         return SignedMessagePart::Ptr(new SignedMessagePart(objectTreeParser, protocol, signature, signedData));
     }
 };
 
 class TextHtmlBodyPartFormatter : public MimeTreeParser::Interface::BodyPartFormatter
 {
 public:
     MessagePart::Ptr process(ObjectTreeParser *objectTreeParser, KMime::Content *node) const Q_DECL_OVERRIDE
     {
         return HtmlMessagePart::Ptr(new HtmlMessagePart(objectTreeParser, node));
     }
 };
 
 class TextPlainBodyPartFormatter : public MimeTreeParser::Interface::BodyPartFormatter
 {
 public:
     MessagePart::Ptr process(ObjectTreeParser *objectTreeParser, KMime::Content *node) const Q_DECL_OVERRIDE
     {
         if (KMime::isAttachment(node)) {
             return AttachmentMessagePart::Ptr(new AttachmentMessagePart(objectTreeParser, node));
         }
         return TextMessagePart::Ptr(new TextMessagePart(objectTreeParser, node));
     }
 };
 
 } // anon namespace
 
 void BodyPartFormatterBaseFactoryPrivate::messageviewer_create_builtin_bodypart_formatters()
 {
     auto any = new AnyTypeBodyPartFormatter;
     auto textPlain = new TextPlainBodyPartFormatter;
     auto pkcs7 = new ApplicationPkcs7MimeBodyPartFormatter;
     auto pgp = new ApplicationPGPEncryptedBodyPartFormatter;
     auto html = new TextHtmlBodyPartFormatter;
     auto headers = new HeadersBodyPartFormatter;
     auto multipartAlternative = new MultiPartAlternativeBodyPartFormatter;
     auto multipartMixed = new MultiPartMixedBodyPartFormatter;
     auto multipartSigned = new MultiPartSignedBodyPartFormatter;
     auto multipartEncrypted = new MultiPartEncryptedBodyPartFormatter;
     auto message = new MessageRfc822BodyPartFormatter;
     auto multipartRelated = new MultiPartRelatedBodyPartFormatter;
 
     insert("application", "octet-stream", any);
     insert("application", "pgp", textPlain);
     insert("application", "pkcs7-mime", pkcs7);
     insert("application", "x-pkcs7-mime", pkcs7);
     insert("application", "pgp-encrypted", pgp);
     insert("application", "*", any);
 
     insert("text", "html", html);
     insert("text", "rtf", any);
     insert("text", "plain", textPlain);
     insert("text", "rfc822-headers", headers);
     insert("text", "*", textPlain);
 
     insert("image", "*", any);
 
     insert("message", "rfc822", message);
     insert("message", "*", any);
 
     insert("multipart", "alternative", multipartAlternative);
     insert("multipart", "encrypted", multipartEncrypted);
     insert("multipart", "signed", multipartSigned);
     insert("multipart", "related", multipartRelated);
     insert("multipart", "*", multipartMixed);
     insert("*", "*", any);
 }
diff --git a/src/core/crypto.cpp b/src/core/crypto.cpp
deleted file mode 100644
index d5a4dd2..0000000
--- a/src/core/crypto.cpp
+++ /dev/null
@@ -1,593 +0,0 @@
-// SPDX-FileCopyrightText: 2009 Constantin Berzan <exit3219@gmail.com>
-// SPDX-FileCopyrightText: 2010 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
-// SPDX-FileCopyrightText: 2010 Leo Franchi <lfranchi@kde.org>
-// SPDX-FileCopyrightText: 2017 Christian Mollekopf <mollekopf@kolabsys.com>
-// SPDX-License-Identifier: LGPL-2.0-or-later
-
-#include "crypto.h"
-
-#include <gpgme.h>
-
-#include <QDebug>
-#include <QFile>
-
-#include <future>
-#include <utility>
-
-using namespace Crypto;
-
-QDebug operator<<(QDebug d, const Key &key)
-{
-    d << key.fingerprint;
-    return d;
-}
-
-QDebug operator<<(QDebug d, const Error &error)
-{
-    d << error.error;
-    return d;
-}
-
-namespace Crypto
-{
-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(nullptr);
-    const gpgme_protocol_t p = protocol == CMS ? GPGME_PROTOCOL_CMS : GPGME_PROTOCOL_OpenPGP;
-    return gpgme_engine_check_version(p);
-}
-
-static std::pair<gpgme_error_t, gpgme_ctx_t> createForProtocol(CryptoProtocol proto)
-{
-    if (auto e = checkEngine(proto)) {
-        qWarning() << "GPG Engine check failed." << e;
-        return std::make_pair(e, nullptr);
-    }
-    gpgme_ctx_t ctx = nullptr;
-    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);
-    }
-    // We want the output to always be ASCII armored
-    gpgme_set_armor(ctx, 1);
-
-    // Trust new keys
-    if (auto e = gpgme_set_ctx_flag(ctx, "trust-model", "tofu+pgp")) {
-        gpgme_release(ctx);
-        return std::make_pair(e, nullptr);
-    }
-
-    // That's a great way to bring signature verification to a crawl
-    if (auto e = gpgme_set_ctx_flag(ctx, "auto-key-retrieve", "0")) {
-        gpgme_release(ctx);
-        return std::make_pair(e, nullptr);
-    }
-
-    return std::make_pair(GPG_ERR_NO_ERROR, ctx);
-}
-
-gpgme_error_t gpgme_passphrase(void *hook, const char *uid_hint, const char *passphrase_info, int prev_was_bad, int fd)
-{
-    Q_UNUSED(hook);
-    Q_UNUSED(prev_was_bad);
-    // uid_hint will be something like "CAA5183608F0FB50 Test1 Kolab <test1@kolab.org>" (CAA518... is the key)
-    // pahhphrase_info will be something like "CAA5183608F0FB50 2E3B7787B1B75920 1 0"
-    qInfo() << "Requested passphrase for " << (uid_hint ? QByteArray{uid_hint} : QByteArray{})
-            << (passphrase_info ? QByteArray{passphrase_info} : QByteArray{});
-
-    QFile file;
-    file.open(fd, QIODevice::WriteOnly);
-    // FIXME hardcoded as a test
-    QByteArray passphrase = QByteArray{"test1"} + QByteArray{"\n"};
-    file.write(passphrase);
-    file.close();
-
-    return 0;
-}
-
-namespace Crypto
-{
-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<int>(length)};
-    gpgme_free(data);
-    return outdata;
-}
-
-static std::vector<Recipient> copyRecipients(gpgme_decrypt_result_t result)
-{
-    std::vector<Recipient> recipients;
-    for (gpgme_recipient_t r = result->recipients; r; r = r->next) {
-        recipients.push_back({QByteArray{r->keyid}, r->status != GPG_ERR_NO_SECKEY});
-    }
-    return recipients;
-}
-
-static std::vector<Signature> copySignatures(gpgme_verify_result_t result)
-{
-    std::vector<Signature> signatures;
-    for (gpgme_signature_t is = result->signatures; is; is = is->next) {
-        Signature sig;
-        sig.fingerprint = QByteArray{is->fpr};
-        sig.creationTime.setSecsSinceEpoch(is->timestamp);
-        if (is->summary & GPGME_SIGSUM_VALID) {
-            sig.result = Signature::Ok;
-        } else {
-            sig.result = Signature::Invalid;
-            if (is->summary & GPGME_SIGSUM_KEY_EXPIRED) {
-                sig.result = Signature::Expired;
-            }
-            if (is->summary & GPGME_SIGSUM_KEY_MISSING) {
-                sig.result = Signature::KeyNotFound;
-            }
-        }
-        sig.status = {is->status};
-        sig.isTrusted = is->validity == GPGME_VALIDITY_FULL || is->validity == GPGME_VALIDITY_ULTIMATE;
-        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, nullptr);
-    gpgme_verify_result_t res = gpgme_op_verify_result(ctx);
-    return {copySignatures(res), {err}};
-}
-
-static DecryptionResult::Result toResult(gpgme_error_t err)
-{
-    if (err == GPG_ERR_NO_DATA) {
-        return DecryptionResult::NotEncrypted;
-    } else if (err == GPG_ERR_NO_SECKEY) {
-        return DecryptionResult::NoSecretKeyError;
-    } else if (err == GPG_ERR_CANCELED || err == GPG_ERR_INV_PASSPHRASE) {
-        return DecryptionResult::PassphraseError;
-    } else if (err == GPG_ERR_NO_ERROR) {
-        return DecryptionResult::NoSecretKeyError;
-    }
-
-    qWarning() << "unknown error" << err << gpgme_strerror(err);
-    return DecryptionResult::NoSecretKeyError;
-}
-
-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, nullptr, 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<DecryptionResult, VerificationResult> Crypto::decryptAndVerify(CryptoProtocol protocol, const QByteArray &ciphertext, QByteArray &outdata)
-{
-    Context context{protocol};
-    if (!context) {
-        qWarning() << "Failed to create context " << gpgme_strerror(context.error);
-        qWarning() << "returning early";
-        return std::make_pair(DecryptionResult{{}, {context.error}, DecryptionResult::NoSecretKeyError}, 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};
-        // We make sure we don't return any plain-text if the decryption failed to prevent EFAIL
-        if (err == GPG_ERR_DECRYPT_FAILED) {
-            return std::make_pair(DecryptionResult{{}, {err}, DecryptionResult::DecryptionError}, VerificationResult{{}, {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);
-    }
-    decryptionResult.result = toResult(err);
-
-    outdata = toBA(out);
-    return std::make_pair(decryptionResult, verificationResult);
-}
-
-static DecryptionResult decryptGPGME(CryptoProtocol protocol, const QByteArray &ciphertext, QByteArray &outdata)
-{
-    Context context{protocol};
-    if (!context) {
-        qWarning() << "Failed to create context " << context.error;
-        return DecryptionResult{{}, 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(ctx, Data{ciphertext}.data, out);
-    if (err) {
-        qWarning() << "Failed to decrypt" << gpgme_strerror(err);
-        // We make sure we don't return any plain-text if the decryption failed to prevent EFAIL
-        if (err == GPG_ERR_DECRYPT_FAILED) {
-            return DecryptionResult{{}, {err}};
-        }
-    }
-
-    DecryptionResult decryptionResult{{}, {err}};
-    if (gpgme_decrypt_result_t res = gpgme_op_decrypt_result(ctx)) {
-        decryptionResult.recipients = copyRecipients(res);
-    }
-
-    decryptionResult.result = toResult(err);
-
-    outdata = toBA(out);
-    return decryptionResult;
-}
-
-DecryptionResult Crypto::decrypt(CryptoProtocol protocol, const QByteArray &ciphertext, QByteArray &outdata)
-{
-    return decryptGPGME(protocol, ciphertext, outdata);
-}
-
-ImportResult Crypto::importKey(CryptoProtocol protocol, const QByteArray &certData)
-{
-    Context context{protocol};
-    if (!context) {
-        qWarning() << "Failed to create context " << context.error;
-        return {0, 0, 0};
-    }
-    if (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 bool validateKey(const gpgme_key_t key)
-{
-    if (key->revoked) {
-        qWarning() << "Key is revoked " << key->fpr;
-        return false;
-    }
-    if (key->expired) {
-        qWarning() << "Key is expired " << key->fpr;
-        return false;
-    }
-    if (key->disabled) {
-        qWarning() << "Key is disabled " << key->fpr;
-        return false;
-    }
-    if (key->invalid) {
-        qWarning() << "Key is invalid " << key->fpr;
-        return false;
-    }
-    return true;
-}
-
-static KeyListResult listKeys(CryptoProtocol protocol, const std::vector<const char *> &patterns, bool secretOnly, int keyListMode, bool importKeys)
-{
-    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};
-    auto zeroTerminatedPatterns = patterns;
-    zeroTerminatedPatterns.push_back(nullptr);
-    if (patterns.size() > 1) {
-        if (auto err = gpgme_op_keylist_ext_start(ctx, const_cast<const char **>(zeroTerminatedPatterns.data()), int(secretOnly), 0)) {
-            result.error = {err};
-            qWarning() << "Error while listing keys:" << result.error;
-        }
-    } else if (patterns.size() == 1) {
-        if (auto err = gpgme_op_keylist_start(ctx, zeroTerminatedPatterns[0], int(secretOnly))) {
-            result.error = {err};
-            qWarning() << "Error while listing keys:" << result.error;
-        }
-    } else {
-        if (auto err = gpgme_op_keylist_start(ctx, nullptr, int(secretOnly))) {
-            result.error = {err};
-            qWarning() << "Error while listing keys:" << result.error;
-        }
-    }
-
-    std::vector<gpgme_key_t> listedKeys;
-    while (true) {
-        gpgme_key_t key;
-        if (auto err = gpgme_op_keylist_next(ctx, &key)) {
-            if (gpgme_err_code(err) != GPG_ERR_EOF) {
-                qWarning() << "Error after listing keys" << result.error << gpgme_strerror(err);
-            }
-            break;
-        }
-
-        listedKeys.push_back(key);
-
-        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.isUsable = validateKey(key);
-        result.keys.push_back(k);
-    }
-    gpgme_op_keylist_end(ctx);
-
-    if (importKeys && !listedKeys.empty()) {
-        listedKeys.push_back(nullptr);
-        if (auto err = gpgme_op_import_keys(ctx, const_cast<gpgme_key_t *>(listedKeys.data()))) {
-            qWarning() << "Error while importing keys" << gpgme_strerror(err);
-        }
-    }
-    return result;
-}
-
-/**
- * Get the given `key` in the armor format.
- */
-Expected<Error, QByteArray> 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.data(), 0, out)) {
-        return makeUnexpected(Error{err});
-    }
-
-    return toBA(out);
-}
-
-Expected<Error, QByteArray> Crypto::signAndEncrypt(const QByteArray &content, const std::vector<Key> &encryptionKeys, const std::vector<Key> &signingKeys)
-{
-    Context context;
-    if (!context) {
-        return makeUnexpected(Error{context.error});
-    }
-
-    for (const auto &signingKey : signingKeys) {
-        qDebug() << "Signing with " << signingKey;
-        // TODO do we have to free those again?
-        gpgme_key_t key;
-        if (auto e = gpgme_get_key(context.context, signingKey.fingerprint.data(), &key, /*secret*/ false)) {
-            qWarning() << "Failed to retrieve signing key " << signingKey.fingerprint << Error{e};
-            return makeUnexpected(Error{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) {
-        qDebug() << "Encrypting to " << k;
-        gpgme_key_t key;
-        if (auto e = gpgme_get_key(context.context, k.fingerprint.data(), &key, /*secret*/ false)) {
-            delete[] keys;
-            qWarning() << "Failed to retrieve key " << k.fingerprint << Error{e};
-            return makeUnexpected(Error{e});
-        } else {
-            if (!key->can_encrypt || !validateKey(key)) {
-                qWarning() << "Key cannot be used for encryption " << k.fingerprint;
-                delete[] keys;
-                return makeUnexpected(Error{e});
-            }
-            *keys_it++ = key;
-        }
-    }
-    *keys_it++ = nullptr;
-
-    gpgme_data_t out;
-    if (auto e = gpgme_data_new(&out)) {
-        qWarning() << "Failed to allocate output buffer";
-        delete[] keys;
-        return makeUnexpected(Error{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:" << gpgme_err_code(err);
-        switch (gpgme_err_code(err)) {
-        case GPG_ERR_UNUSABLE_PUBKEY:
-            for (const auto &k : encryptionKeys) {
-                qWarning() << "Encryption key:" << k;
-            }
-            break;
-        case GPG_ERR_UNUSABLE_SECKEY:
-            for (const auto &k : signingKeys) {
-                qWarning() << "Signing key:" << k;
-            }
-            break;
-        default:
-            break;
-        }
-        return makeUnexpected(Error{err});
-    }
-
-    return toBA(out);
-}
-
-Expected<Error, std::pair<QByteArray, QString>> Crypto::sign(const QByteArray &content, const std::vector<Key> &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.data(), &key, /*secret*/ false)) {
-            qWarning() << "Failed to retrieve signing key " << signingKey.fingerprint << Error{e};
-            return makeUnexpected(Error{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)) {
-            if (gpgme_new_signature_t is = res->signatures) {
-                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 = (QStringLiteral("pgp-") + QString::fromUtf8(algo)).toLower();
-
-    return std::pair<QByteArray, QString>{toBA(out), micAlg};
-}
-
-std::vector<Key> 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<char const *> pattern;
-    std::transform(list.constBegin(), list.constEnd(), std::back_inserter(pattern), [](const QByteArray &s) {
-        return s.constData();
-    });
-
-    const KeyListResult res = listKeys(OpenPGP, pattern, findPrivate, remote ? GPGME_KEYLIST_MODE_EXTERN : GPGME_KEYLIST_MODE_LOCAL, remote);
-    if (res.error) {
-        qWarning() << "Failed to lookup keys: " << res.error;
-        return {};
-    }
-    qDebug() << "Found " << res.keys.size() << " keys for the patterns: " << patterns;
-
-    std::vector<Key> usableKeys;
-    for (const auto &key : res.keys) {
-        if (!key.isUsable) {
-            qWarning() << "Key is not usable: " << key.fingerprint;
-            continue;
-        }
-
-        qDebug() << "Key:" << key.fingerprint;
-        for (const auto &userId : key.userIds) {
-            qDebug() << "  userID:" << userId.email;
-        }
-        usableKeys.push_back(key);
-    }
-    return usableKeys;
-}
diff --git a/src/core/crypto.h b/src/core/crypto.h
deleted file mode 100644
index 5f78705..0000000
--- a/src/core/crypto.h
+++ /dev/null
@@ -1,104 +0,0 @@
-// SPDX-FileCopyrightText: 2016 Christian Mollekopf <mollekopf@kolabsys.com>
-// SPDX-License-Identifier: LGPL-2.0-or-later
-
-#pragma once
-
-#include "errors.h"
-#include "mimetreeparser_core_export.h"
-#include <QByteArray>
-#include <QVariant>
-
-#include <QDateTime>
-#include <functional>
-#include <memory>
-
-namespace Crypto
-{
-
-enum CryptoProtocol { UnknownProtocol, OpenPGP, CMS };
-
-struct UserId {
-    QByteArray name;
-    QByteArray email;
-    QByteArray id;
-};
-
-struct Key {
-    QByteArray keyId;
-    QByteArray shortKeyId;
-    QByteArray fingerprint;
-    bool isUsable = false;
-    std::vector<UserId> userIds;
-};
-
-struct Error {
-    unsigned int error;
-    operator bool() const
-    {
-        return error != 0;
-    }
-};
-
-struct Signature {
-    QByteArray fingerprint;
-    Error status;
-    QDateTime creationTime;
-    enum Result { Ok, NotVerified, Expired, KeyNotFound, Invalid };
-    Result result{NotVerified};
-    bool isTrusted{false};
-};
-
-struct VerificationResult {
-    std::vector<Signature> signatures;
-    Error error;
-};
-
-struct Recipient {
-    QByteArray keyId;
-    bool secretKeyAvailable{false};
-};
-
-struct DecryptionResult {
-    std::vector<Recipient> recipients;
-    Error error;
-    enum Result { NoError, NotEncrypted, PassphraseError, NoSecretKeyError, DecryptionError };
-    Result result{NoError};
-};
-
-struct KeyListResult {
-    std::vector<Key> keys;
-    Error error;
-};
-
-struct ImportResult {
-    int considered;
-    int imported;
-    int unchanged;
-};
-
-MIMETREEPARSER_CORE_EXPORT std::vector<Key> findKeys(const QStringList &filter, bool findPrivate = false, bool remote = false);
-
-MIMETREEPARSER_CORE_EXPORT Expected<Error, QByteArray> exportPublicKey(const Key &key);
-
-MIMETREEPARSER_CORE_EXPORT ImportResult importKey(CryptoProtocol protocol, const QByteArray &certData);
-MIMETREEPARSER_CORE_EXPORT ImportResult importKey(CryptoProtocol protocol, const Key &key);
-
-/**
- * Sign the given content and returns the signing data and the algorithm used
- * for integrity check in the "pgp-<algorithm>" format.
- */
-MIMETREEPARSER_CORE_EXPORT Expected<Error, std::pair<QByteArray, QString>> sign(const QByteArray &content, const std::vector<Key> &signingKeys);
-MIMETREEPARSER_CORE_EXPORT Expected<Error, QByteArray>
-signAndEncrypt(const QByteArray &content, const std::vector<Key> &encryptionKeys, const std::vector<Key> &signingKeys);
-
-MIMETREEPARSER_CORE_EXPORT std::pair<DecryptionResult, VerificationResult>
-decryptAndVerify(CryptoProtocol protocol, const QByteArray &ciphertext, QByteArray &outdata);
-MIMETREEPARSER_CORE_EXPORT DecryptionResult decrypt(CryptoProtocol protocol, const QByteArray &ciphertext, QByteArray &outdata);
-MIMETREEPARSER_CORE_EXPORT VerificationResult verifyDetachedSignature(CryptoProtocol protocol, const QByteArray &signature, const QByteArray &outdata);
-MIMETREEPARSER_CORE_EXPORT VerificationResult verifyOpaqueSignature(CryptoProtocol protocol, const QByteArray &signature, QByteArray &outdata);
-};
-
-Q_DECLARE_METATYPE(Crypto::Key);
-
-MIMETREEPARSER_CORE_EXPORT QDebug operator<<(QDebug d, const Crypto::Key &);
-MIMETREEPARSER_CORE_EXPORT QDebug operator<<(QDebug d, const Crypto::Error &);
diff --git a/src/core/cryptohelper.cpp b/src/core/cryptohelper.cpp
index b1ed365..d9bc7b0 100644
--- a/src/core/cryptohelper.cpp
+++ b/src/core/cryptohelper.cpp
@@ -1,137 +1,145 @@
 // SPDX-FileCopyrightText: 2015 Sandro Knauß <knauss@kolabsys.com>
 // SPDX-FileCopyrightText: 2001,2002 the KPGP authors
 // SPDX-License-Identifier: GPL-2.0-or-later
 
 #include "cryptohelper.h"
 
 using namespace MimeTreeParser;
 
 PGPBlockType Block::determineType() const
 {
     const QByteArray data = text();
-    if (data.startsWith("-----BEGIN PGP SIGNED")) {
+    if (data.startsWith("-----BEGIN PGP PUBLIC KEY BLOCK-----")) {
+        return NoPgpBlock;
+    } else if (data.startsWith("-----BEGIN PGP SIGNED")) {
         return ClearsignedBlock;
     } else if (data.startsWith("-----BEGIN PGP SIGNATURE")) {
         return SignatureBlock;
     } else if (data.startsWith("-----BEGIN PGP PUBLIC")) {
         return PublicKeyBlock;
     } else if (data.startsWith("-----BEGIN PGP PRIVATE") || data.startsWith("-----BEGIN PGP SECRET")) {
         return PrivateKeyBlock;
     } else if (data.startsWith("-----BEGIN PGP MESSAGE")) {
         if (data.startsWith("-----BEGIN PGP MESSAGE PART")) {
             return MultiPgpMessageBlock;
         } else {
             return PgpMessageBlock;
         }
     } else if (data.startsWith("-----BEGIN PGP ARMORED FILE")) {
         return PgpMessageBlock;
     } else if (data.startsWith("-----BEGIN PGP ")) {
         return UnknownBlock;
     } else {
         return NoPgpBlock;
     }
 }
 
-QList<Block> MimeTreeParser::prepareMessageForDecryption(const QByteArray &msg)
+QVector<Block> MimeTreeParser::prepareMessageForDecryption(const QByteArray &msg)
 {
     PGPBlockType pgpBlock = NoPgpBlock;
-    QList<Block> blocks;
+    QVector<Block> blocks;
     int start = -1; // start of the current PGP block
     int lastEnd = -1; // end of the last PGP block
     const int length = msg.length();
 
     if (msg.isEmpty()) {
         return blocks;
     }
+    if (msg.startsWith("-----BEGIN PGP PUBLIC KEY BLOCK-----")) {
+        return blocks;
+    }
 
     if (msg.startsWith("-----BEGIN PGP ")) {
         start = 0;
     } else {
         start = msg.indexOf("\n-----BEGIN PGP ") + 1;
         if (start == 0) {
             blocks.append(Block(msg, NoPgpBlock));
             return blocks;
         }
     }
 
     while (start != -1) {
-        int nextEnd, nextStart;
+        int nextEnd;
+        int nextStart;
 
         // is the PGP block a clearsigned block?
         if (!strncmp(msg.constData() + start + 15, "SIGNED", 6)) {
             pgpBlock = ClearsignedBlock;
         } else {
             pgpBlock = UnknownBlock;
         }
 
         nextEnd = msg.indexOf("\n-----END PGP ", start + 15);
         nextStart = msg.indexOf("\n-----BEGIN PGP ", start + 15);
 
         if (nextEnd == -1) { // Missing END PGP line
             if (lastEnd != -1) {
                 blocks.append(Block(msg.mid(lastEnd + 1), UnknownBlock));
             } else {
                 blocks.append(Block(msg.mid(start), UnknownBlock));
             }
             break;
         }
 
         if ((nextStart == -1) || (nextEnd < nextStart) || (pgpBlock == ClearsignedBlock)) {
             // most likely we found a PGP block (but we don't check if it's valid)
 
             // store the preceding non-PGP block
             if (start - lastEnd - 1 > 0) {
                 blocks.append(Block(msg.mid(lastEnd + 1, start - lastEnd - 1), NoPgpBlock));
             }
 
             lastEnd = msg.indexOf("\n", nextEnd + 14);
             if (lastEnd == -1) {
                 if (start < length) {
                     blocks.append(Block(msg.mid(start)));
                 }
                 break;
             } else {
                 blocks.append(Block(msg.mid(start, lastEnd + 1 - start)));
                 if ((nextStart != -1) && (nextEnd > nextStart)) {
                     nextStart = msg.indexOf("\n-----BEGIN PGP ", lastEnd + 1);
                 }
             }
         }
 
         start = nextStart;
 
         if (start == -1) {
             if (lastEnd + 1 < length) {
                 // rest of mail is no PGP Block
                 blocks.append(Block(msg.mid(lastEnd + 1), NoPgpBlock));
             }
             break;
         } else {
             start++; // move start behind the '\n'
         }
     }
 
     return blocks;
 }
 
+Block::Block() = default;
+
 Block::Block(const QByteArray &m)
     : msg(m)
 {
     mType = determineType();
 }
 
 Block::Block(const QByteArray &m, PGPBlockType t)
     : msg(m)
     , mType(t)
 {
 }
 
 QByteArray MimeTreeParser::Block::text() const
 {
     return msg;
 }
 
 PGPBlockType Block::type() const
 {
     return mType;
 }
diff --git a/src/core/cryptohelper.h b/src/core/cryptohelper.h
index 88d939f..b3ff4b8 100644
--- a/src/core/cryptohelper.h
+++ b/src/core/cryptohelper.h
@@ -1,46 +1,45 @@
 // SPDX-FileCopyrightText: 2015 Sandro Knauß <knauss@kolabsys.com>
 // SPDX-FileCopyrightText: 2001,2002 the KPGP authors
 // SPDX-License-Identifier: GPL-2.0-or-later
 
 #pragma once
 
 #include <QByteArray>
-#include <QList>
+#include <QVector>
 
 namespace MimeTreeParser
 {
-
 enum PGPBlockType {
     UnknownBlock = -1, // BEGIN PGP ???
     NoPgpBlock = 0,
     PgpMessageBlock = 1, // BEGIN PGP MESSAGE
     MultiPgpMessageBlock = 2, // BEGIN PGP MESSAGE, PART X[/Y]
     SignatureBlock = 3, // BEGIN PGP SIGNATURE
     ClearsignedBlock = 4, // BEGIN PGP SIGNED MESSAGE
     PublicKeyBlock = 5, // BEGIN PGP PUBLIC KEY BLOCK
-    PrivateKeyBlock = 6 // BEGIN PGP PRIVATE KEY BLOCK (PGP 2.x: ...SECRET...)
+    PrivateKeyBlock = 6, // BEGIN PGP PRIVATE KEY BLOCK (PGP 2.x: ...SECRET...)
 };
 
 class Block
 {
 public:
-    explicit Block(const QByteArray &m);
+    Block();
+    Block(const QByteArray &m);
 
     Block(const QByteArray &m, PGPBlockType t);
 
-    QByteArray text() const;
-    PGPBlockType type() const;
-    PGPBlockType determineType() const;
+    Q_REQUIRED_RESULT QByteArray text() const;
+    Q_REQUIRED_RESULT PGPBlockType type() const;
+    Q_REQUIRED_RESULT PGPBlockType determineType() const;
 
     QByteArray msg;
-    PGPBlockType mType;
+    PGPBlockType mType = UnknownBlock;
 };
 
 /** Parses the given message and splits it into OpenPGP blocks and
     Non-OpenPGP blocks.
 */
-QList<Block> prepareMessageForDecryption(const QByteArray &msg);
-
+Q_REQUIRED_RESULT QVector<Block> prepareMessageForDecryption(const QByteArray &msg);
 } // namespace MimeTreeParser
 
 Q_DECLARE_TYPEINFO(MimeTreeParser::Block, Q_MOVABLE_TYPE);
diff --git a/src/core/job/qgpgmejobexecutor.cpp b/src/core/job/qgpgmejobexecutor.cpp
new file mode 100644
index 0000000..738c8f0
--- /dev/null
+++ b/src/core/job/qgpgmejobexecutor.cpp
@@ -0,0 +1,140 @@
+/*
+    SPDX-FileCopyrightText: 2008 Volker Krause <vkrause@kde.org>
+
+    SPDX-License-Identifier: GPL-2.0-or-later
+*/
+
+#include "qgpgmejobexecutor.h"
+#include "mimetreeparser_core_debug.h"
+
+#include <QGpgME/DecryptVerifyJob>
+#include <QGpgME/ImportJob>
+#include <QGpgME/VerifyDetachedJob>
+#include <QGpgME/VerifyOpaqueJob>
+
+#include <QEventLoop>
+
+#include <cassert>
+
+using namespace GpgME;
+using namespace MimeTreeParser;
+
+QGpgMEJobExecutor::QGpgMEJobExecutor(QObject *parent)
+    : QObject(parent)
+    , mEventLoop(new QEventLoop(this))
+{
+    setObjectName(QStringLiteral("KleoJobExecutor"));
+}
+
+GpgME::VerificationResult QGpgMEJobExecutor::exec(QGpgME::VerifyDetachedJob *job, const QByteArray &signature, const QByteArray &signedData)
+{
+    qCDebug(MIMETREEPARSER_CORE_LOG) << "Starting detached verification job";
+    connect(job, &QGpgME::VerifyDetachedJob::result, this, qOverload<const GpgME::VerificationResult &>(&QGpgMEJobExecutor::verificationResult));
+    GpgME::Error err = job->start(signature, signedData);
+    if (err) {
+        return VerificationResult(err);
+    }
+    mEventLoop->exec(QEventLoop::ExcludeUserInputEvents);
+    return mVerificationResult;
+}
+
+GpgME::VerificationResult QGpgMEJobExecutor::exec(QGpgME::VerifyOpaqueJob *job, const QByteArray &signedData, QByteArray &plainText)
+{
+    qCDebug(MIMETREEPARSER_CORE_LOG) << "Starting opaque verification job";
+    connect(job,
+            &QGpgME::VerifyOpaqueJob::result,
+            this,
+            qOverload<const GpgME::VerificationResult &, const QByteArray &>(&QGpgMEJobExecutor::verificationResult));
+    GpgME::Error err = job->start(signedData);
+    if (err) {
+        plainText.clear();
+        return VerificationResult(err);
+    }
+    mEventLoop->exec(QEventLoop::ExcludeUserInputEvents);
+    plainText = mData;
+    return mVerificationResult;
+}
+
+std::pair<GpgME::DecryptionResult, GpgME::VerificationResult>
+QGpgMEJobExecutor::exec(QGpgME::DecryptVerifyJob *job, const QByteArray &cipherText, QByteArray &plainText)
+{
+    qCDebug(MIMETREEPARSER_CORE_LOG) << "Starting decryption job";
+    connect(job, &QGpgME::DecryptVerifyJob::result, this, &QGpgMEJobExecutor::decryptResult);
+    GpgME::Error err = job->start(cipherText);
+    if (err) {
+        plainText.clear();
+        return std::make_pair(DecryptionResult(err), VerificationResult(err));
+    }
+    mEventLoop->exec(QEventLoop::ExcludeUserInputEvents);
+    plainText = mData;
+    return std::make_pair(mDecryptResult, mVerificationResult);
+}
+
+GpgME::ImportResult QGpgMEJobExecutor::exec(QGpgME::ImportJob *job, const QByteArray &certData)
+{
+    connect(job, &QGpgME::AbstractImportJob::result, this, &QGpgMEJobExecutor::importResult);
+    GpgME::Error err = job->start(certData);
+    if (err) {
+        return ImportResult(err);
+    }
+    mEventLoop->exec(QEventLoop::ExcludeUserInputEvents);
+    return mImportResult;
+}
+
+Error QGpgMEJobExecutor::auditLogError() const
+{
+    return mAuditLogError;
+}
+
+void QGpgMEJobExecutor::verificationResult(const GpgME::VerificationResult &result)
+{
+    qCDebug(MIMETREEPARSER_CORE_LOG) << "Detached verification job finished";
+    auto job = qobject_cast<QGpgME::Job *>(sender());
+    assert(job);
+    mVerificationResult = result;
+    mAuditLogError = job->auditLogError();
+    mAuditLog = job->auditLogAsHtml();
+    mEventLoop->quit();
+}
+
+void QGpgMEJobExecutor::verificationResult(const GpgME::VerificationResult &result, const QByteArray &plainText)
+{
+    qCDebug(MIMETREEPARSER_CORE_LOG) << "Opaque verification job finished";
+    auto job = qobject_cast<QGpgME::Job *>(sender());
+    assert(job);
+    mVerificationResult = result;
+    mData = plainText;
+    mAuditLogError = job->auditLogError();
+    mAuditLog = job->auditLogAsHtml();
+    mEventLoop->quit();
+}
+
+void QGpgMEJobExecutor::decryptResult(const GpgME::DecryptionResult &decryptionresult,
+                                      const GpgME::VerificationResult &verificationresult,
+                                      const QByteArray &plainText)
+{
+    qCDebug(MIMETREEPARSER_CORE_LOG) << "Decryption job finished";
+    auto job = qobject_cast<QGpgME::Job *>(sender());
+    assert(job);
+    mVerificationResult = verificationresult;
+    mDecryptResult = decryptionresult;
+    mData = plainText;
+    mAuditLogError = job->auditLogError();
+    mAuditLog = job->auditLogAsHtml();
+    mEventLoop->quit();
+}
+
+void QGpgMEJobExecutor::importResult(const GpgME::ImportResult &result)
+{
+    auto job = qobject_cast<QGpgME::Job *>(sender());
+    assert(job);
+    mImportResult = result;
+    mAuditLogError = job->auditLogError();
+    mAuditLog = job->auditLogAsHtml();
+    mEventLoop->quit();
+}
+
+QString QGpgMEJobExecutor::auditLogAsHtml() const
+{
+    return mAuditLog;
+}
diff --git a/src/core/job/qgpgmejobexecutor.h b/src/core/job/qgpgmejobexecutor.h
new file mode 100644
index 0000000..368c691
--- /dev/null
+++ b/src/core/job/qgpgmejobexecutor.h
@@ -0,0 +1,61 @@
+/*
+    SPDX-FileCopyrightText: 2008 Volker Krause <vkrause@kde.org>
+
+    SPDX-License-Identifier: GPL-2.0-or-later
+*/
+
+#pragma once
+
+#include <gpgme++/decryptionresult.h>
+#include <gpgme++/importresult.h>
+#include <gpgme++/verificationresult.h>
+
+#include <QObject>
+
+#include <utility>
+
+class QEventLoop;
+
+namespace QGpgME
+{
+class DecryptVerifyJob;
+class ImportJob;
+class VerifyDetachedJob;
+class VerifyOpaqueJob;
+}
+
+namespace MimeTreeParser
+{
+/**
+  Helper class for synchronous execution of Kleo crypto jobs.
+*/
+class QGpgMEJobExecutor : public QObject
+{
+    Q_OBJECT
+public:
+    explicit QGpgMEJobExecutor(QObject *parent = nullptr);
+
+    GpgME::VerificationResult exec(QGpgME::VerifyDetachedJob *job, const QByteArray &signature, const QByteArray &signedData);
+    GpgME::VerificationResult exec(QGpgME::VerifyOpaqueJob *job, const QByteArray &signedData, QByteArray &plainText);
+    std::pair<GpgME::DecryptionResult, GpgME::VerificationResult> exec(QGpgME::DecryptVerifyJob *job, const QByteArray &cipherText, QByteArray &plainText);
+    GpgME::ImportResult exec(QGpgME::ImportJob *job, const QByteArray &certData);
+
+    Q_REQUIRED_RESULT GpgME::Error auditLogError() const;
+    Q_REQUIRED_RESULT QString auditLogAsHtml() const;
+
+private Q_SLOTS:
+    void verificationResult(const GpgME::VerificationResult &result);
+    void verificationResult(const GpgME::VerificationResult &result, const QByteArray &plainText);
+    void decryptResult(const GpgME::DecryptionResult &decryptionresult, const GpgME::VerificationResult &verificationresult, const QByteArray &plainText);
+    void importResult(const GpgME::ImportResult &result);
+
+private:
+    QEventLoop *const mEventLoop;
+    GpgME::VerificationResult mVerificationResult;
+    GpgME::DecryptionResult mDecryptResult;
+    GpgME::ImportResult mImportResult;
+    QByteArray mData;
+    GpgME::Error mAuditLogError;
+    QString mAuditLog;
+};
+}
diff --git a/src/core/mailcrypto.cpp b/src/core/mailcrypto.cpp
deleted file mode 100644
index 6a3e32b..0000000
--- a/src/core/mailcrypto.cpp
+++ /dev/null
@@ -1,179 +0,0 @@
-// SPDX-FileCopyrightText: 2009 Constantin Berzan <exit3219@gmail.com>
-// SPDX-FileCopyrightText: 2010 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
-// SPDX-FileCopyrightText: 2010 Leo Franchi <lfranchi@kde.org>
-// SPDX-FileCopyrightText: 2017 Christian Mollekopf <mollekopf@kolabsys.com>
-// SPDX-License-Identifier: LGPL-2.0-or-later
-//
-#include "mailcrypto.h"
-
-#include <QDebug>
-
-#include <future>
-#include <utility>
-
-using namespace MailCrypto;
-using namespace Crypto;
-
-static QByteArray canonicalizeContent(KMime::Content *content)
-{
-    // if (d->format & Kleo::InlineOpenPGPFormat) {
-    //     return d->content->body();
-    // } else if (!(d->format & Kleo::SMIMEOpaqueFormat)) {
-
-    // replace "From " and "--" at the beginning of lines
-    // with encoded versions according to RfC 3156, 3
-    // Note: If any line begins with the string "From ", it is strongly
-    //   suggested that either the Quoted-Printable or Base64 MIME encoding
-    //   be applied.
-    const auto encoding = content->contentTransferEncoding()->encoding();
-    if ((encoding == KMime::Headers::CEquPr || encoding == KMime::Headers::CE7Bit) && !content->contentType(false)) {
-        QByteArray body = content->encodedBody();
-        bool changed = false;
-        QList<QByteArray> search;
-        QList<QByteArray> replacements;
-
-        search << "From "
-               << "from "
-               << "-";
-        replacements << "From=20"
-                     << "from=20"
-                     << "=2D";
-
-        if (content->contentTransferEncoding()->encoding() == KMime::Headers::CE7Bit) {
-            for (int i = 0; i < search.size(); ++i) {
-                const auto pos = body.indexOf(search[i]);
-                if (pos == 0 || (pos > 0 && body.at(pos - 1) == '\n')) {
-                    changed = true;
-                    break;
-                }
-            }
-            if (changed) {
-                content->contentTransferEncoding()->setEncoding(KMime::Headers::CEquPr);
-                content->assemble();
-                body = content->encodedBody();
-            }
-        }
-
-        for (int i = 0; i < search.size(); ++i) {
-            const auto pos = body.indexOf(search[i]);
-            if (pos == 0 || (pos > 0 && body.at(pos - 1) == '\n')) {
-                changed = true;
-                body.replace(pos, search[i].size(), replacements[i]);
-            }
-        }
-
-        if (changed) {
-            qDebug() << "Content changed";
-            content->setBody(body);
-            content->contentTransferEncoding()->setDecoded(false);
-        }
-    }
-
-    return KMime::LFtoCRLF(content->encodedContent());
-    // } else {                    // SMimeOpaque doesn't need LFtoCRLF, else it gets munged
-    //     return content->encodedContent();
-    // }
-}
-
-/**
- * Create a message part like this (according to RFC 3156 Section 4):
- *
- * - multipart/encrypted
- *   - application/pgp-encrypted (version information)
- *   - application/octet-stream (given encrypted data)
- *
- * The encrypted data can be generated by the `encrypt` or `signAndEncrypt` functions.
- */
-std::unique_ptr<KMime::Content> createEncryptedPart(QByteArray encryptedData)
-{
-    auto result = std::unique_ptr<KMime::Content>(new KMime::Content);
-
-    result->contentType()->setMimeType("multipart/encrypted");
-    result->contentType()->setBoundary(KMime::multiPartBoundary());
-    result->contentType()->setParameter(QStringLiteral("protocol"), QStringLiteral("application/pgp-encrypted"));
-
-    KMime::Content *controlInformation = new KMime::Content;
-    {
-        controlInformation->contentType()->setMimeType("application/pgp-encrypted");
-        controlInformation->contentDescription()->from7BitString("PGP/MIME version identification");
-        controlInformation->setBody("Version: 1");
-
-        result->addContent(controlInformation);
-    }
-
-    KMime::Content *encryptedPartPart = new KMime::Content;
-    {
-        const QString filename = QStringLiteral("msg.asc");
-
-        encryptedPartPart->contentType()->setMimeType("application/octet-stream");
-        encryptedPartPart->contentType()->setName(filename, "utf-8");
-
-        encryptedPartPart->contentDescription()->from7BitString("OpenPGP encrypted message");
-
-        encryptedPartPart->contentDisposition()->setDisposition(KMime::Headers::CDinline);
-        encryptedPartPart->contentDisposition()->setFilename(filename);
-
-        encryptedPartPart->setBody(encryptedData);
-
-        result->addContent(encryptedPartPart);
-    }
-
-    return result;
-}
-
-/**
- * Create a message part like this (according to RFC 3156 Section 5):
- *
- * + `multipart/signed`
- *   - whatever the given original `message` is (should be canonicalized)
- *   - `application/octet-stream` (the given `signature`)
- *
- * The signature can be generated by the `sign` function.
- */
-std::unique_ptr<KMime::Content> createSignedPart(std::unique_ptr<KMime::Content> message, const QByteArray &signature, const QString &micAlg)
-{
-    auto result = std::unique_ptr<KMime::Content>(new KMime::Content);
-
-    result->contentType()->setMimeType("multipart/signed");
-    result->contentType()->setBoundary(KMime::multiPartBoundary());
-    result->contentType()->setParameter(QStringLiteral("micalg"), micAlg);
-    result->contentType()->setParameter(QStringLiteral("protocol"), QStringLiteral("application/pgp-signature"));
-
-    result->addContent(message.release());
-
-    KMime::Content *signedPartPart = new KMime::Content;
-    signedPartPart->contentType()->setMimeType("application/pgp-signature");
-    signedPartPart->contentType()->setName(QStringLiteral("signature.asc"), "utf-8");
-    signedPartPart->contentDisposition(true)->setDisposition(KMime::Headers::CDattachment);
-    signedPartPart->contentDisposition(true)->setFilename(QStringLiteral("signature.asc"));
-    signedPartPart->contentDescription()->from7BitString("OpenPGP digital signature");
-    signedPartPart->setBody(signature);
-    result->addContent(signedPartPart);
-
-    return result;
-}
-
-Expected<Error, std::unique_ptr<KMime::Content>>
-MailCrypto::processCrypto(std::unique_ptr<KMime::Content> content, const std::vector<Key> &signingKeys, const std::vector<Key> &encryptionKeys)
-{
-    if (!encryptionKeys.empty()) {
-        auto encryptionResult = signAndEncrypt(canonicalizeContent(content.get()), encryptionKeys, signingKeys);
-        if (!encryptionResult) {
-            return makeUnexpected(Error{encryptionResult.error()});
-        }
-        return createEncryptedPart(encryptionResult.value());
-    } else if (!signingKeys.empty()) {
-        auto signingResult = sign(canonicalizeContent(content.get()), signingKeys);
-        if (!signingResult) {
-            return makeUnexpected(Error{signingResult.error()});
-        }
-        QByteArray signingData;
-        QString micAlg;
-        std::tie(signingData, micAlg) = signingResult.value();
-        return createSignedPart(std::move(content), signingData, micAlg);
-    } else {
-        qWarning() << "Processing cryptography, but neither signing nor encrypting";
-        Q_ASSERT(false);
-        return content;
-    }
-}
diff --git a/src/core/mailcrypto.h b/src/core/mailcrypto.h
deleted file mode 100644
index ffb9b32..0000000
--- a/src/core/mailcrypto.h
+++ /dev/null
@@ -1,19 +0,0 @@
-// SPDX-FileCopyrightText: 2016 Christian Mollekopf <mollekopf@kolabsys.com>
-// SPDX-License-Identifier: LGPL-2.0-or-later
-
-#pragma once
-
-#include <KMime/Content>
-
-#include <memory>
-
-#include "crypto.h"
-#include "errors.h"
-
-namespace MailCrypto
-{
-
-Expected<Crypto::Error, std::unique_ptr<KMime::Content>>
-processCrypto(std::unique_ptr<KMime::Content> content, const std::vector<Crypto::Key> &signingKeys, const std::vector<Crypto::Key> &encryptionKeys);
-
-};
diff --git a/src/core/messagepart.cpp b/src/core/messagepart.cpp
index ab36591..6f7b865 100644
--- a/src/core/messagepart.cpp
+++ b/src/core/messagepart.cpp
@@ -1,963 +1,1095 @@
 // SPDX-FileCopyrightText: 2015 Sandro Knauß <knauss@kolabsys.com>
 // SPDX-FileCopyrightText: 2017 Christian Mollekopf <mollekopf@kolabsys.com>
 // SPDX-License-Identifier: LGPL-2.0-or-later
 
 #include "messagepart.h"
 #include "cryptohelper.h"
 #include "mimetreeparser_core_debug.h"
 #include "objecttreeparser.h"
 
+#include "job/qgpgmejobexecutor.h"
 #include "utils.h"
 
 #include <KLocalizedString>
 #include <KMime/Content>
+#include <Libkleo/Compliance>
+#include <Libkleo/KeyCache>
+
+#include <QGpgME/DN>
+#include <QGpgME/DecryptVerifyJob>
+#include <QGpgME/Protocol>
+#include <QGpgME/VerifyDetachedJob>
+#include <QGpgME/VerifyOpaqueJob>
+
+#include <gpgme++/key.h>
+#include <gpgme++/keylistresult.h>
+#include <gpgme.h>
+
 #include <QTextCodec>
 
 using namespace MimeTreeParser;
-using namespace Crypto;
 
 //------MessagePart-----------------------
 MessagePart::MessagePart(ObjectTreeParser *otp, const QString &text, KMime::Content *node)
     : mText(text)
     , mOtp(otp)
     , mParentPart(nullptr)
     , mNode(node) // only null for messagepartlist
     , mError(NoError)
     , mRoot(false)
 {
 }
 
 MessagePart::~MessagePart()
 {
     for (auto n : std::as_const(mNodesToDelete)) {
         delete n;
     }
 }
 
 MessagePart::Disposition MessagePart::disposition() const
 {
     if (!mNode) {
         return Invalid;
     }
     const auto cd = mNode->contentDisposition(false);
     if (!cd) {
         return Invalid;
     }
     switch (cd->disposition()) {
     case KMime::Headers::CDinline:
         return Inline;
     case KMime::Headers::CDattachment:
         return Attachment;
     default:
         return Invalid;
     }
 }
 
 QString MessagePart::filename() const
 {
     if (!mNode) {
         return {};
     }
 
     if (const auto cd = mNode->contentDisposition(false)) {
         const auto name = cd->filename();
         // Allow for a fallback for mails that have a ContentDisposition header, but don't set the filename anyways.
         // Not the recommended way, but exists.
         if (!name.isEmpty()) {
             return name;
         }
     }
     if (const auto ct = mNode->contentType(false)) {
         return ct->name();
     }
     return {};
 }
 
 static KMime::Headers::ContentType *contentType(KMime::Content *node)
 {
     if (node) {
         return node->contentType(false);
     }
     return nullptr;
 }
 
 QByteArray MessagePart::charset() const
 {
     if (!mNode) {
         return "us-ascii";
     }
     if (auto ct = contentType(mNode)) {
         return ct->charset();
     }
     // Per rfc2045 us-ascii is the default
     return "us-ascii";
 }
 
 QByteArray MessagePart::mimeType() const
 {
     if (auto ct = contentType(mNode)) {
         return ct->mimeType();
     }
     return {};
 }
 
 bool MessagePart::isText() const
 {
     if (auto ct = contentType(mNode)) {
         return ct->isText();
     }
     return false;
 }
 
 MessagePart::Error MessagePart::error() const
 {
     return mError;
 }
 
 QString MessagePart::errorString() const
 {
     return mMetaData.errorText;
 }
 
 PartMetaData *MessagePart::partMetaData()
 {
     return &mMetaData;
 }
 
 bool MessagePart::isAttachment() const
 {
     if (mNode) {
         return KMime::isAttachment(mNode);
     }
     return false;
 }
 
 KMime::Content *MessagePart::node() const
 {
     return mNode;
 }
 
 void MessagePart::setIsRoot(bool root)
 {
     mRoot = root;
 }
 
 bool MessagePart::isRoot() const
 {
     return mRoot;
 }
 
 QString MessagePart::text() const
 {
     return mText;
 }
 
 void MessagePart::setText(const QString &text)
 {
     mText = text;
 }
 
 bool MessagePart::isHtml() const
 {
     return false;
 }
 
 MessagePart *MessagePart::parentPart() const
 {
     return mParentPart;
 }
 
 void MessagePart::setParentPart(MessagePart *parentPart)
 {
     mParentPart = parentPart;
 }
 
 QString MessagePart::htmlContent() const
 {
     return text();
 }
 
 QString MessagePart::plaintextContent() const
 {
     return text();
 }
 
 void MessagePart::parseInternal(KMime::Content *node, bool onlyOneMimePart)
 {
     auto subMessagePart = mOtp->parseObjectTreeInternal(node, onlyOneMimePart);
     mRoot = subMessagePart->isRoot();
     for (const auto &part : std::as_const(subMessagePart->subParts())) {
         appendSubPart(part);
     }
 }
 
 void MessagePart::parseInternal(const QByteArray &data)
 {
     auto tempNode = new KMime::Content();
 
     const auto lfData = KMime::CRLFtoLF(data);
     // We have to deal with both bodies and full parts. In inline encrypted/signed parts we can have nested parts,
     // or just plain-text, and both ends up here. setContent defaults to setting only the header, so we have to avoid this.
     if (lfData.contains("\n\n")) {
         tempNode->setContent(lfData);
     } else {
         tempNode->setBody(lfData);
     }
     tempNode->parse();
     tempNode->contentType()->setCharset(charset());
     bindLifetime(tempNode);
 
     if (!tempNode->head().isEmpty()) {
         tempNode->contentDescription()->from7BitString("temporary node");
     }
 
     parseInternal(tempNode);
 }
 
 QString MessagePart::renderInternalText() const
 {
     QString text;
     for (const auto &mp : subParts()) {
         text += mp->text();
     }
     return text;
 }
 
 void MessagePart::appendSubPart(const MessagePart::Ptr &messagePart)
 {
     messagePart->setParentPart(this);
     mBlocks.append(messagePart);
 }
 
 const QVector<MessagePart::Ptr> &MessagePart::subParts() const
 {
     return mBlocks;
 }
 
 bool MessagePart::hasSubParts() const
 {
     return !mBlocks.isEmpty();
 }
 
 QVector<SignedMessagePart *> MessagePart::signatures() const
 {
     QVector<SignedMessagePart *> list;
     if (auto sig = dynamic_cast<SignedMessagePart *>(const_cast<MessagePart *>(this))) {
         list << sig;
     }
     auto parent = parentPart();
     while (parent) {
         if (auto sig = dynamic_cast<SignedMessagePart *>(parent)) {
             list << sig;
         }
         parent = parent->parentPart();
     }
     return list;
 }
 
 QVector<EncryptedMessagePart *> MessagePart::encryptions() const
 {
     QVector<EncryptedMessagePart *> list;
     if (auto sig = dynamic_cast<EncryptedMessagePart *>(const_cast<MessagePart *>(this))) {
         list << sig;
     }
     auto parent = parentPart();
     while (parent) {
         if (auto sig = dynamic_cast<EncryptedMessagePart *>(parent)) {
             list << sig;
         }
         parent = parent->parentPart();
     }
     return list;
 }
 
 KMMsgEncryptionState MessagePart::encryptionState() const
 {
     if (!encryptions().isEmpty()) {
         return KMMsgFullyEncrypted;
     }
     return KMMsgNotEncrypted;
 }
 
 KMMsgSignatureState MessagePart::signatureState() const
 {
     if (!signatures().isEmpty()) {
         return KMMsgFullySigned;
     }
     return KMMsgNotSigned;
 }
 
 void MessagePart::bindLifetime(KMime::Content *node)
 {
     mNodesToDelete << node;
 }
 
 KMime::Headers::Base *MimeTreeParser::MessagePart::header(const char *header) const
 {
     if (node() && node()->hasHeader(header)) {
         return node()->headerByType(header);
     }
     if (auto parent = parentPart()) {
         return parent->header(header);
     }
 
     return nullptr;
 }
 
 //-----MessagePartList----------------------
 MessagePartList::MessagePartList(ObjectTreeParser *otp, KMime::Content *node)
     : MessagePart(otp, QString(), node)
 {
 }
 
 QString MessagePartList::text() const
 {
     return renderInternalText();
 }
 
 QString MessagePartList::plaintextContent() const
 {
     return QString();
 }
 
 QString MessagePartList::htmlContent() const
 {
     return QString();
 }
 
 //-----TextMessageBlock----------------------
 
 TextMessagePart::TextMessagePart(ObjectTreeParser *otp, KMime::Content *node)
     : MessagePartList(otp, node)
     , mSignatureState(KMMsgSignatureStateUnknown)
     , mEncryptionState(KMMsgEncryptionStateUnknown)
 {
     if (!mNode) {
         qCWarning(MIMETREEPARSER_CORE_LOG) << "not a valid node";
         return;
     }
 
     parseContent();
 }
 
 void TextMessagePart::parseContent()
 {
     mSignatureState = KMMsgNotSigned;
     mEncryptionState = KMMsgNotEncrypted;
     const auto blocks = prepareMessageForDecryption(mNode->decodedContent());
     // We also get blocks for unencrypted messages
     if (!blocks.isEmpty()) {
         const auto aCodec = mOtp->codecFor(mNode);
-        const auto cryptProto = OpenPGP;
+        const auto cryptProto = QGpgME::openpgp();
 
         /* The (overall) signature/encrypted status is broken
          * if one unencrypted part is at the beginning or in the middle
          * because mailmain adds an unencrypted part at the end this should not break the overall status
          *
          * That's why we first set the tmp status and if one crypted/signed block comes afterwards, than
          * the status is set to unencryped
          */
         bool fullySignedOrEncrypted = true;
         bool fullySignedOrEncryptedTmp = true;
 
         for (const auto &block : blocks) {
             if (!fullySignedOrEncryptedTmp) {
                 fullySignedOrEncrypted = false;
             }
 
             if (block.type() == NoPgpBlock && !block.text().trimmed().isEmpty()) {
                 fullySignedOrEncryptedTmp = false;
                 appendSubPart(MessagePart::Ptr(new MessagePart(mOtp, aCodec->toUnicode(KMime::CRLFtoLF(block.text())))));
             } else if (block.type() == PgpMessageBlock) {
                 KMime::Content *content = new KMime::Content;
                 content->setBody(block.text());
                 content->parse();
                 content->contentType()->setCharset(charset());
                 EncryptedMessagePart::Ptr mp(new EncryptedMessagePart(mOtp, QString(), cryptProto, content, content, false));
                 mp->bindLifetime(content);
                 mp->setIsEncrypted(true);
                 appendSubPart(mp);
             } else if (block.type() == ClearsignedBlock) {
                 KMime::Content *content = new KMime::Content;
                 content->setBody(block.text());
                 content->parse();
                 content->contentType()->setCharset(charset());
                 SignedMessagePart::Ptr mp(new SignedMessagePart(mOtp, cryptProto, nullptr, content, false));
                 mp->bindLifetime(content);
                 mp->setIsSigned(true);
                 appendSubPart(mp);
             } else {
                 continue;
             }
 
             const auto mp = subParts().last().staticCast<MessagePart>();
             const PartMetaData *messagePart(mp->partMetaData());
 
             if (!messagePart->isEncrypted && !messagePart->isSigned && !block.text().trimmed().isEmpty()) {
                 mp->setText(aCodec->toUnicode(KMime::CRLFtoLF(block.text())));
             }
 
             if (messagePart->isEncrypted) {
                 mEncryptionState = KMMsgPartiallyEncrypted;
             }
 
             if (messagePart->isSigned) {
                 mSignatureState = KMMsgPartiallySigned;
             }
         }
 
         // Do we have an fully Signed/Encrypted Message?
         if (fullySignedOrEncrypted) {
             if (mSignatureState == KMMsgPartiallySigned) {
                 mSignatureState = KMMsgFullySigned;
             }
             if (mEncryptionState == KMMsgPartiallyEncrypted) {
                 mEncryptionState = KMMsgFullyEncrypted;
             }
         }
     }
 }
 
 KMMsgEncryptionState TextMessagePart::encryptionState() const
 {
     if (mEncryptionState == KMMsgNotEncrypted) {
         return MessagePart::encryptionState();
     }
     return mEncryptionState;
 }
 
 KMMsgSignatureState TextMessagePart::signatureState() const
 {
     if (mSignatureState == KMMsgNotSigned) {
         return MessagePart::signatureState();
     }
     return mSignatureState;
 }
 
 //-----AttachmentMessageBlock----------------------
 
 AttachmentMessagePart::AttachmentMessagePart(ObjectTreeParser *otp, KMime::Content *node)
     : TextMessagePart(otp, node)
 {
 }
 
 //-----HtmlMessageBlock----------------------
 
 HtmlMessagePart::HtmlMessagePart(ObjectTreeParser *otp, KMime::Content *node)
     : MessagePart(otp, QString(), node)
 {
     if (!mNode) {
         qCWarning(MIMETREEPARSER_CORE_LOG) << "not a valid node";
         return;
     }
 
     setText(mOtp->codecFor(mNode)->toUnicode(KMime::CRLFtoLF(mNode->decodedContent())));
 }
 
 //-----MimeMessageBlock----------------------
 
 MimeMessagePart::MimeMessagePart(ObjectTreeParser *otp, KMime::Content *node, bool onlyOneMimePart)
     : MessagePart(otp, QString(), node)
 {
     if (!mNode) {
         qCWarning(MIMETREEPARSER_CORE_LOG) << "not a valid node";
         return;
     }
 
     parseInternal(mNode, onlyOneMimePart);
 }
 
 MimeMessagePart::~MimeMessagePart()
 {
 }
 
 QString MimeMessagePart::text() const
 {
     return renderInternalText();
 }
 
 QString MimeMessagePart::plaintextContent() const
 {
     return QString();
 }
 
 QString MimeMessagePart::htmlContent() const
 {
     return QString();
 }
 
 //-----AlternativeMessagePart----------------------
 
 AlternativeMessagePart::AlternativeMessagePart(ObjectTreeParser *otp, KMime::Content *node)
     : MessagePart(otp, QString(), node)
 {
     if (auto dataIcal = findTypeInDirectChildren(mNode, "text/calendar")) {
         mChildParts[MultipartIcal] = MimeMessagePart::Ptr(new MimeMessagePart(mOtp, dataIcal, true));
     }
 
     if (auto dataText = findTypeInDirectChildren(mNode, "text/plain")) {
         mChildParts[MultipartPlain] = MimeMessagePart::Ptr(new MimeMessagePart(mOtp, dataText, true));
     }
 
     if (auto dataHtml = findTypeInDirectChildren(mNode, "text/html")) {
         mChildParts[MultipartHtml] = MimeMessagePart::Ptr(new MimeMessagePart(mOtp, dataHtml, true));
     } else {
         // If we didn't find the HTML part as the first child of the multipart/alternative, it might
         // be that this is a HTML message with images, and text/plain and multipart/related are the
         // immediate children of this multipart/alternative node.
         // In this case, the HTML node is a child of multipart/related.
         // In the case of multipart/related we don't expect multiple html parts, it is usually used to group attachments
         // with html content.
         //
         // In any case, this is not a complete implementation of MIME, but an approximation for the kind of mails we actually see in the wild.
         auto data = [&] {
             if (auto d = findTypeInDirectChildren(mNode, "multipart/related")) {
                 return d;
             }
             return findTypeInDirectChildren(mNode, "multipart/mixed");
         }();
         if (data) {
             QString htmlContent;
             const auto parts = data->contents();
             for (auto p : parts) {
                 if ((!p->contentType()->isEmpty()) && (p->contentType()->mimeType() == "text/html")) {
                     htmlContent += MimeMessagePart(mOtp, p, true).text();
                 } else if (KMime::isAttachment(p)) {
                     appendSubPart(MimeMessagePart::Ptr(new MimeMessagePart(otp, p, true)));
                 }
             }
             mChildParts[MultipartHtml] = MessagePart::Ptr(new MessagePart(mOtp, htmlContent, nullptr));
         }
     }
 }
 
 AlternativeMessagePart::~AlternativeMessagePart()
 {
 }
 
 QList<AlternativeMessagePart::HtmlMode> AlternativeMessagePart::availableModes()
 {
     return mChildParts.keys();
 }
 
 QString AlternativeMessagePart::text() const
 {
     if (mChildParts.contains(MultipartPlain)) {
         return mChildParts[MultipartPlain]->text();
     }
     return QString();
 }
 
 bool AlternativeMessagePart::isHtml() const
 {
     return mChildParts.contains(MultipartHtml);
 }
 
 QString AlternativeMessagePart::plaintextContent() const
 {
     return text();
 }
 
 QString AlternativeMessagePart::htmlContent() const
 {
     if (mChildParts.contains(MultipartHtml)) {
         return mChildParts[MultipartHtml]->text();
     } else {
         return plaintextContent();
     }
 }
 
 QString AlternativeMessagePart::icalContent() const
 {
     if (mChildParts.contains(MultipartIcal)) {
         return mChildParts[MultipartIcal]->text();
     }
     return {};
 }
 
 //-----CertMessageBlock----------------------
 
-CertMessagePart::CertMessagePart(ObjectTreeParser *otp, KMime::Content *node, const CryptoProtocol cryptoProto)
+CertMessagePart::CertMessagePart(ObjectTreeParser *otp, KMime::Content *node, QGpgME::Protocol *cryptoProto)
     : MessagePart(otp, QString(), node)
-    , mProtocol(cryptoProto)
+    , mCryptoProto(cryptoProto)
 {
     if (!mNode) {
         qCWarning(MIMETREEPARSER_CORE_LOG) << "not a valid node";
         return;
     }
 }
 
 CertMessagePart::~CertMessagePart()
 {
 }
 
-void CertMessagePart::import()
-{
-    importKey(mProtocol, mNode->decodedContent());
-}
-
 QString CertMessagePart::text() const
 {
     return QString();
 }
 
 //-----SignedMessageBlock---------------------
 SignedMessagePart::SignedMessagePart(ObjectTreeParser *otp,
-                                     const CryptoProtocol cryptoProto,
+                                     const QGpgME::Protocol *cryptoProto,
                                      KMime::Content *node,
                                      KMime::Content *signedData,
                                      bool parseAfterDecryption)
     : MessagePart(otp, {}, node)
     , mParseAfterDecryption(parseAfterDecryption)
-    , mProtocol(cryptoProto)
+    , mCryptoProto(cryptoProto)
     , mSignedData(signedData)
 {
     mMetaData.isSigned = true;
     mMetaData.isGoodSignature = false;
     mMetaData.status = i18ndc("mimetreeparser", "@info:status", "Wrong Crypto Plug-In.");
 }
 
 SignedMessagePart::~SignedMessagePart()
 {
 }
 
 void SignedMessagePart::setIsSigned(bool isSigned)
 {
     mMetaData.isSigned = isSigned;
 }
 
 bool SignedMessagePart::isSigned() const
 {
     return mMetaData.isSigned;
 }
 
 static QString prettifyDN(const char *uid)
 {
     // We used to use QGpgME::DN::prettyDN here. But I'm not sure what we actually need it for.
     return QString::fromUtf8(uid);
 }
 
-static void sigStatusToMetaData(PartMetaData &mMetaData, const Signature &signature)
+const QGpgME::Protocol *SignedMessagePart::cryptoProto() const
 {
-    mMetaData.isGoodSignature = !signature.status;
-    if (!mMetaData.isGoodSignature) {
-        qWarning() << "The signature is bad." << signature.status;
-    }
-    // save extended signature status flags
-    mMetaData.keyMissing = signature.result == Crypto::Signature::KeyNotFound;
-    mMetaData.keyExpired = signature.result == Crypto::Signature::Expired;
-
-    Key key;
-    if (mMetaData.isGoodSignature) {
-        // Search for the key by its fingerprint so that we can check for trust etc.
-        const auto keys = findKeys({QString::fromUtf8(signature.fingerprint)});
-        if (keys.size() > 1) {
-            // Should not happen
-            qCDebug(MIMETREEPARSER_CORE_LOG) << "Oops: Found more then one Key for Fingerprint: " << signature.fingerprint;
-        }
-        if (keys.empty()) {
-            // Should not happen at this point
-            qCWarning(MIMETREEPARSER_CORE_LOG) << "Oops: Found no Key for Fingerprint: " << signature.fingerprint;
-        } else {
-            key = keys[0];
-        }
-    }
-
-    mMetaData.keyId = key.keyId;
-    if (mMetaData.keyId.isEmpty()) {
-        mMetaData.keyId = signature.fingerprint;
-    }
-    mMetaData.keyIsTrusted = signature.isTrusted;
-    if (!key.userIds.empty()) {
-        mMetaData.signer = prettifyDN(key.userIds[0].id.data());
-    }
-    for (const auto &userId : key.userIds) {
-        QString email = QString::fromUtf8(userId.email);
-        // Work around cryptplug bug where email addresses are specified as angle-addr ( <person@example.org> ),
-        // not addr-spec ( person@example.org ):
-        if (email.startsWith(QLatin1Char('<')) && email.endsWith(QLatin1Char('>'))) {
-            email = email.mid(1, email.length() - 2);
-        }
-        if (!email.isEmpty()) {
-            mMetaData.signerMailAddresses.append(email);
-        }
-    }
-
-    mMetaData.creationTime = signature.creationTime;
-    if (mMetaData.signer.isEmpty()) {
-        if (!key.userIds.empty()) {
-            mMetaData.signer = prettifyDN(key.userIds[0].name.data());
-        }
-        if (!mMetaData.signerMailAddresses.empty()) {
-            if (mMetaData.signer.isEmpty()) {
-                mMetaData.signer = mMetaData.signerMailAddresses.front();
-            } else {
-                mMetaData.signer += QLatin1String(" <") + mMetaData.signerMailAddresses.front() + QLatin1Char('>');
-            }
-        }
-    }
+    return mCryptoProto;
 }
 
 void SignedMessagePart::startVerification()
 {
     if (!mSignedData) {
         return;
     }
 
     mMetaData.isSigned = false;
     mMetaData.status = i18ndc("mimetreeparser", "@info:status", "Wrong Crypto Plug-In.");
     mMetaData.isEncrypted = false;
     mMetaData.isDecryptable = false;
 
     const auto codec = mOtp->codecFor(mSignedData);
 
     // If we have a mNode, this is a detached signature
     if (mNode) {
         const auto signature = mNode->decodedContent();
 
         // This is necessary in case the original data contained CRLF's. Otherwise the signature will not match the data (since KMIME normalizes to LF)
         const QByteArray signedData = KMime::LFtoCRLF(mSignedData->encodedContent());
 
-        setVerificationResult(Crypto::verifyDetachedSignature(mProtocol, signature, signedData), signedData);
+        const auto job = mCryptoProto->verifyDetachedJob();
+        setVerificationResult(job->exec(signature, signedData), signedData);
         setText(codec->toUnicode(KMime::CRLFtoLF(signedData)));
     } else {
         QByteArray outdata;
-        setVerificationResult(Crypto::verifyOpaqueSignature(mProtocol, mSignedData->decodedContent(), outdata), outdata);
+        const auto job = mCryptoProto->verifyOpaqueJob();
+        setVerificationResult(job->exec(mSignedData->decodedContent(), outdata), outdata);
         setText(codec->toUnicode(KMime::CRLFtoLF(outdata)));
     }
 
     if (!mMetaData.isSigned) {
         mMetaData.creationTime = QDateTime();
     }
 }
 
-void SignedMessagePart::setVerificationResult(const VerificationResult &result, const QByteArray &signedData)
+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;
+    }
+}
+
+void SignedMessagePart::sigStatusToMetaData()
+{
+    GpgME::Key key;
+    if (partMetaData()->isSigned) {
+        GpgME::Signature signature = mSignatures.front();
+        mMetaData.status_code = signatureToStatus(signature);
+        mMetaData.isGoodSignature = partMetaData()->status_code == GPGME_SIG_STAT_GOOD;
+        // save extended signature status flags
+        mMetaData.sigSummary = signature.summary();
+
+        if (partMetaData()->isGoodSignature && !key.keyID()) {
+            // Search for the key by its fingerprint so that we can check for
+            // trust etc.
+            key = Kleo::KeyCache::instance()->findByFingerprint(signature.fingerprint());
+            if (key.isNull() && signature.fingerprint()) {
+                // try to find a subkey that was used for signing;
+                // assumes that the key ID is the last 16 characters of the fingerprint
+                const auto fpr = std::string_view{signature.fingerprint()};
+                const auto keyID = std::string{fpr, fpr.size() - 16, 16};
+                const auto subkeys = Kleo::KeyCache::instance()->findSubkeysByKeyID({keyID});
+                if (subkeys.size() > 0) {
+                    key = subkeys[0].parent();
+                }
+            }
+            if (key.isNull()) {
+                qCDebug(MIMETREEPARSER_CORE_LOG) << "Found no key or subkey for fingerprint" << signature.fingerprint();
+            }
+        }
+
+        if (key.keyID()) {
+            partMetaData()->keyId = key.keyID();
+        }
+        if (partMetaData()->keyId.isEmpty()) {
+            partMetaData()->keyId = signature.fingerprint();
+        }
+        partMetaData()->keyTrust = signature.validity();
+        if (key.numUserIDs() > 0 && key.userID(0).id()) {
+            partMetaData()->signer = prettifyDN(key.userID(0).id());
+        }
+        for (const auto &uid : key.userIDs()) {
+            // The following if /should/ always result in TRUE but we
+            // won't trust implicitly the plugin that gave us these data.
+            if (uid.email()) {
+                QString email = QString::fromUtf8(uid.email());
+                if (!email.isEmpty()) {
+                    partMetaData()->signerMailAddresses.append(email);
+                }
+            }
+        }
+
+        if (signature.creationTime()) {
+            partMetaData()->creationTime.setSecsSinceEpoch(signature.creationTime());
+        } else {
+            partMetaData()->creationTime = QDateTime();
+        }
+        if (partMetaData()->signer.isEmpty()) {
+            if (key.numUserIDs() > 0 && key.userID(0).name()) {
+                partMetaData()->signer = prettifyDN(key.userID(0).name());
+            }
+            if (!partMetaData()->signerMailAddresses.empty()) {
+                if (partMetaData()->signer.isEmpty()) {
+                    partMetaData()->signer = partMetaData()->signerMailAddresses.front();
+                } else {
+                    partMetaData()->signer += QLatin1String(" <") + partMetaData()->signerMailAddresses.front() + QLatin1Char('>');
+                }
+            }
+        }
+        if (Kleo::DeVSCompliance::isCompliant()) {
+            partMetaData()->isCompliant = signature.isDeVs();
+            partMetaData()->compliance = Kleo::DeVSCompliance::name(signature.isDeVs());
+        } else {
+            partMetaData()->isCompliant = true;
+        }
+    }
+}
+
+void SignedMessagePart::setVerificationResult(const GpgME::VerificationResult &result, const QByteArray &signedData)
 {
-    const auto signatures = result.signatures;
+    mSignatures = result.signatures();
     // FIXME
     // mMetaData.auditLogError = result.error;
-    if (!signatures.empty()) {
-        mMetaData.isSigned = true;
-        sigStatusToMetaData(mMetaData, signatures.front());
+    mMetaData.isSigned = !mSignatures.empty();
+    if (mMetaData.isSigned) {
+        sigStatusToMetaData();
         if (!signedData.isEmpty() && mParseAfterDecryption) {
             parseInternal(signedData);
         }
     }
 }
 
 QString SignedMessagePart::plaintextContent() const
 {
     if (!mNode) {
         return MessagePart::text();
     } else {
         return QString();
     }
 }
 
 QString SignedMessagePart::htmlContent() const
 {
     if (!mNode) {
         return MessagePart::text();
     } else {
         return QString();
     }
 }
 
 //-----CryptMessageBlock---------------------
 EncryptedMessagePart::EncryptedMessagePart(ObjectTreeParser *otp,
                                            const QString &text,
-                                           const CryptoProtocol cryptoProto,
+                                           const QGpgME::Protocol *cryptoProto,
                                            KMime::Content *node,
                                            KMime::Content *encryptedNode,
                                            bool parseAfterDecryption)
     : MessagePart(otp, text, node)
     , mParseAfterDecryption(parseAfterDecryption)
-    , mProtocol(cryptoProto)
+    , mPassphraseError(false)
+    , mNoSecKey(false)
+    , mDecryptMessage(false)
+    , mCryptoProto(cryptoProto)
     , mEncryptedNode(encryptedNode)
 {
     mMetaData.isSigned = false;
     mMetaData.isGoodSignature = false;
     mMetaData.isEncrypted = false;
     mMetaData.isDecryptable = false;
     mMetaData.status = i18ndc("mimetreeparser", "@info:status", "Wrong Crypto Plug-In.");
 }
 
 void EncryptedMessagePart::setIsEncrypted(bool encrypted)
 {
     mMetaData.isEncrypted = encrypted;
 }
 
 bool EncryptedMessagePart::isEncrypted() const
 {
     return mMetaData.isEncrypted;
 }
 
+const QGpgME::Protocol *EncryptedMessagePart::cryptoProto() const
+{
+    return mCryptoProto;
+}
+
+void EncryptedMessagePart::setDecryptMessage(bool decrypt)
+{
+    mDecryptMessage = decrypt;
+}
+
+bool EncryptedMessagePart::decryptMessage() const
+{
+    return mDecryptMessage;
+}
+
 bool EncryptedMessagePart::isDecryptable() const
 {
     return mMetaData.isDecryptable;
 }
 
+bool EncryptedMessagePart::isNoSecKey() const
+{
+    return mNoSecKey;
+}
+
+bool EncryptedMessagePart::passphraseError() const
+{
+    return mPassphraseError;
+}
+
 bool EncryptedMessagePart::decrypt(KMime::Content &data)
 {
     mError = NoError;
     mMetaData.errorText.clear();
     // FIXME
     //  mMetaData.auditLogError = GpgME::Error();
     mMetaData.auditLog.clear();
 
     const QByteArray ciphertext = data.decodedContent();
     QByteArray plainText;
-    DecryptionResult decryptResult;
-    VerificationResult verifyResult;
-    std::tie(decryptResult, verifyResult) = Crypto::decryptAndVerify(mProtocol, ciphertext, plainText);
-    mMetaData.isSigned = verifyResult.signatures.size() > 0;
+    auto job = mCryptoProto->decryptVerifyJob();
+    const std::pair<GpgME::DecryptionResult, GpgME::VerificationResult> p = job->exec(ciphertext, plainText);
+    auto decryptResult = p.first;
+    auto verifyResult = p.second;
+    mMetaData.isSigned = verifyResult.signatures().size() > 0;
 
     // Normalize CRLF's
     plainText = KMime::CRLFtoLF(plainText);
     const auto codec = mOtp->codecFor(&data);
     const auto decoded = codec->toUnicode(plainText);
 
-    if (verifyResult.signatures.size() > 0) {
+    partMetaData()->isSigned = verifyResult.signatures().size() > 0;
+
+    if (partMetaData()->isSigned) {
         // We simply attach a signed message part to indicate that this content is also signed
         // We're forwarding mNode to not loose the encoding information
-        auto subPart = SignedMessagePart::Ptr(new SignedMessagePart(mOtp, mProtocol, mNode, nullptr));
+        auto subPart = SignedMessagePart::Ptr(new SignedMessagePart(mOtp, mCryptoProto, mNode, nullptr));
         subPart->setText(decoded);
         subPart->setVerificationResult(verifyResult, plainText);
         appendSubPart(subPart);
     }
 
-    if (decryptResult.error && mMetaData.isSigned) {
-        // Only a signed part
-        mMetaData.isEncrypted = false;
-        mDecryptedData = plainText;
-        return true;
-    }
+    mDecryptRecipients.clear();
+    bool cannotDecrypt = false;
+    bool bDecryptionOk = !decryptResult.error();
 
-    if (mMetaData.isEncrypted) {
-        mMetaData.keyId = [&] {
-            for (const auto &recipient : std::as_const(decryptResult.recipients)) {
-                if (recipient.secretKeyAvailable) {
-                    return recipient.keyId;
-                }
+    for (const auto &recipient : decryptResult.recipients()) {
+        if (!recipient.status()) {
+            bDecryptionOk = true;
+        }
+        GpgME::Key key;
+        key = Kleo::KeyCache::instance()->findByKeyIDOrFingerprint(recipient.keyID());
+        if (key.isNull()) {
+            auto ret = Kleo::KeyCache::instance()->findSubkeysByKeyID({recipient.keyID()});
+            if (ret.size() == 1) {
+                key = ret.front().parent();
             }
-            return QByteArray{};
-        }();
+            if (key.isNull()) {
+                qCDebug(MIMETREEPARSER_CORE_LOG) << "Found no Key for KeyID " << recipient.keyID();
+            }
+        }
+        mDecryptRecipients.emplace_back(recipient, key);
     }
 
-    if (!decryptResult.error) {
+    if (!bDecryptionOk && partMetaData()->isSigned) {
+        // Only a signed part
+        partMetaData()->isEncrypted = false;
+        bDecryptionOk = true;
         mDecryptedData = plainText;
-        setText(decoded);
     } else {
-        mMetaData.isEncrypted = decryptResult.result != Crypto::DecryptionResult::NotEncrypted;
-        qWarning() << "Failed to decrypt : " << decryptResult.error;
+        mPassphraseError = decryptResult.error().isCanceled() || decryptResult.error().code() == GPG_ERR_NO_SECKEY;
+        mMetaData.isEncrypted = bDecryptionOk || decryptResult.error().code() != GPG_ERR_NO_DATA;
 
-        if (decryptResult.result == Crypto::DecryptionResult::NoSecretKeyError) {
-            mError = NoKeyError;
-            mMetaData.errorText = i18ndc("mimetreeparser", "@info", "Could not decrypt the data: no key found for recipients.");
-        } else if (decryptResult.result == Crypto::DecryptionResult::PassphraseError) {
-            mError = PassphraseError;
+        if (decryptResult.error().isCanceled()) {
+            setDecryptMessage(false);
+        }
+
+        partMetaData()->errorText = QString::fromLocal8Bit(decryptResult.error().asString());
+        if (Kleo::DeVSCompliance::isCompliant()) {
+            partMetaData()->isCompliant = decryptResult.isDeVs();
+            partMetaData()->compliance = Kleo::DeVSCompliance::name(decryptResult.isDeVs());
         } else {
-            mError = UnknownError;
-            mMetaData.errorText = i18ndc("mimetreeparser", "@info", "Could not decrypt the data.");
+            partMetaData()->isCompliant = true;
+        }
+        if (partMetaData()->isEncrypted && decryptResult.numRecipients() > 0) {
+            partMetaData()->keyId = decryptResult.recipient(0).keyID();
+        }
+
+        if (bDecryptionOk) {
+            mDecryptedData = plainText;
+        } else {
+            mNoSecKey = true;
+            const auto decryRecipients = decryptResult.recipients();
+            for (const GpgME::DecryptionResult::Recipient &recipient : decryRecipients) {
+                mNoSecKey &= (recipient.status().code() == GPG_ERR_NO_SECKEY);
+            }
+            if (!mPassphraseError && !mNoSecKey) { // GpgME do not detect passphrase error correctly
+                mPassphraseError = true;
+            }
         }
-        setText(QString::fromUtf8(mDecryptedData.constData()));
-        return false;
     }
 
-    return true;
+    if (!bDecryptionOk) {
+        QString cryptPlugLibName;
+        mError = UnknownError;
+        if (mCryptoProto) {
+            cryptPlugLibName = mCryptoProto->name();
+        }
+
+        if (mNoSecKey) {
+            mError = NoKeyError;
+        }
+
+        if (mPassphraseError) {
+            mError = PassphraseError;
+        }
+
+        if (!mCryptoProto) {
+            partMetaData()->errorText = i18n("No appropriate crypto plug-in was found.");
+        } else if (cannotDecrypt) {
+            partMetaData()->errorText = i18n("Crypto plug-in \"%1\" cannot decrypt messages.", cryptPlugLibName);
+        } else if (!passphraseError()) {
+            partMetaData()->errorText = i18n("Crypto plug-in \"%1\" could not decrypt the data.", cryptPlugLibName) + QLatin1String("<br />")
+                + i18n("Error: %1", partMetaData()->errorText);
+        }
+    }
+    return bDecryptionOk;
 }
 
 void EncryptedMessagePart::startDecryption(KMime::Content *data)
 {
     mMetaData.isEncrypted = true;
     mMetaData.isDecryptable = decrypt(*data);
 
     if (mParseAfterDecryption && !mMetaData.isSigned) {
         parseInternal(mDecryptedData);
+    } else {
+        setText(QString::fromUtf8(mDecryptedData.constData()));
     }
 }
 
 void EncryptedMessagePart::startDecryption()
 {
     if (mEncryptedNode) {
         startDecryption(mEncryptedNode);
     } else {
         startDecryption(mNode);
     }
 }
 
+std::vector<std::pair<GpgME::DecryptionResult::Recipient, GpgME::Key>> EncryptedMessagePart::decryptRecipients() const
+{
+    return mDecryptRecipients;
+}
+
 QString EncryptedMessagePart::plaintextContent() const
 {
     if (!mNode) {
         return MessagePart::text();
     } else {
         return QString();
     }
 }
 
 QString EncryptedMessagePart::htmlContent() const
 {
     if (!mNode) {
         return MessagePart::text();
     } else {
         return QString();
     }
 }
 
 QString EncryptedMessagePart::text() const
 {
     if (hasSubParts()) {
         auto _mp = (subParts()[0]).dynamicCast<SignedMessagePart>();
         if (_mp) {
             return _mp->text();
-        } else {
-            return MessagePart::text();
         }
-    } else {
-        return MessagePart::text();
     }
+
+    return MessagePart::text();
 }
 
 EncapsulatedRfc822MessagePart::EncapsulatedRfc822MessagePart(ObjectTreeParser *otp, KMime::Content *node, const KMime::Message::Ptr &message)
     : MessagePart(otp, QString(), node)
     , mMessage(message)
 {
     mMetaData.isEncrypted = false;
     mMetaData.isSigned = false;
     mMetaData.isEncapsulatedRfc822Message = true;
 
     if (!mMessage) {
         qCWarning(MIMETREEPARSER_CORE_LOG) << "Node is of type message/rfc822 but doesn't have a message!";
         return;
     }
 
     parseInternal(message.data());
 }
 
 QString EncapsulatedRfc822MessagePart::text() const
 {
     return renderInternalText();
 }
 
 QString EncapsulatedRfc822MessagePart::from() const
 {
     if (auto from = mMessage->from(false)) {
         return from->asUnicodeString();
     }
     return {};
 }
 
 QDateTime EncapsulatedRfc822MessagePart::date() const
 {
     if (auto date = mMessage->date(false)) {
         return date->dateTime();
     }
     return {};
 }
 
 HeadersPart::HeadersPart(ObjectTreeParser *otp, KMime::Content *node)
     : MessagePart(otp, QString(), node)
 {
 }
 
 #include "moc_messagepart.cpp"
diff --git a/src/core/messagepart.h b/src/core/messagepart.h
index 154bf3d..6deb273 100644
--- a/src/core/messagepart.h
+++ b/src/core/messagepart.h
@@ -1,342 +1,371 @@
 // SPDX-FileCopyrightText: 2015 Sandro Knauß <knauss@kolabsys.com>
 // SPDX-License-Identifier: LGPL-2.0-or-later
 
 #pragma once
 
-#include "crypto.h"
 #include "mimetreeparser_core_export.h"
 #include "partmetadata.h"
 
+#include <gpgme++/decryptionresult.h>
+#include <gpgme++/importresult.h>
+#include <gpgme++/verificationresult.h>
+
 #include <KMime/Message>
 
+#include <QMap>
 #include <QSharedPointer>
 #include <QString>
 
 namespace KMime
 {
 class Content;
 }
 
+namespace QGpgME
+{
+class Protocol;
+}
+
 namespace MimeTreeParser
 {
 
 /** Flags for the encryption state. */
 typedef enum { KMMsgEncryptionStateUnknown, KMMsgNotEncrypted, KMMsgPartiallyEncrypted, KMMsgFullyEncrypted, KMMsgEncryptionProblematic } KMMsgEncryptionState;
 
 /** Flags for the signature state. */
 typedef enum { KMMsgSignatureStateUnknown, KMMsgNotSigned, KMMsgPartiallySigned, KMMsgFullySigned, KMMsgSignatureProblematic } KMMsgSignatureState;
 
 class ObjectTreeParser;
 class MultiPartAlternativeBodyPartFormatter;
 
 class SignedMessagePart;
 class EncryptedMessagePart;
 
-using Crypto::CryptoProtocol;
-using Crypto::CryptoProtocol::CMS;
-using Crypto::CryptoProtocol::OpenPGP;
-using Crypto::CryptoProtocol::UnknownProtocol;
-
 class MIMETREEPARSER_CORE_EXPORT MessagePart : public QObject
 {
     Q_OBJECT
     Q_PROPERTY(bool attachment READ isAttachment CONSTANT)
     Q_PROPERTY(bool root READ isRoot CONSTANT)
     Q_PROPERTY(bool isHtml READ isHtml CONSTANT)
     Q_PROPERTY(QString plaintextContent READ plaintextContent CONSTANT)
     Q_PROPERTY(QString htmlContent READ htmlContent CONSTANT)
 public:
     enum Disposition { Inline, Attachment, Invalid };
     using Ptr = QSharedPointer<MessagePart>;
     using List = QVector<Ptr>;
     MessagePart(ObjectTreeParser *otp, const QString &text, KMime::Content *node = nullptr);
 
     virtual ~MessagePart();
 
     virtual QString text() const;
     void setText(const QString &text);
     virtual bool isAttachment() const;
 
     void setIsRoot(bool root);
     bool isRoot() const;
 
     void setParentPart(MessagePart *parentPart);
     MessagePart *parentPart() const;
 
     virtual QString plaintextContent() const;
     virtual QString htmlContent() const;
 
     virtual bool isHtml() const;
 
     QByteArray mimeType() const;
     QByteArray charset() const;
     QString filename() const;
     Disposition disposition() const;
     bool isText() const;
 
     enum Error { NoError = 0, PassphraseError, NoKeyError, UnknownError };
 
     Error error() const;
     QString errorString() const;
 
     PartMetaData *partMetaData();
 
     void appendSubPart(const MessagePart::Ptr &messagePart);
     const QVector<MessagePart::Ptr> &subParts() const;
     bool hasSubParts() const;
 
     KMime::Content *node() const;
 
     virtual KMMsgSignatureState signatureState() const;
     virtual KMMsgEncryptionState encryptionState() const;
 
     QVector<SignedMessagePart *> signatures() const;
     QVector<EncryptedMessagePart *> encryptions() const;
 
     /**
      * Retrieve the header @header in this part or any parent parent.
      *
      * Useful for MemoryHole support.
      */
     KMime::Headers::Base *header(const char *header) const;
 
     void bindLifetime(KMime::Content *);
 
 protected:
     void parseInternal(KMime::Content *node, bool onlyOneMimePart = false);
     void parseInternal(const QByteArray &data);
     QString renderInternalText() const;
 
     QString mText;
     ObjectTreeParser *mOtp;
     PartMetaData mMetaData;
     MessagePart *mParentPart;
     KMime::Content *mNode;
     QVector<KMime::Content *> mNodesToDelete;
     Error mError;
 
 private:
     QVector<MessagePart::Ptr> mBlocks;
     bool mRoot;
 };
 
 class MimeMessagePart : public MessagePart
 {
     Q_OBJECT
 public:
     typedef QSharedPointer<MimeMessagePart> Ptr;
     MimeMessagePart(MimeTreeParser::ObjectTreeParser *otp, KMime::Content *node, bool onlyOneMimePart = false);
     virtual ~MimeMessagePart();
 
     QString text() const Q_DECL_OVERRIDE;
 
     QString plaintextContent() const Q_DECL_OVERRIDE;
     QString htmlContent() const Q_DECL_OVERRIDE;
 
 private:
     friend class AlternativeMessagePart;
 };
 
 class MessagePartList : public MessagePart
 {
     Q_OBJECT
 public:
     typedef QSharedPointer<MessagePartList> Ptr;
     MessagePartList(MimeTreeParser::ObjectTreeParser *otp, KMime::Content *node);
     virtual ~MessagePartList() = default;
 
     QString text() const Q_DECL_OVERRIDE;
 
     QString plaintextContent() const Q_DECL_OVERRIDE;
     QString htmlContent() const Q_DECL_OVERRIDE;
 };
 
 class TextMessagePart : public MessagePartList
 {
     Q_OBJECT
 public:
     typedef QSharedPointer<TextMessagePart> Ptr;
     TextMessagePart(MimeTreeParser::ObjectTreeParser *otp, KMime::Content *node);
     virtual ~TextMessagePart() = default;
 
     KMMsgSignatureState signatureState() const Q_DECL_OVERRIDE;
     KMMsgEncryptionState encryptionState() const Q_DECL_OVERRIDE;
 
 private:
     void parseContent();
 
     KMMsgSignatureState mSignatureState;
     KMMsgEncryptionState mEncryptionState;
 
     friend class ObjectTreeParser;
 };
 
 class AttachmentMessagePart : public TextMessagePart
 {
     Q_OBJECT
 public:
     typedef QSharedPointer<AttachmentMessagePart> Ptr;
     AttachmentMessagePart(MimeTreeParser::ObjectTreeParser *otp, KMime::Content *node);
     virtual ~AttachmentMessagePart() = default;
     virtual bool isAttachment() const Q_DECL_OVERRIDE
     {
         return true;
     }
 };
 
 class HtmlMessagePart : public MessagePart
 {
     Q_OBJECT
 public:
     typedef QSharedPointer<HtmlMessagePart> Ptr;
     HtmlMessagePart(MimeTreeParser::ObjectTreeParser *otp, KMime::Content *node);
     virtual ~HtmlMessagePart() = default;
     bool isHtml() const Q_DECL_OVERRIDE
     {
         return true;
     };
 };
 
 class AlternativeMessagePart : public MessagePart
 {
     Q_OBJECT
 public:
     enum HtmlMode {
         Normal, ///< A normal plaintext message, non-multipart
         Html, ///< A HTML message, non-multipart
         MultipartPlain, ///< A multipart/alternative message, the plain text part is currently displayed
         MultipartHtml, ///< A multipart/altervative message, the HTML part is currently displayed
         MultipartIcal ///< A multipart/altervative message, the ICal part is currently displayed
     };
 
     typedef QSharedPointer<AlternativeMessagePart> Ptr;
     AlternativeMessagePart(MimeTreeParser::ObjectTreeParser *otp, KMime::Content *node);
     virtual ~AlternativeMessagePart();
 
     QString text() const Q_DECL_OVERRIDE;
 
     bool isHtml() const Q_DECL_OVERRIDE;
 
     QString plaintextContent() const Q_DECL_OVERRIDE;
     QString htmlContent() const Q_DECL_OVERRIDE;
     QString icalContent() const;
 
     QList<HtmlMode> availableModes();
 
 private:
     QMap<HtmlMode, MessagePart::Ptr> mChildParts;
 
     friend class ObjectTreeParser;
     friend class MultiPartAlternativeBodyPartFormatter;
 };
 
 class CertMessagePart : public MessagePart
 {
     Q_OBJECT
 public:
     typedef QSharedPointer<CertMessagePart> Ptr;
-    CertMessagePart(MimeTreeParser::ObjectTreeParser *otp, KMime::Content *node, const CryptoProtocol cryptoProto);
+    CertMessagePart(MimeTreeParser::ObjectTreeParser *otp, KMime::Content *node, QGpgME::Protocol *cryptoProto);
     virtual ~CertMessagePart();
 
     QString text() const Q_DECL_OVERRIDE;
-    void import();
 
 private:
-    const CryptoProtocol mProtocol;
+    const QGpgME::Protocol *mCryptoProto;
+    GpgME::ImportResult mInportResult;
 };
 
 class EncapsulatedRfc822MessagePart : public MessagePart
 {
     Q_OBJECT
 public:
     typedef QSharedPointer<EncapsulatedRfc822MessagePart> Ptr;
     EncapsulatedRfc822MessagePart(MimeTreeParser::ObjectTreeParser *otp, KMime::Content *node, const KMime::Message::Ptr &message);
     virtual ~EncapsulatedRfc822MessagePart() = default;
 
     QString text() const Q_DECL_OVERRIDE;
     QString from() const;
     QDateTime date() const;
 
 private:
     const KMime::Message::Ptr mMessage;
 };
 
 class EncryptedMessagePart : public MessagePart
 {
     Q_OBJECT
-    Q_PROPERTY(bool isEncrypted READ isEncrypted CONSTANT)
+    Q_PROPERTY(bool decryptMessage READ decryptMessage WRITE setDecryptMessage)
+    Q_PROPERTY(bool isEncrypted READ isEncrypted)
+    Q_PROPERTY(bool isNoSecKey READ isNoSecKey)
+    Q_PROPERTY(bool passphraseError READ passphraseError)
 public:
     typedef QSharedPointer<EncryptedMessagePart> Ptr;
     EncryptedMessagePart(ObjectTreeParser *otp,
                          const QString &text,
-                         const CryptoProtocol protocol,
+                         const QGpgME::Protocol *protocol,
                          KMime::Content *node,
                          KMime::Content *encryptedNode = nullptr,
                          bool parseAfterDecryption = true);
 
     virtual ~EncryptedMessagePart() = default;
 
     QString text() const Q_DECL_OVERRIDE;
 
+    void setDecryptMessage(bool decrypt);
+    Q_REQUIRED_RESULT bool decryptMessage() const;
+
     void setIsEncrypted(bool encrypted);
-    bool isEncrypted() const;
+    Q_REQUIRED_RESULT bool isEncrypted() const;
 
-    bool isDecryptable() const;
+    Q_REQUIRED_RESULT bool isDecryptable() const;
+
+    Q_REQUIRED_RESULT bool isNoSecKey() const;
+    Q_REQUIRED_RESULT bool passphraseError() const;
 
     void startDecryption(KMime::Content *data);
     void startDecryption();
 
     QByteArray mDecryptedData;
 
-    QString plaintextContent() const Q_DECL_OVERRIDE;
-    QString htmlContent() const Q_DECL_OVERRIDE;
+    QString plaintextContent() const override;
+    QString htmlContent() const override;
+
+    const QGpgME::Protocol *cryptoProto() const;
+
+    std::vector<std::pair<GpgME::DecryptionResult::Recipient, GpgME::Key>> decryptRecipients() const;
 
 private:
     bool decrypt(KMime::Content &data);
     bool mParseAfterDecryption{true};
 
 protected:
-    const CryptoProtocol mProtocol;
+    bool mPassphraseError;
+    bool mNoSecKey;
+    bool mDecryptMessage;
+    const QGpgME::Protocol *mCryptoProto;
     QByteArray mVerifiedText;
+    std::vector<std::pair<GpgME::DecryptionResult::Recipient, GpgME::Key>> mDecryptRecipients;
     KMime::Content *mEncryptedNode;
 };
 
 class SignedMessagePart : public MessagePart
 {
     Q_OBJECT
     Q_PROPERTY(bool isSigned READ isSigned CONSTANT)
 public:
     typedef QSharedPointer<SignedMessagePart> Ptr;
-    SignedMessagePart(ObjectTreeParser *otp, const CryptoProtocol protocol, KMime::Content *node, KMime::Content *signedData, bool parseAfterDecryption = true);
+    SignedMessagePart(ObjectTreeParser *otp,
+                      const QGpgME::Protocol *protocol,
+                      KMime::Content *node,
+                      KMime::Content *signedData,
+                      bool parseAfterDecryption = true);
 
     virtual ~SignedMessagePart();
 
     void setIsSigned(bool isSigned);
     bool isSigned() const;
 
     void startVerification();
 
     QString plaintextContent() const Q_DECL_OVERRIDE;
     QString htmlContent() const Q_DECL_OVERRIDE;
 
+    const QGpgME::Protocol *cryptoProto() const;
+
 private:
-    void setVerificationResult(const Crypto::VerificationResult &result, const QByteArray &signedData);
+    void sigStatusToMetaData();
+    void setVerificationResult(const GpgME::VerificationResult &result, const QByteArray &signedData);
     bool mParseAfterDecryption{true};
 
 protected:
-    CryptoProtocol mProtocol;
+    const QGpgME::Protocol *mCryptoProto;
     KMime::Content *mSignedData;
+    std::vector<GpgME::Signature> mSignatures;
 
     friend EncryptedMessagePart;
 };
 
 class HeadersPart : public MessagePart
 {
     Q_OBJECT
 public:
     typedef QSharedPointer<HeadersPart> Ptr;
     HeadersPart(ObjectTreeParser *otp, KMime::Content *node);
     virtual ~HeadersPart() = default;
 };
 
 }
diff --git a/src/core/objecttreeparser.cpp b/src/core/objecttreeparser.cpp
index 572652e..fd36c2a 100644
--- a/src/core/objecttreeparser.cpp
+++ b/src/core/objecttreeparser.cpp
@@ -1,503 +1,488 @@
 // This file is part of KMail, the KDE mail client.
 // SPDX-FileCopyrightText: 2003      Marc Mutz <mutz@kde.org>
 // SPDX-FileCopyrightText: 2002-2004 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net
 // SPDX-FileCopyrightText: 2009 Andras Mantia <andras@kdab.net>
 // SPDX-FileCopyrightText: 2015 Sandro Knauß <sknauss@kde.org>
 // SPDX-FileCopyrightText: 2017 Christian Mollekopf <mollekopf@kolabsystems.com>
 // SPDX-License-Identifier: GPL-2.0-or-later
 
 #include "objecttreeparser.h"
 
 #include "bodypartformatterbasefactory.h"
 
 #include "bodypartformatter.h"
 
 #include <KMime/Message>
 
 #include <KCharsets>
 #include <QByteArray>
 #include <QDebug>
 #include <QMimeDatabase>
 #include <QRegularExpression>
 #include <QTextCodec>
 #include <QTextStream>
 #include <QUrl>
 
 using namespace MimeTreeParser;
 
 /*
  * Collect message parts bottom up.
  * Filter to avoid evaluating a subtree.
  * Select parts to include it in the result set. Selecting a part in a branch will keep any parent parts from being selected.
  */
 static QVector<MessagePart::Ptr> collect(MessagePart::Ptr start,
                                          const std::function<bool(const MessagePart::Ptr &)> &evaluateSubtree,
                                          const std::function<bool(const MessagePart::Ptr &)> &select)
 {
     auto ptr = start.dynamicCast<MessagePart>();
     Q_ASSERT(ptr);
     MessagePart::List list;
     if (evaluateSubtree(ptr)) {
         for (const auto &p : ptr->subParts()) {
             list << ::collect(p, evaluateSubtree, select);
         }
     }
 
     // Don't consider this part if we already selected a subpart
     if (list.isEmpty()) {
         if (select(ptr)) {
             list << start;
         }
     }
     return list;
 }
 
 QString ObjectTreeParser::plainTextContent()
 {
     QString content;
     if (mParsedPart) {
         auto plainParts = ::collect(
             mParsedPart,
             [](const MessagePart::Ptr &) {
                 return true;
             },
             [](const MessagePart::Ptr &part) {
                 if (part->isAttachment()) {
                     return false;
                 }
                 if (dynamic_cast<MimeTreeParser::TextMessagePart *>(part.data())) {
                     return true;
                 }
                 if (dynamic_cast<MimeTreeParser::AlternativeMessagePart *>(part.data())) {
                     return true;
                 }
                 return false;
             });
         for (const auto &part : plainParts) {
             content += part->text();
         }
     }
     return content;
 }
 
 QString ObjectTreeParser::htmlContent()
 {
     QString content;
     if (mParsedPart) {
         MessagePart::List contentParts = ::collect(
             mParsedPart,
             [](const MessagePart::Ptr &) {
                 return true;
             },
             [](const MessagePart::Ptr &part) {
                 if (dynamic_cast<MimeTreeParser::HtmlMessagePart *>(part.data())) {
                     return true;
                 }
                 if (dynamic_cast<MimeTreeParser::AlternativeMessagePart *>(part.data())) {
                     return true;
                 }
                 return false;
             });
         for (const auto &part : contentParts) {
             if (auto p = dynamic_cast<MimeTreeParser::AlternativeMessagePart *>(part.data())) {
                 content += p->htmlContent();
             } else {
                 content += part->text();
             }
         }
     }
     return content;
 }
 
 static void print(QTextStream &stream, KMime::Content *node, const QString prefix = {})
 {
     QByteArray mediaType("text");
     QByteArray subType("plain");
     if (node->contentType(false) && !node->contentType()->mediaType().isEmpty() && !node->contentType()->subType().isEmpty()) {
         mediaType = node->contentType()->mediaType();
         subType = node->contentType()->subType();
     }
     stream << prefix << "! " << mediaType << subType << " isAttachment: " << KMime::isAttachment(node) << "\n";
     const auto contents = node->contents();
     for (const auto nodeContent : contents) {
         print(stream, nodeContent, prefix + QLatin1String(" "));
     }
 }
 
 static void print(QTextStream &stream, const MessagePart &messagePart, const QByteArray pre = {})
 {
     stream << pre << "# " << messagePart.metaObject()->className() << " isAttachment: " << messagePart.isAttachment() << "\n";
     const auto subParts = messagePart.subParts();
     for (const auto &subPart : subParts) {
         print(stream, *subPart, pre + " ");
     }
 }
 
 QString ObjectTreeParser::structureAsString() const
 {
     QString string;
     QTextStream stream{&string};
 
     if (mTopLevelContent) {
         ::print(stream, mTopLevelContent);
     }
     if (mParsedPart) {
         ::print(stream, *mParsedPart);
     }
     return string;
 }
 
 void ObjectTreeParser::print()
 {
     qInfo().noquote() << structureAsString();
 }
 
 static KMime::Content *find(KMime::Content *node, const std::function<bool(KMime::Content *)> &select)
 {
     QByteArray mediaType("text");
     QByteArray subType("plain");
     if (node->contentType(false) && !node->contentType()->mediaType().isEmpty() && !node->contentType()->subType().isEmpty()) {
         mediaType = node->contentType()->mediaType();
         subType = node->contentType()->subType();
     }
     if (select(node)) {
         return node;
     }
     const auto contents = node->contents();
     for (const auto nodeContent : contents) {
         if (const auto content = find(nodeContent, select)) {
             return content;
         }
     }
     return nullptr;
 }
 
 KMime::Content *ObjectTreeParser::find(const std::function<bool(KMime::Content *)> &select)
 {
     return ::find(mTopLevelContent, select);
 }
 
 MessagePart::List ObjectTreeParser::collectContentParts()
 {
     return collectContentParts(mParsedPart);
 }
 
 MessagePart::List ObjectTreeParser::collectContentParts(MessagePart::Ptr start)
 {
     return ::collect(
         start,
         [start](const MessagePart::Ptr &part) {
             // Ignore the top-level
             if (start.data() == part.data()) {
                 return true;
             }
             if (auto encapsulatedPart = part.dynamicCast<MimeTreeParser::EncapsulatedRfc822MessagePart>()) {
                 return false;
             }
             return true;
         },
         [start](const MessagePart::Ptr &part) {
             if (const auto attachment = dynamic_cast<MimeTreeParser::AttachmentMessagePart *>(part.data())) {
                 return attachment->mimeType() == "text/calendar";
             } else if (const auto text = dynamic_cast<MimeTreeParser::TextMessagePart *>(part.data())) {
                 auto enc = dynamic_cast<MimeTreeParser::EncryptedMessagePart *>(text->parentPart());
                 if (enc && enc->error()) {
                     return false;
                 }
 
                 return true;
             } else if (dynamic_cast<MimeTreeParser::AlternativeMessagePart *>(part.data())) {
                 return true;
             } else if (dynamic_cast<MimeTreeParser::HtmlMessagePart *>(part.data())) {
                 // Don't if we have an alternative part as parent
                 return true;
             } else if (dynamic_cast<MimeTreeParser::EncapsulatedRfc822MessagePart *>(part.data())) {
                 if (start.data() == part.data()) {
                     return false;
                 }
                 return true;
             } else if (const auto enc = dynamic_cast<MimeTreeParser::EncryptedMessagePart *>(part.data())) {
                 if (enc->error()) {
                     return true;
                 }
                 // If we have a textpart with encrypted and unencrypted subparts we want to return the textpart
                 if (dynamic_cast<MimeTreeParser::TextMessagePart *>(enc->parentPart())) {
                     return false;
                 }
             } else if (const auto sig = dynamic_cast<MimeTreeParser::SignedMessagePart *>(part.data())) {
                 // Signatures without subparts already contain the text
                 return !sig->hasSubParts();
             }
             return false;
         });
 }
 
 MessagePart::List ObjectTreeParser::collectAttachmentParts()
 {
     MessagePart::List contentParts = ::collect(
         mParsedPart,
         [](const MessagePart::Ptr &) {
             return true;
         },
         [](const MessagePart::Ptr &part) {
             return part->isAttachment();
         });
     return contentParts;
 }
 
 void ObjectTreeParser::decryptParts()
 {
     decryptAndVerify();
 }
 
 /*
  * This naive implementation assumes that there is an encrypted part wrapping a signature.
  * For other cases we would have to process both recursively (I think?)
  */
 void ObjectTreeParser::decryptAndVerify()
 {
     // We first decrypt
     ::collect(
         mParsedPart,
         [](const MessagePart::Ptr &) {
             return true;
         },
         [](const MessagePart::Ptr &part) {
             if (const auto enc = dynamic_cast<MimeTreeParser::EncryptedMessagePart *>(part.data())) {
                 enc->startDecryption();
             }
             return false;
         });
     // And then verify the available signatures
     ::collect(
         mParsedPart,
         [](const MessagePart::Ptr &) {
             return true;
         },
         [](const MessagePart::Ptr &part) {
             if (const auto enc = dynamic_cast<MimeTreeParser::SignedMessagePart *>(part.data())) {
                 enc->startVerification();
             }
             return false;
         });
 }
 
-void ObjectTreeParser::importCertificates()
-{
-    MessagePart::List contentParts = ::collect(
-        mParsedPart,
-        [](const MessagePart::Ptr &) {
-            return true;
-        },
-        [](const MessagePart::Ptr &part) {
-            if (const auto cert = dynamic_cast<MimeTreeParser::CertMessagePart *>(part.data())) {
-                cert->import();
-            }
-            return false;
-        });
-}
-
 QString ObjectTreeParser::resolveCidLinks(const QString &html)
 {
     auto text = html;
     static const auto regex = QRegularExpression(QLatin1String("(src)\\s*=\\s*(\"|')(cid:[^\"']+)\\2"));
     auto it = regex.globalMatch(text);
     while (it.hasNext()) {
         const auto match = it.next();
         const auto link = QUrl(match.captured(3));
         auto cid = link.path();
         auto mailMime = const_cast<KMime::Content *>(find([=](KMime::Content *content) {
             if (!content || !content->contentID(false)) {
                 return false;
             }
             return QString::fromLatin1(content->contentID(false)->identifier()) == cid;
         }));
         if (mailMime) {
             const auto contentType = mailMime->contentType(false);
             if (!contentType) {
                 qWarning() << "No content type, skipping";
                 continue;
             }
             QMimeDatabase mimeDb;
             const auto mimetype = mimeDb.mimeTypeForName(QString::fromLatin1(contentType->mimeType())).name();
             if (mimetype.startsWith(QLatin1String("image/"))) {
                 // We reencode to base64 below.
                 const auto data = mailMime->decodedContent();
                 if (data.isEmpty()) {
                     qWarning() << "Attachment is empty.";
                     continue;
                 }
                 text.replace(match.captured(0), QString::fromLatin1("src=\"data:%1;base64,%2\"").arg(mimetype, QString::fromLatin1(data.toBase64())));
             }
         } else {
             qWarning() << "Failed to find referenced attachment: " << cid;
         }
     }
     return text;
 }
 
 //-----------------------------------------------------------------------------
 
 void ObjectTreeParser::parseObjectTree(const QByteArray &mimeMessage)
 {
     const auto mailData = KMime::CRLFtoLF(mimeMessage);
     mMsg = KMime::Message::Ptr(new KMime::Message);
     mMsg->setContent(mailData);
     mMsg->parse();
     // We avoid using mMsg->contentType()->charset(), because that will just return kmime's defaultCharset(), ISO-8859-1
     const auto charset = mMsg->contentType()->parameter(QStringLiteral("charset")).toLatin1();
     if (charset.isEmpty()) {
         mMsg->contentType()->setCharset("us-ascii");
     }
     parseObjectTree(mMsg.data());
 }
 
 void ObjectTreeParser::parseObjectTree(KMime::Content *node)
 {
     mTopLevelContent = node;
     mParsedPart = parseObjectTreeInternal(node, false);
 }
 
 MessagePart::Ptr ObjectTreeParser::parsedPart() const
 {
     return mParsedPart;
 }
 
 /*
  * This will lookup suitable formatters based on the type,
  * and let them generate a list of parts.
  * If the formatter generated a list of parts, then those are taken, otherwise we move on to the next match.
  */
 MessagePart::List ObjectTreeParser::processType(KMime::Content *node, const QByteArray &mediaType, const QByteArray &subType)
 {
     static MimeTreeParser::BodyPartFormatterBaseFactory factory;
     const auto sub = factory.subtypeRegistry(mediaType.constData());
     const auto range = sub.equal_range(subType.constData());
     for (auto it = range.first; it != range.second; ++it) {
         const auto formatter = it->second;
         if (!formatter) {
             continue;
         }
         const auto list = formatter->processList(this, node);
         if (!list.isEmpty()) {
             return list;
         }
     }
     return {};
 }
 
 MessagePart::Ptr ObjectTreeParser::parseObjectTreeInternal(KMime::Content *node, bool onlyOneMimePart)
 {
     if (!node) {
         return MessagePart::Ptr();
     }
 
     auto parsedPart = MessagePart::Ptr(new MessagePartList(this, node));
     parsedPart->setIsRoot(node->isTopLevel());
     const auto contents = node->parent() ? node->parent()->contents() : KMime::Content::List{node};
     for (int i = contents.indexOf(node); i < contents.size(); ++i) {
         node = contents.at(i);
 
         QByteArray mediaType("text");
         QByteArray subType("plain");
         if (node->contentType(false) && !node->contentType()->mediaType().isEmpty() && !node->contentType()->subType().isEmpty()) {
             mediaType = node->contentType()->mediaType();
             subType = node->contentType()->subType();
         }
 
         auto messageParts = [&] {
             // Try the specific type handler
             {
                 auto list = processType(node, mediaType, subType);
                 if (!list.isEmpty()) {
                     return list;
                 }
             }
             // Fallback to the generic handler
             {
                 auto list = processType(node, mediaType, "*");
                 if (!list.isEmpty()) {
                     return list;
                 }
             }
             // Fallback to the default handler
             return defaultHandling(node);
         }();
 
         for (const auto &part : messageParts) {
             parsedPart->appendSubPart(part);
         }
 
         if (onlyOneMimePart) {
             break;
         }
     }
 
     return parsedPart;
 }
 
 QVector<MessagePart::Ptr> ObjectTreeParser::defaultHandling(KMime::Content *node)
 {
     if (node->contentType()->mimeType() == QByteArrayLiteral("application/octet-stream")
         && (node->contentType()->name().endsWith(QLatin1String("p7m")) || node->contentType()->name().endsWith(QLatin1String("p7s"))
             || node->contentType()->name().endsWith(QLatin1String("p7c")))) {
         auto list = processType(node, "application", "pkcs7-mime");
         if (!list.isEmpty()) {
             return list;
         }
     }
 
     return {AttachmentMessagePart::Ptr(new AttachmentMessagePart(this, node))};
 }
 
 static QTextCodec *getLocalCodec()
 {
     auto codec = QTextCodec::codecForLocale();
 
     // In the case of Japan. Japanese locale name is "eucjp" but
     // The Japanese mail systems normally used "iso-2022-jp" of locale name.
     // We want to change locale name from eucjp to iso-2022-jp at KMail only.
 
     // (Introduction to i18n, 6.6 Limit of Locale technology):
     // EUC-JP is the de-facto standard for UNIX systems, ISO 2022-JP
     // is the standard for Internet, and Shift-JIS is the encoding
     // for Windows and Macintosh.
     if (codec) {
         const QByteArray codecNameLower = codec->name().toLower();
         if (codecNameLower == "eucjp"
 #if defined Q_OS_WIN || defined Q_OS_MACX
             || codecNameLower == "shift-jis" // OK?
 #endif
         ) {
             codec = QTextCodec::codecForName("jis7");
             // QTextCodec *cdc = QTextCodec::codecForName("jis7");
             // QTextCodec::setCodecForLocale(cdc);
             // KLocale::global()->setEncoding(cdc->mibEnum());
         }
     }
     return codec;
 }
 
 const QTextCodec *ObjectTreeParser::codecFor(KMime::Content *node) const
 {
     static auto localCodec = getLocalCodec();
     if (!node) {
         return localCodec;
     }
 
     QByteArray charset = node->contentType()->charset().toLower();
 
     // utf-8 is a superset of us-ascii, so we don't lose anything if we use it instead
     // utf-8 is used so widely nowadays that it is a good idea to use it to fix issues with broken clients.
     if (charset == "us-ascii") {
         charset = "utf-8";
     }
     if (!charset.isEmpty()) {
         if (auto c = QTextCodec::codecForName(charset)) {
             return c;
         }
     }
     // no charset means us-ascii (RFC 2045), so using local encoding should
     // be okay
     return localCodec;
 }
diff --git a/src/core/objecttreeparser.h b/src/core/objecttreeparser.h
index d66a355..03fa67b 100644
--- a/src/core/objecttreeparser.h
+++ b/src/core/objecttreeparser.h
@@ -1,106 +1,103 @@
 // SPDX-FileCopyrightText: 2016 Sandro Knauß <sknauss@kde.org>
 // SPDX-FileCopyrightText: 2003 Marc Mutz <mutz@kde.org>
 // SPDX-FileCopyrightText: 2002-2003, 2009 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net
 // SPDX-FileCopyrightText: 2009 Andras Mantia <andras@kdab.net>
 // SPDX-License-Identifier: LGPL-2.0-or-later
 
 #pragma once
 
 #include "messagepart.h"
 
 #include "mimetreeparser_core_export.h"
 #include <QSharedPointer>
 #include <functional>
 
 class QString;
 
 namespace KMime
 {
 class Content;
 }
 
 class QTextCodec;
 
 namespace MimeTreeParser
 {
 
 /**
     Entry point to parse mime messages.
 
     Content returned by the ObjectTreeParser (including messageparts),
     is normalized to not contain any CRLF's but only LF's (just like KMime).
 */
 class MIMETREEPARSER_CORE_EXPORT ObjectTreeParser
 {
     // Disable copy
     ObjectTreeParser(const ObjectTreeParser &other);
 
 public:
     explicit ObjectTreeParser() = default;
     virtual ~ObjectTreeParser() = default;
 
     QString structureAsString() const;
     void print();
 
     /**
      * The text of the message, ie. what would appear in the
      * composer's text editor if this was edited or replied to.
      * This is usually the content of the first text/plain MIME part.
      */
     QString plainTextContent();
 
     /**
      * Similar to plainTextContent(), but returns the HTML source of the first text/html MIME part.
      */
     QString htmlContent();
 
     /** Parse beginning at a given node and recursively parsing
       the children of that node and it's next sibling. */
     void parseObjectTree(KMime::Content *node);
     void parseObjectTree(const QByteArray &mimeMessage);
     MessagePart::Ptr parsedPart() const;
     KMime::Content *find(const std::function<bool(KMime::Content *)> &select);
     MessagePart::List collectContentParts();
     MessagePart::List collectContentParts(MessagePart::Ptr start);
     MessagePart::List collectAttachmentParts();
 
     /** Decrypt parts and verify signatures */
     void decryptAndVerify();
     // DEPRECATED calls decryptAndVerify
     void decryptParts();
 
-    /** Import any certificates found in the message */
-    void importCertificates();
-
     /** Embedd content referenced by cid by inlining */
     QString resolveCidLinks(const QString &html);
 
 private:
     /**
      * Does the actual work for parseObjectTree. Unlike parseObjectTree(), this does not change the
      * top-level content.
      */
     MessagePart::Ptr parseObjectTreeInternal(KMime::Content *node, bool mOnlyOneMimePart);
     MessagePart::List processType(KMime::Content *node, const QByteArray &mediaType, const QByteArray &subType);
 
     MessagePart::List defaultHandling(KMime::Content *node);
 
     const QTextCodec *codecFor(KMime::Content *node) const;
 
     KMime::Content *mTopLevelContent{nullptr};
     MessagePart::Ptr mParsedPart;
 
     KMime::Message::Ptr mMsg;
 
     friend class MessagePart;
     friend class EncryptedMessagePart;
     friend class SignedMessagePart;
     friend class EncapsulatedRfc822MessagePart;
     friend class TextMessagePart;
     friend class HtmlMessagePart;
     friend class TextPlainBodyPartFormatter;
     friend class MultiPartSignedBodyPartFormatter;
     friend class ApplicationPkcs7MimeBodyPartFormatter;
 };
 
 }
diff --git a/src/core/partmetadata.h b/src/core/partmetadata.h
index 472d9a2..3f8ff83 100644
--- a/src/core/partmetadata.h
+++ b/src/core/partmetadata.h
@@ -1,39 +1,61 @@
 // SPDX-FileCopyrightText: 2002-2003 Karl-Heinz Zimmer <khz@kde.org>
 // SPDX-FileCopyrightText: 2003 Marc Mutz <mutz@kde.org>
 // SPDX-License-Identifier: GPL-2.0-or-later
 
 #pragma once
 
 #include <QDateTime>
 #include <QStringList>
+#include <gpgme++/context.h>
+#include <gpgme++/verificationresult.h>
 
 namespace MimeTreeParser
 {
 
 class PartMetaData
 {
 public:
-    bool keyMissing = false;
-    bool keyExpired = false;
-    bool keyRevoked = false;
-    bool sigExpired = false;
-    bool crlMissing = false;
-    bool crlTooOld = false;
+    PartMetaData()
+        : isSigned(false)
+        , isGoodSignature(false)
+        , isEncrypted(false)
+        , isDecryptable(false)
+        , inProgress(false)
+        , technicalProblem(false)
+        , isEncapsulatedRfc822Message(false)
+        , isCompliant(false)
+        , keyRevoked(false)
+    {
+    }
+
+    GpgME::Signature::Summary sigSummary = GpgME::Signature::None;
+    QString signClass;
     QString signer;
     QStringList signerMailAddresses;
     QByteArray keyId;
-    bool keyIsTrusted = false;
+    GpgME::Signature::Validity keyTrust = GpgME::Signature::Validity::Unknown;
     QString status; // to be used for unknown plug-ins
+    int status_code = 0; // = GPGME_SIG_STAT_NONE; to be used for i18n of OpenPGP and S/MIME CryptPlugs
     QString errorText;
     QDateTime creationTime;
     QString decryptionError;
     QString auditLog;
-    bool isSigned = false;
-    bool isGoodSignature = false;
-    bool isEncrypted = false;
-    bool isDecryptable = false;
-    bool technicalProblem = false;
-    bool isEncapsulatedRfc822Message = false;
+    QString compliance; // textual representation of compliance status; empty if compliance isn't enforced
+    GpgME::Error auditLogError;
+    bool isSigned : 1;
+    bool isGoodSignature : 1;
+    bool isEncrypted : 1;
+    bool isDecryptable : 1;
+    bool inProgress : 1;
+    bool technicalProblem : 1;
+    bool isEncapsulatedRfc822Message : 1;
+    bool isCompliant : 1; // corresponds to the isDeVS flag of signature or decryption result
+    bool keyRevoked : 1;
+
+    inline bool isTrusted()
+    {
+        return keyTrust == GpgME::Signature::Full || keyTrust == GpgME::Signature::Ultimate;
+    }
 };
 
 }
diff --git a/src/core/partmodel.cpp b/src/core/partmodel.cpp
index 07858c7..098b5dc 100644
--- a/src/core/partmodel.cpp
+++ b/src/core/partmodel.cpp
@@ -1,544 +1,548 @@
 // SPDX-FileCopyrightText: 2016 Sandro Knauß <knauss@kolabsys.com>
 // SPDX-License-Identifier: LGPL-2.0-or-later
 
 #include "partmodel.h"
 
 #include "htmlutils.h"
 #include "objecttreeparser.h"
 
 #include <KLocalizedString>
 
 #include <QDebug>
 #include <QRegularExpression>
 #include <QStringLiteral>
 #include <QTextDocument>
 
 // We return a pair containing the trimmed string, as well as a boolean indicating whether the string was trimmed or not
 std::pair<QString, bool> PartModel::trim(const QString &text)
 {
     // The delimiters have <p>.? prefixed including the .? because sometimes we get a byte order mark <feff> (seen with user-agent:
     // Microsoft-MacOutlook/10.1d.0.190908) We match both regulard withspace with \s and non-breaking spaces with \u00A0
     const QList<QRegularExpression> delimiters{
         // English
         QRegularExpression{QStringLiteral("<p>.?-+Original(\\s|\u00A0)Message-+"), QRegularExpression::CaseInsensitiveOption},
         // The remainder is not quoted
         QRegularExpression{QStringLiteral("<p>.?On.*wrote:"), QRegularExpression::CaseInsensitiveOption},
         // The remainder is quoted
         QRegularExpression{QStringLiteral("&gt; On.*wrote:"), QRegularExpression::CaseInsensitiveOption},
 
         // German
         // Forwarded
         QRegularExpression{QStringLiteral("<p>.?Von:.*</p>"), QRegularExpression::CaseInsensitiveOption},
         // Reply
         QRegularExpression{QStringLiteral("<p>.?Am.*schrieb.*:</p>"), QRegularExpression::CaseInsensitiveOption},
         // Signature
         QRegularExpression{QStringLiteral("<p>.?--(\\s|\u00A0)<br>"), QRegularExpression::CaseInsensitiveOption},
     };
 
     for (const auto &expression : delimiters) {
         auto i = expression.globalMatch(text);
         while (i.hasNext()) {
             const auto match = i.next();
             const int startOffset = match.capturedStart(0);
             // This is a very simplistic detection for an inline reply where we would have the patterns before the actual message content.
             // We simply ignore anything we find within the first few lines.
             if (startOffset >= 5) {
                 return {text.mid(0, startOffset), true};
             } else {
                 // Search for the next delimiter
                 continue;
             }
         }
     }
 
     return {text, false};
 }
 
 static QString addCss(const QString &s)
 {
     // Get the default font from QApplication
     static const QString fontFamily = QFont{}.family();
     // overflow:hidden ensures no scrollbars are ever shown.
     static const QString css = QStringLiteral("<style>\n")
         + QStringLiteral(
               "body {\n"
               "  overflow:hidden;\n"
               "  font-family: \"%1\" ! important;\n"
               "  color: #31363b ! important;\n"
               "  background-color: #fcfcfc ! important\n"
               "}\n")
               .arg(fontFamily)
         + QStringLiteral("blockquote { \n"
                          "  border-left: 2px solid #bdc3c7 ! important;\n"
                          "}\n")
         + QStringLiteral("</style>");
 
     const QString header = QLatin1String(
                                "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n"
                                "<html><head><title></title>")
         + css + QLatin1String("</head>\n<body>\n");
     return header + s + QStringLiteral("</body></html>");
 }
 
 class PartModelPrivate
 {
 public:
     PartModelPrivate(PartModel *q_ptr, const std::shared_ptr<MimeTreeParser::ObjectTreeParser> &parser)
         : q(q_ptr)
         , mParser(parser)
     {
         collectContents();
     }
 
     ~PartModelPrivate() = default;
 
     void checkPart(const MimeTreeParser::MessagePart::Ptr part)
     {
         mMimeTypeCache[part.data()] = part->mimeType();
         // Extract the content of the part and
         mContents.insert(part.data(), extractContent(part.data()));
     }
 
     // Recursively find encapsulated messages
     void findEncapsulated(const MimeTreeParser::EncapsulatedRfc822MessagePart::Ptr &e)
     {
         mEncapsulatedParts[e.data()] = mParser->collectContentParts(e);
         for (const auto &subPart : std::as_const(mEncapsulatedParts[e.data()])) {
             checkPart(subPart);
             mParents[subPart.data()] = e.data();
             if (auto encapsulatedSub = subPart.dynamicCast<MimeTreeParser::EncapsulatedRfc822MessagePart>()) {
                 findEncapsulated(encapsulatedSub);
             }
         }
     }
 
     QVariant extractContent(MimeTreeParser::MessagePart *messagePart)
     {
         if (auto alternativePart = dynamic_cast<MimeTreeParser::AlternativeMessagePart *>(messagePart)) {
             if (alternativePart->availableModes().contains(MimeTreeParser::AlternativeMessagePart::MultipartIcal)) {
                 return alternativePart->icalContent();
             }
         }
 
         auto preprocessPlaintext = [&](const QString &text) {
             // We always do rich text (so we get highlighted links and stuff).
             // NOTE: this inserts non-breaking spaces instead of regular spaces.
             const auto html = Qt::convertFromPlainText(text);
             if (trimMail) {
                 const auto result = PartModel::trim(html);
                 isTrimmed = result.second;
                 Q_EMIT q->trimMailChanged();
                 return MimeTreeParser::linkify(result.first);
             }
             return MimeTreeParser::linkify(html);
         };
 
         if (messagePart->isHtml()) {
             if (dynamic_cast<MimeTreeParser::AlternativeMessagePart *>(messagePart)) {
                 containsHtmlAndPlain = true;
                 Q_EMIT q->containsHtmlChanged();
                 if (!showHtml) {
                     return preprocessPlaintext(messagePart->plaintextContent());
                 }
             }
             return addCss(mParser->resolveCidLinks(messagePart->htmlContent()));
         }
 
         if (auto attachmentPart = dynamic_cast<MimeTreeParser::AttachmentMessagePart *>(messagePart)) {
             auto node = attachmentPart->node();
             if (node && mMimeTypeCache[attachmentPart] == "text/calendar") {
                 return messagePart->text();
             }
         }
 
         return preprocessPlaintext(messagePart->text());
     }
 
     QVariant contentForPart(MimeTreeParser::MessagePart *messagePart) const
     {
         return mContents.value(messagePart);
     }
 
     void collectContents()
     {
         mEncapsulatedParts.clear();
         mParents.clear();
         mContents.clear();
         containsHtmlAndPlain = false;
         isTrimmed = false;
 
         const auto parts = mParser->collectContentParts();
         for (const auto &part : parts) {
             checkPart(part);
             if (auto encapsulatedPart = part.dynamicCast<MimeTreeParser::EncapsulatedRfc822MessagePart>()) {
                 findEncapsulated(encapsulatedPart);
             }
         }
         for (const auto &part : parts) {
             if (mMimeTypeCache[part.data()] == "text/calendar") {
                 mParts.prepend(part);
             } else {
                 mParts.append(part);
             }
         }
     }
 
     PartModel *q;
     MimeTreeParser::MessagePart::List mParts;
     QHash<MimeTreeParser::MessagePart *, QByteArray> mMimeTypeCache;
     QHash<MimeTreeParser::MessagePart *, MimeTreeParser::MessagePart::List> mEncapsulatedParts;
     QHash<MimeTreeParser::MessagePart *, MimeTreeParser::MessagePart *> mParents;
     QMap<MimeTreeParser::MessagePart *, QVariant> mContents;
     std::shared_ptr<MimeTreeParser::ObjectTreeParser> mParser;
     bool showHtml{false};
     bool containsHtmlAndPlain{false};
     bool trimMail{false};
     bool isTrimmed{false};
 };
 
 PartModel::PartModel(std::shared_ptr<MimeTreeParser::ObjectTreeParser> parser)
     : d(std::unique_ptr<PartModelPrivate>(new PartModelPrivate(this, parser)))
 {
 }
 
 PartModel::~PartModel()
 {
 }
 
 void PartModel::setShowHtml(bool html)
 {
     if (d->showHtml != html) {
         beginResetModel();
         d->showHtml = html;
         d->collectContents();
         endResetModel();
         Q_EMIT showHtmlChanged();
     }
 }
 
 bool PartModel::showHtml() const
 {
     return d->showHtml;
 }
 
 void PartModel::setTrimMail(bool trim)
 {
     if (d->trimMail != trim) {
         beginResetModel();
         d->trimMail = trim;
         d->collectContents();
         endResetModel();
         Q_EMIT trimMailChanged();
     }
 }
 
 bool PartModel::trimMail() const
 {
     return d->trimMail;
 }
 
 bool PartModel::isTrimmed() const
 {
     return d->isTrimmed;
 }
 
 bool PartModel::containsHtml() const
 {
     return d->containsHtmlAndPlain;
 }
 
 QHash<int, QByteArray> PartModel::roleNames() const
 {
     QHash<int, QByteArray> roles;
     roles[TypeRole] = "type";
     roles[ContentRole] = "content";
     roles[IsEmbeddedRole] = "embedded";
     roles[IsEncryptedRole] = "encrypted";
     roles[IsSignedRole] = "signed";
     roles[SecurityLevelRole] = "securityLevel";
     roles[EncryptionSecurityLevelRole] = "encryptionSecurityLevel";
     roles[SignatureSecurityLevelRole] = "signatureSecurityLevel";
     roles[ErrorType] = "errorType";
     roles[ErrorString] = "errorString";
     roles[IsErrorRole] = "error";
     roles[SenderRole] = "sender";
     roles[SignatureDetails] = "signatureDetails";
     roles[EncryptionDetails] = "encryptionDetails";
     roles[DateRole] = "date";
     return roles;
 }
 
 QModelIndex PartModel::index(int row, int column, const QModelIndex &parent) const
 {
     if (row < 0 || column != 0) {
         return QModelIndex();
     }
     if (parent.isValid()) {
         const auto part = static_cast<MimeTreeParser::MessagePart *>(parent.internalPointer());
         auto encapsulatedPart = dynamic_cast<MimeTreeParser::EncapsulatedRfc822MessagePart *>(part);
 
         if (encapsulatedPart) {
             const auto parts = d->mEncapsulatedParts[encapsulatedPart];
             if (row < parts.size()) {
                 return createIndex(row, column, parts.at(row).data());
             }
         }
         return QModelIndex();
     }
     if (row < d->mParts.size()) {
         return createIndex(row, column, d->mParts.at(row).data());
     }
     return QModelIndex();
 }
 
-SignatureInfo *encryptionInfo(MimeTreeParser::MessagePart *messagePart)
+SignatureInfo encryptionInfo(MimeTreeParser::MessagePart *messagePart)
 {
-    auto signatureInfo = new SignatureInfo;
+    SignatureInfo signatureInfo;
     const auto encryptions = messagePart->encryptions();
     if (encryptions.size() > 1) {
         qWarning() << "Can't deal with more than one encryption";
     }
     for (const auto &encryptionPart : encryptions) {
-        signatureInfo->keyId = encryptionPart->partMetaData()->keyId;
+        signatureInfo.keyId = encryptionPart->partMetaData()->keyId;
+        signatureInfo.cryptoProto = encryptionPart->cryptoProto();
+        signatureInfo.decryptRecipients = encryptionPart->decryptRecipients();
     }
     return signatureInfo;
 };
 
-SignatureInfo *signatureInfo(MimeTreeParser::MessagePart *messagePart)
+SignatureInfo signatureInfo(MimeTreeParser::MessagePart *messagePart)
 {
-    auto signatureInfo = new SignatureInfo;
+    SignatureInfo signatureInfo;
     const auto signatures = messagePart->signatures();
     if (signatures.size() > 1) {
         qWarning() << "Can't deal with more than one signature";
     }
     for (const auto &signaturePart : signatures) {
-        signatureInfo->keyId = signaturePart->partMetaData()->keyId;
-        signatureInfo->keyMissing = signaturePart->partMetaData()->keyMissing;
-        signatureInfo->keyExpired = signaturePart->partMetaData()->keyExpired;
-        signatureInfo->keyRevoked = signaturePart->partMetaData()->keyRevoked;
-        signatureInfo->sigExpired = signaturePart->partMetaData()->sigExpired;
-        signatureInfo->crlMissing = signaturePart->partMetaData()->crlMissing;
-        signatureInfo->crlTooOld = signaturePart->partMetaData()->crlTooOld;
-        signatureInfo->signer = signaturePart->partMetaData()->signer;
-        signatureInfo->signerMailAddresses = signaturePart->partMetaData()->signerMailAddresses;
-        signatureInfo->signatureIsGood = signaturePart->partMetaData()->isGoodSignature;
-        signatureInfo->keyIsTrusted = signaturePart->partMetaData()->keyIsTrusted;
+        signatureInfo.keyId = signaturePart->partMetaData()->keyId;
+        signatureInfo.cryptoProto = signaturePart->cryptoProto();
+        signatureInfo.keyMissing = signaturePart->partMetaData()->sigSummary & GpgME::Signature::KeyMissing;
+        signatureInfo.keyExpired = signaturePart->partMetaData()->sigSummary & GpgME::Signature::KeyExpired;
+        signatureInfo.keyRevoked = signaturePart->partMetaData()->sigSummary & GpgME::Signature::KeyRevoked;
+        signatureInfo.sigExpired = signaturePart->partMetaData()->sigSummary & GpgME::Signature::SigExpired;
+        signatureInfo.crlMissing = signaturePart->partMetaData()->sigSummary & GpgME::Signature::CrlMissing;
+        signatureInfo.crlTooOld = signaturePart->partMetaData()->sigSummary & GpgME::Signature::CrlTooOld;
+        signatureInfo.signer = signaturePart->partMetaData()->signer;
+        signatureInfo.isCompliant = signaturePart->partMetaData()->isCompliant;
+        signatureInfo.signerMailAddresses = signaturePart->partMetaData()->signerMailAddresses;
+        signatureInfo.signatureIsGood = signaturePart->partMetaData()->isGoodSignature;
+        signatureInfo.keyTrust = signaturePart->partMetaData()->keyTrust;
     }
     return signatureInfo;
 }
 
 QVariant PartModel::data(const QModelIndex &index, int role) const
 {
     if (!index.isValid()) {
         return QVariant();
     }
 
     if (index.internalPointer()) {
         const auto messagePart = static_cast<MimeTreeParser::MessagePart *>(index.internalPointer());
         // qWarning() << "Found message part " << messagePart->metaObject()->className() << messagePart->partMetaData()->status << messagePart->error();
         Q_ASSERT(messagePart);
         switch (role) {
         case Qt::DisplayRole:
             return QStringLiteral("Content%1");
         case SenderRole: {
             if (auto e = dynamic_cast<MimeTreeParser::EncapsulatedRfc822MessagePart *>(messagePart)) {
                 return e->from();
             }
             return {};
         }
         case DateRole: {
             if (auto e = dynamic_cast<MimeTreeParser::EncapsulatedRfc822MessagePart *>(messagePart)) {
                 return e->date();
             }
             return {};
         }
         case TypeRole: {
             if (messagePart->error()) {
                 return QVariant::fromValue(Types::Error);
             }
             if (dynamic_cast<MimeTreeParser::EncapsulatedRfc822MessagePart *>(messagePart)) {
                 return QVariant::fromValue(Types::Encapsulated);
             }
             if (auto alternativePart = dynamic_cast<MimeTreeParser::AlternativeMessagePart *>(messagePart)) {
                 if (alternativePart->availableModes().contains(MimeTreeParser::AlternativeMessagePart::MultipartIcal)) {
                     return QVariant::fromValue(Types::Ical);
                 }
             }
             if (auto attachmentPart = dynamic_cast<MimeTreeParser::AttachmentMessagePart *>(messagePart)) {
                 auto node = attachmentPart->node();
                 if (!node) {
                     qWarning() << "no content for attachment";
                     return {};
                 }
                 if (d->mMimeTypeCache[attachmentPart] == "text/calendar") {
                     return QVariant::fromValue(Types::Ical);
                 }
             }
             if (!d->showHtml && d->containsHtmlAndPlain) {
                 return QVariant::fromValue(Types::Plain);
             }
             // For simple html we don't need a browser
             auto complexHtml = [&] {
                 if (messagePart->isHtml()) {
                     const auto text = messagePart->htmlContent();
                     if (text.contains(QStringLiteral("<!DOCTYPE html PUBLIC"))) {
                         // We can probably deal with this if it adheres to the strict dtd
                         //(that's what our composer produces as well)
                         if (!text.contains(QStringLiteral("http://www.w3.org/TR/REC-html40/strict.dtd"))) {
                             return true;
                         }
                     }
                     // Blockquotes don't support any styling which would be necessary so they become readable.
                     if (text.contains(QStringLiteral("blockquote"))) {
                         return true;
                     }
                     // Media queries are too advanced
                     if (text.contains(QStringLiteral("@media"))) {
                         return true;
                     }
                     // auto css properties are not supported e.g margin-left: auto;
                     if (text.contains(QStringLiteral(": auto;"))) {
                         return true;
                     }
                     return false;
                 } else {
                     return false;
                 }
             }();
             if (complexHtml) {
                 return QVariant::fromValue(Types::Html);
             }
             return QVariant::fromValue(Types::Plain);
         }
         case IsEmbeddedRole:
             return false;
         case IsErrorRole:
             return messagePart->error();
         case ContentRole:
             return d->contentForPart(messagePart);
         case IsEncryptedRole:
             return messagePart->encryptionState() != MimeTreeParser::KMMsgNotEncrypted;
         case IsSignedRole:
             return messagePart->signatureState() != MimeTreeParser::KMMsgNotSigned;
         case SecurityLevelRole: {
             auto signature = messagePart->signatureState();
             auto encryption = messagePart->encryptionState();
             bool messageIsSigned = signature == MimeTreeParser::KMMsgPartiallySigned || signature == MimeTreeParser::KMMsgFullySigned;
             bool messageIsEncrypted = encryption == MimeTreeParser::KMMsgPartiallyEncrypted || encryption == MimeTreeParser::KMMsgFullyEncrypted;
 
             if (messageIsSigned) {
-                auto sigInfo = std::unique_ptr<SignatureInfo>{signatureInfo(messagePart)};
-                if (!sigInfo->signatureIsGood) {
-                    if (sigInfo->keyMissing || sigInfo->keyExpired) {
-                        return QStringLiteral("notsogood");
+                const auto sigInfo = signatureInfo(messagePart);
+                if (!sigInfo.signatureIsGood) {
+                    if (sigInfo.keyMissing || sigInfo.keyExpired) {
+                        return SecurityLevel::NotSoGood;
                     }
-                    return QStringLiteral("bad");
+                    return SecurityLevel::Bad;
                 }
             }
             // All good
-            if (messageIsSigned || messageIsEncrypted) {
-                return QStringLiteral("good");
+            if ((messageIsSigned || messageIsEncrypted) && !messagePart->error()) {
+                return SecurityLevel::Good;
             }
             // No info
-            return QStringLiteral("unknown");
+            return SecurityLevel::Unknow;
         }
         case EncryptionSecurityLevelRole: {
             auto encryption = messagePart->encryptionState();
             bool messageIsEncrypted = encryption == MimeTreeParser::KMMsgPartiallyEncrypted || encryption == MimeTreeParser::KMMsgFullyEncrypted;
             if (messagePart->error()) {
-                return QStringLiteral("bad");
+                return SecurityLevel::Bad;
             }
             // All good
             if (messageIsEncrypted) {
-                return QStringLiteral("good");
+                return SecurityLevel::Good;
             }
             // No info
-            return QStringLiteral("unknown");
+            return SecurityLevel::Unknow;
         }
         case SignatureSecurityLevelRole: {
             auto signature = messagePart->signatureState();
             bool messageIsSigned = signature == MimeTreeParser::KMMsgPartiallySigned || signature == MimeTreeParser::KMMsgFullySigned;
             if (messageIsSigned) {
-                auto sigInfo = std::unique_ptr<SignatureInfo>{signatureInfo(messagePart)};
-                if (!sigInfo->signatureIsGood) {
-                    if (sigInfo->keyMissing || sigInfo->keyExpired) {
-                        return QStringLiteral("notsogood");
+                const auto sigInfo = signatureInfo(messagePart);
+                if (!sigInfo.signatureIsGood) {
+                    if (sigInfo.keyMissing || sigInfo.keyExpired) {
+                        return SecurityLevel::NotSoGood;
                     }
-                    return QStringLiteral("bad");
+                    return SecurityLevel::Bad;
                 }
-                return QStringLiteral("good");
+                return SecurityLevel::Good;
             }
             // No info
-            return QStringLiteral("unknown");
+            return SecurityLevel::Unknow;
         }
         case SignatureDetails:
             return QVariant::fromValue(signatureInfo(messagePart));
         case EncryptionDetails:
             return QVariant::fromValue(encryptionInfo(messagePart));
         case ErrorType:
             return messagePart->error();
         case ErrorString: {
             switch (messagePart->error()) {
             case MimeTreeParser::MessagePart::NoKeyError:
                 return i18ndc("mimetreeparser", "@info:status", "No key available.");
             case MimeTreeParser::MessagePart::PassphraseError:
                 return i18ndc("mimetreeparser", "@info:status", "Wrong passphrase.");
             case MimeTreeParser::MessagePart::UnknownError:
                 break;
             default:
                 break;
             }
             return messagePart->errorString();
         }
         }
     }
     return QVariant();
 }
 
 QModelIndex PartModel::parent(const QModelIndex &index) const
 {
     if (index.isValid()) {
         if (auto indexPart = static_cast<MimeTreeParser::MessagePart *>(index.internalPointer())) {
             for (const auto &part : std::as_const(d->mParts)) {
                 if (part.data() == indexPart) {
                     return QModelIndex();
                 }
             }
             const auto parentPart = d->mParents[indexPart];
             Q_ASSERT(parentPart);
             int row = 0;
             const auto parts = d->mEncapsulatedParts[parentPart];
             for (const auto &part : parts) {
                 if (part.data() == indexPart) {
                     break;
                 }
                 row++;
             }
             return createIndex(row, 0, parentPart);
         }
         return {};
     }
     return {};
 }
 
 int PartModel::rowCount(const QModelIndex &parent) const
 {
     if (parent.isValid()) {
         const auto part = static_cast<MimeTreeParser::MessagePart *>(parent.internalPointer());
         auto encapsulatedPart = dynamic_cast<MimeTreeParser::EncapsulatedRfc822MessagePart *>(part);
 
         if (encapsulatedPart) {
             const auto parts = d->mEncapsulatedParts[encapsulatedPart];
             return parts.size();
         }
         return 0;
     }
     return d->mParts.count();
 }
 
 int PartModel::columnCount(const QModelIndex &) const
 {
     return 1;
 }
 
 #include "moc_partmodel.cpp"
diff --git a/src/core/partmodel.h b/src/core/partmodel.h
index 939a0bf..744696a 100644
--- a/src/core/partmodel.h
+++ b/src/core/partmodel.h
@@ -1,115 +1,134 @@
 // SPDX-FileCopyrightText: 2016 Christian Mollekopf <mollekopf@kolabsys.com>
 // SPDX-License-Identifier: LGPL-2.0-or-later
 
 #pragma once
 
 #include <QObject>
 
 #include <QAbstractItemModel>
 #include <QModelIndex>
+#include <gpgme++/decryptionresult.h>
+#include <gpgme++/key.h>
 
 #include "mimetreeparser_core_export.h"
 #include <memory>
 
+namespace QGpgME
+{
+class Protocol;
+}
+
 namespace MimeTreeParser
 {
 class ObjectTreeParser;
 }
 class PartModelPrivate;
 
 class MIMETREEPARSER_CORE_EXPORT PartModel : public QAbstractItemModel
 {
     Q_OBJECT
     Q_PROPERTY(bool showHtml READ showHtml WRITE setShowHtml NOTIFY showHtmlChanged)
     Q_PROPERTY(bool containsHtml READ containsHtml NOTIFY containsHtmlChanged)
     Q_PROPERTY(bool trimMail READ trimMail WRITE setTrimMail NOTIFY trimMailChanged)
     Q_PROPERTY(bool isTrimmed READ isTrimmed NOTIFY trimMailChanged)
 public:
     PartModel(std::shared_ptr<MimeTreeParser::ObjectTreeParser> parser);
     ~PartModel();
 
     static std::pair<QString, bool> trim(const QString &text);
 
 public:
     enum class Types : quint8 {
         Error,
         Encapsulated,
         Ical,
         Plain,
         None,
         Html,
     };
     Q_ENUM(Types);
 
     enum Roles {
         TypeRole = Qt::UserRole + 1,
         ContentRole,
         IsEmbeddedRole,
         IsEncryptedRole,
         IsSignedRole,
         IsErrorRole,
         SecurityLevelRole,
         EncryptionSecurityLevelRole,
         SignatureSecurityLevelRole,
         SignatureDetails,
         EncryptionDetails,
         ErrorType,
         ErrorString,
         SenderRole,
         DateRole
     };
 
+    enum SecurityLevel {
+        Unknow,
+        Good,
+        NotSoGood,
+        Bad,
+    };
+    Q_ENUM(SecurityLevel);
+
     QHash<int, QByteArray> roleNames() const Q_DECL_OVERRIDE;
     QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
     QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE;
     QModelIndex parent(const QModelIndex &index) const Q_DECL_OVERRIDE;
     int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
     int columnCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
 
     void setShowHtml(bool html);
     bool showHtml() const;
     bool containsHtml() const;
 
     void setTrimMail(bool trim);
     bool trimMail() const;
     bool isTrimmed() const;
 
 Q_SIGNALS:
     void showHtmlChanged();
     void trimMailChanged();
     void containsHtmlChanged();
 
 private:
     std::unique_ptr<PartModelPrivate> d;
 };
 
-class SignatureInfo : public QObject
+class MIMETREEPARSER_CORE_EXPORT SignatureInfo
 {
-    Q_OBJECT
+    Q_GADGET
     Q_PROPERTY(QByteArray keyId MEMBER keyId CONSTANT)
     Q_PROPERTY(bool keyMissing MEMBER keyMissing CONSTANT)
     Q_PROPERTY(bool keyRevoked MEMBER keyRevoked CONSTANT)
     Q_PROPERTY(bool keyExpired MEMBER keyExpired CONSTANT)
     Q_PROPERTY(bool sigExpired MEMBER sigExpired CONSTANT)
     Q_PROPERTY(bool crlMissing MEMBER crlMissing CONSTANT)
     Q_PROPERTY(bool crlTooOld MEMBER crlTooOld CONSTANT)
 
     Q_PROPERTY(QString signer MEMBER signer CONSTANT)
     Q_PROPERTY(QStringList signerMailAddresses MEMBER signerMailAddresses CONSTANT)
     Q_PROPERTY(bool signatureIsGood MEMBER signatureIsGood CONSTANT)
-    Q_PROPERTY(bool keyIsTrusted MEMBER keyIsTrusted CONSTANT)
+    Q_PROPERTY(bool isCompliant MEMBER isCompliant CONSTANT)
+    Q_PROPERTY(GpgME::Signature::Validity keyTrust MEMBER keyTrust CONSTANT)
 
 public:
     bool keyRevoked = false;
     bool keyExpired = false;
     bool sigExpired = false;
     bool keyMissing = false;
     bool crlMissing = false;
     bool crlTooOld = false;
+    bool isCompliant = false;
+    GpgME::Signature::Validity keyTrust;
     QByteArray keyId;
+    const QGpgME::Protocol *cryptoProto = nullptr;
+    std::vector<std::pair<GpgME::DecryptionResult::Recipient, GpgME::Key>> decryptRecipients;
 
     QString signer;
     QStringList signerMailAddresses;
     bool signatureIsGood = false;
-    bool keyIsTrusted = false;
 };
diff --git a/src/quick/qml/MailViewer.qml b/src/quick/qml/MailViewer.qml
index 366fa41..0f2fb8e 100644
--- a/src/quick/qml/MailViewer.qml
+++ b/src/quick/qml/MailViewer.qml
@@ -1,205 +1,201 @@
 // SPDX-FileCopyrightText: 2022 Carl Schwan <carl@carlschwan.eu>
 // SPDX-FileCopyrightText: 2022 Devin Lin <espidev@gmail.com>
 // SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
 
 import QtQuick 2.15
 import QtQuick.Layouts 1.15
 import QtQuick.Controls 2.15 as QQC2
 import QtGraphicalEffects 1.15
 
 import org.kde.pim.mimetreeparser 1.0
 import org.kde.kirigami 2.20 as Kirigami
 import './private'
 
 Kirigami.ScrollablePage {
     id: root
 
     property alias message: mailPartView.message
 
     readonly property string subject: mailPartView.subject
     readonly property string from: mailPartView.from
     readonly property string sender: mailPartView.sender
     readonly property string to: mailPartView.to
     readonly property date dateTime: mailPartView.dateTime
 
     /**
      * This property holds the url to a custom ical part QML component.
      *
      * This allow apps to overwrite it and provide special handling for email
      * invitation.
      */
     property url icalCustomComponent
 
     Kirigami.Theme.colorSet: Kirigami.Theme.View
     Kirigami.Theme.inherit: false
 
-    padding: Kirigami.Units.largeSpacing * 2
+    padding: Kirigami.Units.largeSpacing
 
     title: i18ndc("mimetreeparser", "@title:window", "Message viewer")
 
     header: QQC2.ToolBar {
         id: mailHeader
 
         padding: root.padding
         visible: root.from.length > 0 || root.to.length > 0 || root.subject.length > 0
 
         Kirigami.Theme.inherit: false
         Kirigami.Theme.colorSet: Kirigami.Theme.View
 
         background: Item {
             Rectangle {
                 anchors.fill: parent
                 color: Kirigami.Theme.alternateBackgroundColor
             }
 
             Kirigami.Separator {
                 anchors.top: parent.top
                 anchors.left: parent.left
                 anchors.right: parent.right
             }
 
             Kirigami.Separator {
                 anchors.bottom: parent.bottom
                 anchors.left: parent.left
                 anchors.right: parent.right
             }
         }
 
         contentItem: GridLayout {
             rowSpacing: Kirigami.Units.smallSpacing
             columnSpacing: Kirigami.Units.smallSpacing
 
             columns: 3
 
             QQC2.Label {
                 text: i18ndc("mimetreeparser", "@label", "Subject:")
-                font.bold: true
                 visible: root.subject.length > 0
 
                 Layout.rightMargin: Kirigami.Units.largeSpacing
             }
 
             QQC2.Label {
                 text: root.subject
                 visible: text.length > 0
                 elide: Text.ElideRight
 
                 Layout.fillWidth: true
             }
 
             QQC2.Label {
                 text: root.dateTime.toLocaleString(Qt.locale(), Locale.ShortFormat)
                 visible: text.length > 0
                 horizontalAlignment: Text.AlignRight
             }
 
             QQC2.Label {
                 text: i18ndc("mimetreeparser", "@label", "From:")
-                font.bold: true
                 visible: root.from.length > 0
 
                 Layout.rightMargin: Kirigami.Units.largeSpacing
             }
 
             QQC2.Label {
                 text: root.from
                 visible: text.length > 0
                 elide: Text.ElideRight
 
                 Layout.fillWidth: true
                 Layout.columnSpan: 2
             }
 
             QQC2.Label {
                 text: i18ndc("mimetreeparser", "@label", "Sender:")
-                font.bold: true
                 visible: root.sender.length > 0 && root.sender !== root.from
 
                 Layout.rightMargin: Kirigami.Units.largeSpacing
             }
 
             QQC2.Label {
                 visible: root.sender.length > 0 && root.sender !== root.from
                 text: root.sender
                 elide: Text.ElideRight
 
                 Layout.fillWidth: true
                 Layout.columnSpan: 2
             }
 
             QQC2.Label {
                 text: i18ndc("mimetreeparser", "@label", "To:")
-                font.bold: true
                 visible: root.to.length > 0
 
                 Layout.rightMargin: Kirigami.Units.largeSpacing
             }
 
             QQC2.Label {
                 text: root.to
                 elide: Text.ElideRight
                 visible: root.to.length > 0
 
                 Layout.fillWidth: true
                 Layout.columnSpan: 2
             }
         }
     }
 
     MailPartView {
         id: mailPartView
         padding: root.padding
         icalCustomComponent: root.icalCustomComponent
     }
 
     footer: QQC2.ToolBar {
         padding: root.padding
 
         Kirigami.Theme.inherit: false
         Kirigami.Theme.colorSet: Kirigami.Theme.View
 
         background: Item {
             Kirigami.Separator {
                 anchors {
                     left: parent.left
                     right: parent.right
                     top: undefined
                     bottom:  parent.bottom
                 }
             }
         }
 
         Flow {
             anchors.fill: parent
             spacing: Kirigami.Units.smallSpacing
             Repeater {
                 model: mailPartView.attachmentModel
 
                 delegate: AttachmentDelegate {
                     id: attachmentDelegate
 
                     required property int index
                     required property string iconName
 
                     icon.name: iconName
 
                     clip: true
 
                     actionIcon: 'download'
                     actionTooltip: i18ndc("mimetreeparser", "@action:button", "Save attachment")
                     onExecute: mailPartView.attachmentModel.saveAttachmentToDisk(index)
                     onClicked: mailPartView.attachmentModel.openAttachment(index)
                     onPublicKeyImport: mailPartView.attachmentModel.importPublicKey(index)
                 }
             }
         }
     }
 
     Connections {
         target: mailPartView.attachmentModel
 
         function onInfo(message) {
             applicationWindow().showPassiveNotification(message);
         }
     }
 }
diff --git a/src/quick/qml/private/MailPartModel.qml b/src/quick/qml/private/MailPartModel.qml
index 5dd81c9..3a97a2f 100644
--- a/src/quick/qml/private/MailPartModel.qml
+++ b/src/quick/qml/private/MailPartModel.qml
@@ -1,169 +1,170 @@
 // SPDX-FileCopyrightText: 2016 Michael Bohlender <michael.bohlender@kdemail.net>
 // SPDX-FileCopyrightText: 2022 Carl Schwan <carl@carlschwan.eu>
 // SPDX-License-Identifier: GPL-2.0-or-later
 
 import QtQuick 2.15
 import QtQuick.Controls 2.15 as QQC2
 import QtQuick.Layouts 1.15
 import QtQml.Models 2.2
 import org.kde.pim.mimetreeparser 1.0
 import org.kde.kirigami 2.19 as Kirigami
 
 DelegateModel {
     id: root
 
     property string searchString: ""
     property bool autoLoadImages: false
     property int padding: Kirigami.Units.largeSpacing
     property url icalCustomComponent
 
     delegate: RowLayout {
         id: partColumn
 
-        width: ListView.view.width
+        width: ListView.view.width - Kirigami.Units.largeSpacing
+        x: Kirigami.Units.smallSpacing
 
         function getType(securityLevel) {
-            if (securityLevel == "good") {
+            if (securityLevel === PartModel.Good) {
                 return Kirigami.MessageType.Positive
             }
-            if (securityLevel == "bad") {
+            if (securityLevel === PartModel.Bad) {
                 return Kirigami.MessageType.Error
             }
-            if (securityLevel == "notsogood") {
+            if (securityLevel === PartModel.NotSoGood) {
                 return Kirigami.MessageType.Warning
             }
             return Kirigami.MessageType.Information
         }
 
         function getColor(securityLevel) {
-            if (securityLevel == "good") {
+            if (securityLevel === PartModel.Good) {
                 return Kirigami.Theme.positiveTextColor
             }
-            if (securityLevel == "bad") {
+            if (securityLevel === PartModel.Bad) {
                 return Kirigami.Theme.negativeTextColor
             }
-            if (securityLevel == "notsogood") {
+            if (securityLevel === PartModel.NotSoGood) {
                 return Kirigami.Theme.neutralTextColor
             }
             return "transparent"
         }
 
         function getDetails(signatureDetails) {
             let details = "";
             if (signatureDetails.keyMissing) {
                 details += i18ndc("mimetreeparser", "@label", "This message has been signed using the key %1.", signatureDetails.keyId) + "\n";
                 details += i18ndc("mimetreeparser", "@label", "The key details are not available.")
             } else {
                 details += i18ndc("mimetreeparser", "@label", "This message has been signed using the key %1 by %2.", signatureDetails.keyId, signatureDetails.signer) + "\n";
                 if (signatureDetails.keyRevoked) {
                     details += "\n" + i18ndc("mimetreeparser", "@label", "The key was revoked.")
                 }
                 if (signatureDetails.keyExpired) {
                     details += "\n" + i18ndc("mimetreeparser", "@label", "The key has expired.")
                 }
                 if (signatureDetails.keyIsTrusted) {
                     details += "\n" + i18ndc("mimetreeparser", "@label", "You are trusting this key.")
                 }
                 if (!signatureDetails.signatureIsGood && !signatureDetails.keyRevoked && !signatureDetails.keyExpired && !signatureDetails.keyIsTrusted) {
                     details += "\n" + i18ndc("mimetreeparser", "@label", "The signature is invalid.")
                 }
             }
             return details
         }
 
         QQC2.Control {
             Layout.preferredWidth: Kirigami.Units.smallSpacing
             Layout.fillHeight: true
 
             visible: model.encrypted
 
             background: Rectangle {
                 id: border
 
                 color: getColor(model.securityLevel)
                 opacity: 0.5
             }
 
             QQC2.ToolTip.text: getDetails(model.encryptionSecurityLevel)
             QQC2.ToolTip.visible: hovered
             QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
         }
 
         ColumnLayout {
             Banner {
                 iconName: "mail-encrypted"
                 type: getType(model.encryptionSecurityLevel)
                 visible: model.encrypted
-                text: model.encryptionDetails.keyId.length === 0 ? i18n("This message is encrypted but we don't have the key for it.") : i18n("This message is encrypted to the key: %1", model.encryptionDetails.keyId);
+                text: !model.encryptionDetails.keyId ? i18n("This message is encrypted but we don't have the key for it.") : i18n("This message is encrypted to the key: %1", model.encryptionDetails.keyId);
 
                 Layout.fillWidth: true
             }
 
             Banner {
                 iconName: 'mail-signed'
                 visible: model.signed
                 type: getType(model.signatureSecurityLevel)
                 text: getDetails(model.signatureDetails)
 
                 Layout.fillWidth: true
             }
 
             Loader {
                 id: partLoader
 
                 Layout.preferredHeight: item ? item.contentHeight : 0
                 Layout.fillWidth: true
                 Layout.leftMargin: root.padding
                 Layout.rightMargin: root.padding
 
                 Component.onCompleted: {
                     switch (model.type + 0) {
                         case PartModel.Plain:
                             partLoader.setSource("TextPart.qml", {
                                 content: model.content,
                                 embedded: model.embedded,
                             })
                             break
                         case PartModel.Html:
                             partLoader.setSource("HtmlPart.qml", {
                                 content: model.content,
                             })
                             break;
                         case PartModel.Error:
                             partLoader.setSource("ErrorPart.qml", {
                                 errorType: model.errorType,
                                 errorString: model.errorString,
                             })
                             break;
                         case PartModel.Encapsulated:
                             partLoader.setSource("MailPart.qml", {
                                 rootIndex: root.modelIndex(index),
                                 model: root.model,
                                 sender: model.sender,
                                 date: model.date,
                             })
                             break;
                         case PartModel.Ical:
                             partLoader.setSource(root.icalCustomComponent ? root.icalCustomComponent : "ICalPart.qml", {
                                 content: model.content,
                             })
                             break;
                     }
                 }
 
                 Binding {
                     target: partLoader.item
                     property: "searchString"
                     value: root.searchString
                     when: partLoader.status === Loader.Ready
                 }
                 Binding {
                     target: partLoader.item
                     property: "autoLoadImages"
                     value: root.autoLoadImages
                     when: partLoader.status === Loader.Ready
                 }
             }
         }
     }
 }
diff --git a/src/widgets/CMakeLists.txt b/src/widgets/CMakeLists.txt
index 67a6143..274feee 100644
--- a/src/widgets/CMakeLists.txt
+++ b/src/widgets/CMakeLists.txt
@@ -1,146 +1,151 @@
 # SPDX-FileCopyrightText: 2023 Carl Schwan <carl.schwan@gnupg.com>
 # SPDX-License-Identifier: BSD-3-Clause
 
 ecm_setup_version(PROJECT
     VARIABLE_PREFIX MIMETREEPARSER_WIDGETS
     VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/mimetreeparser_widgets_version.h"
     PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/KPim6MimeTreeParserWidgetsConfigVersion.cmake"
     SOVERSION 6
 )
 
 add_library(KPim6MimeTreeParserWidgets)
 add_library(KPim6::MimeTreeParserWidgets
     ALIAS KPim6MimeTreeParserWidgets)
 
 target_sources(KPim6MimeTreeParserWidgets PRIVATE
      messageviewer.h
      messageviewer.cpp
      attachmentview_p.h
      attachmentview.cpp
+     messagecontainerwidget_p.h
+     messagecontainerwidget.cpp
+     urlhandler_p.h
+     urlhandler.cpp
 )
 
 ecm_qt_declare_logging_category(KPim6MimeTreeParserWidgets
     HEADER mimetreeparser_widgets_debug.h
     IDENTIFIER MIMETREEPARSER_WIDGET_LOG
     CATEGORY_NAME org.kde.pim.mimetreeparser.widgets
     DESCRIPTION "mimetreeparser (pim lib)"
     EXPORT MIMETREEPARSER
 )
 
 if (COMPILE_WITH_UNITY_CMAKE_SUPPORT)
     set_target_properties(KPim6MimeTreeParserWidgets PROPERTIES UNITY_BUILD ON)
 endif()
 generate_export_header(KPim6MimeTreeParserWidgets BASE_NAME mimetreeparser_widgets)
 
 
 target_include_directories(KPim6MimeTreeParserWidgets INTERFACE "$<INSTALL_INTERFACE:${KDE_INSTALL_INCLUDEDIR}/KPim6/MimeTreeParserWidgets>")
 
 target_link_libraries(KPim6MimeTreeParserWidgets
 PUBLIC
     Qt6::Widgets
     KPim6::MimeTreeParserCore
 PRIVATE
     KF6::Codecs
     KF6::I18n
     KF6::CalendarCore
     KF6::WidgetsAddons
+    KF6::Libkleo
     Gpgme::Gpgme
 )
 
 set_target_properties(KPim6MimeTreeParserWidgets PROPERTIES
 	VERSION ${MIMETREEPARSER_WIDGETS_VERSION}
 	SOVERSION ${MIMETREEPARSER_WIDGETS_SOVERSION}
     EXPORT_NAME MimeTreeParserWidgets
 )
 
 ecm_generate_pri_file(BASE_NAME MimeTreeParserWidgets
     LIB_NAME KPim6MimeTreeParserWidgets
     DEPS "MimeTreeParserWidgets" 
     FILENAME_VAR PRI_FILENAME
 )
 
 install(FILES ${PRI_FILENAME} DESTINATION ${ECM_MKSPECS_INSTALL_DIR})
 
 install(TARGETS
     KPim6MimeTreeParserWidgets
     EXPORT KPim6MimeTreeParserWidgetsTargets ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}
 )
 
 ecm_generate_headers(MimeTreeParserWidgets_CamelCase_HEADERS
     HEADER_NAMES
         MessageViewer
     REQUIRED_HEADERS MimeTreeParserWidgets_HEADERS
     PREFIX MimeTreeParserWidgets
 )
 
 install(FILES
     ${MimeTreeParserWidgets_CamelCase_HEADERS}
     DESTINATION ${KDE_INSTALL_INCLUDEDIR}/KPim6/MimeTreeParserWidgets/MimeTreeParserWidgets
     COMPONENT Devel
 )
 install(FILES
     ${CMAKE_CURRENT_BINARY_DIR}/mimetreeparser_widgets_export.h
     ${MimeTreeParserWidgets_HEADERS}
     DESTINATION ${KDE_INSTALL_INCLUDEDIR}/KPim6/MimeTreeParserWidgets/mimetreeparserwidgets
     COMPONENT Devel
 )
 
 if (BUILD_QCH)
     ecm_add_qch(
         KPim6MimeTreeParserWidgets_QCH
         NAME MimeTreeParserWidgets
         BASE_NAME KPim6MimeTreeParserWidgets
         VERSION ${PIM_VERSION}
         ORG_DOMAIN org.kde
         # using only public headers, to cover only public API
         SOURCES ${MimeTreeParserWidgets_HEADERS}
         MD_MAINPAGE "${CMAKE_SOURCE_DIR}/README.md"
         LINK_QCHS
             Qt6Core_QCH
         INCLUDE_DIRS
             ${CMAKE_CURRENT_BINARY_DIR}
         BLANK_MACROS
             MIMETREEPARSER_WIDGETS_EXPORT
         TAGFILE_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR}
         QCH_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR}
         COMPONENT Devel
     )
 endif()
 
 ########### CMake Config Files ###########
 set(CMAKECONFIG_INSTALL_DIR "${KDE_INSTALL_CMAKEPACKAGEDIR}/KPim6MimeTreeParserWidgets")
 
 if (BUILD_QCH)
     ecm_install_qch_export(
         TARGETS KPim6MimeTreeParserWidgets_QCH
         FILE KPim6MimeTreeParserWidgetsQchTargets.cmake
         DESTINATION "${CMAKECONFIG_INSTALL_DIR}"
         COMPONENT Devel
     )
     set(PACKAGE_INCLUDE_QCHTARGETS "include(\"\${CMAKE_CURRENT_LIST_DIR}/KPim6MimeTreeParserWidgetsQchTargets.cmake\")")
 endif()
 
 configure_package_config_file(
     "${CMAKE_CURRENT_SOURCE_DIR}/KPimMimeTreeParserWidgetsConfig.cmake.in"
     "${CMAKE_CURRENT_BINARY_DIR}/KPim6MimeTreeParserWidgetsConfig.cmake"
     INSTALL_DESTINATION ${CMAKECONFIG_INSTALL_DIR}
 )
 
 install(FILES
     "${CMAKE_CURRENT_BINARY_DIR}/KPim6MimeTreeParserWidgetsConfig.cmake"
     "${CMAKE_CURRENT_BINARY_DIR}/KPim6MimeTreeParserWidgetsConfigVersion.cmake"
     DESTINATION "${CMAKECONFIG_INSTALL_DIR}"
     COMPONENT Devel
 )
 
 install(EXPORT KPim6MimeTreeParserWidgetsTargets
     DESTINATION "${CMAKECONFIG_INSTALL_DIR}"
     FILE KPim6MimeTreeParserWidgetsTargets.cmake
     NAMESPACE KPim6::
 )
 
 install(FILES
    ${CMAKE_CURRENT_BINARY_DIR}/mimetreeparser_widgets_version.h
    DESTINATION ${KDE_INSTALL_INCLUDEDIR}/KPim6/MimeTreeParserWidgets
    COMPONENT Devel
 )
diff --git a/src/widgets/messagecontainerwidget.cpp b/src/widgets/messagecontainerwidget.cpp
new file mode 100644
index 0000000..4038451
--- /dev/null
+++ b/src/widgets/messagecontainerwidget.cpp
@@ -0,0 +1,268 @@
+// SPDX-FileCopyrightText: 2023 g10 Code GmbH
+// SPDX-FileContributor: Carl Schwan <carl.schwan@gnupg.com>
+// SPDX-License-Identifier: LGPL-2.0-or-later
+
+#include "messagecontainerwidget_p.h"
+#include "urlhandler_p.h"
+
+#include <KLocalizedString>
+#include <KMessageWidget>
+#include <Libkleo/Compliance>
+#include <QGpgME/Protocol>
+
+#include <QDebug>
+#include <QLabel>
+#include <QPaintEvent>
+#include <QPainter>
+#include <QResizeEvent>
+#include <QVBoxLayout>
+
+namespace
+{
+
+const int borderWidth = 5;
+
+QColor getColor(PartModel::SecurityLevel securityLevel)
+{
+    if (securityLevel == PartModel::Good) {
+        return QColor(39, 174, 96); // Window: ForegroundPositive
+    }
+    if (securityLevel == PartModel::Bad) {
+        return QColor(218, 68, 83); // Window: ForegroundNegative
+    }
+    if (securityLevel == PartModel::NotSoGood) {
+        return QColor(246, 116, 0); // Window: ForegroundNeutral
+    }
+    return QColor();
+}
+
+KMessageWidget::MessageType getType(PartModel::SecurityLevel securityLevel)
+{
+    if (securityLevel == PartModel::Good) {
+        return KMessageWidget::MessageType::Positive;
+    }
+    if (securityLevel == PartModel::Bad) {
+        return KMessageWidget::MessageType::Error;
+    }
+    if (securityLevel == PartModel::NotSoGood) {
+        return KMessageWidget::MessageType::Warning;
+    }
+    return KMessageWidget::MessageType::Information;
+}
+
+QString getDetails(const SignatureInfo &signatureDetails)
+{
+    QString href;
+    if (signatureDetails.cryptoProto) {
+        href = QStringLiteral("messageviewer:showCertificate#%1 ### %2 ### %3")
+                   .arg(signatureDetails.cryptoProto->displayName(), signatureDetails.cryptoProto->name(), QString::fromLatin1(signatureDetails.keyId));
+    }
+
+    QString details;
+    if (signatureDetails.keyMissing) {
+        if (Kleo::DeVSCompliance::isCompliant() && signatureDetails.isCompliant) {
+            details += i18ndc("mimetreeparser",
+                              "@label",
+                              "This message has been signed VS-NfD compliant using the key <a href=\"%1\">%2</a>.",
+                              href,
+                              QString::fromUtf8(signatureDetails.keyId))
+                + QLatin1Char('\n');
+        } else {
+            details += i18ndc("mimetreeparser",
+                              "@label",
+                              "This message has been signed using the key <a href=\"%1\">%2</a>.",
+                              href,
+                              QString::fromUtf8(signatureDetails.keyId))
+                + QLatin1Char('\n');
+        }
+        details += i18ndc("mimetreeparser", "@label", "The key details are not available.");
+    } else {
+        if (Kleo::DeVSCompliance::isCompliant() && signatureDetails.isCompliant) {
+            details += i18ndc("mimetreeparser", "@label", "This message has been signed VS-NfD compliant by %1.", signatureDetails.signer.toHtmlEscaped());
+        } else {
+            details += i18ndc("mimetreeparser", "@label", "This message has been signed by %1.", signatureDetails.signer.toHtmlEscaped());
+        }
+        if (signatureDetails.keyRevoked) {
+            details += QLatin1Char('\n') + i18ndc("mimetreeparser", "@label", "The <a href=\"%1\">key</a> was revoked.", href);
+        }
+        if (signatureDetails.keyExpired) {
+            details += QLatin1Char('\n') + i18ndc("mimetreeparser", "@label", "The <a href=\"%1\">key</a> was expired.", href);
+        }
+
+        if (signatureDetails.keyTrust == GpgME::Signature::Unknown) {
+            details +=
+                QLatin1Char(' ') + i18ndc("mimetreeparser", "@label", "The signature is valid, but the <a href=\"%1\">key</a>'s validity is unknown.", href);
+        } else if (signatureDetails.keyTrust == GpgME::Signature::Marginal) {
+            details +=
+                QLatin1Char(' ') + i18ndc("mimetreeparser", "@label", "The signature is valid and the <a href=\"%1\">key</a> is marginally trusted.", href);
+        } else if (signatureDetails.keyTrust == GpgME::Signature::Full) {
+            details += QLatin1Char(' ') + i18ndc("mimetreeparser", "@label", "The signature is valid and the <a href=\"%1\">key</a> is fully trusted.", href);
+        } else if (signatureDetails.keyTrust == GpgME::Signature::Ultimate) {
+            details +=
+                QLatin1Char(' ') + i18ndc("mimetreeparser", "@label", "The signature is valid and the <a href=\"%1\">key</a> is ultimately trusted.", href);
+        } else {
+            details += QLatin1Char(' ') + i18ndc("mimetreeparser", "@label", "The signature is valid, but the <a href=\"%1\">key</a> is untrusted.", href);
+        }
+        if (!signatureDetails.signatureIsGood && !signatureDetails.keyRevoked && !signatureDetails.keyExpired
+            && signatureDetails.keyTrust != GpgME::Signature::Unknown) {
+            details += QLatin1Char(' ') + i18ndc("mimetreeparser", "@label", "The signature is invalid.");
+        }
+    }
+    return details;
+}
+
+}
+
+MessageWidgetContainer::MessageWidgetContainer(bool isSigned,
+                                               const SignatureInfo &signatureInfo,
+                                               PartModel::SecurityLevel signatureSecurityLevel,
+                                               bool displaySignatureInfo,
+                                               bool isEncrypted,
+                                               const SignatureInfo &encryptionInfo,
+                                               PartModel::SecurityLevel encryptionSecurityLevel,
+                                               bool displayEncryptionInfo,
+                                               UrlHandler *urlHandler,
+                                               QWidget *parent)
+    : QFrame(parent)
+    , m_isSigned(isSigned)
+    , m_signatureInfo(signatureInfo)
+    , m_signatureSecurityLevel(signatureSecurityLevel)
+    , m_displaySignatureInfo(displaySignatureInfo)
+    , m_isEncrypted(isEncrypted)
+    , m_encryptionInfo(encryptionInfo)
+    , m_encryptionSecurityLevel(encryptionSecurityLevel)
+    , m_displayEncryptionInfo(displayEncryptionInfo)
+    , m_urlHandler(urlHandler)
+{
+    createLayout();
+}
+
+MessageWidgetContainer::~MessageWidgetContainer() = default;
+
+void MessageWidgetContainer::paintEvent(QPaintEvent *event)
+{
+    Q_UNUSED(event)
+
+    if (!m_isSigned && !m_isEncrypted) {
+        return;
+    }
+
+    QPainter painter(this);
+    if (layoutDirection() == Qt::RightToLeft) {
+        auto r = rect();
+        r.setX(width() - borderWidth);
+        r.setWidth(borderWidth);
+        const QColor color = getColor(PartModel::SecurityLevel::Good);
+        painter.setRenderHint(QPainter::Antialiasing);
+        painter.setBrush(QColor(color));
+        painter.setPen(QPen(Qt::NoPen));
+        painter.drawRect(r);
+    } else {
+        auto r = rect();
+        r.setWidth(borderWidth);
+        const QColor color = getColor(PartModel::SecurityLevel::Good);
+        painter.setRenderHint(QPainter::Antialiasing);
+        painter.setBrush(QColor(color));
+        painter.setPen(QPen(Qt::NoPen));
+        painter.drawRect(r);
+    }
+}
+
+bool MessageWidgetContainer::event(QEvent *event)
+{
+    if (event->type() == QEvent::Polish && !layout()) {
+        createLayout();
+    }
+
+    return QFrame::event(event);
+}
+
+void MessageWidgetContainer::createLayout()
+{
+    delete layout();
+
+    auto vLayout = new QVBoxLayout(this);
+
+    if (m_isSigned || m_isEncrypted) {
+        if (layoutDirection() == Qt::RightToLeft) {
+            layout()->setContentsMargins(0, 0, borderWidth * 2, 0);
+        } else {
+            layout()->setContentsMargins(borderWidth * 2, 0, 0, 0);
+        }
+    }
+
+    if (m_isEncrypted && m_displayEncryptionInfo) {
+        auto encryptionMessage = new KMessageWidget(this);
+        encryptionMessage->setMessageType(getType(m_encryptionSecurityLevel));
+        encryptionMessage->setCloseButtonVisible(false);
+        encryptionMessage->setIcon(QIcon::fromTheme(QStringLiteral("mail-encrypted")));
+
+        QString text;
+        if (m_encryptionInfo.keyId.isEmpty()) {
+            if (Kleo::DeVSCompliance::isCompliant() && m_encryptionInfo.isCompliant) {
+                text = i18n("This message is VS-NfD compliant encrypted but we don't have the key for it.", QString::fromUtf8(m_encryptionInfo.keyId));
+            } else {
+                text = i18n("This message is encrypted but we don't have the key for it.");
+            }
+        } else {
+            if (Kleo::DeVSCompliance::isCompliant() && m_encryptionInfo.isCompliant) {
+                text = i18n("This message is VS-NfD compliant encrypted.");
+            } else {
+                text = i18n("This message is encrypted.");
+            }
+        }
+
+        encryptionMessage->setText(text + QLatin1Char(' ') + QStringLiteral("<a href=\"messageviewer:showDetails\">Details</a>"));
+
+        connect(encryptionMessage, &KMessageWidget::linkActivated, this, [this, encryptionMessage, text](const QString &link) {
+            QUrl url(link);
+            if (url.path() == QStringLiteral("showDetails")) {
+                QString newText = text + QStringLiteral(" ") + i18n("The message is encrypted for the following keys:") + QStringLiteral("<ul>");
+
+                for (const auto &recipient : m_encryptionInfo.decryptRecipients) {
+                    if (recipient.second.keyID()) {
+                        const auto href = QStringLiteral("messageviewer:showCertificate#%1 ### %2 ### %3")
+                                              .arg(m_encryptionInfo.cryptoProto->displayName(),
+                                                   m_encryptionInfo.cryptoProto->name(),
+                                                   QString::fromLatin1(recipient.second.keyID()));
+
+                        newText += QStringLiteral("<li><a href=\"%1\">0x%2</a></li>").arg(href, QString::fromLatin1(recipient.second.keyID()));
+                    } else {
+                        const auto href = QStringLiteral("messageviewer:showCertificate#%1 ### %2 ### %3")
+                                              .arg(m_encryptionInfo.cryptoProto->displayName(),
+                                                   m_encryptionInfo.cryptoProto->name(),
+                                                   QString::fromLatin1(recipient.first.keyID()));
+
+                        newText +=
+                            QStringLiteral("<li><a href=\"%1\">0x%2</a> (%3)</li>").arg(href, QString::fromLatin1(recipient.first.keyID()), i18n("Unknown key"));
+                    }
+                }
+
+                newText += QStringLiteral("</ul>");
+
+                encryptionMessage->setText(newText);
+                return;
+            }
+
+            if (url.path() == QStringLiteral("showCertificate")) {
+                m_urlHandler->handleClick(QUrl(link), window()->windowHandle());
+            }
+        });
+
+        vLayout->addWidget(encryptionMessage);
+    }
+
+    if (m_isSigned && m_displaySignatureInfo) {
+        auto signatureMessage = new KMessageWidget(this);
+        signatureMessage->setIcon(QIcon::fromTheme(QStringLiteral("mail-signed")));
+        signatureMessage->setCloseButtonVisible(false);
+        signatureMessage->setText(getDetails(m_signatureInfo));
+        connect(signatureMessage, &KMessageWidget::linkActivated, this, [this](const QString &link) {
+            m_urlHandler->handleClick(QUrl(link), window()->windowHandle());
+        });
+        signatureMessage->setMessageType(getType(m_signatureSecurityLevel));
+        signatureMessage->setWordWrap(true);
+
+        vLayout->addWidget(signatureMessage);
+    }
+}
diff --git a/src/widgets/messagecontainerwidget_p.h b/src/widgets/messagecontainerwidget_p.h
new file mode 100644
index 0000000..6bc742c
--- /dev/null
+++ b/src/widgets/messagecontainerwidget_p.h
@@ -0,0 +1,48 @@
+// SPDX-FileCopyrightText: 2023 g10 Code GmbH
+// SPDX-FileContributor: Carl Schwan <carl.schwan@gnupg.com>
+// SPDX-License-Identifier: LGPL-2.0-or-later
+
+#pragma once
+
+#include <MimeTreeParserCore/PartModel>
+#include <QFrame>
+
+class SignatureInfo;
+class QPaintEvent;
+class QResizeEvent;
+class UrlHandler;
+
+class MessageWidgetContainer : public QFrame
+{
+public:
+    explicit MessageWidgetContainer(bool isSigned,
+                                    const SignatureInfo &signatureInfo,
+                                    PartModel::SecurityLevel signatureSecurityLevel,
+                                    bool displaySignatureInfo,
+                                    bool isEncrypted,
+                                    const SignatureInfo &encryptionInfo,
+                                    PartModel::SecurityLevel encryptionSecurityLevel,
+                                    bool displayEncryptionInfo,
+                                    UrlHandler *urlHandler,
+                                    QWidget *parent = nullptr);
+    ~MessageWidgetContainer();
+
+protected:
+    void paintEvent(QPaintEvent *event) override;
+    bool event(QEvent *event) override;
+
+private:
+    void createLayout();
+
+    bool m_isSigned;
+    SignatureInfo const m_signatureInfo;
+    PartModel::SecurityLevel m_signatureSecurityLevel;
+    bool m_displaySignatureInfo;
+
+    bool m_isEncrypted;
+    SignatureInfo const m_encryptionInfo;
+    PartModel::SecurityLevel m_encryptionSecurityLevel;
+    bool m_displayEncryptionInfo;
+
+    UrlHandler *const m_urlHandler;
+};
diff --git a/src/widgets/messageviewer.cpp b/src/widgets/messageviewer.cpp
index 78430e8..af1e2cd 100644
--- a/src/widgets/messageviewer.cpp
+++ b/src/widgets/messageviewer.cpp
@@ -1,282 +1,340 @@
 // SPDX-FileCopyrightText: 2023 Carl Schwan <carl.schwan@gnupg.com>
 // SPDX-License-Identifier: LGPL-2.0-or-later
 
 #include "messageviewer.h"
 #include "../core/objecttreeparser.h"
 #include "attachmentview_p.h"
+#include "messagecontainerwidget_p.h"
+#include "urlhandler_p.h"
 #include <KCalendarCore/Event>
 #include <KCalendarCore/ICalFormat>
 #include <KCalendarCore/Incidence>
+#include <KMessageWidget>
 #define TRANSLATION_DOMAIN "mimetreeparser"
 #include <KLocalizedString>
-#include <KMessageWidget>
 #include <MimeTreeParserCore/AttachmentModel>
 #include <MimeTreeParserCore/MessageParser>
 #include <MimeTreeParserCore/PartModel>
 #include <QAction>
 #include <QDebug>
 #include <QFormLayout>
 #include <QGroupBox>
 #include <QLabel>
 #include <QMenu>
 #include <QScrollArea>
 #include <QVBoxLayout>
 
 using namespace MimeTreeParser::Widgets;
 
 class MessageViewer::Private
 {
 public:
     Private(MessageViewer *q_ptr)
         : q{q_ptr}
     {
         createActions();
     }
 
     MessageViewer *q;
 
     QVBoxLayout *layout = nullptr;
     KMime::Message::Ptr message;
     MessageParser parser;
     QVector<QWidget *> widgets;
     QScrollArea *scrollArea = nullptr;
     QFormLayout *formLayout = nullptr;
     AttachmentView *attachmentView = nullptr;
     MimeTreeParser::MessagePart::List selectedParts;
+    UrlHandler *urlHandler = nullptr;
 
     QAction *saveAttachmentAction = nullptr;
     QAction *openAttachmentAction = nullptr;
     QAction *importPublicKeyAction = nullptr;
 
     void createActions()
     {
         saveAttachmentAction = new QAction(QIcon::fromTheme(QStringLiteral("document-save-as")), i18n("&Save Attachment As..."), q);
         connect(saveAttachmentAction, &QAction::triggered, q, [this]() {
             saveSelectedAttachments();
         });
 
         openAttachmentAction = new QAction(i18nc("to open", "Open"), q);
         connect(openAttachmentAction, &QAction::triggered, q, [this]() {
             openSelectedAttachments();
         });
 
         importPublicKeyAction = new QAction(i18nc("@action:inmenu", "Import public key"), q);
         connect(importPublicKeyAction, &QAction::triggered, q, [this]() {
             importPublicKey();
         });
     }
 
     void openSelectedAttachments();
     void saveSelectedAttachments();
     void selectionChanged();
     void showContextMenu();
     void importPublicKey();
     void recursiveBuildViewer(PartModel *parts, QVBoxLayout *layout, const QModelIndex &parent);
 };
 
 void MessageViewer::Private::openSelectedAttachments()
 {
     Q_ASSERT(selectedParts.count() >= 1);
     for (const auto &part : std::as_const(selectedParts)) {
         parser.attachments()->openAttachment(part);
     }
 }
 
 void MessageViewer::Private::saveSelectedAttachments()
 {
     Q_ASSERT(selectedParts.count() >= 1);
     for (const auto &part : std::as_const(selectedParts)) {
         parser.attachments()->saveAttachmentToDisk(part);
     }
 }
 
 void MessageViewer::Private::importPublicKey()
 {
     Q_ASSERT(selectedParts.count() == 1);
     parser.attachments()->importPublicKey(selectedParts[0]);
 }
 
 void MessageViewer::Private::showContextMenu()
 {
     const int numberOfParts(selectedParts.count());
     QMenu menu;
     if (numberOfParts == 1) {
         const QString mimetype = QString::fromLatin1(selectedParts.first()->mimeType());
         if (mimetype == QLatin1String("application/pgp-keys")) {
             menu.addAction(importPublicKeyAction);
         }
     }
 
     menu.addAction(openAttachmentAction);
     menu.addAction(saveAttachmentAction);
 
     menu.exec(QCursor::pos());
 }
 
 void MessageViewer::Private::selectionChanged()
 {
     const QModelIndexList selectedRows = attachmentView->selectionModel()->selectedRows();
     MimeTreeParser::MessagePart::List selectedParts;
     selectedParts.reserve(selectedRows.count());
     for (const QModelIndex &index : selectedRows) {
         auto part = attachmentView->model()->data(index, AttachmentModel::AttachmentPartRole).value<MimeTreeParser::MessagePart::Ptr>();
         selectedParts.append(part);
     }
     this->selectedParts = selectedParts;
 }
 
 MessageViewer::MessageViewer(QWidget *parent)
     : QSplitter(Qt::Vertical, parent)
     , d(std::make_unique<MessageViewer::Private>(this))
 {
     setObjectName(QStringLiteral("MessageViewerSplitter"));
     setChildrenCollapsible(false);
     setSizes({0});
 
     auto headersArea = new QWidget(this);
     headersArea->setSizePolicy(sizePolicy().horizontalPolicy(), QSizePolicy::Expanding);
     addWidget(headersArea);
 
+    d->urlHandler = new UrlHandler(this);
+
     d->formLayout = new QFormLayout(headersArea);
 
     auto widget = new QWidget(this);
     d->layout = new QVBoxLayout(widget);
     d->layout->setSizeConstraint(QLayout::SetMinAndMaxSize);
 
     auto scrollArea = new QScrollArea(this);
     scrollArea->setWidget(widget);
     scrollArea->setWidgetResizable(true);
+    scrollArea->setBackgroundRole(QPalette::Base);
     addWidget(scrollArea);
     setStretchFactor(1, 2);
 
     d->attachmentView = new AttachmentView(this);
     addWidget(d->attachmentView);
 
     connect(d->attachmentView, &AttachmentView::contextMenuRequested, this, [this] {
         d->selectionChanged();
         d->showContextMenu();
     });
+
+    setMinimumSize(400, 400);
 }
 
 MessageViewer::~MessageViewer()
 {
     qDeleteAll(d->widgets);
     d->widgets.clear();
 }
 
 KMime::Message::Ptr MessageViewer::message() const
 {
     return d->parser.message();
 }
 
 void MessageViewer::Private::recursiveBuildViewer(PartModel *parts, QVBoxLayout *layout, const QModelIndex &parent)
 {
     for (int i = 0, count = parts->rowCount(parent); i < count; i++) {
         const auto type = static_cast<PartModel::Types>(parts->data(parts->index(i, 0, parent), PartModel::TypeRole).toUInt());
         const auto content = parts->data(parts->index(i, 0, parent), PartModel::ContentRole).toString();
 
+        const auto signatureInfo = parts->data(parts->index(i, 0, parent), PartModel::SignatureDetails).value<SignatureInfo>();
+        const auto isSigned = parts->data(parts->index(i, 0, parent), PartModel::IsSignedRole).toBool();
+        const auto signatureSecurityLevel =
+            static_cast<PartModel::SecurityLevel>(parts->data(parts->index(i, 0, parent), PartModel::SignatureSecurityLevelRole).toInt());
+
+        const auto encryptionInfo = parts->data(parts->index(i, 0, parent), PartModel::EncryptionDetails).value<SignatureInfo>();
+        const auto isEncrypted = parts->data(parts->index(i, 0, parent), PartModel::IsEncryptedRole).toBool();
+        const auto encryptionSecurityLevel =
+            static_cast<PartModel::SecurityLevel>(parts->data(parts->index(i, 0, parent), PartModel::EncryptionSecurityLevelRole).toInt());
+
+        const auto displayEncryptionInfo =
+            i == 0 || parts->data(parts->index(i - 1, 0, parent), PartModel::EncryptionDetails).value<SignatureInfo>().keyId != encryptionInfo.keyId;
+
+        const auto displaySignatureInfo =
+            i == 0 || parts->data(parts->index(i - 1, 0, parent), PartModel::SignatureDetails).value<SignatureInfo>().keyId != signatureInfo.keyId;
+
         switch (type) {
         case PartModel::Types::Plain: {
-            auto label = new QLabel();
-            label->setText(content);
-            widgets.append(label);
-            layout->addWidget(label);
+            auto container = new MessageWidgetContainer(isSigned,
+                                                        signatureInfo,
+                                                        signatureSecurityLevel,
+                                                        displaySignatureInfo,
+                                                        isEncrypted,
+                                                        encryptionInfo,
+                                                        encryptionSecurityLevel,
+                                                        displayEncryptionInfo,
+                                                        urlHandler);
+            auto label = new QLabel(content);
+            label->setTextInteractionFlags(Qt::TextSelectableByMouse);
+            container->layout()->addWidget(label);
+            widgets.append(container);
+            layout->addWidget(container);
             break;
         }
         case PartModel::Types::Ical: {
+            auto container = new MessageWidgetContainer(isSigned,
+                                                        signatureInfo,
+                                                        signatureSecurityLevel,
+                                                        displaySignatureInfo,
+                                                        isEncrypted,
+                                                        encryptionInfo,
+                                                        encryptionSecurityLevel,
+                                                        displayEncryptionInfo,
+                                                        urlHandler);
+
             KCalendarCore::ICalFormat format;
             auto incidence = format.fromString(content);
 
-            auto widget = new QGroupBox;
+            auto widget = new QGroupBox(container);
             widget->setTitle(i18n("Invitation"));
 
             auto incidenceLayout = new QFormLayout(widget);
             incidenceLayout->addRow(i18n("&Summary:"), new QLabel(incidence->summary()));
             incidenceLayout->addRow(i18n("&Organizer:"), new QLabel(incidence->organizer().fullName()));
             if (incidence->location().length() > 0) {
                 incidenceLayout->addRow(i18n("&Location:"), new QLabel(incidence->location()));
             }
             incidenceLayout->addRow(i18n("&Start date:"), new QLabel(incidence->dtStart().toLocalTime().toString()));
             if (const auto event = incidence.dynamicCast<KCalendarCore::Event>()) {
                 incidenceLayout->addRow(i18n("&End date:"), new QLabel(event->dtEnd().toLocalTime().toString()));
             }
             if (incidence->description().length() > 0) {
                 incidenceLayout->addRow(i18n("&Details:"), new QLabel(incidence->description()));
             }
 
-            widgets.append(widget);
-            layout->addWidget(widget);
+            container->layout()->addWidget(widget);
+
+            widgets.append(container);
+            layout->addWidget(container);
             break;
         }
         case PartModel::Types::Encapsulated: {
-            auto groupBox = new QGroupBox;
+            auto container = new MessageWidgetContainer(isSigned,
+                                                        signatureInfo,
+                                                        signatureSecurityLevel,
+                                                        displaySignatureInfo,
+                                                        isEncrypted,
+                                                        encryptionInfo,
+                                                        encryptionSecurityLevel,
+                                                        displayEncryptionInfo,
+                                                        urlHandler);
+
+            auto groupBox = new QGroupBox(container);
             groupBox->setSizePolicy(QSizePolicy::MinimumExpanding, q->sizePolicy().verticalPolicy());
             groupBox->setTitle(i18n("Encapsulated email"));
 
             auto encapsulatedLayout = new QVBoxLayout(groupBox);
 
             auto header = new QWidget(groupBox);
             auto headerLayout = new QFormLayout(header);
             const auto from = parts->data(parts->index(i, 0, parent), PartModel::SenderRole).toString();
             const auto date = parts->data(parts->index(i, 0, parent), PartModel::DateRole).toDateTime();
             headerLayout->addRow(i18n("From:"), new QLabel(from));
             headerLayout->addRow(i18n("Date:"), new QLabel(date.toLocalTime().toString()));
 
             encapsulatedLayout->addWidget(header);
 
             recursiveBuildViewer(parts, encapsulatedLayout, parts->index(i, 0, parent));
 
-            widgets.append(groupBox);
-            layout->addWidget(groupBox);
+            container->layout()->addWidget(groupBox);
+
+            widgets.append(container);
+            layout->addWidget(container);
             break;
         }
 
         case PartModel::Types::Error: {
             const auto errorString = parts->data(parts->index(i, 0, parent), PartModel::ErrorString).toString();
             auto errorWidget = new KMessageWidget(i18n("Error: %1", errorString));
             errorWidget->setMessageType(KMessageWidget::MessageType::Error);
             widgets.append(errorWidget);
             layout->addWidget(errorWidget);
             break;
         }
         default:
             qWarning() << parts->data(parts->index(i, 0, parent), PartModel::ContentRole) << type;
         }
     }
 }
 
 void MessageViewer::setMessage(const KMime::Message::Ptr message)
 {
     d->parser.setMessage(message);
 
     for (int i = d->formLayout->rowCount() - 1; i >= 0; i--) {
         d->formLayout->removeRow(i);
     }
     if (!d->parser.subject().isEmpty()) {
         d->formLayout->addRow(i18n("&Subject:"), new QLabel(d->parser.subject()));
     }
     if (!d->parser.from().isEmpty()) {
         d->formLayout->addRow(i18n("&From:"), new QLabel(d->parser.from()));
     }
     if (!d->parser.to().isEmpty()) {
         d->formLayout->addRow(i18n("&To:"), new QLabel(d->parser.to()));
     }
 
     const auto parts = d->parser.parts();
 
     for (auto widget : std::as_const(d->widgets)) {
         d->layout->removeWidget(widget);
         delete widget;
     }
     d->widgets.clear();
 
     d->recursiveBuildViewer(parts, d->layout, {});
     d->layout->addStretch();
 
     d->attachmentView->setModel(d->parser.attachments());
     d->attachmentView->setVisible(d->parser.attachments()->rowCount() > 0);
 
     connect(d->attachmentView->selectionModel(), &QItemSelectionModel::selectionChanged, this, [this] {
         d->selectionChanged();
     });
 }
diff --git a/src/widgets/urlhandler.cpp b/src/widgets/urlhandler.cpp
new file mode 100644
index 0000000..bf2aacc
--- /dev/null
+++ b/src/widgets/urlhandler.cpp
@@ -0,0 +1,69 @@
+// SPDX-FileCopyrightText: 2023 g10 Code GmbH
+// SPDX-FileContributor: Carl Schwan <carl.schwan@gnupg.com>
+// SPDX-License-Identifier: LGPL-2.0-or-later
+
+#include "urlhandler_p.h"
+
+#include "mimetreeparser_widgets_debug.h"
+
+#include <KLocalizedString>
+#include <KMessageBox>
+#include <QProcess>
+#include <QStandardPaths>
+#include <QUrl>
+
+UrlHandler::UrlHandler(QObject *parent)
+    : QObject(parent)
+{
+}
+
+bool UrlHandler::handleClick(const QUrl &url, QWindow *window)
+{
+    if (!url.hasFragment()) {
+        return false;
+    }
+    QString displayName;
+    QString libName;
+    QString keyId;
+    if (!foundSMIMEData(url.path() + QLatin1Char('#') + QUrl::fromPercentEncoding(url.fragment().toLatin1()), displayName, libName, keyId)) {
+        return false;
+    }
+    QStringList lst;
+    lst << QStringLiteral("--parent-windowid") << QString::number(static_cast<qlonglong>(window->winId())) << QStringLiteral("--query") << keyId;
+    const QString exec = QStandardPaths::findExecutable(QStringLiteral("kleopatra"));
+    if (exec.isEmpty()) {
+        qCWarning(MIMETREEPARSER_WIDGET_LOG) << "Could not find kleopatra executable in PATH";
+        KMessageBox::errorWId(window->winId(),
+                              i18n("Could not start certificate manager. "
+                                   "Please check your installation."),
+                              i18n("KMail Error"));
+        return false;
+    }
+    QProcess::startDetached(exec, lst);
+    return true;
+}
+
+bool UrlHandler::foundSMIMEData(const QString &aUrl, QString &displayName, QString &libName, QString &keyId)
+{
+    static QString showCertMan(QStringLiteral("showCertificate#"));
+    displayName.clear();
+    libName.clear();
+    keyId.clear();
+    int i1 = aUrl.indexOf(showCertMan);
+    if (-1 < i1) {
+        i1 += showCertMan.length();
+        int i2 = aUrl.indexOf(QLatin1String(" ### "), i1);
+        if (i1 < i2) {
+            displayName = aUrl.mid(i1, i2 - i1);
+            i1 = i2 + 5;
+            i2 = aUrl.indexOf(QLatin1String(" ### "), i1);
+            if (i1 < i2) {
+                libName = aUrl.mid(i1, i2 - i1);
+                i2 += 5;
+
+                keyId = aUrl.mid(i2);
+            }
+        }
+    }
+    return !keyId.isEmpty();
+}
diff --git a/src/widgets/urlhandler_p.h b/src/widgets/urlhandler_p.h
new file mode 100644
index 0000000..0011666
--- /dev/null
+++ b/src/widgets/urlhandler_p.h
@@ -0,0 +1,18 @@
+// SPDX-FileCopyrightText: 2023 g10 Code GmbH
+// SPDX-FileContributor: Carl Schwan <carl.schwan@gnupg.com>
+// SPDX-License-Identifier: LGPL-2.0-or-later
+
+#pragma once
+
+#include <QObject>
+#include <QWindow>
+
+class UrlHandler : public QObject
+{
+public:
+    explicit UrlHandler(QObject *parent = nullptr);
+    bool handleClick(const QUrl &url, QWindow *window);
+
+private:
+    bool foundSMIMEData(const QString &aUrl, QString &displayName, QString &libName, QString &keyId);
+};