Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F31845933
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
14 KB
Subscribers
None
View Options
diff --git a/src/commands/revokecertificationcommand.cpp b/src/commands/revokecertificationcommand.cpp
index 6cf4adf9c..de8eb5c0f 100644
--- a/src/commands/revokecertificationcommand.cpp
+++ b/src/commands/revokecertificationcommand.cpp
@@ -1,385 +1,385 @@
/*
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<UserID::Signature> revokableCertifications(const GpgME::UserID &userId)
{
auto signatures = userId.signatures();
std::sort(signatures.begin(), signatures.end());
std::reverse(signatures.begin(), signatures.end());
auto last = std::unique(signatures.begin(), signatures.end(), [](const auto &sig1, const auto &sig2) {
return !qstricmp(sig1.signerKeyID(), sig2.signerKeyID());
});
signatures.erase(last, signatures.end());
erase_if(signatures, [](const auto &certification) {
return userCanRevokeCertification(certification) != CertificationCanBeRevoked;
});
std::reverse(signatures.begin(), signatures.end());
return signatures;
}
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;
}
const auto certifications = revokableCertifications(userId);
std::transform(certifications.begin(), certifications.end(), std::back_inserter(keys), [](const auto &signature) {
return KeyAndSignature{KeyCache::instance()->findByKeyIDOrFingerprint(signature.signerKeyID()), signature};
});
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::prettyID(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
Details
Attached
Mime Type
text/x-diff
Expires
Sat, Nov 8, 4:43 AM (1 d, 2 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
24/c6/b1537339dd1c4376c3f192d36237
Attached To
rKLEOPATRA Kleopatra
Event Timeline
Log In to Comment