File indexing completed on 2024-06-23 05:13:45

0001 /* -*- mode: c++; c-basic-offset:4 -*-
0002     commands/revokekeycommand.cpp
0003 
0004     This file is part of Kleopatra, the KDE keymanager
0005     SPDX-FileCopyrightText: 2022 g10 Code GmbH
0006     SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
0007 
0008     SPDX-License-Identifier: GPL-2.0-or-later
0009 */
0010 
0011 #include <config-kleopatra.h>
0012 
0013 #include "command_p.h"
0014 #include "dialogs/revokekeydialog.h"
0015 #include "revokekeycommand.h"
0016 
0017 #include <Libkleo/Formatting>
0018 
0019 #include <KLocalizedString>
0020 
0021 #include <QGpgME/RevokeKeyJob>
0022 
0023 #include "kleopatra_debug.h"
0024 #include <QGpgME/Protocol>
0025 
0026 using namespace Kleo;
0027 using namespace GpgME;
0028 
0029 class RevokeKeyCommand::Private : public Command::Private
0030 {
0031     friend class ::RevokeKeyCommand;
0032     RevokeKeyCommand *q_func() const
0033     {
0034         return static_cast<RevokeKeyCommand *>(q);
0035     }
0036 
0037 public:
0038     explicit Private(RevokeKeyCommand *qq, KeyListController *c = nullptr);
0039     ~Private() override;
0040 
0041     void start();
0042     void cancel();
0043 
0044 private:
0045     void ensureDialogCreated();
0046     void onDialogAccepted();
0047     void onDialogRejected();
0048 
0049     std::unique_ptr<QGpgME::RevokeKeyJob> startJob();
0050     void onJobResult(const Error &err);
0051     void showError(const Error &err);
0052 
0053 private:
0054     Key key;
0055     QPointer<RevokeKeyDialog> dialog;
0056     QPointer<QGpgME::RevokeKeyJob> job;
0057 };
0058 
0059 RevokeKeyCommand::Private *RevokeKeyCommand::d_func()
0060 {
0061     return static_cast<Private *>(d.get());
0062 }
0063 const RevokeKeyCommand::Private *RevokeKeyCommand::d_func() const
0064 {
0065     return static_cast<const Private *>(d.get());
0066 }
0067 
0068 #define d d_func()
0069 #define q q_func()
0070 
0071 RevokeKeyCommand::Private::Private(RevokeKeyCommand *qq, KeyListController *c)
0072     : Command::Private{qq, c}
0073 {
0074 }
0075 
0076 RevokeKeyCommand::Private::~Private() = default;
0077 
0078 namespace
0079 {
0080 Key getKey(const std::vector<Key> &keys)
0081 {
0082     if (keys.size() != 1) {
0083         qCWarning(KLEOPATRA_LOG) << "Expected exactly one key, but got" << keys.size();
0084         return {};
0085     }
0086     const Key key = keys.front();
0087     if (key.protocol() != GpgME::OpenPGP) {
0088         qCWarning(KLEOPATRA_LOG) << "Expected OpenPGP key, but got" << Formatting::displayName(key.protocol()) << "key";
0089         return {};
0090     }
0091     return key;
0092 }
0093 }
0094 
0095 void RevokeKeyCommand::Private::start()
0096 {
0097     key = getKey(keys());
0098     if (key.isNull()) {
0099         finished();
0100         return;
0101     }
0102 
0103     if (key.isRevoked()) {
0104         information(i18nc("@info", "This key has already been revoked."));
0105         finished();
0106         return;
0107     }
0108 
0109     ensureDialogCreated();
0110     Q_ASSERT(dialog);
0111 
0112     dialog->setKey(key);
0113     dialog->show();
0114 }
0115 
0116 void RevokeKeyCommand::Private::cancel()
0117 {
0118     if (job) {
0119         job->slotCancel();
0120     }
0121     job.clear();
0122 }
0123 
0124 void RevokeKeyCommand::Private::ensureDialogCreated()
0125 {
0126     if (dialog) {
0127         return;
0128     }
0129 
0130     dialog = new RevokeKeyDialog;
0131     applyWindowID(dialog);
0132     dialog->setAttribute(Qt::WA_DeleteOnClose);
0133 
0134     connect(dialog, &QDialog::accepted, q, [this]() {
0135         onDialogAccepted();
0136     });
0137     connect(dialog, &QDialog::rejected, q, [this]() {
0138         onDialogRejected();
0139     });
0140 }
0141 
0142 void RevokeKeyCommand::Private::onDialogAccepted()
0143 {
0144     auto revokeJob = startJob();
0145     if (!revokeJob) {
0146         finished();
0147         return;
0148     }
0149     job = revokeJob.release();
0150 }
0151 
0152 void RevokeKeyCommand::Private::onDialogRejected()
0153 {
0154     canceled();
0155 }
0156 
0157 namespace
0158 {
0159 std::vector<std::string> toStdStrings(const QStringList &l)
0160 {
0161     std::vector<std::string> v;
0162     v.reserve(l.size());
0163     std::transform(std::begin(l), std::end(l), std::back_inserter(v), std::mem_fn(&QString::toStdString));
0164     return v;
0165 }
0166 
0167 auto descriptionToLines(const QString &description)
0168 {
0169     std::vector<std::string> lines;
0170     if (!description.isEmpty()) {
0171         lines = toStdStrings(description.split(QLatin1Char('\n')));
0172     }
0173     return lines;
0174 }
0175 }
0176 
0177 std::unique_ptr<QGpgME::RevokeKeyJob> RevokeKeyCommand::Private::startJob()
0178 {
0179     std::unique_ptr<QGpgME::RevokeKeyJob> revokeJob{QGpgME::openpgp()->revokeKeyJob()};
0180     Q_ASSERT(revokeJob);
0181 
0182     connect(revokeJob.get(), &QGpgME::RevokeKeyJob::result, q, [this](const GpgME::Error &err) {
0183         onJobResult(err);
0184     });
0185     connect(revokeJob.get(), &QGpgME::Job::jobProgress, q, &Command::progress);
0186 
0187     const auto description = descriptionToLines(dialog->description());
0188     const GpgME::Error err = revokeJob->start(key, dialog->reason(), description);
0189     if (err) {
0190         showError(err);
0191         return {};
0192     }
0193     Q_EMIT q->info(i18nc("@info:status", "Revoking key..."));
0194 
0195     return revokeJob;
0196 }
0197 
0198 void RevokeKeyCommand::Private::onJobResult(const Error &err)
0199 {
0200     if (err) {
0201         showError(err);
0202         finished();
0203         return;
0204     }
0205 
0206     if (!err.isCanceled()) {
0207         information(i18nc("@info", "The key was revoked successfully."), i18nc("@title:window", "Key Revoked"));
0208     }
0209     finished();
0210 }
0211 
0212 void RevokeKeyCommand::Private::showError(const Error &err)
0213 {
0214     error(xi18nc("@info",
0215                  "<para>An error occurred during the revocation:</para>"
0216                  "<para><message>%1</message></para>",
0217                  Formatting::errorAsString(err)),
0218           i18nc("@title:window", "Revocation Failed"));
0219 }
0220 
0221 RevokeKeyCommand::RevokeKeyCommand(QAbstractItemView *v, KeyListController *c)
0222     : Command{v, new Private{this, c}}
0223 {
0224 }
0225 
0226 RevokeKeyCommand::RevokeKeyCommand(const GpgME::Key &key)
0227     : Command{key, new Private{this}}
0228 {
0229 }
0230 
0231 RevokeKeyCommand::~RevokeKeyCommand() = default;
0232 
0233 void RevokeKeyCommand::doStart()
0234 {
0235     d->start();
0236 }
0237 
0238 void RevokeKeyCommand::doCancel()
0239 {
0240     d->cancel();
0241 }
0242 
0243 #undef d
0244 #undef q
0245 
0246 #include "moc_revokekeycommand.cpp"