diff --git a/autotests/newkeyapprovaldialogtest.cpp b/autotests/newkeyapprovaldialogtest.cpp
index fc97772a..6f628b10 100644
--- a/autotests/newkeyapprovaldialogtest.cpp
+++ b/autotests/newkeyapprovaldialogtest.cpp
@@ -1,830 +1,911 @@
 /*
     autotests/newkeyapprovaldialogtest.cpp
 
     This file is part of libkleopatra's test suite.
     SPDX-FileCopyrightText: 2021 g10 Code GmbH
     SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
 
     SPDX-License-Identifier: GPL-2.0-or-later
 */
 
 #include <Libkleo/KeyCache>
 #include <Libkleo/KeySelectionCombo>
 #include <Libkleo/NewKeyApprovalDialog>
+#include <Libkleo/Test>
 
 #include <QCheckBox>
 #include <QLabel>
 #include <QObject>
 #include <QPushButton>
 #include <QRadioButton>
 #include <QSignalSpy>
 #include <QTest>
 
 #include <gpgme++/key.h>
 
 #include <gpgme.h>
 
 #include <memory>
 #include <set>
 
 using namespace Kleo;
 
 namespace QTest
 {
 template <>
 inline char *toString(const bool &t)
 {
     return t ? qstrdup("true") : qstrdup("false");
 }
 
 template <>
 inline bool qCompare(bool const &t1, bool const &t2, const char *actual, const char *expected,
                      const char *file, int line)
 {
     return compare_helper(t1 == t2, "Compared values are not the same",
                           toString(t1), toString(t2), actual, expected, file, line);
 }
 }
 
 namespace
 {
 
 enum class KeyUsage {
     AnyUsage,
     Sign,
     Encrypt,
 };
 
-GpgME::Key createTestKey(const char *uid, GpgME::Protocol protocol = GpgME::UnknownProtocol, KeyUsage usage = KeyUsage::AnyUsage)
+auto mapValidity(GpgME::UserID::Validity validity)
+{
+    switch (validity) {
+    default:
+    case GpgME::UserID::Unknown: return GPGME_VALIDITY_UNKNOWN;
+    case GpgME::UserID::Undefined: return GPGME_VALIDITY_UNDEFINED;
+    case GpgME::UserID::Never: return GPGME_VALIDITY_NEVER;
+    case GpgME::UserID::Marginal: return GPGME_VALIDITY_MARGINAL;
+    case GpgME::UserID::Full: return GPGME_VALIDITY_FULL;
+    case GpgME::UserID::Ultimate: return GPGME_VALIDITY_ULTIMATE;
+    }
+}
+
+GpgME::Key createTestKey(const char *uid, GpgME::Protocol protocol = GpgME::UnknownProtocol, KeyUsage usage = KeyUsage::AnyUsage,
+                         GpgME::UserID::Validity validity = GpgME::UserID::Full)
 {
     static int count = 0;
     count++;
 
     gpgme_key_t key;
     gpgme_key_from_uid(&key, uid);
     Q_ASSERT(key);
+    Q_ASSERT(key->uids);
     if (protocol != GpgME::UnknownProtocol) {
         key->protocol = protocol == GpgME::OpenPGP ? GPGME_PROTOCOL_OpenPGP : GPGME_PROTOCOL_CMS;
     }
     const QByteArray fingerprint = QByteArray::number(count, 16).rightJustified(40, '0');
     key->fpr = strdup(fingerprint.constData());
     key->revoked = 0;
     key->expired = 0;
     key->disabled = 0;
     key->can_encrypt = int(usage == KeyUsage::AnyUsage || usage == KeyUsage::Encrypt);
     key->can_sign = int(usage == KeyUsage::AnyUsage || usage == KeyUsage::Sign);
     key->secret = 1;
+    key->uids->validity = mapValidity(validity);
 
     return GpgME::Key(key, false);
 }
 
 auto testKey(const char *email, GpgME::Protocol protocol = GpgME::UnknownProtocol)
 {
     const auto keys = KeyCache::instance()->findByEMailAddress(email);
     for (const auto &key: keys) {
         if (protocol == GpgME::UnknownProtocol || key.protocol() == protocol) {
             return key;
         }
     }
     return GpgME::Key();
 }
 
 void waitForKeySelectionCombosBeingInitialized(const QDialog *dialog)
 {
     QVERIFY(dialog);
     auto combo = dialog->findChild<KeySelectionCombo *>();
     QVERIFY(combo);
 
     const auto spy = std::make_unique<QSignalSpy>(combo, &KeySelectionCombo::keyListingFinished);
     QVERIFY(spy->isValid());
     QVERIFY(spy->wait(10));
 }
 
 template <typename T>
 struct Widgets
 {
     std::vector<T *> visible;
     std::vector<T *> hidden;
 };
 
 template <typename T>
 Widgets<T> visibleAndHiddenWidgets(const QList<T *> &widgets)
 {
     Widgets<T> result;
     std::partition_copy(std::begin(widgets), std::end(widgets),
                         std::back_inserter(result.visible),
                         std::back_inserter(result.hidden),
                         std::mem_fn(&QWidget::isVisible));
     return result;
 }
 
 enum Visibility {
     IsHidden,
     IsVisible
 };
 
 enum CheckedState {
     IsUnchecked,
     IsChecked
 };
 
 template <typename T>
 void verifyProtocolButton(const T *button, Visibility expectedVisibility, CheckedState expectedCheckedState)
 {
     QVERIFY(button);
     QCOMPARE(button->isVisible(), expectedVisibility == IsVisible);
     QCOMPARE(button->isChecked(), expectedCheckedState == IsChecked);
 }
 
 template <typename T>
 void verifyWidgetVisibility(const T *widget, Visibility expectedVisibility)
 {
     QVERIFY(widget);
     QCOMPARE(widget->isVisible(), expectedVisibility == IsVisible);
 }
 
 template <typename T>
 void verifyWidgetsVisibility(const QList<T> &widgets, Visibility expectedVisibility)
 {
     for (auto w: widgets) {
         verifyWidgetVisibility(w, expectedVisibility);
     }
 }
 
 void verifyProtocolLabels(const QList<QLabel *> &labels, int expectedNumber, Visibility expectedVisibility)
 {
     QCOMPARE(labels.size(), expectedNumber);
     verifyWidgetsVisibility(labels, expectedVisibility);
 }
 
 }
 
 class NewKeyApprovalDialogTest: public QObject
 {
     Q_OBJECT
 
 private:
     // copied from NewKeyApprovalDialog::Private
     enum Action {
         Unset,
         GenerateKey,
         IgnoreKey,
     };
 private Q_SLOTS:
     void init()
     {
         // hold a reference to the key cache to avoid rebuilding while the test is running
         mKeyCache = KeyCache::instance();
 
         KeyCache::mutableInstance()->setKeys({
             createTestKey("sender@example.net", GpgME::OpenPGP, KeyUsage::AnyUsage),
             createTestKey("sender@example.net", GpgME::CMS, KeyUsage::AnyUsage),
             createTestKey("Full Trust <prefer-openpgp@example.net>", GpgME::OpenPGP, KeyUsage::Encrypt),
             createTestKey("Trusted S/MIME <prefer-smime@example.net>", GpgME::CMS, KeyUsage::Encrypt),
+            createTestKey("Marginal Validity <marginal-openpgp@example.net>", GpgME::OpenPGP, KeyUsage::Encrypt, GpgME::UserID::Marginal),
         });
     }
 
     void cleanup()
     {
         // verify that nobody else holds a reference to the key cache
         QVERIFY(mKeyCache.use_count() == 1);
         mKeyCache.reset();
     }
 
     void test__both_protocols_allowed__mixed_not_allowed__openpgp_preferred()
     {
         const GpgME::Protocol forcedProtocol = GpgME::UnknownProtocol;
         const bool allowMixed = false;
         const QString sender = QStringLiteral("sender@example.net");
         const KeyResolver::Solution preferredSolution = {
             GpgME::OpenPGP,
             {testKey("sender@example.net", GpgME::OpenPGP)},
             {
                 {QStringLiteral("prefer-openpgp@example.net"), {testKey("Full Trust <prefer-openpgp@example.net>", GpgME::OpenPGP)}},
                 {QStringLiteral("prefer-smime@example.net"), {}},
                 {QStringLiteral("sender@example.net"), {testKey("sender@example.net", GpgME::OpenPGP)}}
             }
         };
         const KeyResolver::Solution alternativeSolution = {
             GpgME::CMS,
             {testKey("sender@example.net", GpgME::CMS)},
             {
                 {QStringLiteral("prefer-openpgp@example.net"), {}},
                 {QStringLiteral("prefer-smime@example.net"), {testKey("Trusted S/MIME <prefer-smime@example.net>", GpgME::CMS)}},
                 {QStringLiteral("sender@example.net"), {testKey("sender@example.net", GpgME::CMS)}}
             }
         };
 
         const auto dialog = std::make_unique<NewKeyApprovalDialog>(true,
                                                                    true,
                                                                    sender,
                                                                    preferredSolution,
                                                                    alternativeSolution,
                                                                    allowMixed,
                                                                    forcedProtocol);
         dialog->show();
 
         verifyProtocolButton(dialog->findChild<QRadioButton *>("openpgp button"), IsVisible, IsChecked);
         verifyProtocolButton(dialog->findChild<QRadioButton *>("smime button"), IsVisible, IsUnchecked);
         const auto signingKeyWidgets = visibleAndHiddenWidgets(dialog->findChildren<KeySelectionCombo *>(QStringLiteral("signing key")));
         QCOMPARE(signingKeyWidgets.visible.size(), 1);
         QCOMPARE(signingKeyWidgets.hidden.size(), 1);
         QCOMPARE(signingKeyWidgets.visible[0]->defaultKey(GpgME::OpenPGP),
                  preferredSolution.signingKeys[0].primaryFingerprint());
         QCOMPARE(signingKeyWidgets.hidden[0]->defaultKey(GpgME::CMS),
                  alternativeSolution.signingKeys[0].primaryFingerprint());
 
         const auto encryptionKeyWidgets = visibleAndHiddenWidgets(dialog->findChildren<KeySelectionCombo *>(QStringLiteral("encryption key")));
         QCOMPARE(encryptionKeyWidgets.visible.size(), 3);
         QCOMPARE(encryptionKeyWidgets.hidden.size(), 3);
 
         // encryption key widgets for sender come first (visible for OpenPGP, hidden for S/MIME)
         QCOMPARE(encryptionKeyWidgets.visible[0]->property("address").toString(), sender);
         QCOMPARE(encryptionKeyWidgets.visible[0]->defaultKey(GpgME::OpenPGP),
                  preferredSolution.encryptionKeys.value(sender)[0].primaryFingerprint());
         QCOMPARE(encryptionKeyWidgets.hidden[0]->property("address").toString(), sender);
         QCOMPARE(encryptionKeyWidgets.hidden[0]->defaultKey(GpgME::CMS),
                  alternativeSolution.encryptionKeys.value(sender)[0].primaryFingerprint());
 
         // encryption key widgets for other recipients follow (visible for OpenPGP, hidden for S/MIME)
         QCOMPARE(encryptionKeyWidgets.visible[1]->property("address").toString(), "prefer-openpgp@example.net");
         QCOMPARE(encryptionKeyWidgets.visible[1]->defaultKey(GpgME::OpenPGP),
                  preferredSolution.encryptionKeys.value("prefer-openpgp@example.net")[0].primaryFingerprint());
         QCOMPARE(encryptionKeyWidgets.hidden[1]->property("address").toString(), "prefer-openpgp@example.net");
         QVERIFY(encryptionKeyWidgets.hidden[1]->defaultKey(GpgME::CMS).isEmpty());
         QCOMPARE(encryptionKeyWidgets.visible[2]->property("address").toString(), "prefer-smime@example.net");
         QVERIFY(encryptionKeyWidgets.visible[2]->defaultKey(GpgME::OpenPGP).isEmpty());
         QCOMPARE(encryptionKeyWidgets.hidden[2]->property("address").toString(), "prefer-smime@example.net");
         QCOMPARE(encryptionKeyWidgets.hidden[2]->defaultKey(GpgME::CMS),
                  alternativeSolution.encryptionKeys.value("prefer-smime@example.net")[0].primaryFingerprint());
     }
 
     void test__both_protocols_allowed__mixed_not_allowed__smime_preferred()
     {
         const GpgME::Protocol forcedProtocol = GpgME::UnknownProtocol;
         const bool allowMixed = false;
         const QString sender = QStringLiteral("sender@example.net");
         const KeyResolver::Solution preferredSolution = {
             GpgME::CMS,
             {testKey("sender@example.net", GpgME::CMS)},
             {
                 {QStringLiteral("prefer-openpgp@example.net"), {}},
                 {QStringLiteral("prefer-smime@example.net"), {testKey("Trusted S/MIME <prefer-smime@example.net>", GpgME::CMS)}},
                 {QStringLiteral("sender@example.net"), {testKey("sender@example.net", GpgME::CMS)}}
             }
         };
         const KeyResolver::Solution alternativeSolution = {
             GpgME::OpenPGP,
             {testKey("sender@example.net", GpgME::OpenPGP)},
             {
                 {QStringLiteral("prefer-openpgp@example.net"), {testKey("Full Trust <prefer-openpgp@example.net>", GpgME::OpenPGP)}},
                 {QStringLiteral("prefer-smime@example.net"), {}},
                 {QStringLiteral("sender@example.net"), {testKey("sender@example.net", GpgME::OpenPGP)}}
             }
         };
 
         const auto dialog = std::make_unique<NewKeyApprovalDialog>(true,
                                                                    true,
                                                                    sender,
                                                                    preferredSolution,
                                                                    alternativeSolution,
                                                                    allowMixed,
                                                                    forcedProtocol);
         dialog->show();
 
         verifyProtocolButton(dialog->findChild<QRadioButton *>("openpgp button"), IsVisible, IsUnchecked);
         verifyProtocolButton(dialog->findChild<QRadioButton *>("smime button"), IsVisible, IsChecked);
         const auto signingKeyWidgets = visibleAndHiddenWidgets(dialog->findChildren<KeySelectionCombo *>(QStringLiteral("signing key")));
         QCOMPARE(signingKeyWidgets.visible.size(), 1);
         QCOMPARE(signingKeyWidgets.hidden.size(), 1);
         QCOMPARE(signingKeyWidgets.visible[0]->defaultKey(GpgME::CMS),
                  preferredSolution.signingKeys[0].primaryFingerprint());
         QCOMPARE(signingKeyWidgets.hidden[0]->defaultKey(GpgME::OpenPGP),
                  alternativeSolution.signingKeys[0].primaryFingerprint());
 
         const auto encryptionKeyWidgets = visibleAndHiddenWidgets(dialog->findChildren<KeySelectionCombo *>(QStringLiteral("encryption key")));
         QCOMPARE(encryptionKeyWidgets.visible.size(), 3);
         QCOMPARE(encryptionKeyWidgets.hidden.size(), 3);
 
         // encryption key widgets for sender come first (visible for S/MIME, hidden for OpenPGP)
         QCOMPARE(encryptionKeyWidgets.visible[0]->property("address").toString(), sender);
         QCOMPARE(encryptionKeyWidgets.visible[0]->defaultKey(GpgME::CMS),
                  preferredSolution.encryptionKeys.value(sender)[0].primaryFingerprint());
         QCOMPARE(encryptionKeyWidgets.hidden[0]->property("address").toString(), sender);
         QCOMPARE(encryptionKeyWidgets.hidden[0]->defaultKey(GpgME::OpenPGP),
                  alternativeSolution.encryptionKeys.value(sender)[0].primaryFingerprint());
 
         // encryption key widgets for other recipients follow (visible for OpenPGP, hidden for S/MIME)
         QCOMPARE(encryptionKeyWidgets.visible[1]->property("address").toString(), "prefer-openpgp@example.net");
         QVERIFY(encryptionKeyWidgets.visible[1]->defaultKey(GpgME::CMS).isEmpty());
         QCOMPARE(encryptionKeyWidgets.hidden[1]->property("address").toString(), "prefer-openpgp@example.net");
         QCOMPARE(encryptionKeyWidgets.hidden[1]->defaultKey(GpgME::OpenPGP),
                  alternativeSolution.encryptionKeys.value("prefer-openpgp@example.net")[0].primaryFingerprint());
         QCOMPARE(encryptionKeyWidgets.visible[2]->property("address").toString(), "prefer-smime@example.net");
         QCOMPARE(encryptionKeyWidgets.visible[2]->defaultKey(GpgME::CMS),
                  preferredSolution.encryptionKeys.value("prefer-smime@example.net")[0].primaryFingerprint());
         QCOMPARE(encryptionKeyWidgets.hidden[2]->property("address").toString(), "prefer-smime@example.net");
         QVERIFY(encryptionKeyWidgets.hidden[2]->defaultKey(GpgME::OpenPGP).isEmpty());
     }
 
     void test__openpgp_only()
     {
         const GpgME::Protocol forcedProtocol = GpgME::OpenPGP;
         const bool allowMixed = false;
         const QString sender = QStringLiteral("sender@example.net");
         const KeyResolver::Solution preferredSolution = {
             GpgME::OpenPGP,
             {testKey("sender@example.net", GpgME::OpenPGP)},
             {
                 {QStringLiteral("prefer-openpgp@example.net"), {testKey("Full Trust <prefer-openpgp@example.net>", GpgME::OpenPGP)}},
                 {QStringLiteral("prefer-smime@example.net"), {}},
                 {QStringLiteral("sender@example.net"), {testKey("sender@example.net", GpgME::OpenPGP)}}
             }
         };
         const KeyResolver::Solution alternativeSolution = {};
 
         const auto dialog = std::make_unique<NewKeyApprovalDialog>(true,
                                                                    true,
                                                                    sender,
                                                                    preferredSolution,
                                                                    alternativeSolution,
                                                                    allowMixed,
                                                                    forcedProtocol);
         dialog->show();
 
         verifyProtocolButton(dialog->findChild<QRadioButton *>("openpgp button"), IsHidden, IsChecked);
         verifyProtocolButton(dialog->findChild<QRadioButton *>("smime button"), IsHidden, IsUnchecked);
         const auto signingKeyWidgets = visibleAndHiddenWidgets(dialog->findChildren<KeySelectionCombo *>(QStringLiteral("signing key")));
         QCOMPARE(signingKeyWidgets.visible.size(), 1);
         QCOMPARE(signingKeyWidgets.hidden.size(), 0);
         QCOMPARE(signingKeyWidgets.visible[0]->defaultKey(GpgME::OpenPGP),
                  preferredSolution.signingKeys[0].primaryFingerprint());
 
         const auto encryptionKeyWidgets = visibleAndHiddenWidgets(dialog->findChildren<KeySelectionCombo *>(QStringLiteral("encryption key")));
         QCOMPARE(encryptionKeyWidgets.visible.size(), 3);
         QCOMPARE(encryptionKeyWidgets.hidden.size(), 0);
 
         // encryption key widget for sender comes first
         QCOMPARE(encryptionKeyWidgets.visible[0]->property("address").toString(), sender);
         QCOMPARE(encryptionKeyWidgets.visible[0]->defaultKey(GpgME::OpenPGP),
                  preferredSolution.encryptionKeys.value(sender)[0].primaryFingerprint());
 
         // encryption key widgets for other recipients follow
         QCOMPARE(encryptionKeyWidgets.visible[1]->property("address").toString(), "prefer-openpgp@example.net");
         QCOMPARE(encryptionKeyWidgets.visible[1]->defaultKey(GpgME::OpenPGP),
                  preferredSolution.encryptionKeys.value("prefer-openpgp@example.net")[0].primaryFingerprint());
         QCOMPARE(encryptionKeyWidgets.visible[2]->property("address").toString(), "prefer-smime@example.net");
         QVERIFY(encryptionKeyWidgets.visible[2]->defaultKey(GpgME::OpenPGP).isEmpty());
     }
 
     void test__smime_only()
     {
         const GpgME::Protocol forcedProtocol = GpgME::CMS;
         const bool allowMixed = false;
         const QString sender = QStringLiteral("sender@example.net");
         const KeyResolver::Solution preferredSolution = {
             GpgME::CMS,
             {testKey("sender@example.net", GpgME::CMS)},
             {
                 {QStringLiteral("prefer-openpgp@example.net"), {}},
                 {QStringLiteral("prefer-smime@example.net"), {testKey("Trusted S/MIME <prefer-smime@example.net>", GpgME::CMS)}},
                 {QStringLiteral("sender@example.net"), {testKey("sender@example.net", GpgME::CMS)}}
             }
         };
         const KeyResolver::Solution alternativeSolution = {};
 
         const auto dialog = std::make_unique<NewKeyApprovalDialog>(true,
                                                                    true,
                                                                    sender,
                                                                    preferredSolution,
                                                                    alternativeSolution,
                                                                    allowMixed,
                                                                    forcedProtocol);
         dialog->show();
 
         verifyProtocolButton(dialog->findChild<QRadioButton *>("openpgp button"), IsHidden, IsUnchecked);
         verifyProtocolButton(dialog->findChild<QRadioButton *>("smime button"), IsHidden, IsChecked);
         const auto signingKeyWidgets = visibleAndHiddenWidgets(dialog->findChildren<KeySelectionCombo *>(QStringLiteral("signing key")));
         QCOMPARE(signingKeyWidgets.visible.size(), 1);
         QCOMPARE(signingKeyWidgets.hidden.size(), 0);
         QCOMPARE(signingKeyWidgets.visible[0]->defaultKey(GpgME::CMS),
                  preferredSolution.signingKeys[0].primaryFingerprint());
 
         const auto encryptionKeyWidgets = visibleAndHiddenWidgets(dialog->findChildren<KeySelectionCombo *>(QStringLiteral("encryption key")));
         QCOMPARE(encryptionKeyWidgets.visible.size(), 3);
         QCOMPARE(encryptionKeyWidgets.hidden.size(), 0);
 
         // encryption key widget for sender comes first
         QCOMPARE(encryptionKeyWidgets.visible[0]->property("address").toString(), sender);
         QCOMPARE(encryptionKeyWidgets.visible[0]->defaultKey(GpgME::CMS),
                  preferredSolution.encryptionKeys.value(sender)[0].primaryFingerprint());
 
         // encryption key widgets for other recipients follow
         QCOMPARE(encryptionKeyWidgets.visible[1]->property("address").toString(), "prefer-openpgp@example.net");
         QVERIFY(encryptionKeyWidgets.visible[1]->defaultKey(GpgME::CMS).isEmpty());
         QCOMPARE(encryptionKeyWidgets.visible[2]->property("address").toString(), "prefer-smime@example.net");
         QCOMPARE(encryptionKeyWidgets.visible[2]->defaultKey(GpgME::CMS),
                  preferredSolution.encryptionKeys.value("prefer-smime@example.net")[0].primaryFingerprint());
     }
 
     void test__both_protocols_allowed__mixed_allowed()
     {
         const GpgME::Protocol forcedProtocol = GpgME::UnknownProtocol;
         const bool allowMixed = true;
         const QString sender = QStringLiteral("sender@example.net");
         const KeyResolver::Solution preferredSolution = {
             GpgME::UnknownProtocol,
             {testKey("sender@example.net", GpgME::OpenPGP), testKey("sender@example.net", GpgME::CMS)},
             {
                 {QStringLiteral("prefer-openpgp@example.net"), {testKey("Full Trust <prefer-openpgp@example.net>", GpgME::OpenPGP)}},
                 {QStringLiteral("prefer-smime@example.net"), {testKey("Trusted S/MIME <prefer-smime@example.net>", GpgME::CMS)}},
                 {QStringLiteral("unknown@example.net"), {}},
                 {QStringLiteral("sender@example.net"), {testKey("sender@example.net", GpgME::OpenPGP), testKey("sender@example.net", GpgME::CMS)}}
             }
         };
         const KeyResolver::Solution alternativeSolution = {};
 
         const auto dialog = std::make_unique<NewKeyApprovalDialog>(true,
                                                                    true,
                                                                    sender,
                                                                    preferredSolution,
                                                                    alternativeSolution,
                                                                    allowMixed,
                                                                    forcedProtocol);
         dialog->show();
 
         verifyProtocolButton(dialog->findChild<QCheckBox *>("openpgp button"), IsVisible, IsChecked);
         verifyProtocolButton(dialog->findChild<QCheckBox *>("smime button"), IsVisible, IsChecked);
         verifyProtocolLabels(dialog->findChildren<QLabel *>("protocol label"), 4, IsVisible);
         const auto signingKeyWidgets = visibleAndHiddenWidgets(dialog->findChildren<KeySelectionCombo *>(QStringLiteral("signing key")));
         QCOMPARE(signingKeyWidgets.visible.size(), 2);
         QCOMPARE(signingKeyWidgets.hidden.size(), 0);
         QCOMPARE(signingKeyWidgets.visible[0]->defaultKey(GpgME::OpenPGP),
                  preferredSolution.signingKeys[0].primaryFingerprint());
         QCOMPARE(signingKeyWidgets.visible[1]->defaultKey(GpgME::CMS),
                  preferredSolution.signingKeys[1].primaryFingerprint());
 
         const auto encryptionKeyWidgets = visibleAndHiddenWidgets(dialog->findChildren<KeySelectionCombo *>(QStringLiteral("encryption key")));
         QCOMPARE(encryptionKeyWidgets.visible.size(), 5);
         QCOMPARE(encryptionKeyWidgets.hidden.size(), 0);
 
         // encryption key widgets for sender come first
         QCOMPARE(encryptionKeyWidgets.visible[0]->property("address").toString(), sender);
         QCOMPARE(encryptionKeyWidgets.visible[0]->defaultKey(GpgME::OpenPGP),
                  preferredSolution.encryptionKeys.value(sender)[0].primaryFingerprint());
         QCOMPARE(encryptionKeyWidgets.visible[1]->property("address").toString(), sender);
         QCOMPARE(encryptionKeyWidgets.visible[1]->defaultKey(GpgME::CMS),
                  preferredSolution.encryptionKeys.value(sender)[1].primaryFingerprint());
 
         // encryption key widgets for other recipients follow
         QCOMPARE(encryptionKeyWidgets.visible[2]->property("address").toString(), "prefer-openpgp@example.net");
         QCOMPARE(encryptionKeyWidgets.visible[2]->defaultKey(GpgME::UnknownProtocol),
                  preferredSolution.encryptionKeys.value("prefer-openpgp@example.net")[0].primaryFingerprint());
         QCOMPARE(encryptionKeyWidgets.visible[3]->property("address").toString(), "prefer-smime@example.net");
         QCOMPARE(encryptionKeyWidgets.visible[3]->defaultKey(GpgME::UnknownProtocol),
                  preferredSolution.encryptionKeys.value("prefer-smime@example.net")[0].primaryFingerprint());
         QCOMPARE(encryptionKeyWidgets.visible[4]->property("address").toString(), "unknown@example.net");
         QVERIFY(encryptionKeyWidgets.visible[4]->defaultKey(GpgME::UnknownProtocol).isEmpty());
     }
 
     void test__both_protocols_allowed__mixed_allowed__openpgp_only_preferred_solution()
     {
         const GpgME::Protocol forcedProtocol = GpgME::UnknownProtocol;
         const bool allowMixed = true;
         const QString sender = QStringLiteral("sender@example.net");
         const KeyResolver::Solution preferredSolution = {
             GpgME::OpenPGP,
             {testKey("sender@example.net", GpgME::OpenPGP), testKey("sender@example.net", GpgME::CMS)},
             {
                 {QStringLiteral("prefer-openpgp@example.net"), {testKey("Full Trust <prefer-openpgp@example.net>", GpgME::OpenPGP)}},
                 {QStringLiteral("unknown@example.net"), {}},
                 {QStringLiteral("sender@example.net"), {testKey("sender@example.net", GpgME::OpenPGP), testKey("sender@example.net", GpgME::CMS)}}
             }
         };
         const KeyResolver::Solution alternativeSolution = {};
 
         const auto dialog = std::make_unique<NewKeyApprovalDialog>(true,
                                                                    true,
                                                                    sender,
                                                                    preferredSolution,
                                                                    alternativeSolution,
                                                                    allowMixed,
                                                                    forcedProtocol);
         dialog->show();
 
         verifyProtocolButton(dialog->findChild<QCheckBox *>("openpgp button"), IsVisible, IsChecked);
         verifyProtocolButton(dialog->findChild<QCheckBox *>("smime button"), IsVisible, IsUnchecked);
         verifyProtocolLabels(dialog->findChildren<QLabel *>("protocol label"), 4, IsHidden);
         const auto signingKeyWidgets = visibleAndHiddenWidgets(dialog->findChildren<KeySelectionCombo *>(QStringLiteral("signing key")));
         QCOMPARE(signingKeyWidgets.visible.size(), 1);
         QCOMPARE(signingKeyWidgets.hidden.size(), 1);
         QCOMPARE(signingKeyWidgets.visible[0]->defaultKey(GpgME::OpenPGP),
                  preferredSolution.signingKeys[0].primaryFingerprint());
         QCOMPARE(signingKeyWidgets.hidden[0]->defaultKey(GpgME::CMS),
                  preferredSolution.signingKeys[1].primaryFingerprint());
 
         const auto encryptionKeyWidgets = visibleAndHiddenWidgets(dialog->findChildren<KeySelectionCombo *>(QStringLiteral("encryption key")));
         QCOMPARE(encryptionKeyWidgets.visible.size(), 3);
         QCOMPARE(encryptionKeyWidgets.hidden.size(), 1);
 
         // encryption key widgets for sender come first
         QCOMPARE(encryptionKeyWidgets.visible[0]->property("address").toString(), sender);
         QCOMPARE(encryptionKeyWidgets.visible[0]->defaultKey(GpgME::OpenPGP),
                  preferredSolution.encryptionKeys.value(sender)[0].primaryFingerprint());
         QCOMPARE(encryptionKeyWidgets.hidden[0]->property("address").toString(), sender);
         QCOMPARE(encryptionKeyWidgets.hidden[0]->defaultKey(GpgME::CMS),
                  preferredSolution.encryptionKeys.value(sender)[1].primaryFingerprint());
 
         // encryption key widgets for other recipients follow
         QCOMPARE(encryptionKeyWidgets.visible[1]->property("address").toString(), "prefer-openpgp@example.net");
         QCOMPARE(encryptionKeyWidgets.visible[1]->defaultKey(GpgME::UnknownProtocol),
                  preferredSolution.encryptionKeys.value("prefer-openpgp@example.net")[0].primaryFingerprint());
         QCOMPARE(encryptionKeyWidgets.visible[2]->property("address").toString(), "unknown@example.net");
         QVERIFY(encryptionKeyWidgets.visible[2]->defaultKey(GpgME::UnknownProtocol).isEmpty());
     }
 
     void test__both_protocols_allowed__mixed_allowed__smime_only_preferred_solution()
     {
         const GpgME::Protocol forcedProtocol = GpgME::UnknownProtocol;
         const bool allowMixed = true;
         const QString sender = QStringLiteral("sender@example.net");
         const KeyResolver::Solution preferredSolution = {
             GpgME::CMS,
             {testKey("sender@example.net", GpgME::OpenPGP), testKey("sender@example.net", GpgME::CMS)},
             {
                 {QStringLiteral("prefer-smime@example.net"), {testKey("Trusted S/MIME <prefer-smime@example.net>", GpgME::CMS)}},
                 {QStringLiteral("unknown@example.net"), {}},
                 {QStringLiteral("sender@example.net"), {testKey("sender@example.net", GpgME::OpenPGP), testKey("sender@example.net", GpgME::CMS)}}
             }
         };
         const KeyResolver::Solution alternativeSolution = {};
 
         const auto dialog = std::make_unique<NewKeyApprovalDialog>(true,
                                                                    true,
                                                                    sender,
                                                                    preferredSolution,
                                                                    alternativeSolution,
                                                                    allowMixed,
                                                                    forcedProtocol);
         dialog->show();
 
         verifyProtocolButton(dialog->findChild<QCheckBox *>("openpgp button"), IsVisible, IsUnchecked);
         verifyProtocolButton(dialog->findChild<QCheckBox *>("smime button"), IsVisible, IsChecked);
         verifyProtocolLabels(dialog->findChildren<QLabel *>("protocol label"), 4, IsHidden);
         const auto signingKeyWidgets = visibleAndHiddenWidgets(dialog->findChildren<KeySelectionCombo *>(QStringLiteral("signing key")));
         QCOMPARE(signingKeyWidgets.visible.size(), 1);
         QCOMPARE(signingKeyWidgets.hidden.size(), 1);
         QCOMPARE(signingKeyWidgets.visible[0]->defaultKey(GpgME::CMS),
                  preferredSolution.signingKeys[1].primaryFingerprint());
         QCOMPARE(signingKeyWidgets.hidden[0]->defaultKey(GpgME::OpenPGP),
                  preferredSolution.signingKeys[0].primaryFingerprint());
 
         const auto encryptionKeyWidgets = visibleAndHiddenWidgets(dialog->findChildren<KeySelectionCombo *>(QStringLiteral("encryption key")));
         QCOMPARE(encryptionKeyWidgets.visible.size(), 3);
         QCOMPARE(encryptionKeyWidgets.hidden.size(), 1);
 
         // encryption key widgets for sender come first
         QCOMPARE(encryptionKeyWidgets.visible[0]->property("address").toString(), sender);
         QCOMPARE(encryptionKeyWidgets.visible[0]->defaultKey(GpgME::CMS),
                  preferredSolution.encryptionKeys.value(sender)[1].primaryFingerprint());
         QCOMPARE(encryptionKeyWidgets.hidden[0]->property("address").toString(), sender);
         QCOMPARE(encryptionKeyWidgets.hidden[0]->defaultKey(GpgME::OpenPGP),
                  preferredSolution.encryptionKeys.value(sender)[0].primaryFingerprint());
 
         // encryption key widgets for other recipients follow
         QCOMPARE(encryptionKeyWidgets.visible[1]->property("address").toString(), "prefer-smime@example.net");
         QCOMPARE(encryptionKeyWidgets.visible[1]->defaultKey(GpgME::UnknownProtocol),
                  preferredSolution.encryptionKeys.value("prefer-smime@example.net")[0].primaryFingerprint());
         QCOMPARE(encryptionKeyWidgets.visible[2]->property("address").toString(), "unknown@example.net");
         QVERIFY(encryptionKeyWidgets.visible[2]->defaultKey(GpgME::UnknownProtocol).isEmpty());
     }
 
     void test__both_protocols_allowed__mixed_allowed__no_sender_keys()
     {
         const GpgME::Protocol forcedProtocol = GpgME::UnknownProtocol;
         const bool allowMixed = true;
         const QString sender = QStringLiteral("sender@example.net");
         const KeyResolver::Solution preferredSolution = {
             GpgME::UnknownProtocol,
             {},
             {
                 {QStringLiteral("prefer-openpgp@example.net"), {testKey("Full Trust <prefer-openpgp@example.net>", GpgME::OpenPGP)}},
                 {QStringLiteral("prefer-smime@example.net"), {testKey("Trusted S/MIME <prefer-smime@example.net>", GpgME::CMS)}},
                 {QStringLiteral("unknown@example.net"), {}},
                 {QStringLiteral("sender@example.net"), {}}
             }
         };
         const KeyResolver::Solution alternativeSolution = {};
 
         const auto dialog = std::make_unique<NewKeyApprovalDialog>(true,
                                                                    true,
                                                                    sender,
                                                                    preferredSolution,
                                                                    alternativeSolution,
                                                                    allowMixed,
                                                                    forcedProtocol);
         dialog->show();
 
         const auto signingKeyWidgets = visibleAndHiddenWidgets(dialog->findChildren<KeySelectionCombo *>(QStringLiteral("signing key")));
         QCOMPARE(signingKeyWidgets.visible.size(), 2);
         QCOMPARE(signingKeyWidgets.hidden.size(), 0);
 
         const auto encryptionKeyWidgets = visibleAndHiddenWidgets(dialog->findChildren<KeySelectionCombo *>(QStringLiteral("encryption key")));
         QCOMPARE(encryptionKeyWidgets.visible.size(), 5);
         QCOMPARE(encryptionKeyWidgets.hidden.size(), 0);
 
         // encryption key widgets for sender come first
         QCOMPARE(encryptionKeyWidgets.visible[0]->property("address").toString(), sender);
         QCOMPARE(encryptionKeyWidgets.visible[1]->property("address").toString(), sender);
 
         // encryption key widgets for other recipients follow
         QCOMPARE(encryptionKeyWidgets.visible[2]->property("address").toString(), "prefer-openpgp@example.net");
         QCOMPARE(encryptionKeyWidgets.visible[2]->defaultKey(GpgME::UnknownProtocol),
                  preferredSolution.encryptionKeys.value("prefer-openpgp@example.net")[0].primaryFingerprint());
         QCOMPARE(encryptionKeyWidgets.visible[3]->property("address").toString(), "prefer-smime@example.net");
         QCOMPARE(encryptionKeyWidgets.visible[3]->defaultKey(GpgME::UnknownProtocol),
                  preferredSolution.encryptionKeys.value("prefer-smime@example.net")[0].primaryFingerprint());
         QCOMPARE(encryptionKeyWidgets.visible[4]->property("address").toString(), "unknown@example.net");
         QVERIFY(encryptionKeyWidgets.visible[4]->defaultKey(GpgME::UnknownProtocol).isEmpty());
     }
 
     void test__both_protocols_allowed__mixed_allowed__encrypt_only()
     {
         const GpgME::Protocol forcedProtocol = GpgME::UnknownProtocol;
         const bool allowMixed = true;
         const QString sender = QStringLiteral("sender@example.net");
         const KeyResolver::Solution preferredSolution = {
             GpgME::UnknownProtocol,
             {testKey("sender@example.net", GpgME::OpenPGP), testKey("sender@example.net", GpgME::CMS)},
             {
                 {QStringLiteral("prefer-openpgp@example.net"), {testKey("Full Trust <prefer-openpgp@example.net>", GpgME::OpenPGP)}},
                 {QStringLiteral("prefer-smime@example.net"), {testKey("Trusted S/MIME <prefer-smime@example.net>", GpgME::CMS)}},
                 {QStringLiteral("unknown@example.net"), {}},
                 {QStringLiteral("sender@example.net"), {testKey("sender@example.net", GpgME::OpenPGP), testKey("sender@example.net", GpgME::CMS)}}
             }
         };
         const KeyResolver::Solution alternativeSolution = {};
 
         const auto dialog = std::make_unique<NewKeyApprovalDialog>(true,
                                                                    false,
                                                                    sender,
                                                                    preferredSolution,
                                                                    alternativeSolution,
                                                                    allowMixed,
                                                                    forcedProtocol);
         dialog->show();
 
         const auto signingKeyWidgets = visibleAndHiddenWidgets(dialog->findChildren<KeySelectionCombo *>(QStringLiteral("signing key")));
         QCOMPARE(signingKeyWidgets.visible.size(), 0);
         QCOMPARE(signingKeyWidgets.hidden.size(), 0);
 
         const auto encryptionKeyWidgets = visibleAndHiddenWidgets(dialog->findChildren<KeySelectionCombo *>(QStringLiteral("encryption key")));
         QCOMPARE(encryptionKeyWidgets.visible.size(), 5);
         QCOMPARE(encryptionKeyWidgets.hidden.size(), 0);
     }
 
     void test__ok_button_shows_generate_if_generate_is_selected()
     {
         const GpgME::Protocol forcedProtocol = GpgME::UnknownProtocol;
         const bool allowMixed = true;
         const QString sender = QStringLiteral("sender@example.net");
         const KeyResolver::Solution preferredSolution = {
             GpgME::OpenPGP,
             {}, // no signing keys to get "Generate key" choice in OpenPGP combo
             {{QStringLiteral("sender@example.net"), {}}} // no encryption keys to get "Generate key" choice in OpenPGP combo
         };
         const KeyResolver::Solution alternativeSolution = {};
 
         const auto dialog = std::make_unique<NewKeyApprovalDialog>(true,
                                                                    true,
                                                                    sender,
                                                                    preferredSolution,
                                                                    alternativeSolution,
                                                                    allowMixed,
                                                                    forcedProtocol);
         dialog->show();
         waitForKeySelectionCombosBeingInitialized(dialog.get());
 
         const auto okButton = dialog->findChild<QPushButton *>("ok button");
         QVERIFY(okButton);
         QVERIFY(okButton->text() != "Generate");
 
         {
             // get the first signing key combo which is the OpenPGP one
             const auto signingKeyCombo = dialog->findChild<KeySelectionCombo *>("signing key");
             verifyWidgetVisibility(signingKeyCombo, IsVisible);
             const auto originalIndex = signingKeyCombo->currentIndex();
             const auto generateIndex = signingKeyCombo->findData(GenerateKey);
             QVERIFY(generateIndex != -1);
             signingKeyCombo->setCurrentIndex(generateIndex);
             QCOMPARE(okButton->text(), "Generate");
             signingKeyCombo->setCurrentIndex(originalIndex);
             QVERIFY(okButton->text() != "Generate");
         }
         {
             // get the first encryption key combo which is the OpenPGP one for the sender
             const auto encryptionKeyCombo = dialog->findChild<KeySelectionCombo *>("encryption key");
             verifyWidgetVisibility(encryptionKeyCombo, IsVisible);
             const auto originalIndex = encryptionKeyCombo->currentIndex();
             const auto generateIndex = encryptionKeyCombo->findData(GenerateKey);
             QVERIFY(generateIndex != -1);
             encryptionKeyCombo->setCurrentIndex(generateIndex);
             QCOMPARE(okButton->text(), "Generate");
             encryptionKeyCombo->setCurrentIndex(originalIndex);
             QVERIFY(okButton->text() != "Generate");
         }
     }
 
     void test__ok_button_does_not_show_generate_if_generate_is_selected_in_hidden_combos()
     {
         const GpgME::Protocol forcedProtocol = GpgME::UnknownProtocol;
         const bool allowMixed = true;
         const QString sender = QStringLiteral("sender@example.net");
         const KeyResolver::Solution preferredSolution = {
             GpgME::CMS, // enables S/MIME as default protocol, hides OpenPGP combos
             {}, // no signing keys to get "Generate key" choice in OpenPGP combo
             {{QStringLiteral("sender@example.net"), {}}} // no encryption keys to get "Generate key" choice in OpenPGP combo
         };
         const KeyResolver::Solution alternativeSolution = {};
 
         const auto dialog = std::make_unique<NewKeyApprovalDialog>(true,
                                                                    true,
                                                                    sender,
                                                                    preferredSolution,
                                                                    alternativeSolution,
                                                                    allowMixed,
                                                                    forcedProtocol);
         dialog->show();
         waitForKeySelectionCombosBeingInitialized(dialog.get());
 
         const auto okButton = dialog->findChild<QPushButton *>("ok button");
         QVERIFY(okButton);
         QVERIFY(okButton->text() != "Generate");
 
         {
             // get the first signing key combo which is the OpenPGP one
             const auto signingKeyCombo = dialog->findChild<KeySelectionCombo *>("signing key");
             verifyWidgetVisibility(signingKeyCombo, IsHidden);
             const auto originalIndex = signingKeyCombo->currentIndex();
             const auto generateIndex = signingKeyCombo->findData(GenerateKey);
             QVERIFY(generateIndex != -1);
             signingKeyCombo->setCurrentIndex(generateIndex);
             QVERIFY(okButton->text() != "Generate");
             signingKeyCombo->setCurrentIndex(originalIndex);
             QVERIFY(okButton->text() != "Generate");
         }
         {
             // get the first encryption key combo which is the OpenPGP one for the sender
             const auto encryptionKeyCombo = dialog->findChild<KeySelectionCombo *>("encryption key");
             verifyWidgetVisibility(encryptionKeyCombo, IsHidden);
             const auto originalIndex = encryptionKeyCombo->currentIndex();
             const auto generateIndex = encryptionKeyCombo->findData(GenerateKey);
             QVERIFY(generateIndex != -1);
             encryptionKeyCombo->setCurrentIndex(generateIndex);
             QVERIFY(okButton->text() != "Generate");
             encryptionKeyCombo->setCurrentIndex(originalIndex);
             QVERIFY(okButton->text() != "Generate");
         }
     }
 
     void test__ok_button_is_disabled_if_ignore_is_selected_in_all_visible_encryption_combos()
     {
         const GpgME::Protocol forcedProtocol = GpgME::UnknownProtocol;
         const bool allowMixed = true;
         const QString sender = QStringLiteral("sender@example.net");
         const KeyResolver::Solution preferredSolution = {
             GpgME::OpenPGP,
             {}, // no signing keys to get "Generate key" choice in OpenPGP combo
             {{QStringLiteral("sender@example.net"), {}}} // no encryption keys to get "Generate key" choice in OpenPGP combo
         };
         const KeyResolver::Solution alternativeSolution = {};
 
         const auto dialog = std::make_unique<NewKeyApprovalDialog>(true,
                                                                    true,
                                                                    sender,
                                                                    preferredSolution,
                                                                    alternativeSolution,
                                                                    allowMixed,
                                                                    forcedProtocol);
         dialog->show();
         waitForKeySelectionCombosBeingInitialized(dialog.get());
 
         const auto okButton = dialog->findChild<QPushButton *>("ok button");
         QVERIFY(okButton);
         QVERIFY(okButton->isEnabled());
 
         const auto encryptionKeyWidgets = visibleAndHiddenWidgets(dialog->findChildren<KeySelectionCombo *>(QStringLiteral("encryption key")));
         for (auto combo: encryptionKeyWidgets.visible) {
             const auto ignoreIndex = combo->findData(IgnoreKey);
             QVERIFY(ignoreIndex != -1);
             combo->setCurrentIndex(ignoreIndex);
         }
         QVERIFY(!okButton->isEnabled());
     }
 
+    void test__vs_de_compliance__all_keys_fully_valid()
+    {
+        const GpgME::Protocol forcedProtocol = GpgME::UnknownProtocol;
+        const bool allowMixed = true;
+        const QString sender = QStringLiteral("sender@example.net");
+        const KeyResolver::Solution preferredSolution = {
+            GpgME::UnknownProtocol,
+            {testKey("sender@example.net", GpgME::OpenPGP), testKey("sender@example.net", GpgME::CMS)},
+            {
+                {QStringLiteral("prefer-openpgp@example.net"), {testKey("Full Trust <prefer-openpgp@example.net>", GpgME::OpenPGP)}},
+                {QStringLiteral("prefer-smime@example.net"), {testKey("Trusted S/MIME <prefer-smime@example.net>", GpgME::CMS)}},
+                {QStringLiteral("sender@example.net"), {testKey("sender@example.net", GpgME::OpenPGP), testKey("sender@example.net", GpgME::CMS)}}
+            }
+        };
+        const KeyResolver::Solution alternativeSolution = {};
+
+        Tests::FakeCryptoConfigStringValue fakeCompliance{"gpg", "compliance", QStringLiteral("de-vs")};
+        const auto dialog = std::make_unique<NewKeyApprovalDialog>(true,
+                                                                   true,
+                                                                   sender,
+                                                                   preferredSolution,
+                                                                   alternativeSolution,
+                                                                   allowMixed,
+                                                                   forcedProtocol);
+        dialog->show();
+        waitForKeySelectionCombosBeingInitialized(dialog.get());
+
+        const auto complianceLabel = dialog->findChild<QLabel *>("compliance label");
+        verifyWidgetVisibility(complianceLabel, IsVisible);
+        QVERIFY(!complianceLabel->text().contains(" not "));
+    }
+
+    void test__vs_de_compliance__not_all_keys_fully_valid()
+    {
+        const GpgME::Protocol forcedProtocol = GpgME::UnknownProtocol;
+        const bool allowMixed = true;
+        const QString sender = QStringLiteral("sender@example.net");
+        const KeyResolver::Solution preferredSolution = {
+            GpgME::UnknownProtocol,
+            {testKey("sender@example.net", GpgME::OpenPGP), testKey("sender@example.net", GpgME::CMS)},
+            {
+                {QStringLiteral("marginal-openpgp@example.net"), {testKey("Marginal Validity <marginal-openpgp@example.net>", GpgME::OpenPGP)}},
+                {QStringLiteral("sender@example.net"), {testKey("sender@example.net", GpgME::OpenPGP), testKey("sender@example.net", GpgME::CMS)}}
+            }
+        };
+        const KeyResolver::Solution alternativeSolution = {};
+
+        Tests::FakeCryptoConfigStringValue fakeCompliance{"gpg", "compliance", QStringLiteral("de-vs")};
+        const auto dialog = std::make_unique<NewKeyApprovalDialog>(true,
+                                                                   true,
+                                                                   sender,
+                                                                   preferredSolution,
+                                                                   alternativeSolution,
+                                                                   allowMixed,
+                                                                   forcedProtocol);
+        dialog->show();
+        waitForKeySelectionCombosBeingInitialized(dialog.get());
+
+        const auto complianceLabel = dialog->findChild<QLabel *>("compliance label");
+        verifyWidgetVisibility(complianceLabel, IsVisible);
+        QVERIFY(complianceLabel->text().contains(" not "));
+    }
+
 private:
     std::shared_ptr<const KeyCache> mKeyCache;
 };
 
 QTEST_MAIN(NewKeyApprovalDialogTest)
 #include "newkeyapprovaldialogtest.moc"
