Page MenuHome GnuPG

No OneTemporary

diff --git a/src/commands/revokecertificationcommand.cpp b/src/commands/revokecertificationcommand.cpp
index 070345c38..80049abeb 100644
--- a/src/commands/revokecertificationcommand.cpp
+++ b/src/commands/revokecertificationcommand.cpp
@@ -1,370 +1,390 @@
/* -*- mode: c++; c-basic-offset:4 -*-
commands/revokecertificationcommand.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2020, 2022 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-kleopatra.h>
#include "revokecertificationcommand.h"
#include "command_p.h"
#include "exportopenpgpcertstoservercommand.h"
#include <kleopatra_debug.h>
#include <Libkleo/Algorithm>
#include <Libkleo/Formatting>
#include <Libkleo/KeyCache>
#include <Libkleo/KeyHelpers>
#include <KGuiItem>
#include <KLocalizedString>
#include <KMessageBox>
#include <KStandardGuiItem>
#include <QGpgME/Protocol>
#include <QGpgME/QuickJob>
#include <gpgme++/engineinfo.h>
using namespace Kleo;
using namespace Kleo::Commands;
using namespace GpgME;
using namespace QGpgME;
namespace
{
enum class InputType {
Key,
UserIDs,
Certifications,
};
struct CertificationData {
UserID userId;
Key certificationKey;
UserID::Signature signature;
};
struct KeyAndSignature {
Key key;
UserID::Signature signature;
};
static std::vector<KeyAndSignature> getCertificationKeys(const GpgME::UserID &userId)
{
std::vector<KeyAndSignature> keys;
if (userId.numSignatures() == 0) {
qCWarning(KLEOPATRA_LOG) << __func__ << "- Error: Signatures of user ID" << QString::fromUtf8(userId.id()) << "not available";
return keys;
}
- std::vector<GpgME::UserID::Signature> revokableCertifications;
- Kleo::copy_if(userId.signatures(), std::back_inserter(revokableCertifications), [](const auto &certification) {
- return userCanRevokeCertification(certification) == CertificationCanBeRevoked;
+
+ const auto signatures = userId.signatures();
+ std::vector<KeyAndSignature> certifications;
+
+ std::transform(signatures.begin(), signatures.end(), std::back_inserter(certifications), [](const auto &signature) {
+ return KeyAndSignature{KeyCache::instance()->findByKeyIDOrFingerprint(signature.signerKeyID()), signature};
});
- Kleo::transform(revokableCertifications, std::back_inserter(keys), [](const auto &certification) {
- return KeyAndSignature{KeyCache::instance()->findByKeyIDOrFingerprint(certification.signerKeyID()), certification};
+
+ std::sort(certifications.begin(), certifications.end(), [](const auto &lhs, const auto &rhs) {
+ return lhs.signature.creationTime() > rhs.signature.creationTime();
});
+
+ for (const auto &certification : certifications) {
+ if (std::find_if(keys.begin(),
+ keys.end(),
+ [certification](const auto &it) {
+ return !qstricmp(it.key.keyID(), certification.key.keyID());
+ })
+ == keys.end()) {
+ keys.push_back(certification);
+ }
+ }
+
+ erase_if(keys, [](const auto &certification) {
+ return userCanRevokeCertification(certification.signature) != CertificationCanBeRevoked;
+ });
+
return keys;
}
static bool confirmRevocations(QWidget *parent, const std::vector<CertificationData> &certifications)
{
KMessageBox::ButtonCode answer;
if (certifications.size() == 1) {
const auto [userId, certificationKey, signature] = certifications.front();
const auto message = xi18nc("@info",
"<para>You are about to revoke the certification of user ID<nl/>%1<nl/>made with the key<nl/>%2.</para>",
QString::fromUtf8(userId.id()),
Formatting::formatForComboBox(certificationKey));
answer = KMessageBox::questionTwoActions(parent,
message,
i18nc("@title:window", "Confirm Revocation"),
KGuiItem{i18n("Revoke Certification")},
KStandardGuiItem::cancel());
} else {
QStringList l;
Kleo::transform(certifications, std::back_inserter(l), [](const auto &c) {
return i18n("User ID '%1' certified with key %2", QString::fromUtf8(c.userId.id()), Formatting::formatForComboBox(c.certificationKey));
});
const auto message = i18np("You are about to revoke the following certification:", //
"You are about to revoke the following %1 certifications:",
certifications.size());
answer = KMessageBox::questionTwoActionsList(parent,
message,
l,
i18nc("@title:window", "Confirm Revocation"),
KGuiItem{i18n("Revoke Certifications")},
KStandardGuiItem::cancel());
}
return answer == KMessageBox::ButtonCode::PrimaryAction;
}
}
class RevokeCertificationCommand::Private : public Command::Private
{
friend class ::Kleo::Commands::RevokeCertificationCommand;
RevokeCertificationCommand *q_func() const
{
return static_cast<RevokeCertificationCommand *>(q);
}
public:
Private(InputType i, RevokeCertificationCommand *qq, KeyListController *c = nullptr);
void init();
private:
std::vector<CertificationData> getCertificationsToRevoke();
void scheduleNextRevocation();
QGpgME::QuickJob *createJob();
void slotResult(const Error &err);
private:
InputType inputType = InputType::Key;
Key certificationTarget;
std::vector<UserID> uids;
std::vector<CertificationData> certificationsToRevoke;
std::vector<CertificationData> completedRevocations;
QPointer<QGpgME::QuickJob> job;
};
RevokeCertificationCommand::Private *RevokeCertificationCommand::d_func()
{
return static_cast<Private *>(d.get());
}
const RevokeCertificationCommand::Private *RevokeCertificationCommand::d_func() const
{
return static_cast<const Private *>(d.get());
}
#define d d_func()
#define q q_func()
RevokeCertificationCommand::Private::Private(InputType i, RevokeCertificationCommand *qq, KeyListController *c)
: Command::Private{qq, c}
, inputType{i}
{
}
void RevokeCertificationCommand::Private::init()
{
const std::vector<Key> keys_ = keys();
if (keys_.size() != 1) {
qCWarning(KLEOPATRA_LOG) << q << "Expected exactly one key, but got" << keys_.size();
return;
}
if (keys_.front().protocol() != GpgME::OpenPGP) {
qCWarning(KLEOPATRA_LOG) << q << "Expected OpenPGP key, but got" << keys_.front().protocolAsString();
return;
}
certificationTarget = keys_.front();
}
std::vector<CertificationData> RevokeCertificationCommand::Private::getCertificationsToRevoke()
{
if (inputType != InputType::Certifications) {
// ensure that the certifications of the key have been loaded
if (certificationTarget.userID(0).numSignatures() == 0) {
certificationTarget.update();
}
// build list of user IDs and revokable certifications
const auto userIDsToConsider = (inputType == InputType::Key) ? certificationTarget.userIDs() : uids;
for (const auto &userId : userIDsToConsider) {
Kleo::transform(getCertificationKeys(userId), std::back_inserter(certificationsToRevoke), [userId](const auto &k) {
return CertificationData{userId, k.key, k.signature};
});
}
}
Kleo::erase_if(certificationsToRevoke, [](const auto &c) {
return c.certificationKey.isNull();
});
return certificationsToRevoke;
}
void RevokeCertificationCommand::Private::scheduleNextRevocation()
{
if (!certificationsToRevoke.empty()) {
const auto nextCertification = certificationsToRevoke.back();
job = createJob();
if (!job) {
qCWarning(KLEOPATRA_LOG) << q << "Failed to create job";
finished();
return;
}
job->startRevokeSignature(certificationTarget, nextCertification.certificationKey, {nextCertification.userId});
} else {
if (std::any_of(completedRevocations.begin(), completedRevocations.end(), [](const auto &revocation) {
return revocation.signature.isExportable();
})) {
const auto message = xi18ncp("@info",
"<para>The certification has been revoked successfully.</para>"
"<para>Do you want to publish the revocation?</para>",
"<para>%1 certifications have been revoked successfully.</para>"
"<para>Do you want to publish the revocations?</para>",
completedRevocations.size());
const auto yesButton = KGuiItem{i18ncp("@action:button", "Publish Revocation", "Publish Revocations", completedRevocations.size()),
QIcon::fromTheme(QStringLiteral("view-certificate-export-server"))};
const auto answer = KMessageBox::questionTwoActions(parentWidgetOrView(),
message,
i18nc("@title:window", "Confirm Publication"),
yesButton,
KStandardGuiItem::cancel(),
{},
KMessageBox::Notify | KMessageBox::Dangerous);
if (answer == KMessageBox::ButtonCode::PrimaryAction) {
const auto cmd = new ExportOpenPGPCertsToServerCommand{certificationTarget};
cmd->start();
}
}
finished();
}
}
void RevokeCertificationCommand::Private::slotResult(const Error &err)
{
if (err.isCanceled()) {
canceled();
return;
}
if (err) {
const auto failedRevocation = certificationsToRevoke.back();
error(xi18nc("@info",
"<para>The revocation of the certification of user ID<nl/>%1<nl/>made with key<nl/>%2<nl/>failed:</para>"
"<para><message>%3</message></para>",
Formatting::prettyNameAndEMail(failedRevocation.userId),
Formatting::formatForComboBox(failedRevocation.certificationKey),
Formatting::errorAsString(err)));
finished();
return;
}
completedRevocations.push_back(certificationsToRevoke.back());
certificationsToRevoke.pop_back();
scheduleNextRevocation();
}
QGpgME::QuickJob *RevokeCertificationCommand::Private::createJob()
{
const auto j = QGpgME::openpgp()->quickJob();
if (j) {
connect(j, &QGpgME::Job::jobProgress, q, &Command::progress);
connect(j, &QGpgME::QuickJob::result, q, [this](const GpgME::Error &error) {
slotResult(error);
});
}
return j;
}
RevokeCertificationCommand::RevokeCertificationCommand(QAbstractItemView *v, KeyListController *c)
: Command{v, new Private{InputType::Key, this, c}}
{
d->init();
}
RevokeCertificationCommand::RevokeCertificationCommand(const GpgME::Key &key)
: Command{key, new Private{InputType::Key, this}}
{
d->init();
}
RevokeCertificationCommand::RevokeCertificationCommand(const GpgME::UserID &uid)
: Command{uid.parent(), new Private{InputType::UserIDs, this}}
{
std::vector<UserID>(1, uid).swap(d->uids);
d->init();
}
RevokeCertificationCommand::RevokeCertificationCommand(const std::vector<GpgME::UserID> &uids)
: Command{uids.empty() ? Key{} : uids.front().parent(), new Private{InputType::UserIDs, this}}
{
d->uids = uids;
d->init();
}
RevokeCertificationCommand::RevokeCertificationCommand(const GpgME::UserID::Signature &signature)
: Command{signature.parent().parent(), new Private{InputType::Certifications, this}}
{
if (!signature.isNull()) {
const Key certificationKey = KeyCache::instance()->findByKeyIDOrFingerprint(signature.signerKeyID());
d->certificationsToRevoke = {{signature.parent(), certificationKey, signature}};
}
d->init();
}
RevokeCertificationCommand::~RevokeCertificationCommand()
{
qCDebug(KLEOPATRA_LOG) << this << __func__;
}
// static
bool RevokeCertificationCommand::isSupported()
{
return engineInfo(GpgEngine).engineVersion() >= "2.2.24";
}
void RevokeCertificationCommand::doStart()
{
if (d->certificationTarget.isNull()) {
d->finished();
return;
}
if (!std::ranges::all_of(d->uids, userIDBelongsToKey(d->certificationTarget))) {
qCWarning(KLEOPATRA_LOG) << this << "User ID <-> Key mismatch!";
d->finished();
return;
}
const auto certificationsToRevoke = d->getCertificationsToRevoke();
if (certificationsToRevoke.empty()) {
switch (d->inputType) {
case InputType::Key:
d->information(i18n("You cannot revoke any certifications of this key."));
break;
case InputType::UserIDs:
d->information(i18np("You cannot revoke any certifications of this user ID.", //
"You cannot revoke any certifications of these user IDs.",
d->uids.size()));
break;
case InputType::Certifications:
d->information(i18n("You cannot revoke this certification."));
break;
}
d->finished();
return;
}
if (!confirmRevocations(d->parentWidgetOrView(), certificationsToRevoke)) {
d->canceled();
return;
}
d->scheduleNextRevocation();
}
void RevokeCertificationCommand::doCancel()
{
qCDebug(KLEOPATRA_LOG) << this << __func__;
if (d->job) {
d->job->slotCancel();
}
}
#undef d
#undef q
#include "moc_revokecertificationcommand.cpp"

File Metadata

Mime Type
text/x-diff
Expires
Mon, Dec 23, 3:28 PM (1 d, 1 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
f1/02/4ebadc9f2767013c980589615caf

Event Timeline