Page MenuHome GnuPG

No OneTemporary

diff --git a/src/commands/refreshcertificatecommand.cpp b/src/commands/refreshcertificatecommand.cpp
index c4c728bae..dc632472a 100644
--- a/src/commands/refreshcertificatecommand.cpp
+++ b/src/commands/refreshcertificatecommand.cpp
@@ -1,305 +1,410 @@
/* -*- mode: c++; c-basic-offset:4 -*-
commands/refreshcertificatecommand.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 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 "refreshcertificatecommand.h"
#include "command_p.h"
#include <Libkleo/Formatting>
#include <KLocalizedString>
#include <KMessageBox>
#include <QGpgME/Protocol>
#if QGPGME_SUPPORTS_KEY_REFRESH
#include <QGpgME/RefreshKeysJob>
#include <QGpgME/ReceiveKeysJob>
#endif
+#if QGPGME_SUPPORTS_WKD_REFRESH_JOB
+#include <QGpgME/WKDRefreshJob>
+#endif
#include <gpgme++/importresult.h>
#include "kleopatra_debug.h"
using namespace Kleo;
using namespace GpgME;
class RefreshCertificateCommand::Private : public Command::Private
{
friend class ::RefreshCertificateCommand;
RefreshCertificateCommand *q_func() const
{
return static_cast<RefreshCertificateCommand *>(q);
}
public:
explicit Private(RefreshCertificateCommand *qq);
~Private() override;
void start();
void cancel();
#if QGPGME_SUPPORTS_KEY_REFRESH
- std::unique_ptr<QGpgME::ReceiveKeysJob> startOpenPGPJob();
+ std::unique_ptr<QGpgME::ReceiveKeysJob> startReceiveKeysJob();
std::unique_ptr<QGpgME::RefreshKeysJob> startSMIMEJob();
#endif
- void onOpenPGPJobResult(const ImportResult &result);
+#if QGPGME_SUPPORTS_WKD_REFRESH_JOB
+ std::unique_ptr<QGpgME::WKDRefreshJob> startWKDRefreshJob();
+#endif
+ void onReceiveKeysJobResult(const ImportResult &result);
+ void onWKDRefreshJobResult(const ImportResult &result);
void onSMIMEJobResult(const Error &err);
+ void showOpenPGPResult();
void showError(const Error &err);
private:
Key key;
#if QGPGME_SUPPORTS_KEY_REFRESH
QPointer<QGpgME::Job> job;
#endif
+ ImportResult receiveKeysResult;
+ ImportResult wkdRefreshResult;
};
RefreshCertificateCommand::Private *RefreshCertificateCommand::d_func()
{
return static_cast<Private *>(d.get());
}
const RefreshCertificateCommand::Private *RefreshCertificateCommand::d_func() const
{
return static_cast<const Private *>(d.get());
}
#define d d_func()
#define q q_func()
RefreshCertificateCommand::Private::Private(RefreshCertificateCommand *qq)
: Command::Private{qq}
{
}
RefreshCertificateCommand::Private::~Private() = default;
namespace
{
Key getKey(const std::vector<Key> &keys)
{
if (keys.size() != 1) {
qCWarning(KLEOPATRA_LOG) << "Expected exactly one key, but got" << keys.size();
return {};
}
const Key key = keys.front();
if (key.protocol() == GpgME::UnknownProtocol) {
qCWarning(KLEOPATRA_LOG) << "Key has unknown protocol";
return {};
}
return key;
}
}
void RefreshCertificateCommand::Private::start()
{
key = getKey(keys());
if (key.isNull()) {
finished();
return;
}
#if QGPGME_SUPPORTS_KEY_REFRESH
std::unique_ptr<QGpgME::Job> refreshJob;
switch (key.protocol()) {
case GpgME::OpenPGP:
- refreshJob = startOpenPGPJob();
+ refreshJob = startReceiveKeysJob();
break;
case GpgME::CMS:
refreshJob = startSMIMEJob();
break;
default:
; // cannot happen ;-)
}
if (!refreshJob) {
finished();
return;
}
job = refreshJob.release();
#else
error(i18n("The backend does not support updating individual certificates."));
finished();
#endif
}
void RefreshCertificateCommand::Private::cancel()
{
#if QGPGME_SUPPORTS_KEY_REFRESH
if (job) {
job->slotCancel();
}
job.clear();
#endif
}
#if QGPGME_SUPPORTS_KEY_REFRESH
-std::unique_ptr<QGpgME::ReceiveKeysJob> RefreshCertificateCommand::Private::startOpenPGPJob()
+std::unique_ptr<QGpgME::ReceiveKeysJob> RefreshCertificateCommand::Private::startReceiveKeysJob()
{
std::unique_ptr<QGpgME::ReceiveKeysJob> refreshJob{QGpgME::openpgp()->receiveKeysJob()};
Q_ASSERT(refreshJob);
- connect(refreshJob.get(), &QGpgME::ReceiveKeysJob::result,
- q, [this](const GpgME::ImportResult &result) {
- onOpenPGPJobResult(result);
- });
+ connect(refreshJob.get(), &QGpgME::ReceiveKeysJob::result, q, [this](const GpgME::ImportResult &result) {
+ onReceiveKeysJobResult(result);
+ });
#if QGPGME_JOB_HAS_NEW_PROGRESS_SIGNALS
connect(refreshJob.get(), &QGpgME::Job::jobProgress,
q, &Command::progress);
#else
connect(refreshJob.get(), &QGpgME::Job::progress,
q, [this](const QString &, int current, int total) { Q_EMIT q->progress(current, total); });
#endif
const GpgME::Error err = refreshJob->start({QString::fromLatin1(key.primaryFingerprint())});
if (err) {
showError(err);
return {};
}
Q_EMIT q->info(i18nc("@info:status", "Updating key..."));
return refreshJob;
}
std::unique_ptr<QGpgME::RefreshKeysJob> RefreshCertificateCommand::Private::startSMIMEJob()
{
std::unique_ptr<QGpgME::RefreshKeysJob> refreshJob{QGpgME::smime()->refreshKeysJob()};
Q_ASSERT(refreshJob);
connect(refreshJob.get(), &QGpgME::RefreshKeysJob::result,
q, [this](const GpgME::Error &err) {
onSMIMEJobResult(err);
});
#if QGPGME_JOB_HAS_NEW_PROGRESS_SIGNALS
connect(refreshJob.get(), &QGpgME::Job::jobProgress,
q, &Command::progress);
#else
connect(refreshJob.get(), &QGpgME::Job::progress,
q, [this](const QString &, int current, int total) { Q_EMIT q->progress(current, total); });
#endif
const GpgME::Error err = refreshJob->start({key});
if (err) {
showError(err);
return {};
}
Q_EMIT q->info(i18nc("@info:status", "Updating certificate..."));
return refreshJob;
}
#endif
+#if QGPGME_SUPPORTS_WKD_REFRESH_JOB
+std::unique_ptr<QGpgME::WKDRefreshJob> RefreshCertificateCommand::Private::startWKDRefreshJob()
+{
+ // check if key is eligible for WKD refresh, i.e. if any user ID has WKD as origin
+ const auto userIds = key.userIDs();
+ const auto eligibleForWKDRefresh = std::any_of(userIds.begin(), userIds.end(), [](const auto &userId) {
+ return !userId.isRevoked() && !userId.addrSpec().empty() && userId.origin() == Key::OriginWKD;
+ });
+ if (!eligibleForWKDRefresh) {
+ wkdRefreshResult = ImportResult{Error::fromCode(GPG_ERR_USER_1)};
+ return {};
+ }
+
+ std::unique_ptr<QGpgME::WKDRefreshJob> refreshJob{QGpgME::openpgp()->wkdRefreshJob()};
+ Q_ASSERT(refreshJob);
+
+ connect(refreshJob.get(), &QGpgME::WKDRefreshJob::result, q, [this](const GpgME::ImportResult &result) {
+ onWKDRefreshJobResult(result);
+ });
+ connect(refreshJob.get(), &QGpgME::Job::jobProgress, q, &Command::progress);
+
+ const GpgME::Error err = refreshJob->start({key});
+ if (err) {
+ wkdRefreshResult = ImportResult{err};
+ return {};
+ }
+ Q_EMIT q->info(i18nc("@info:status", "Updating key..."));
+
+ return refreshJob;
+}
+#endif
+
namespace
{
static auto informationOnChanges(const ImportResult &result)
{
QString text;
// if additional keys have been retrieved via WKD, then most of the below
// details are just a guess and may concern the additional keys instead of
// the refresh keys; this could only be clarified by a thorough comparison of
// unrefreshed and refreshed key
if (result.numUnchanged() == result.numConsidered()) {
// if numUnchanged < numConsidered, then it is not clear whether the refreshed key
// hasn't changed or whether another key retrieved via WKD hasn't changed
text = i18n("The key hasn't changed.");
} else if (result.newRevocations() > 0) {
// it is possible that a revoked key has been newly imported via WKD,
// but it is much more likely that the refreshed key was revoked
text = i18n("The key has been revoked.");
} else {
// it doesn't make much sense to list below details if the key has been revoked
text = i18n("The key has been updated.");
QStringList details;
if (result.newUserIDs() > 0) {
details.push_back(i18n("New user IDs: %1", result.newUserIDs()));
}
if (result.newSubkeys() > 0) {
details.push_back(i18n("New subkeys: %1", result.newSubkeys()));
}
if (result.newSignatures() > 0) {
details.push_back(i18n("New signatures: %1", result.newSignatures()));
}
if (!details.empty()) {
text += QLatin1String{"<br><br>"} + details.join(QLatin1String{"<br>"});
}
}
text = QLatin1String{"<p>"} + text + QLatin1String{"</p>"};
if (result.numImported() > 0) {
text += QLatin1String{"<p>"} + i18np("Additionally, one new key has been retrieved.",
"Additionally, %1 new keys have been retrieved.",
result.numImported()) + QLatin1String{"</p>"};
}
return text;
}
}
-void RefreshCertificateCommand::Private::onOpenPGPJobResult(const ImportResult &result)
+void RefreshCertificateCommand::Private::onReceiveKeysJobResult(const ImportResult &result)
{
- if (result.error()) {
- showError(result.error());
+ receiveKeysResult = result;
+
+ if (result.error().isCanceled()) {
finished();
return;
}
- if (!result.error().isCanceled()) {
- information(informationOnChanges(result),
- i18nc("@title:window", "Key Updated"));
+#if QGPGME_SUPPORTS_WKD_REFRESH_JOB
+ std::unique_ptr<QGpgME::Job> refreshJob = startWKDRefreshJob();
+ if (!refreshJob) {
+ showOpenPGPResult();
+ return;
+ }
+ job = refreshJob.release();
+#else
+ if (result.error()) {
+ showError(result.error());
+ } else {
+ information(informationOnChanges(result), i18nc("@title:window", "Key Updated"));
}
+
finished();
+#endif
+}
+
+void RefreshCertificateCommand::Private::onWKDRefreshJobResult(const ImportResult &result)
+{
+ wkdRefreshResult = result;
+
+ showOpenPGPResult();
}
void RefreshCertificateCommand::Private::onSMIMEJobResult(const Error &err)
{
if (err) {
showError(err);
finished();
return;
}
if (!err.isCanceled()) {
information(i18nc("@info", "The certificate has been updated."),
i18nc("@title:window", "Certificate Updated"));
}
finished();
}
+void RefreshCertificateCommand::Private::showOpenPGPResult()
+{
+ if (wkdRefreshResult.error().code() == GPG_ERR_USER_1 || wkdRefreshResult.error().isCanceled()) {
+ if (receiveKeysResult.error()) {
+ showError(receiveKeysResult.error());
+ } else {
+ information(informationOnChanges(receiveKeysResult), i18nc("@title:window", "Key Updated"));
+ }
+ finished();
+ return;
+ }
+
+ if (receiveKeysResult.error() && wkdRefreshResult.error()) {
+ error(xi18nc("@info",
+ "<para>Updating the certificate from a keyserver, an LDAP server, or Active Directory failed:</para>"
+ "<para><message>%1</message></para>"
+ "<para>Updating the certificate via Web Key Directory failed:</para>"
+ "<para><message>%2</message></para>",
+ Formatting::errorAsString(receiveKeysResult.error()),
+ Formatting::errorAsString(wkdRefreshResult.error())),
+ i18nc("@title:window", "Update Failed"));
+ finished();
+ return;
+ }
+
+ QString text;
+ text += QLatin1String{"<p><strong>"} + i18nc("@info", "Result of update from keyserver, LDAP server, or Active Directory") + QLatin1String{"</strong></p>"};
+ if (receiveKeysResult.error()) {
+ text += xi18nc("@info", "<para>The update failed: <message>%1</message></para>", Formatting::errorAsString(receiveKeysResult.error()));
+ } else {
+ text += informationOnChanges(receiveKeysResult);
+ }
+
+ text += QLatin1String{"<p><strong>"} + i18nc("@info", "Result of update via Web Key Directory") + QLatin1String{"</strong></p>"};
+ if (wkdRefreshResult.error()) {
+ text += xi18nc("@info", "<para>The update failed: <message>%1</message></para>", Formatting::errorAsString(wkdRefreshResult.error()));
+ } else {
+ text += informationOnChanges(wkdRefreshResult);
+ }
+
+ information(text, i18nc("@title:window", "Key Updated"));
+
+ finished();
+}
+
void RefreshCertificateCommand::Private::showError(const Error &err)
{
error(xi18nc("@info",
"<para>An error occurred while updating the certificate:</para>"
"<para><message>%1</message></para>",
Formatting::errorAsString(err)),
i18nc("@title:window", "Update Failed"));
}
RefreshCertificateCommand::RefreshCertificateCommand(const GpgME::Key &key)
: Command{key, new Private{this}}
{
}
RefreshCertificateCommand::~RefreshCertificateCommand() = default;
void RefreshCertificateCommand::doStart()
{
d->start();
}
void RefreshCertificateCommand::doCancel()
{
d->cancel();
}
#undef d
#undef q
#include "moc_refreshcertificatecommand.cpp"

File Metadata

Mime Type
text/x-diff
Expires
Sat, Jul 12, 10:23 AM (3 h, 30 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
84/c2/c6879205b729a5b57a5d0cb2b26c

Event Timeline