diff --git a/src/ui/newkeyapprovaldialog.cpp b/src/ui/newkeyapprovaldialog.cpp
index ef1a8171..13db2128 100644
--- a/src/ui/newkeyapprovaldialog.cpp
+++ b/src/ui/newkeyapprovaldialog.cpp
@@ -1,917 +1,920 @@
 /*  -*- c++ -*-
     newkeyapprovaldialog.cpp
 
     This file is part of libkleopatra, the KDE keymanagement library
     SPDX-FileCopyrightText: 2018 Intevation GmbH
 
     SPDX-License-Identifier: GPL-2.0-or-later
 */
 
 #include "newkeyapprovaldialog.h"
 #include "kleo/defaultkeyfilter.h"
 #include "keyselectioncombo.h"
 #include "progressdialog.h"
 #include "utils/formatting.h"
 
 #include "libkleo_debug.h"
 
 #include <QApplication>
 #include <QButtonGroup>
 #include <QCheckBox>
 #include <QDesktopWidget>
 #include <QDialogButtonBox>
 #include <QGroupBox>
 #include <QLabel>
 #include <QMap>
 #include <QPushButton>
 #include <QRadioButton>
 #include <QScrollArea>
 #include <QToolTip>
 #include <QVBoxLayout>
 
 #include <QGpgME/DefaultKeyGenerationJob>
 #include <QGpgME/CryptoConfig>
 #include <QGpgME/Protocol>
 #include <gpgme++/keygenerationresult.h>
 #include <gpgme++/key.h>
 
 #include <KLocalizedString>
 #include <KMessageBox>
 
 using namespace Kleo;
 using namespace GpgME;
 
 QDebug operator<<(QDebug debug, const GpgME::Key &key)
 {
     if (key.isNull()) {
         debug << "Null";
     } else {
         debug << Formatting::summaryLine(key);
     }
     return debug.maybeSpace();
 }
 
 namespace {
 class EncryptFilter: public DefaultKeyFilter
 {
 public:
     EncryptFilter() : DefaultKeyFilter()
     {
         setCanEncrypt(DefaultKeyFilter::Set);
     }
 };
 static std::shared_ptr<KeyFilter> s_encryptFilter = std::shared_ptr<KeyFilter>(new EncryptFilter);
 
 class OpenPGPFilter: public DefaultKeyFilter
 {
 public:
     OpenPGPFilter() : DefaultKeyFilter()
     {
         setIsOpenPGP(DefaultKeyFilter::Set);
         setCanEncrypt(DefaultKeyFilter::Set);
     }
 };
 static std::shared_ptr<KeyFilter> s_pgpEncryptFilter = std::shared_ptr<KeyFilter> (new OpenPGPFilter);
 
 class OpenPGPSignFilter: public DefaultKeyFilter
 {
 public:
     OpenPGPSignFilter() : DefaultKeyFilter()
     {
         /* Also list unusable keys to make it transparent why they are unusable */
         setDisabled(DefaultKeyFilter::NotSet);
         setRevoked(DefaultKeyFilter::NotSet);
         setExpired(DefaultKeyFilter::NotSet);
         setCanSign(DefaultKeyFilter::Set);
         setHasSecret(DefaultKeyFilter::Set);
         setIsOpenPGP(DefaultKeyFilter::Set);
     }
 };
 static std::shared_ptr<KeyFilter> s_pgpSignFilter = std::shared_ptr<KeyFilter> (new OpenPGPSignFilter);
 
 class SMIMEFilter: public DefaultKeyFilter
 {
 public:
     SMIMEFilter(): DefaultKeyFilter()
     {
         setIsOpenPGP(DefaultKeyFilter::NotSet);
         setCanEncrypt(DefaultKeyFilter::Set);
     }
 };
 static std::shared_ptr<KeyFilter> s_smimeEncryptFilter = std::shared_ptr<KeyFilter> (new SMIMEFilter);
 
 class SMIMESignFilter: public DefaultKeyFilter
 {
 public:
     SMIMESignFilter(): DefaultKeyFilter()
     {
         setDisabled(DefaultKeyFilter::NotSet);
         setRevoked(DefaultKeyFilter::NotSet);
         setExpired(DefaultKeyFilter::NotSet);
         setCanSign(DefaultKeyFilter::Set);
         setIsOpenPGP(DefaultKeyFilter::NotSet);
         setHasSecret(DefaultKeyFilter::Set);
     }
 };
 static std::shared_ptr<KeyFilter> s_smimeSignFilter = std::shared_ptr<KeyFilter> (new SMIMESignFilter);
 
 /* Some decoration and a button to remove the filter for a keyselectioncombo */
 class ComboWidget: public QWidget
 {
     Q_OBJECT
 public:
     explicit ComboWidget(KeySelectionCombo *combo):
         mCombo(combo),
         mFilterBtn(new QPushButton)
     {
         auto hLay = new QHBoxLayout(this);
         auto infoBtn = new QPushButton;
         infoBtn->setIcon(QIcon::fromTheme(QStringLiteral("help-contextual")));
         infoBtn->setIconSize(QSize(22,22));
         infoBtn->setFlat(true);
         hLay->addWidget(infoBtn);
         hLay->addWidget(combo, 1);
         hLay->addWidget(mFilterBtn, 0);
 
         connect(infoBtn, &QPushButton::clicked, this, [this, infoBtn] () {
             QToolTip::showText(infoBtn->mapToGlobal(QPoint()) + QPoint(infoBtn->width(), 0),
                     mCombo->currentData(Qt::ToolTipRole).toString(), infoBtn, QRect(), 30000);
         });
 
         // FIXME: This is ugly to enforce but otherwise the
         // icon is broken.
         combo->setMinimumHeight(22);
         mFilterBtn->setMinimumHeight(23);
 
         updateFilterButton();
 
         connect(mFilterBtn, &QPushButton::clicked, this, [this] () {
             const QString curFilter = mCombo->idFilter();
             if (curFilter.isEmpty()) {
                 setIdFilter(mLastIdFilter);
                 mLastIdFilter = QString();
             } else {
                 setIdFilter(QString());
                 mLastIdFilter = curFilter;
             }
         });
     }
 
     void setIdFilter(const QString &id)
     {
         mCombo->setIdFilter(id);
         updateFilterButton();
     }
 
     void updateFilterButton()
     {
         if (mCombo->idFilter().isEmpty()) {
             mFilterBtn->setIcon(QIcon::fromTheme(QStringLiteral("kt-add-filters")));
             mFilterBtn->setToolTip(i18n("Show keys matching the email address"));
         } else {
             mFilterBtn->setIcon(QIcon::fromTheme(QStringLiteral("kt-remove-filters")));
             mFilterBtn->setToolTip(i18n("Show all keys"));
         }
     }
 
     KeySelectionCombo *combo()
     {
         return mCombo;
     }
 
     GpgME::Protocol fixedProtocol() const
     {
         return mFixedProtocol;
     }
 
     void setFixedProtocol(GpgME::Protocol proto)
     {
         mFixedProtocol = proto;
     }
 
 private:
     KeySelectionCombo *mCombo;
     QPushButton *mFilterBtn;
     QString mLastIdFilter;
     GpgME::Protocol mFixedProtocol = GpgME::UnknownProtocol;
 };
 
 static enum GpgME::UserID::Validity keyValidity(const GpgME::Key &key)
 {
     enum GpgME::UserID::Validity validity = GpgME::UserID::Validity::Unknown;
 
     for (const auto &uid: key.userIDs()) {
         if (validity == GpgME::UserID::Validity::Unknown
             || validity > uid.validity()) {
             validity = uid.validity();
         }
     }
 
     return validity;
 }
 
 static bool key_has_addr(const GpgME::Key &key, const QString &addr)
 {
     for (const auto &uid: key.userIDs()) {
         if (QString::fromStdString(uid.addrSpec()).toLower() == addr.toLower()) {
             return true;
         }
     }
     return false;
 }
 
 bool anyKeyHasProtocol(const std::vector<Key> &keys, Protocol protocol)
 {
     return std::any_of(std::begin(keys), std::end(keys), [protocol] (const auto &key) { return key.protocol() == protocol; });
 }
 
 Key findfirstKeyOfType(const std::vector<Key> &keys, Protocol protocol)
 {
     const auto it = std::find_if(std::begin(keys), std::end(keys), [protocol] (const auto &key) { return key.protocol() == protocol; });
     return it != std::end(keys) ? *it : Key();
 }
 
 } // namespace
 
 class NewKeyApprovalDialog::Private
 {
 private:
     enum Action {
         Unset,
         GenerateKey,
         IgnoreKey,
     };
 public:
     enum {
         OpenPGPButtonId = 1,
         SMIMEButtonId = 2
     };
 
     Private(NewKeyApprovalDialog *qq,
             bool encrypt,
             bool sign,
             GpgME::Protocol forcedProtocol,
             GpgME::Protocol presetProtocol,
             const QString &sender,
             bool allowMixed)
         : mForcedProtocol{forcedProtocol}
         , mSender{sender}
         , mSign{sign}
         , mEncrypt{encrypt}
         , mAllowMixed{allowMixed}
         , q{qq}
     {
         Q_ASSERT(forcedProtocol == GpgME::UnknownProtocol || presetProtocol == GpgME::UnknownProtocol || presetProtocol == forcedProtocol);
         Q_ASSERT(!allowMixed || (allowMixed && forcedProtocol == GpgME::UnknownProtocol));
         Q_ASSERT(!(!allowMixed && presetProtocol == GpgME::UnknownProtocol));
 
         // We do the translation here to avoid having the same string multiple times.
         mGenerateTooltip = i18nc("@info:tooltip for a 'Generate new key pair' action "
                                  "in a combobox when a user does not yet have an OpenPGP or S/MIME key.",
                                  "Generate a new key using your E-Mail address.<br/><br/>"
                                  "The key is necessary to decrypt and sign E-Mails. "
                                  "You will be asked for a passphrase to protect this key and the protected key "
                                  "will be stored in your home directory.");
         mMainLay = new QVBoxLayout;
 
         QDialogButtonBox *btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
         mOkButton = btnBox->button(QDialogButtonBox::Ok);
 #ifndef NDEBUG
         mOkButton->setObjectName(QStringLiteral("ok button"));
 #endif
         QObject::connect (btnBox, &QDialogButtonBox::accepted, q, [this] () {
                 accepted();
             });
         QObject::connect (btnBox, &QDialogButtonBox::rejected, q, &QDialog::reject);
 
         mScrollArea = new QScrollArea;
         mScrollArea->setWidget(new QWidget);
         mScrollLayout = new QVBoxLayout;
         mScrollArea->widget()->setLayout(mScrollLayout);
         mScrollArea->setWidgetResizable(true);
         mScrollArea->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContentsOnFirstShow);
         mScrollArea->setFrameStyle(QFrame::NoFrame);
         mScrollLayout->setContentsMargins(0, 0, 0, 0);
 
         q->setWindowTitle(i18nc("@title:window", "Security approval"));
 
         auto fmtLayout = new QHBoxLayout;
         mFormatBtns = new QButtonGroup;
         QAbstractButton *pgpBtn;
         QAbstractButton *smimeBtn;
         if (mAllowMixed) {
             pgpBtn = new QCheckBox(i18n("OpenPGP"));
             smimeBtn = new QCheckBox(i18n("S/MIME"));
         } else {
             pgpBtn = new QRadioButton(i18n("OpenPGP"));
             smimeBtn = new QRadioButton(i18n("S/MIME"));
         }
 #ifndef NDEBUG
         pgpBtn->setObjectName(QStringLiteral("openpgp button"));
         smimeBtn->setObjectName(QStringLiteral("smime button"));
 #endif
         mFormatBtns->addButton(pgpBtn, OpenPGPButtonId);
         mFormatBtns->addButton(smimeBtn, SMIMEButtonId);
         mFormatBtns->setExclusive(!mAllowMixed);
 
         fmtLayout->addStretch(-1);
         fmtLayout->addWidget(pgpBtn);
         fmtLayout->addWidget(smimeBtn);
         mMainLay->addLayout(fmtLayout);
 
         if (mForcedProtocol != GpgME::UnknownProtocol) {
             pgpBtn->setChecked(mForcedProtocol == GpgME::OpenPGP);
             smimeBtn->setChecked(mForcedProtocol == GpgME::CMS);
             pgpBtn->setVisible(false);
             smimeBtn->setVisible(false);
         } else {
             pgpBtn->setChecked(presetProtocol == GpgME::OpenPGP || presetProtocol == GpgME::UnknownProtocol);
             smimeBtn->setChecked(presetProtocol == GpgME::CMS || presetProtocol == GpgME::UnknownProtocol);
         }
 
         QObject::connect(mFormatBtns, &QButtonGroup::idClicked,
                          q, [this](int buttonId) {
                              // ensure that at least one protocol button is checked
                              if (mAllowMixed
                                     && !mFormatBtns->button(OpenPGPButtonId)->isChecked()
                                     && !mFormatBtns->button(SMIMEButtonId)->isChecked()) {
                                  mFormatBtns->button(buttonId == OpenPGPButtonId ? SMIMEButtonId : OpenPGPButtonId)->setChecked(true);
                              }
                              updateWidgets();
                         });
 
         mMainLay->addWidget(mScrollArea);
 
         mComplianceLbl = new QLabel;
         mComplianceLbl->setVisible(false);
+#ifndef NDEBUG
+        mComplianceLbl->setObjectName(QStringLiteral("compliance label"));
+#endif
 
         auto btnLayout = new QHBoxLayout;
         btnLayout->addWidget(mComplianceLbl);
         btnLayout->addWidget(btnBox);
         mMainLay->addLayout(btnLayout);
 
         q->setLayout(mMainLay);
     }
 
     ~Private() = default;
 
     Protocol currentProtocol()
     {
         const bool openPGPButtonChecked = mFormatBtns->button(OpenPGPButtonId)->isChecked();
         const bool smimeButtonChecked = mFormatBtns->button(SMIMEButtonId)->isChecked();
         if (mAllowMixed) {
             if (openPGPButtonChecked && !smimeButtonChecked) {
                 return OpenPGP;
             }
             if (!openPGPButtonChecked && smimeButtonChecked) {
                 return CMS;
             }
         } else if (openPGPButtonChecked) {
             return OpenPGP;
         } else if (smimeButtonChecked) {
             return CMS;
         }
         return UnknownProtocol;
     }
 
     auto findVisibleKeySelectionComboWithGenerateKey()
     {
         const auto it = std::find_if(std::begin(mAllCombos), std::end(mAllCombos),
                                      [] (auto combo) {
                                          return combo->isVisible()
                                              && combo->currentData(Qt::UserRole).toInt() == GenerateKey;
                                      });
         return it != std::end(mAllCombos) ? *it : nullptr;
     }
 
     void generateKey(KeySelectionCombo *combo)
     {
         const auto &addr = combo->property("address").toString();
         auto job = new QGpgME::DefaultKeyGenerationJob(q);
         auto progress = new Kleo::ProgressDialog(job, i18n("Generating key for '%1'...", addr) + QStringLiteral("\n\n") +
                                                  i18n("This can take several minutes."), q);
         progress->setWindowFlags(progress->windowFlags() & ~Qt::WindowContextHelpButtonHint);
         progress->setWindowTitle(i18nc("@title:window", "Key generation"));
         progress->setModal(true);
         progress->setAutoClose(true);
         progress->setMinimumDuration(0);
         progress->setValue(0);
 
         mRunningJobs << job;
         connect (job, &QGpgME::DefaultKeyGenerationJob::result, q,
             [this, job, combo] (const GpgME::KeyGenerationResult &result) {
                 handleKeyGenResult(result, job, combo);
             });
         job->start(addr, QString());
         return;
     }
 
     void handleKeyGenResult(const GpgME::KeyGenerationResult &result, QGpgME::Job *job, KeySelectionCombo *combo)
     {
         mLastError = result.error();
         if (!mLastError || mLastError.isCanceled()) {
             combo->setDefaultKey(QString::fromLatin1(result.fingerprint()), GpgME::OpenPGP);
             connect (combo, &KeySelectionCombo::keyListingFinished, q, [this, job] () {
                     mRunningJobs.removeAll(job);
                 });
             combo->refreshKeys();
         } else {
             mRunningJobs.removeAll(job);
         }
     }
 
     void checkAccepted()
     {
         if (mLastError || mLastError.isCanceled()) {
             KMessageBox::error(q, QString::fromLocal8Bit(mLastError.asString()), i18n("Operation Failed"));
             mRunningJobs.clear();
             return;
         }
 
         if (!mRunningJobs.empty()) {
             return;
         }
         /* Save the keys */
         const Protocol protocol = currentProtocol();
 
         mAcceptedResult.encryptionKeys.clear();
         mAcceptedResult.signingKeys.clear();
 
         for (const auto combo: qAsConst(mEncCombos)) {
             const auto &addr = combo->property("address").toString();
             const auto &key = combo->currentKey();
             if (!combo->isVisible()) {
                 continue;
             }
             if (protocol != UnknownProtocol && key.protocol() != protocol) {
                 continue;
             }
             mAcceptedResult.encryptionKeys[addr].push_back(key);
         }
         for (const auto combo: qAsConst(mSigningCombos)) {
             const auto key = combo->currentKey();
             if (!combo->isVisible()) {
                 continue;
             }
             if (protocol != UnknownProtocol && key.protocol() != protocol) {
                 continue;
             }
             mAcceptedResult.signingKeys.push_back(key);
         }
 
         q->accept();
     }
 
     void accepted()
     {
         // We can assume everything was validly resolved, otherwise
         // the OK button would have been disabled.
         // Handle custom items now.
         if (auto combo = findVisibleKeySelectionComboWithGenerateKey()) {
             generateKey(combo);
             return;
         }
         checkAccepted();
     }
 
     auto encryptionKeyFilter(Protocol protocol)
     {
         switch (protocol) {
         case OpenPGP:
             return s_pgpEncryptFilter;
         case CMS:
             return s_smimeEncryptFilter;
         default:
             return s_encryptFilter;
         }
     }
 
     void updateWidgets()
     {
         const Protocol protocol = currentProtocol();
         const auto encryptionFilter = encryptionKeyFilter(protocol);
 
         for (auto combo: qAsConst(mSigningCombos)) {
             auto widget = qobject_cast<ComboWidget *>(combo->parentWidget());
             if (!widget) {
                 qCDebug(LIBKLEO_LOG) << "Failed to find signature combo widget";
                 continue;
             }
             widget->setVisible(protocol == UnknownProtocol || widget->fixedProtocol() == UnknownProtocol || widget->fixedProtocol() == protocol);
         }
         for (auto combo: qAsConst(mEncCombos)) {
             auto widget = qobject_cast<ComboWidget *>(combo->parentWidget());
             if (!widget) {
                 qCDebug(LIBKLEO_LOG) << "Failed to find combo widget";
                 continue;
             }
             widget->setVisible(protocol == UnknownProtocol || widget->fixedProtocol() == UnknownProtocol || widget->fixedProtocol() == protocol);
             if (widget->isVisible() && combo->property("address") != mSender) {
                 combo->setKeyFilter(encryptionFilter);
             }
         }
         // hide the labels indicating the protocol of the sender's keys if only a single protocol is active
         const auto protocolLabels = q->findChildren<QLabel *>(QStringLiteral("protocol label"));
         for (auto label: protocolLabels) {
             label->setVisible(protocol == UnknownProtocol);
         }
     }
 
     auto createProtocolLabel(Protocol protocol)
     {
         auto label = new QLabel(Formatting::displayName(protocol));
         label->setObjectName(QStringLiteral("protocol label"));
         return label;
     }
 
     ComboWidget *createSigningCombo(const QString &addr, const GpgME::Key &key, Protocol protocol = UnknownProtocol)
     {
         Q_ASSERT(!key.isNull() || protocol != UnknownProtocol);
         protocol = !key.isNull() ? key.protocol() : protocol;
 
         auto combo = new KeySelectionCombo();
         auto comboWidget = new ComboWidget(combo);
 #ifndef NDEBUG
         combo->setObjectName(QStringLiteral("signing key"));
 #endif
         if (protocol == GpgME::OpenPGP) {
             combo->setKeyFilter(s_pgpSignFilter);
         } else if (protocol == GpgME::CMS) {
             combo->setKeyFilter(s_smimeSignFilter);
         }
         if (key.isNull() || key_has_addr(key, mSender)) {
             comboWidget->setIdFilter(mSender);
         }
         comboWidget->setFixedProtocol(protocol);
         if (!key.isNull()) {
             combo->setDefaultKey(QString::fromLatin1(key.primaryFingerprint()), protocol);
         }
         if (key.isNull() && protocol == OpenPGP) {
             combo->appendCustomItem(QIcon::fromTheme(QStringLiteral("document-new")),
                                     i18n("Generate a new key pair"), GenerateKey,
                                     mGenerateTooltip);
         }
         combo->appendCustomItem(QIcon::fromTheme(QStringLiteral("emblem-unavailable")),
                 i18n("Don't confirm identity and integrity"), IgnoreKey,
                 i18nc("@info:tooltip for not selecting a key for signing.",
                       "The E-Mail will not be cryptographically signed."));
 
         mSigningCombos << combo;
         mAllCombos << combo;
         combo->setProperty("address", addr);
 
         connect(combo, &KeySelectionCombo::currentKeyChanged, q, [this] () {
             updateOkButton();
         });
         connect(combo, QOverload<int>::of(&QComboBox::currentIndexChanged), q, [this] () {
             updateOkButton();
         });
 
         return comboWidget;
     }
 
     void setSigningKeys(std::vector<GpgME::Key> preferredKeys, std::vector<GpgME::Key> alternativeKeys)
     {
         auto group = new QGroupBox(i18nc("Caption for signing key selection", "Confirm identity '%1' as:", mSender));
         group->setAlignment(Qt::AlignLeft);
         auto sigLayout = new QVBoxLayout(group);
 
         const bool mayNeedOpenPGP = mForcedProtocol != CMS;
         const bool mayNeedCMS = mForcedProtocol != OpenPGP;
         if (mayNeedOpenPGP) {
             if (mAllowMixed) {
                 sigLayout->addWidget(createProtocolLabel(OpenPGP));
             }
             const Key preferredKey = findfirstKeyOfType(preferredKeys, OpenPGP);
             const Key alternativeKey = findfirstKeyOfType(alternativeKeys, OpenPGP);
             if (!preferredKey.isNull()) {
                 qCDebug(LIBKLEO_LOG) << "setSigningKeys - creating signing combo for" << preferredKey;
                 auto comboWidget = createSigningCombo(mSender, preferredKey);
                 sigLayout->addWidget(comboWidget);
             } else if (!alternativeKey.isNull()) {
                 qCDebug(LIBKLEO_LOG) << "setSigningKeys - creating signing combo for" << alternativeKey;
                 auto comboWidget = createSigningCombo(mSender, alternativeKey);
                 sigLayout->addWidget(comboWidget);
             } else {
                 qCDebug(LIBKLEO_LOG) << "setSigningKeys - creating signing combo for OpenPGP key";
                 auto comboWidget = createSigningCombo(mSender, Key(), OpenPGP);
                 sigLayout->addWidget(comboWidget);
             }
         }
         if (mayNeedCMS) {
             if (mAllowMixed) {
                 sigLayout->addWidget(createProtocolLabel(CMS));
             }
             const Key preferredKey = findfirstKeyOfType(preferredKeys, CMS);
             const Key alternativeKey = findfirstKeyOfType(alternativeKeys, CMS);
             if (!preferredKey.isNull()) {
                 qCDebug(LIBKLEO_LOG) << "setSigningKeys - creating signing combo for" << preferredKey;
                 auto comboWidget = createSigningCombo(mSender, preferredKey);
                 sigLayout->addWidget(comboWidget);
             } else if (!alternativeKey.isNull()) {
                 qCDebug(LIBKLEO_LOG) << "setSigningKeys - creating signing combo for" << alternativeKey;
                 auto comboWidget = createSigningCombo(mSender, alternativeKey);
                 sigLayout->addWidget(comboWidget);
             } else {
                 qCDebug(LIBKLEO_LOG) << "setSigningKeys - creating signing combo for S/MIME key";
                 auto comboWidget = createSigningCombo(mSender, Key(), CMS);
                 sigLayout->addWidget(comboWidget);
             }
         }
 
         mScrollLayout->addWidget(group);
     }
 
     ComboWidget *createEncryptionCombo(const QString &addr, const GpgME::Key &key, Protocol fixedProtocol)
     {
         auto combo = new KeySelectionCombo(false);
         auto comboWidget = new ComboWidget(combo);
 #ifndef NDEBUG
         combo->setObjectName(QStringLiteral("encryption key"));
 #endif
         if (fixedProtocol == GpgME::OpenPGP) {
             combo->setKeyFilter(s_pgpEncryptFilter);
         } else if (fixedProtocol == GpgME::CMS) {
             combo->setKeyFilter(s_smimeEncryptFilter);
         } else {
             combo->setKeyFilter(s_encryptFilter);
         }
         if (key.isNull() || key_has_addr (key, addr)) {
             comboWidget->setIdFilter(addr);
         }
         comboWidget->setFixedProtocol(fixedProtocol);
         if (!key.isNull()) {
             combo->setDefaultKey(QString::fromLatin1(key.primaryFingerprint()), fixedProtocol);
         }
 
         if (addr == mSender && key.isNull() && fixedProtocol == OpenPGP) {
             combo->appendCustomItem(QIcon::fromTheme(QStringLiteral("document-new")),
                                     i18n("Generate a new key pair"), GenerateKey,
                                     mGenerateTooltip);
         }
 
         combo->appendCustomItem(QIcon::fromTheme(QStringLiteral("emblem-unavailable")),
                 i18n("No key. Recipient will be unable to decrypt."), IgnoreKey,
                 i18nc("@info:tooltip for No Key selected for a specific recipient.",
                         "Do not select a key for this recipient.<br/><br/>"
                         "The recipient will receive the encrypted E-Mail, but it can only "
                         "be decrypted with the other keys selected in this dialog."));
 
         connect(combo, &KeySelectionCombo::currentKeyChanged, q, [this] () {
             updateOkButton();
         });
         connect(combo, QOverload<int>::of(&QComboBox::currentIndexChanged), q, [this] () {
             updateOkButton();
         });
 
         mEncCombos << combo;
         mAllCombos << combo;
         combo->setProperty("address", addr);
         return comboWidget;
     }
 
     void addEncryptionAddr(const QString &addr,
                            Protocol preferredKeysProtocol, const std::vector<GpgME::Key> &preferredKeys,
                            Protocol alternativeKeysProtocol, const std::vector<GpgME::Key> &alternativeKeys,
                            QGridLayout *encGrid)
     {
         if (addr == mSender) {
             const bool mayNeedOpenPGP = mForcedProtocol != CMS;
             const bool mayNeedCMS = mForcedProtocol != OpenPGP;
             if (mayNeedOpenPGP) {
                 if (mAllowMixed) {
                     encGrid->addWidget(createProtocolLabel(OpenPGP), encGrid->rowCount(), 0);
                 }
                 for (const auto &key : preferredKeys) {
                     if (key.protocol() == OpenPGP) {
                         qCDebug(LIBKLEO_LOG) << "setEncryptionKeys -" << addr << "- creating encryption combo for" << key;
                         auto comboWidget = createEncryptionCombo(addr, key, OpenPGP);
                         encGrid->addWidget(comboWidget, encGrid->rowCount(), 0, 1, 2);
                     }
                 }
                 for (const auto &key : alternativeKeys) {
                     if (key.protocol() == OpenPGP) {
                         qCDebug(LIBKLEO_LOG) << "setEncryptionKeys -" << addr << "- creating encryption combo for" << key;
                         auto comboWidget = createEncryptionCombo(addr, key, OpenPGP);
                         encGrid->addWidget(comboWidget, encGrid->rowCount(), 0, 1, 2);
                     }
                 }
                 if (!anyKeyHasProtocol(preferredKeys, OpenPGP) && !anyKeyHasProtocol(alternativeKeys, OpenPGP)) {
                     qCDebug(LIBKLEO_LOG) << "setEncryptionKeys -" << addr << "- creating encryption combo for OpenPGP key";
                     auto comboWidget = createEncryptionCombo(addr, GpgME::Key(), OpenPGP);
                     encGrid->addWidget(comboWidget, encGrid->rowCount(), 0, 1, 2);
                 }
             }
             if (mayNeedCMS) {
                 if (mAllowMixed) {
                     encGrid->addWidget(createProtocolLabel(CMS), encGrid->rowCount(), 0);
                 }
                 for (const auto &key : preferredKeys) {
                     if (key.protocol() == CMS) {
                         qCDebug(LIBKLEO_LOG) << "setEncryptionKeys -" << addr << "- creating encryption combo for" << key;
                         auto comboWidget = createEncryptionCombo(addr, key, CMS);
                         encGrid->addWidget(comboWidget, encGrid->rowCount(), 0, 1, 2);
                     }
                 }
                 for (const auto &key : alternativeKeys) {
                     if (key.protocol() == CMS) {
                         qCDebug(LIBKLEO_LOG) << "setEncryptionKeys -" << addr << "- creating encryption combo for" << key;
                         auto comboWidget = createEncryptionCombo(addr, key, CMS);
                         encGrid->addWidget(comboWidget, encGrid->rowCount(), 0, 1, 2);
                     }
                 }
                 if (!anyKeyHasProtocol(preferredKeys, CMS) && !anyKeyHasProtocol(alternativeKeys, CMS)) {
                     qCDebug(LIBKLEO_LOG) << "setEncryptionKeys -" << addr << "- creating encryption combo for S/MIME key";
                     auto comboWidget = createEncryptionCombo(addr, GpgME::Key(), CMS);
                     encGrid->addWidget(comboWidget, encGrid->rowCount(), 0, 1, 2);
                 }
             }
         } else {
             encGrid->addWidget(new QLabel(addr), encGrid->rowCount(), 0);
 
             for (const auto &key : preferredKeys) {
                 qCDebug(LIBKLEO_LOG) << "setEncryptionKeys -" << addr << "- creating encryption combo for" << key;
                 auto comboWidget = createEncryptionCombo(addr, key, preferredKeysProtocol);
                 encGrid->addWidget(comboWidget, encGrid->rowCount(), 0, 1, 2);
             }
             for (const auto &key : alternativeKeys) {
                 qCDebug(LIBKLEO_LOG) << "setEncryptionKeys -" << addr << "- creating encryption combo for" << key;
                 auto comboWidget = createEncryptionCombo(addr, key, alternativeKeysProtocol);
                 encGrid->addWidget(comboWidget, encGrid->rowCount(), 0, 1, 2);
             }
             if (!mAllowMixed) {
                 if (preferredKeys.empty()) {
                     qCDebug(LIBKLEO_LOG) << "setEncryptionKeys -" << addr << "- creating encryption combo for" << Formatting::displayName(preferredKeysProtocol) << "key";
                     auto comboWidget = createEncryptionCombo(addr, GpgME::Key(), preferredKeysProtocol);
                     encGrid->addWidget(comboWidget, encGrid->rowCount(), 0, 1, 2);
                 }
                 if (alternativeKeys.empty() && alternativeKeysProtocol != UnknownProtocol) {
                     qCDebug(LIBKLEO_LOG) << "setEncryptionKeys -" << addr << "- creating encryption combo for" << Formatting::displayName(alternativeKeysProtocol) << "key";
                     auto comboWidget = createEncryptionCombo(addr, GpgME::Key(), alternativeKeysProtocol);
                     encGrid->addWidget(comboWidget, encGrid->rowCount(), 0, 1, 2);
                 }
             } else {
                 if (preferredKeys.empty() && alternativeKeys.empty()) {
                     qCDebug(LIBKLEO_LOG) << "setEncryptionKeys -" << addr << "- creating encryption combo for any key";
                     auto comboWidget = createEncryptionCombo(addr, GpgME::Key(), UnknownProtocol);
                     encGrid->addWidget(comboWidget, encGrid->rowCount(), 0, 1, 2);
                 }
             }
         }
     }
 
     void setEncryptionKeys(Protocol preferredKeysProtocol, const QMap<QString, std::vector<GpgME::Key>> &preferredKeys,
                            Protocol alternativeKeysProtocol, const QMap<QString, std::vector<GpgME::Key>> &alternativeKeys)
     {
         {
             auto group = new QGroupBox(i18nc("Encrypt to self (email address):", "Encrypt to self (%1):", mSender));
             group->setAlignment(Qt::AlignLeft);
             auto encGrid = new QGridLayout(group);
 
             addEncryptionAddr(mSender, preferredKeysProtocol, preferredKeys.value(mSender), alternativeKeysProtocol, alternativeKeys.value(mSender), encGrid);
 
             mScrollLayout->addWidget(group);
         }
 
         auto group = new QGroupBox(i18n("Encrypt to others:"));
         group->setAlignment(Qt::AlignLeft);
         auto encGrid = new QGridLayout;
         group->setLayout(encGrid);
         mScrollLayout->addWidget(group);
 
         for (auto it = std::begin(preferredKeys); it != std::end(preferredKeys); ++it) {
             const auto &address = it.key();
             const auto &keys = it.value();
             if (address != mSender) {
                 addEncryptionAddr(address, preferredKeysProtocol, keys, alternativeKeysProtocol, alternativeKeys.value(address), encGrid);
             }
         }
 
         encGrid->setColumnStretch(1, -1);
         mScrollLayout->addStretch(-1);
     }
 
     void updateOkButton()
     {
         static QString origOkText = mOkButton->text();
         const bool isGenerate = bool(findVisibleKeySelectionComboWithGenerateKey());
         const bool allVisibleEncryptionKeysAreIgnored = std::all_of(std::begin(mEncCombos), std::end(mEncCombos),
                                                                     [] (auto combo) {
                                                                         return !combo->isVisible()
                                                                             || combo->currentData(Qt::UserRole).toInt() == IgnoreKey;
                                                                     });
 
         // If we don't encrypt the ok button is always enabled. But otherwise
         // we only enable it if we encrypt to at least one recipient.
         mOkButton->setEnabled(!mEncrypt || !allVisibleEncryptionKeysAreIgnored);
 
         mOkButton->setText(isGenerate ? i18n("Generate") : origOkText);
 
         if (Formatting::complianceMode() != QLatin1String("de-vs")) {
             return;
         }
 
         // Handle compliance
         bool de_vs = true;
 
         const Protocol protocol = currentProtocol();
 
         for (const auto combo: qAsConst(mEncCombos)) {
             const auto &key = combo->currentKey();
             if (!combo->isVisible()) {
                 continue;
             }
             if (protocol != UnknownProtocol && key.protocol() != protocol) {
                 continue;
             }
             if (!Formatting::isKeyDeVs(key) || keyValidity(key) < GpgME::UserID::Validity::Full) {
                 de_vs = false;
                 break;
             }
         }
         if (de_vs) {
             for (const auto combo: qAsConst(mSigningCombos)) {
                 const auto key = combo->currentKey();
                 if (!combo->isVisible()) {
                     continue;
                 }
                 if (protocol != UnknownProtocol && key.protocol() != protocol) {
                     continue;
                 }
                 if (!Formatting::isKeyDeVs(key) || keyValidity(key) < GpgME::UserID::Validity::Full) {
                     de_vs = false;
                     break;
                 }
             }
         }
 
         mOkButton->setIcon(QIcon::fromTheme(de_vs
                     ? QStringLiteral("security-high")
                     : QStringLiteral("security-medium")));
         mOkButton->setStyleSheet(QStringLiteral("background-color: ") + (de_vs
                     ? QStringLiteral("#D5FAE2")  // KColorScheme(QPalette::Active, KColorScheme::View).background(KColorScheme::PositiveBackground).color().name()
                     : QStringLiteral("#FAE9EB"))); //KColorScheme(QPalette::Active, KColorScheme::View).background(KColorScheme::NegativeBackground).color().name()));
         mComplianceLbl->setText(de_vs
                 ? i18nc("%1 is a placeholder for the name of a compliance mode. E.g. NATO RESTRICTED compliant or VS-NfD compliant",
                     "%1 communication possible.", Formatting::deVsString())
                 : i18nc("%1 is a placeholder for the name of a compliance mode. E.g. NATO RESTRICTED compliant or VS-NfD compliant",
                     "%1 communication not possible.", Formatting::deVsString()));
         mComplianceLbl->setVisible(true);
     }
 
     GpgME::Protocol mForcedProtocol;
     QList<KeySelectionCombo *> mSigningCombos;
     QList<KeySelectionCombo *> mEncCombos;
     QList<KeySelectionCombo *> mAllCombos;
     QScrollArea *mScrollArea;
     QVBoxLayout *mScrollLayout;
     QPushButton *mOkButton;
     QVBoxLayout *mMainLay;
     QButtonGroup *mFormatBtns;
     QString mSender;
     bool mSign;
     bool mEncrypt;
     bool mAllowMixed;
     NewKeyApprovalDialog *q;
     QList <QGpgME::Job *> mRunningJobs;
     GpgME::Error mLastError;
     QLabel *mComplianceLbl;
     KeyResolver::Solution mAcceptedResult;
     QString mGenerateTooltip;
 };
 
 NewKeyApprovalDialog::NewKeyApprovalDialog(bool encrypt,
                                            bool sign,
                                            const QString &sender,
                                            KeyResolver::Solution preferredSolution,
                                            KeyResolver::Solution alternativeSolution,
                                            bool allowMixed,
                                            GpgME::Protocol forcedProtocol,
                                            QWidget *parent,
                                            Qt::WindowFlags f)
     : QDialog(parent, f)
     , d{std::make_unique<Private>(this, encrypt, sign, forcedProtocol, preferredSolution.protocol, sender, allowMixed)}
 {
     if (sign) {
         d->setSigningKeys(std::move(preferredSolution.signingKeys), std::move(alternativeSolution.signingKeys));
     }
     if (encrypt) {
         d->setEncryptionKeys(allowMixed ? UnknownProtocol : preferredSolution.protocol, std::move(preferredSolution.encryptionKeys),
                              allowMixed ? UnknownProtocol : alternativeSolution.protocol, std::move(alternativeSolution.encryptionKeys));
     }
     d->updateWidgets();
     d->updateOkButton();
 
     const auto size = sizeHint();
     const auto desk = QApplication::desktop()->screenGeometry(this);
     resize(QSize(desk.width() / 3, qMin(size.height(), desk.height() / 2)));
 }
 
 Kleo::NewKeyApprovalDialog::~NewKeyApprovalDialog() = default;
 
 KeyResolver::Solution NewKeyApprovalDialog::result()
 {
     return d->mAcceptedResult;
 }
 
 #include "newkeyapprovaldialog.moc"
