diff --git a/src/commands/revokecertificationcommand.cpp b/src/commands/revokecertificationcommand.cpp index a65747179..f278bea98 100644 --- a/src/commands/revokecertificationcommand.cpp +++ b/src/commands/revokecertificationcommand.cpp @@ -1,266 +1,282 @@ /* -*- mode: c++; c-basic-offset:4 -*- commands/revokecertificationcommand.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2020 g10 Code GmbH SPDX-FileContributor: Ingo Klöcker SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "revokecertificationcommand.h" #include "command_p.h" #include "exportopenpgpcertstoservercommand.h" #include "dialogs/revokecertificationdialog.h" #include #include #include #include #include #include #include #include #include "kleopatra_debug.h" using namespace Kleo; using namespace Kleo::Commands; using namespace GpgME; using namespace QGpgME; class RevokeCertificationCommand::Private : public Command::Private { friend class ::Kleo::Commands::RevokeCertificationCommand; RevokeCertificationCommand *q_func() const { return static_cast(q); } public: explicit Private(RevokeCertificationCommand *qq, KeyListController *c); ~Private() override; void init(); private: void slotDialogAccepted(); void slotDialogRejected(); void slotResult(const Error &err); private: void ensureDialogCreated(); QGpgME::QuickJob *createJob(); private: Key certificationKey; Key certificationTarget; std::vector uids; QPointer dialog; QPointer job; }; RevokeCertificationCommand::Private *RevokeCertificationCommand::d_func() { return static_cast(d.get()); } const RevokeCertificationCommand::Private *RevokeCertificationCommand::d_func() const { return static_cast(d.get()); } #define d d_func() #define q q_func() RevokeCertificationCommand::Private::Private(RevokeCertificationCommand *qq, KeyListController *c) : Command::Private(qq, c) { } RevokeCertificationCommand::Private::~Private() { } void RevokeCertificationCommand::Private::init() { const std::vector keys_ = keys(); if (keys_.size() != 1) { qCWarning(KLEOPATRA_LOG) << "RevokeCertificationCommand::Private::init: Expected exactly one key, but got" << keys_.size(); return; } if (keys_.front().protocol() != GpgME::OpenPGP) { qCWarning(KLEOPATRA_LOG) << "RevokeCertificationCommand::Private::init: Expected OpenPGP key, but got" << keys_.front().protocolAsString(); return; } certificationTarget = keys_.front(); } void RevokeCertificationCommand::Private::slotDialogAccepted() { const auto certificationKey = dialog->selectedCertificationKey(); const auto selectedUserIDs = dialog->selectedUserIDs(); if (certificationKey.isNull() || selectedUserIDs.empty()) { qCDebug(KLEOPATRA_LOG) << "No certification key or no user IDs selected -> skipping revocation"; finished(); return; } job = createJob(); if (!job) { qCDebug(KLEOPATRA_LOG) << "Failed to create QuickJob"; finished(); return; } job->startRevokeSignature(certificationTarget, dialog->selectedCertificationKey(), dialog->selectedUserIDs()); } void RevokeCertificationCommand::Private::slotDialogRejected() { canceled(); } void RevokeCertificationCommand::Private::slotResult(const Error &err) { if (err.isCanceled()) { // do nothing } else if (err) { error(i18n("

An error occurred while trying to revoke the certification of

" "%1:

\t%2

", Formatting::formatForComboBox(certificationTarget), QString::fromUtf8(err.asString())), i18n("Revocation Error")); } else { information(i18n("Revocation successful."), i18n("Revocation Succeeded")); if (dialog && dialog->sendToServer()) { auto const cmd = new ExportOpenPGPCertsToServerCommand(certificationTarget); cmd->start(); } } finished(); } void RevokeCertificationCommand::Private::ensureDialogCreated() { if (dialog) { return; } dialog = new RevokeCertificationDialog; applyWindowID(dialog); dialog->setAttribute(Qt::WA_DeleteOnClose); connect(dialog, &QDialog::accepted, q, [this]() { slotDialogAccepted(); }); connect(dialog, &QDialog::rejected, q, [this]() { slotDialogRejected(); }); } QGpgME::QuickJob *RevokeCertificationCommand::Private::createJob() { Q_ASSERT(!job); Q_ASSERT(certificationTarget.protocol() == OpenPGP); const auto backend = QGpgME::openpgp(); if (!backend) { return nullptr; } QuickJob *const j = backend->quickJob(); if (j) { connect(j, &Job::progress, 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(this, c)) { d->init(); } RevokeCertificationCommand::RevokeCertificationCommand(const GpgME::Key &key) : Command(key, new Private(this, nullptr)) { d->init(); } RevokeCertificationCommand::RevokeCertificationCommand(const GpgME::UserID &uid) : Command(uid.parent(), new Private(this, nullptr)) { std::vector(1, uid).swap(d->uids); d->init(); } RevokeCertificationCommand::RevokeCertificationCommand(const std::vector &uids) : Command{uids.empty() ? Key{} : uids.front().parent(), new Private{this, nullptr}} { d->uids = uids; d->init(); } RevokeCertificationCommand::RevokeCertificationCommand(const GpgME::UserID::Signature &signature) : Command(signature.parent().parent(), new Private(this, nullptr)) { std::vector(1, signature.parent()).swap(d->uids); d->certificationKey = KeyCache::instance()->findByKeyIDOrFingerprint(signature.signerKeyID()); d->init(); } RevokeCertificationCommand::~RevokeCertificationCommand() { qCDebug(KLEOPATRA_LOG) << "~RevokeCertificationCommand()"; } // static bool RevokeCertificationCommand::isSupported() { return engineInfo(GpgEngine).engineVersion() >= "2.2.24"; } void RevokeCertificationCommand::doStart() { if (d->certificationTarget.isNull()) { d->finished(); return; } if (!Kleo::all_of(d->uids, userIDBelongsToKey(d->certificationTarget))) { qCWarning(KLEOPATRA_LOG) << "User ID <-> Key mismatch!"; d->finished(); return; } + // ensure that the certifications of the key have been loaded + if (d->certificationTarget.userID(0).numSignatures() == 0) { + d->certificationTarget.update(); + } + + // check if there are any certifications the user can revoke + const auto userIDsToConsider = d->uids.empty() ? d->certificationTarget.userIDs() : d->uids; + std::vector revokableUserIDs; + std::copy_if(userIDsToConsider.begin(), userIDsToConsider.end(), std::back_inserter(revokableUserIDs), &Kleo::userCanRevokeCertifications); + if (revokableUserIDs.empty()) { + const auto message = d->uids.empty() // + ? i18n("You cannot revoke any certifications of this key.") + : i18np("You cannot revoke any certifications of this user ID.", "You cannot revoke any certifications of these user IDs.", d->uids.size()); + KMessageBox::information(d->parentWidgetOrView(), message); + d->finished(); + return; + } + d->ensureDialogCreated(); Q_ASSERT(d->dialog); d->dialog->setCertificateToRevoke(d->certificationTarget); - if (!d->uids.empty()) { - d->dialog->setSelectedUserIDs(d->uids); - } + d->dialog->setSelectedUserIDs(revokableUserIDs); if (!d->certificationKey.isNull()) { d->dialog->setSelectedCertificationKey(d->certificationKey); } d->dialog->show(); } void RevokeCertificationCommand::doCancel() { qCDebug(KLEOPATRA_LOG) << "RevokeCertificationCommand::doCancel()"; if (d->job) { d->job->slotCancel(); } } #undef d #undef q #include "moc_revokecertificationcommand.cpp"