Page MenuHome GnuPG

No OneTemporary

diff --git a/autotests/newkeyapprovaldialogtest.cpp b/autotests/newkeyapprovaldialogtest.cpp
index 6f628b10..9d5b1731 100644
--- a/autotests/newkeyapprovaldialogtest.cpp
+++ b/autotests/newkeyapprovaldialogtest.cpp
@@ -1,911 +1,942 @@
/*
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,
};
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 "));
}
+ void test__vs_de_compliance__null_keys_are_ignored()
+ {
+ 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("unknown@example.net"), {}},
+ {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 c4e11ca5..58fec635 100644
--- a/src/ui/newkeyapprovaldialog.cpp
+++ b/src/ui/newkeyapprovaldialog.cpp
@@ -1,905 +1,908 @@
/* -*- 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(mAllCombos)) {
- const auto &key = combo->currentKey();
if (!combo->isVisible()) {
continue;
}
+ const auto key = combo->currentKey();
+ if (key.isNull()) {
+ 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"

File Metadata

Mime Type
text/x-diff
Expires
Sat, May 10, 9:23 AM (1 h, 18 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
ff/32/b83e2b9fea5b1747675cf8b28307

Event Timeline