diff --git a/src/utils/formatting.cpp b/src/utils/formatting.cpp
index a0061ffa..0d455403 100644
--- a/src/utils/formatting.cpp
+++ b/src/utils/formatting.cpp
@@ -1,1212 +1,1205 @@
 /* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; -*-
 
     utils/formatting.cpp
 
     This file is part of Kleopatra, the KDE keymanager
     SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB
 
     SPDX-License-Identifier: GPL-2.0-or-later
 */
 
 #include "formatting.h"
 #include "kleo/dn.h"
 #include "kleo/keyfiltermanager.h"
 #include "kleo/keygroup.h"
 
-#include "utils/compat.h"
+#include "utils/cryptoconfig.h"
 
 #include <gpgme++/key.h>
 #include <gpgme++/importresult.h>
 
 #include <QGpgME/CryptoConfig>
 #include <QGpgME/Protocol>
 
 #include <KLocalizedString>
 #include <KEmailAddress>
 
 #include <QString>
 #include <QStringList>
 #include <QDateTime>
 #include <QTextDocument> // for Qt::escape
 #include <QLocale>
 #include <QIcon>
 #include <QRegularExpression>
 
 #include "models/keycache.h"
 
 using namespace GpgME;
 using namespace Kleo;
 
 //
 // Name
 //
 
 QString Formatting::prettyName(int proto, const char *id, const char *name_, const char *comment_)
 {
 
     if (proto == OpenPGP) {
         const QString name = QString::fromUtf8(name_);
         if (name.isEmpty()) {
             return QString();
         }
         const QString comment = QString::fromUtf8(comment_);
         if (comment.isEmpty()) {
             return name;
         }
         return QStringLiteral("%1 (%2)").arg(name, comment);
     }
 
     if (proto == CMS) {
         const DN subject(id);
         const QString cn = subject[QStringLiteral("CN")].trimmed();
         if (cn.isEmpty()) {
             return subject.prettyDN();
         }
         return cn;
     }
 
     return QString();
 }
 
 QString Formatting::prettyNameAndEMail(int proto, const char *id, const char *name_, const char *email_, const char *comment_)
 {
     return prettyNameAndEMail(proto, QString::fromUtf8(id), QString::fromUtf8(name_), prettyEMail(email_, id), QString::fromUtf8(comment_));
 }
 
 QString Formatting::prettyNameAndEMail(int proto, const QString &id, const QString &name, const QString &email, const QString &comment)
 {
 
     if (proto == OpenPGP) {
         if (name.isEmpty()) {
             if (email.isEmpty()) {
                 return QString();
             } else if (comment.isEmpty()) {
                 return QStringLiteral("<%1>").arg(email);
             } else {
                 return QStringLiteral("(%2) <%1>").arg(email, comment);
             }
         }
         if (email.isEmpty()) {
             if (comment.isEmpty()) {
                 return name;
             } else {
                 return QStringLiteral("%1 (%2)").arg(name, comment);
             }
         }
         if (comment.isEmpty()) {
             return QStringLiteral("%1 <%2>").arg(name, email);
         } else {
             return QStringLiteral("%1 (%3) <%2>").arg(name, email, comment);
         }
     }
 
     if (proto == CMS) {
         const DN subject(id);
         const QString cn = subject[QStringLiteral("CN")].trimmed();
         if (cn.isEmpty()) {
             return subject.prettyDN();
         }
         return cn;
     }
     return QString();
 }
 
 QString Formatting::prettyUserID(const UserID &uid)
 {
     if (uid.parent().protocol() == OpenPGP) {
         return prettyNameAndEMail(uid);
     }
     const QByteArray id = QByteArray(uid.id()).trimmed();
     if (id.startsWith('<')) {
         return prettyEMail(uid.email(), uid.id());
     }
     if (id.startsWith('('))
         // ### parse uri/dns:
     {
         return QString::fromUtf8(uid.id());
     } else {
         return DN(uid.id()).prettyDN();
     }
 }
 
 QString Formatting::prettyKeyID(const char *id)
 {
     if (!id) {
         return QString();
     }
     return QLatin1String("0x") + QString::fromLatin1(id).toUpper();
 }
 
 QString Formatting::prettyNameAndEMail(const UserID &uid)
 {
     return prettyNameAndEMail(uid.parent().protocol(), uid.id(), uid.name(), uid.email(), uid.comment());
 }
 
 QString Formatting::prettyNameAndEMail(const Key &key)
 {
     return prettyNameAndEMail(key.userID(0));
 }
 
 QString Formatting::prettyName(const Key &key)
 {
     return prettyName(key.userID(0));
 }
 
 QString Formatting::prettyName(const UserID &uid)
 {
     return prettyName(uid.parent().protocol(), uid.id(), uid.name(), uid.comment());
 }
 
 QString Formatting::prettyName(const UserID::Signature &sig)
 {
     return prettyName(OpenPGP, sig.signerUserID(), sig.signerName(), sig.signerComment());
 }
 
 //
 // EMail
 //
 
 QString Formatting::prettyEMail(const Key &key)
 {
     for (unsigned int i = 0, end = key.numUserIDs(); i < end; ++i) {
         const QString email = prettyEMail(key.userID(i));
         if (!email.isEmpty()) {
             return email;
         }
     }
     return QString();
 }
 
 QString Formatting::prettyEMail(const UserID &uid)
 {
     return prettyEMail(uid.email(), uid.id());
 }
 
 QString Formatting::prettyEMail(const UserID::Signature &sig)
 {
     return prettyEMail(sig.signerEmail(), sig.signerUserID());
 }
 
 QString Formatting::prettyEMail(const char *email_, const char *id)
 {
     QString email, name, comment;
     if (email_ && KEmailAddress::splitAddress(QString::fromUtf8(email_),
                                               name, email, comment) == KEmailAddress::AddressOk) {
         return email;
     } else {
         return DN(id)[QStringLiteral("EMAIL")].trimmed();
     }
 }
 
 //
 // Tooltip
 //
 
 namespace
 {
 
 static QString protect_whitespace(QString s)
 {
     static const QLatin1Char SP(' '), NBSP('\xA0');
     return s.replace(SP, NBSP);
 }
 
 template <typename T_arg>
 QString format_row(const QString &field, const T_arg &arg)
 {
     return QStringLiteral("<tr><th>%1:</th><td>%2</td></tr>").arg(protect_whitespace(field), arg);
 }
 QString format_row(const QString &field, const QString &arg)
 {
     return QStringLiteral("<tr><th>%1:</th><td>%2</td></tr>").arg(protect_whitespace(field), arg.toHtmlEscaped());
 }
 QString format_row(const QString &field, const char *arg)
 {
     return format_row(field, QString::fromUtf8(arg));
 }
 
 QString format_keytype(const Key &key)
 {
     const Subkey subkey = key.subkey(0);
     if (key.hasSecret()) {
         return i18n("%1-bit %2 (secret key available)", subkey.length(), QLatin1String(subkey.publicKeyAlgorithmAsString()));
     } else {
         return i18n("%1-bit %2", subkey.length(), QLatin1String(subkey.publicKeyAlgorithmAsString()));
     }
 }
 
 QString format_subkeytype(const Subkey &subkey)
 {
     const auto algo = subkey.publicKeyAlgorithm();
 
     if (algo == Subkey::AlgoECC ||
         algo == Subkey::AlgoECDSA ||
         algo == Subkey::AlgoECDH ||
         algo == Subkey::AlgoEDDSA) {
         return QString::fromStdString(subkey.algoName());
     }
     return i18n("%1-bit %2", subkey.length(), QLatin1String(subkey.publicKeyAlgorithmAsString()));
 }
 
 QString format_keyusage(const Key &key)
 {
     QStringList capabilities;
     if (key.canReallySign()) {
         if (key.isQualified()) {
             capabilities.push_back(i18n("Signing (Qualified)"));
         } else {
             capabilities.push_back(i18n("Signing"));
         }
     }
     if (key.canEncrypt()) {
         capabilities.push_back(i18n("Encryption"));
     }
     if (key.canCertify()) {
         capabilities.push_back(i18n("Certifying User-IDs"));
     }
     if (key.canAuthenticate()) {
         capabilities.push_back(i18n("SSH Authentication"));
     }
     return capabilities.join(QLatin1String(", "));
 }
 
 QString format_subkeyusage(const Subkey &subkey)
 {
     QStringList capabilities;
     if (subkey.canSign()) {
         if (subkey.isQualified()) {
             capabilities.push_back(i18n("Signing (Qualified)"));
         } else {
             capabilities.push_back(i18n("Signing"));
         }
     }
     if (subkey.canEncrypt()) {
         capabilities.push_back(i18n("Encryption"));
     }
     if (subkey.canCertify()) {
         capabilities.push_back(i18n("Certifying User-IDs"));
     }
     if (subkey.canAuthenticate()) {
         capabilities.push_back(i18n("SSH Authentication"));
     }
     return capabilities.join(QLatin1String(", "));
 }
 
 static QString time_t2string(time_t t)
 {
     QDateTime dt;
     dt.setTime_t(t);
     return QLocale().toString(dt, QLocale::ShortFormat);
 }
 
 static QString make_red(const QString &txt)
 {
     return QLatin1String("<font color=\"red\">") + txt.toHtmlEscaped() + QLatin1String("</font>");
 }
 
 }
 
 QString Formatting::toolTip(const Key &key, int flags)
 {
     if (flags == 0 || (key.protocol() != CMS && key.protocol() != OpenPGP)) {
         return QString();
     }
 
     const Subkey subkey = key.subkey(0);
 
     QString result;
     if (flags & Validity) {
         if (key.protocol() == OpenPGP || (key.keyListMode() & Validate)) {
             if (key.isRevoked()) {
                 result = make_red(i18n("Revoked"));
             } else if (key.isExpired()) {
                 result = make_red(i18n("Expired"));
             } else if (key.isDisabled()) {
                 result = i18n("Disabled");
             } else if (key.keyListMode() & GpgME::Validate) {
                 unsigned int fullyTrusted = 0;
                 for (const auto &uid: key.userIDs()) {
                     if (uid.validity() >= UserID::Validity::Full) {
                         fullyTrusted++;
                     }
                 }
                 if (fullyTrusted == key.numUserIDs()) {
                     result = i18n("All User-IDs are certified.");
                     const auto compliance = complianceStringForKey(key);
                     if (!compliance.isEmpty()) {
                         result += QStringLiteral("<br>") + compliance;
                     }
                 } else {
                     result = i18np("One User-ID is not certified.", "%1 User-IDs are not certified.", key.numUserIDs() - fullyTrusted);
                 }
             } else {
                 result = i18n("The validity cannot be checked at the moment.");
             }
         } else {
             result = i18n("The validity cannot be checked at the moment.");
         }
     }
     if (flags == Validity) {
         return result;
     }
 
     result += QLatin1String("<table border=\"0\">");
     if (key.protocol() == CMS) {
         if (flags & SerialNumber) {
             result += format_row(i18n("Serial number"), key.issuerSerial());
         }
         if (flags & Issuer) {
             result += format_row(i18n("Issuer"), key.issuerName());
         }
     }
     if (flags & UserIDs) {
         const std::vector<UserID> uids = key.userIDs();
         if (!uids.empty())
             result += format_row(key.protocol() == CMS
                                  ? i18n("Subject")
                                  : i18n("User-ID"), prettyUserID(uids.front()));
         if (uids.size() > 1)
             for (auto it = uids.begin() + 1, end = uids.end(); it != end; ++it)
                 if (!it->isRevoked() && !it->isInvalid()) {
                     result += format_row(i18n("a.k.a."), prettyUserID(*it));
                 }
     }
     if (flags & ExpiryDates) {
         result += format_row(i18n("Created"), time_t2string(subkey.creationTime()));
 
         if (key.isExpired()) {
             result += format_row(i18n("Expired"), time_t2string(subkey.expirationTime()));
         } else if (!subkey.neverExpires()) {
             result += format_row(i18n("Expires"), time_t2string(subkey.expirationTime()));
         }
     }
     if (flags & CertificateType) {
         result += format_row(i18n("Type"), format_keytype(key));
     }
     if (flags & CertificateUsage) {
         result += format_row(i18n("Usage"), format_keyusage(key));
     }
     if (flags & KeyID) {
         result += format_row(i18n("Key-ID"), QString::fromLatin1(key.shortKeyID()));
     }
     if (flags & Fingerprint) {
         result += format_row(i18n("Fingerprint"), key.primaryFingerprint());
     }
     if (flags & OwnerTrust) {
         if (key.protocol() == OpenPGP) {
             result += format_row(i18n("Certification trust"), ownerTrustShort(key));
         } else if (key.isRoot()) {
             result += format_row(i18n("Trusted issuer?"),
                                  key.userID(0).validity() == UserID::Ultimate ? i18n("Yes") :
                                  /* else */                                     i18n("No"));
         }
     }
 
     if (flags & StorageLocation) {
         if (const char *card = subkey.cardSerialNumber()) {
             result += format_row(i18n("Stored"), i18nc("stored...", "on SmartCard with serial no. %1", QString::fromUtf8(card)));
         } else {
             result += format_row(i18n("Stored"), i18nc("stored...", "on this computer"));
         }
     }
     if (flags & Subkeys) {
         for (const auto &sub: key.subkeys()) {
             result += QLatin1String("<hr/>");
             result += format_row(i18n("Subkey"), sub.fingerprint());
             if (sub.isRevoked()) {
                 result += format_row(i18n("Status"), i18n("Revoked"));
             } else if (sub.isExpired()) {
                 result += format_row(i18n("Status"), i18n("Expired"));
             }
             if (flags & ExpiryDates) {
                 result += format_row(i18n("Created"), time_t2string(sub.creationTime()));
 
                 if (key.isExpired()) {
                     result += format_row(i18n("Expired"), time_t2string(sub.expirationTime()));
                 } else if (!subkey.neverExpires()) {
                     result += format_row(i18n("Expires"), time_t2string(sub.expirationTime()));
                 }
             }
 
             if (flags & CertificateType) {
                 result += format_row(i18n("Type"), format_subkeytype(sub));
             }
             if (flags & CertificateUsage) {
                 result += format_row(i18n("Usage"), format_subkeyusage(sub));
             }
             if (flags & StorageLocation) {
                 if (const char *card = sub.cardSerialNumber()) {
                     result += format_row(i18n("Stored"), i18nc("stored...", "on SmartCard with serial no. %1", QString::fromUtf8(card)));
                 } else {
                     result += format_row(i18n("Stored"), i18nc("stored...", "on this computer"));
                 }
             }
         }
     }
     result += QLatin1String("</table>");
 
     return result;
 }
 
 namespace
 {
 template <typename Container>
 QString getValidityStatement(const Container &keys)
 {
     const bool allKeysAreOpenPGP = std::all_of(keys.cbegin(), keys.cend(), [] (const Key &key) { return key.protocol() == OpenPGP; });
     const bool allKeysAreValidated = std::all_of(keys.cbegin(), keys.cend(), [] (const Key &key) { return key.keyListMode() & Validate; });
     if (allKeysAreOpenPGP || allKeysAreValidated) {
         const bool someKeysAreBad = std::any_of(keys.cbegin(), keys.cend(), std::mem_fn(&Key::isBad));
         if (someKeysAreBad) {
             return i18n("Some keys are revoked, expired, disabled, or invalid.");
         } else {
             const bool allKeysAreFullyValid = std::all_of(keys.cbegin(), keys.cend(), &Formatting::uidsHaveFullValidity);
             if (allKeysAreFullyValid) {
                 return i18n("All keys are certified.");
             } else {
                 return i18n("Some keys are not certified.");
             }
         }
     }
     return i18n("The validity of the keys cannot be checked at the moment.");
 }
 }
 
 QString Formatting::toolTip(const KeyGroup &group, int flags)
 {
     static const unsigned int maxNumKeysForTooltip = 20;
 
     if (group.isNull()) {
         return QString();
     }
 
     const KeyGroup::Keys &keys = group.keys();
     if (keys.size() == 0) {
         return i18nc("@info:tooltip", "This group does not contain any keys.");
     }
 
     const QString validity = (flags & Validity) ? getValidityStatement(keys) : QString();
     if (flags == Validity) {
         return validity;
     }
 
     // list either up to maxNumKeysForTooltip keys or (maxNumKeysForTooltip-1) keys followed by "and n more keys"
     const unsigned int numKeysForTooltip = keys.size() > maxNumKeysForTooltip ? maxNumKeysForTooltip - 1 : keys.size();
 
     QStringList result;
     result.reserve(3 + 2 + numKeysForTooltip + 2);
     if (!validity.isEmpty()) {
         result.push_back("<p>");
         result.push_back(validity.toHtmlEscaped());
         result.push_back("</p>");
     }
 
     result.push_back("<p>");
     result.push_back(i18n("Keys:"));
     {
         auto it = keys.cbegin();
         for (unsigned int i = 0; i < numKeysForTooltip; ++i, ++it) {
             result.push_back(QLatin1String("<br>") + Formatting::summaryLine(*it).toHtmlEscaped());
         }
     }
     if (keys.size() > numKeysForTooltip) {
         result.push_back(QLatin1String("<br>") + i18ncp("this follows a list of keys", "and 1 more key", "and %1 more keys", keys.size() - numKeysForTooltip));
     }
     result.push_back("</p>");
 
     return result.join(QLatin1Char('\n'));
 }
 
 //
 // Creation and Expiration
 //
 
 namespace
 {
 static QDate time_t2date(time_t t)
 {
     if (!t) {
         return {};
     }
     QDateTime dt;
     dt.setTime_t(t);
     return dt.date();
 }
 static QString date2string(const QDate &date)
 {
     return QLocale().toString(date, QLocale::ShortFormat);
 }
 
 template <typename T>
 QString expiration_date_string(const T &tee)
 {
     return tee.neverExpires() ? QString() : date2string(time_t2date(tee.expirationTime()));
 }
 template <typename T>
 QDate creation_date(const T &tee)
 {
     return time_t2date(tee.creationTime());
 }
 template <typename T>
 QDate expiration_date(const T &tee)
 {
     return time_t2date(tee.expirationTime());
 }
 }
 
 QString Formatting::dateString(time_t t)
 {
     return date2string(time_t2date(t));
 }
 
 QString Formatting::expirationDateString(const Key &key)
 {
     return expiration_date_string(key.subkey(0));
 }
 
 QString Formatting::expirationDateString(const Subkey &subkey)
 {
     return expiration_date_string(subkey);
 }
 
 QString Formatting::expirationDateString(const UserID::Signature &sig)
 {
     return expiration_date_string(sig);
 }
 
 QDate Formatting::expirationDate(const Key &key)
 {
     return expiration_date(key.subkey(0));
 }
 
 QDate Formatting::expirationDate(const Subkey &subkey)
 {
     return expiration_date(subkey);
 }
 
 QDate Formatting::expirationDate(const UserID::Signature &sig)
 {
     return expiration_date(sig);
 }
 
 QString Formatting::creationDateString(const Key &key)
 {
     return date2string(creation_date(key.subkey(0)));
 }
 
 QString Formatting::creationDateString(const Subkey &subkey)
 {
     return date2string(creation_date(subkey));
 }
 
 QString Formatting::creationDateString(const UserID::Signature &sig)
 {
     return date2string(creation_date(sig));
 }
 
 QDate Formatting::creationDate(const Key &key)
 {
     return creation_date(key.subkey(0));
 }
 
 QDate Formatting::creationDate(const Subkey &subkey)
 {
     return creation_date(subkey);
 }
 
 QDate Formatting::creationDate(const UserID::Signature &sig)
 {
     return creation_date(sig);
 }
 
 //
 // Types
 //
 
 QString Formatting::displayName(Protocol p)
 {
     if (p == CMS) {
         return i18nc("X.509/CMS encryption standard", "S/MIME");
     }
     if (p == OpenPGP) {
         return i18n("OpenPGP");
     }
     return i18nc("Unknown encryption protocol", "Unknown");
 }
 
 QString Formatting::type(const Key &key)
 {
     return displayName(key.protocol());
 }
 
 QString Formatting::type(const Subkey &subkey)
 {
     return QString::fromUtf8(subkey.publicKeyAlgorithmAsString());
 }
 
 QString Formatting::type(const KeyGroup &group)
 {
     Q_UNUSED(group)
     return i18nc("a group of keys/certificates", "Group");
 }
 
 //
 // Status / Validity
 //
 
 QString Formatting::ownerTrustShort(const Key &key)
 {
     return ownerTrustShort(key.ownerTrust());
 }
 
 QString Formatting::ownerTrustShort(Key::OwnerTrust trust)
 {
     switch (trust) {
     case Key::Unknown:   return i18nc("unknown trust level", "unknown");
     case Key::Never:     return i18n("untrusted");
     case Key::Marginal:  return i18nc("marginal trust", "marginal");
     case Key::Full:      return i18nc("full trust", "full");
     case Key::Ultimate:  return i18nc("ultimate trust", "ultimate");
     case Key::Undefined: return i18nc("undefined trust", "undefined");
     default:
         Q_ASSERT(!"unexpected owner trust value");
         break;
     }
     return QString();
 }
 
 QString Formatting::validityShort(const Subkey &subkey)
 {
     if (subkey.isRevoked()) {
         return i18n("revoked");
     }
     if (subkey.isExpired()) {
         return i18n("expired");
     }
     if (subkey.isDisabled()) {
         return i18n("disabled");
     }
     if (subkey.isInvalid()) {
         return i18n("invalid");
     }
     return i18nc("as in good/valid signature", "good");
 }
 
 QString Formatting::validityShort(const UserID &uid)
 {
     if (uid.isRevoked()) {
         return i18n("revoked");
     }
     if (uid.isInvalid()) {
         return i18n("invalid");
     }
     switch (uid.validity()) {
     case UserID::Unknown:   return i18nc("unknown trust level", "unknown");
     case UserID::Undefined: return i18nc("undefined trust", "undefined");
     case UserID::Never:     return i18n("untrusted");
     case UserID::Marginal:  return i18nc("marginal trust", "marginal");
     case UserID::Full:      return i18nc("full trust", "full");
     case UserID::Ultimate:  return i18nc("ultimate trust", "ultimate");
     }
     return QString();
 }
 
 QString Formatting::validityShort(const UserID::Signature &sig)
 {
     switch (sig.status()) {
     case UserID::Signature::NoError:
         if (!sig.isInvalid()) {
             /* See RFC 4880 Section 5.2.1 */
             switch (sig.certClass()) {
             case 0x10: /* Generic */
             case 0x11: /* Persona */
             case 0x12: /* Casual */
             case 0x13: /* Positive */
                 return i18n("valid");
             case 0x30:
                 return i18n("revoked");
             default:
                 return i18n("class %1", sig.certClass());
             }
         }
         Q_FALLTHROUGH();
         // fall through:
     case UserID::Signature::GeneralError:
         return i18n("invalid");
     case UserID::Signature::SigExpired:   return i18n("expired");
     case UserID::Signature::KeyExpired:   return i18n("certificate expired");
     case UserID::Signature::BadSignature: return i18nc("fake/invalid signature", "bad");
     case UserID::Signature::NoPublicKey: {
             /* GnuPG returns the same error for no public key as for expired
              * or revoked certificates. */
             const auto key = KeyCache::instance()->findByKeyIDOrFingerprint (sig.signerKeyID());
             if (key.isNull()) {
                 return i18n("no public key");
             } else if (key.isExpired()) {
                 return i18n("key expired");
             } else if (key.isRevoked()) {
                 return i18n("key revoked");
             } else if (key.isDisabled()) {
                 return i18n("key disabled");
             }
             /* can't happen */
             return QStringLiteral("unknown");
         }
     }
     return QString();
 }
 
 QIcon Formatting::validityIcon(const UserID::Signature &sig)
 {
     switch (sig.status()) {
     case UserID::Signature::NoError:
         if (!sig.isInvalid()) {
             /* See RFC 4880 Section 5.2.1 */
             switch (sig.certClass()) {
             case 0x10: /* Generic */
             case 0x11: /* Persona */
             case 0x12: /* Casual */
             case 0x13: /* Positive */
                 return QIcon::fromTheme(QStringLiteral("emblem-success"));
             case 0x30:
                 return QIcon::fromTheme(QStringLiteral("emblem-error"));
             default:
                 return QIcon();
             }
         }
         Q_FALLTHROUGH();
         // fall through:
     case UserID::Signature::BadSignature:
     case UserID::Signature::GeneralError:
         return QIcon::fromTheme(QStringLiteral("emblem-error"));
     case UserID::Signature::SigExpired:
     case UserID::Signature::KeyExpired:
         return QIcon::fromTheme(QStringLiteral("emblem-information"));
     case UserID::Signature::NoPublicKey:
         return QIcon::fromTheme(QStringLiteral("emblem-question"));
     }
     return QIcon();
 }
 
 QString Formatting::formatKeyLink(const Key &key)
 {
     if (key.isNull()) {
         return QString();
     }
     return QStringLiteral("<a href=\"key:%1\">%2</a>").arg(QLatin1String(key.primaryFingerprint()), Formatting::prettyName(key));
 }
 
 QString Formatting::formatForComboBox(const GpgME::Key &key)
 {
     const QString name = prettyName(key);
     QString mail = prettyEMail(key);
     if (!mail.isEmpty()) {
         mail = QLatin1Char('<') + mail + QLatin1Char('>');
     }
     return i18nc("name, email, key id", "%1 %2 (%3)", name, mail, QLatin1String(key.shortKeyID())).simplified();
 }
 
 namespace
 {
 
 static QString keyToString(const Key &key)
 {
 
     Q_ASSERT(!key.isNull());
 
     const QString email = Formatting::prettyEMail(key);
     const QString name = Formatting::prettyName(key);
 
     if (name.isEmpty()) {
         return email;
     } else if (email.isEmpty()) {
         return name;
     } else {
         return QStringLiteral("%1 <%2>").arg(name, email);
     }
 }
 
 }
 
 const char *Formatting::summaryToString(const Signature::Summary summary)
 {
     if (summary & Signature::Red) {
         return "RED";
     }
     if (summary & Signature::Green) {
         return "GREEN";
     }
     return "YELLOW";
 }
 
 QString Formatting::signatureToString(const Signature &sig, const Key &key)
 {
     if (sig.isNull()) {
         return QString();
     }
 
     const bool red   = (sig.summary() & Signature::Red);
     const bool valid = (sig.summary() & Signature::Valid);
 
     if (red)
         if (key.isNull())
             if (const char *fpr = sig.fingerprint()) {
                 return i18n("Bad signature by unknown certificate %1: %2", QString::fromLatin1(fpr), QString::fromLocal8Bit(sig.status().asString()));
             } else {
                 return i18n("Bad signature by an unknown certificate: %1", QString::fromLocal8Bit(sig.status().asString()));
             }
         else {
             return i18n("Bad signature by %1: %2", keyToString(key), QString::fromLocal8Bit(sig.status().asString()));
         }
 
     else if (valid)
         if (key.isNull())
             if (const char *fpr = sig.fingerprint()) {
                 return i18n("Good signature by unknown certificate %1.", QString::fromLatin1(fpr));
             } else {
                 return i18n("Good signature by an unknown certificate.");
             }
         else {
             return i18n("Good signature by %1.", keyToString(key));
         }
 
     else if (key.isNull())
         if (const char *fpr = sig.fingerprint()) {
             return i18n("Invalid signature by unknown certificate %1: %2", QString::fromLatin1(fpr), QString::fromLocal8Bit(sig.status().asString()));
         } else {
             return i18n("Invalid signature by an unknown certificate: %1", QString::fromLocal8Bit(sig.status().asString()));
         }
     else {
         return i18n("Invalid signature by %1: %2", keyToString(key), QString::fromLocal8Bit(sig.status().asString()));
     }
 }
 
 //
 // ImportResult
 //
 
 QString Formatting::importMetaData(const Import &import, const QStringList &ids)
 {
     const QString result = importMetaData(import);
     if (result.isEmpty()) {
         return QString();
     } else
         return result + QLatin1Char('\n') +
                i18n("This certificate was imported from the following sources:") + QLatin1Char('\n') +
                ids.join(QLatin1Char('\n'));
 }
 
 QString Formatting::importMetaData(const Import &import)
 {
 
     if (import.isNull()) {
         return QString();
     }
 
     if (import.error().isCanceled()) {
         return i18n("The import of this certificate was canceled.");
     }
     if (import.error())
         return i18n("An error occurred importing this certificate: %1",
                     QString::fromLocal8Bit(import.error().asString()));
 
     const unsigned int status = import.status();
     if (status & Import::NewKey)
         return (status & Import::ContainedSecretKey)
                ? i18n("This certificate was new to your keystore. The secret key is available.")
                : i18n("This certificate is new to your keystore.");
 
     QStringList results;
     if (status & Import::NewUserIDs) {
         results.push_back(i18n("New user-ids were added to this certificate by the import."));
     }
     if (status & Import::NewSignatures) {
         results.push_back(i18n("New signatures were added to this certificate by the import."));
     }
     if (status & Import::NewSubkeys) {
         results.push_back(i18n("New subkeys were added to this certificate by the import."));
     }
 
     return results.empty()
            ? i18n("The import contained no new data for this certificate. It is unchanged.")
            : results.join(QLatin1Char('\n'));
 }
 
 //
 // Overview in CertificateDetailsDialog
 //
 
 QString Formatting::formatOverview(const Key &key)
 {
     return toolTip(key, AllOptions);
 }
 
 QString Formatting::usageString(const Subkey &sub)
 {
     QStringList usageStrings;
     if (sub.canCertify()) {
         usageStrings << i18n("Certify");
     }
     if (sub.canSign()) {
         usageStrings << i18n("Sign");
     }
     if (sub.canEncrypt()) {
         usageStrings << i18n("Encrypt");
     }
     if (sub.canAuthenticate()) {
         usageStrings << i18n("Authenticate");
     }
     return usageStrings.join(QLatin1String(", "));
 }
 
 QString Formatting::summaryLine(const Key &key)
 {
     return keyToString(key) + QLatin1Char(' ') +
            i18nc("(validity, protocol, creation date)",
                  "(%1, %2, created: %3)",
 		 Formatting::complianceStringShort(key),
 		 displayName(key.protocol()),
                  Formatting::creationDateString(key));
 }
 
 QString Formatting::summaryLine(const KeyGroup &group)
 {
     switch (group.source()) {
         case KeyGroup::ApplicationConfig:
         case KeyGroup::GnuPGConfig:
             return i18ncp("name of group of keys (n key(s), validity)",
                           "%2 (1 key, %3)", "%2 (%1 keys, %3)",
                           group.keys().size(), group.name(), Formatting::complianceStringShort(group));
         case KeyGroup::Tags:
             return i18ncp("name of group of keys (n key(s), validity, tag)",
                           "%2 (1 key, %3, tag)", "%2 (%1 keys, %3, tag)",
                           group.keys().size(), group.name(), Formatting::complianceStringShort(group));
         default:
             return i18ncp("name of group of keys (n key(s), validity, group ...)",
                           "%2 (1 key, %3, unknown origin)", "%2 (%1 keys, %3, unknown origin)",
                           group.keys().size(), group.name(), Formatting::complianceStringShort(group));
     }
 }
 
 namespace
 {
 QIcon iconForValidity(UserID::Validity validity)
 {
     switch (validity) {
         case UserID::Ultimate:
         case UserID::Full:
         case UserID::Marginal:
             return QIcon::fromTheme(QStringLiteral("emblem-success"));
         case UserID::Never:
             return QIcon::fromTheme(QStringLiteral("emblem-error"));
         case UserID::Undefined:
         case UserID::Unknown:
         default:
             return QIcon::fromTheme(QStringLiteral("emblem-information"));
     }
 }
 }
 
 // Icon for certificate selection indication
 QIcon Formatting::iconForUid(const UserID &uid)
 {
     return iconForValidity(uid.validity());
 }
 
 QString Formatting::validity(const UserID &uid)
 {
     switch (uid.validity()) {
         case UserID::Ultimate:
             return i18n("The certificate is marked as your own.");
         case UserID::Full:
             return i18n("The certificate belongs to this recipient.");
         case UserID::Marginal:
             return i18n("The trust model indicates marginally that the certificate belongs to this recipient.");
         case UserID::Never:
             return i18n("This certificate should not be used.");
         case UserID::Undefined:
         case UserID::Unknown:
         default:
             return i18n("There is no indication that this certificate belongs to this recipient.");
     }
 }
 
 QString Formatting::validity(const KeyGroup &group)
 {
     if (group.isNull()) {
         return QString();
     }
 
     const KeyGroup::Keys &keys = group.keys();
     if (keys.size() == 0) {
         return i18n("This group does not contain any keys.");
     }
 
     return getValidityStatement(keys);
 }
 
 namespace
 {
 UserID::Validity minimalValidityOfNotRevokedUserIDs(const Key &key)
 {
     std::vector<UserID> userIDs = key.userIDs();
     const auto endOfNotRevokedUserIDs = std::remove_if(userIDs.begin(), userIDs.end(), std::mem_fn(&UserID::isRevoked));
     const int minValidity = std::accumulate(userIDs.begin(), endOfNotRevokedUserIDs, UserID::Ultimate + 1,
                                             [] (int validity, const UserID &userID) {
                                                 return std::min(validity, static_cast<int>(userID.validity()));
                                             });
     return minValidity <= UserID::Ultimate ? static_cast<UserID::Validity>(minValidity) : UserID::Unknown;
 }
 
 template <typename Container>
 UserID::Validity minimalValidity(const Container& keys)
 {
     const int minValidity = std::accumulate(keys.cbegin(), keys.cend(), UserID::Ultimate + 1,
                                             [] (int validity, const Key &key) {
                                                 return std::min<int>(validity, minimalValidityOfNotRevokedUserIDs(key));
                                             });
     return minValidity <= UserID::Ultimate ? static_cast<UserID::Validity>(minValidity) : UserID::Unknown;
 }
 }
 
 QIcon Formatting::validityIcon(const KeyGroup &group)
 {
     return iconForValidity(minimalValidity(group.keys()));
 }
 
 bool Formatting::uidsHaveFullValidity(const Key &key)
 {
     return minimalValidityOfNotRevokedUserIDs(key) >= UserID::Full;
 }
 
 QString Formatting::complianceMode()
 {
-    const QGpgME::CryptoConfig *const config = QGpgME::cryptoConfig();
-    if (!config) {
-        return QString();
-    }
-    const QGpgME::CryptoConfigEntry *const entry = getCryptoConfigEntry(config, "gpg", "compliance");
-    if (!entry || entry->stringValue() == QLatin1String("gnupg")) {
-        return QString();
-    }
-    return entry->stringValue();
+    const auto complianceValue = getCryptoConfigStringValue("gpg", "compliance");
+    return complianceValue == QLatin1String("gnupg") ? QString() : complianceValue;
 }
 
 bool Formatting::isKeyDeVs(const GpgME::Key &key)
 {
     for (const auto &sub: key.subkeys()) {
         if (sub.isExpired() || sub.isRevoked()) {
             // Ignore old subkeys
             continue;
         }
         if (!sub.isDeVs()) {
             return false;
         }
     }
     return true;
 }
 
 QString Formatting::complianceStringForKey(const GpgME::Key &key)
 {
     // There will likely be more in the future for other institutions
     // for now we only have DE-VS
     if (complianceMode() == QLatin1String("de-vs")) {
         if (uidsHaveFullValidity(key) && isKeyDeVs(key)) {
             return i18nc("%1 is a placeholder for the name of a compliance mode. E.g. NATO RESTRICTED compliant or VS-NfD compliant",
                          "May be used for %1 communication.", deVsString());
         } else {
             return i18nc("VS-NfD-conforming is a German standard for restricted documents. For which special restrictions about algorithms apply. The string describes if a key is compliant to that..",
                          "May <b>not</b> be used for %1 communication.", deVsString());
         }
     }
     return QString();
 }
 
 QString Formatting::complianceStringShort(const GpgME::Key &key)
 {
     const bool keyValidityChecked = (key.keyListMode() & GpgME::Validate);
     if (keyValidityChecked && Formatting::uidsHaveFullValidity(key)) {
         if (complianceMode() == QLatin1String("de-vs")
             && Formatting::isKeyDeVs(key)) {
             return QStringLiteral("★ ") + deVsString(true);
         }
         return i18nc("As in all user IDs are valid.", "certified");
     }
     if (key.isExpired()) {
         return i18n("expired");
     }
     if (key.isRevoked()) {
         return i18n("revoked");
     }
     if (key.isDisabled()) {
         return i18n("disabled");
     }
     if (key.isInvalid()) {
         return i18n("invalid");
     }
     if (keyValidityChecked) {
         return i18nc("As in not all user IDs are valid.", "not certified");
     }
 
     return i18nc("The validity of the user IDs has not been/could not be checked", "not checked");
 }
 
 QString Formatting::complianceStringShort(const KeyGroup &group)
 {
     const KeyGroup::Keys &keys = group.keys();
 
     const bool allKeysFullyValid = std::all_of(keys.cbegin(), keys.cend(), &Formatting::uidsHaveFullValidity);
     if (allKeysFullyValid) {
         return i18nc("As in all keys are valid.", "all certified");
     }
 
     return i18nc("As in not all keys are valid.", "not all certified");
 }
 
 QString Formatting::prettyID(const char *id)
 {
     if (!id) {
         return QString();
     }
     QString ret = QString::fromLatin1(id).toUpper().replace(QRegularExpression(QStringLiteral("(....)")),
                                                             QStringLiteral("\\1 ")).trimmed();
     // For the standard 10 group fingerprint let us use a double space in the
     // middle to increase readability
     if (ret.size() == 49) {
         ret.insert(24, QLatin1Char(' '));
     }
     return ret;
 }
 
 QString Formatting::origin(int o)
 {
     switch (o) {
         case Key::OriginKS:
             return i18n("Keyserver");
         case Key::OriginDane:
             return QStringLiteral("DANE");
         case Key::OriginWKD:
             return QStringLiteral("WKD");
         case Key::OriginURL:
             return QStringLiteral("URL");
         case Key::OriginFile:
             return i18n("File import");
         case Key::OriginSelf:
             return i18n("Generated");
         case Key::OriginOther:
         case Key::OriginUnknown:
         default:
           return i18n("Unknown");
     }
 }
 
 QString Formatting::deVsString(bool compliant)
 {
     const auto filter = KeyFilterManager::instance()->keyFilterByID(compliant ?
             QStringLiteral("de-vs-filter") :
             QStringLiteral("not-de-vs-filter"));
     if (!filter) {
         return compliant ? i18n("VS-NfD compliant") : i18n("Not VS-NfD compliant");
     }
     return filter->name();
 }