Commit 9e2dc624 authored by Ingo Klöcker's avatar Ingo Klöcker
Browse files

Add possibility to refresh an individual certificate

This allows the user to update a certificate explicitly. For OpenPGP keys
a --locate-external-keys and a --recv-keys is performed. For S/MIME
certificates the CRLs are updated and the certificate is revalidated.

GnuPG-bug-id: 5951
parent 6020c349
Pipeline #170860 passed with stage
in 4 minutes and 52 seconds
......@@ -111,6 +111,7 @@ if (QGpgme_VERSION VERSION_GREATER_EQUAL "1.17.0")
endif()
if (QGpgme_VERSION VERSION_GREATER_EQUAL "1.17.2")
set(QGPGME_SUPPORTS_KEY_REVOCATION 1)
set(QGPGME_SUPPORTS_KEY_REFRESH 1)
endif()
# Kdepimlibs packages
......
......@@ -50,3 +50,6 @@
/* Defined if QGpgME supports revoking own OpenPGP keys */
#cmakedefine QGPGME_SUPPORTS_KEY_REVOCATION 1
/* Defined if QGpgME supports refreshing keys */
#cmakedefine QGPGME_SUPPORTS_KEY_REFRESH 1
......@@ -220,6 +220,7 @@ set(_kleopatra_SRCS
commands/importcertificatefromkeyservercommand.cpp
commands/lookupcertificatescommand.cpp
commands/reloadkeyscommand.cpp
commands/refreshcertificatecommand.cpp
commands/refreshx509certscommand.cpp
commands/refreshopenpgpcertscommand.cpp
commands/deletecertificatescommand.cpp
......
/* -*- 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 <KLocalizedString>
#include <KMessageBox>
#include <QGpgME/Protocol>
#ifdef QGPGME_SUPPORTS_KEY_REFRESH
#include <QGpgME/RefreshKeysJob>
#endif
#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();
#ifdef QGPGME_SUPPORTS_KEY_REFRESH
std::unique_ptr<QGpgME::RefreshKeysJob> startJob();
#endif
void onJobResult(const Error &err);
void showError(const Error &err);
private:
Key key;
#ifdef QGPGME_SUPPORTS_KEY_REFRESH
QPointer<QGpgME::RefreshKeysJob> job;
#endif
};
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;
}
#ifdef QGPGME_SUPPORTS_KEY_REFRESH
auto refreshJob = startJob();
if (!refreshJob) {
finished();
return;
}
job = refreshJob.release();
#else
KMessageBox::error(parentWidgetOrView(), i18n("The backend does not support refreshing individual certificates."));
finished();
#endif
}
void RefreshCertificateCommand::Private::cancel()
{
#ifdef QGPGME_SUPPORTS_KEY_REFRESH
if (job) {
job->slotCancel();
}
job.clear();
#endif
}
#ifdef QGPGME_SUPPORTS_KEY_REFRESH
std::unique_ptr<QGpgME::RefreshKeysJob> RefreshCertificateCommand::Private::startJob()
{
auto jobFactory = key.protocol() == GpgME::OpenPGP ? QGpgME::openpgp() : QGpgME::smime();
Q_ASSERT(jobFactory);
std::unique_ptr<QGpgME::RefreshKeysJob> refreshJob{jobFactory->refreshKeysJob()};
Q_ASSERT(refreshJob);
connect(refreshJob.get(), &QGpgME::RefreshKeysJob::result,
q, [this](const GpgME::Error &err) {
onJobResult(err);
});
connect(refreshJob.get(), &QGpgME::Job::progress,
q, &Command::progress);
const GpgME::Error err = refreshJob->start({key});
if (err) {
showError(err);
return {};
}
Q_EMIT q->info(i18nc("@info:status", "Refreshing key..."));
return refreshJob;
}
#endif
void RefreshCertificateCommand::Private::onJobResult(const Error &err)
{
if (err) {
showError(err);
finished();
return;
}
if (!err.isCanceled()) {
information(i18nc("@info", "The key was refreshed successfully."),
i18nc("@title:window", "Key Refreshed"));
}
finished();
}
void RefreshCertificateCommand::Private::showError(const Error &err)
{
error(xi18nc("@info",
"<para>An error occurred while refreshing the key:</para>"
"<para><message>%1</message></para>",
QString::fromLocal8Bit(err.asString())),
i18nc("@title:window", "Refreshing 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"
/* -*- mode: c++; c-basic-offset:4 -*-
commands/refreshcertificatecommand.h
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
*/
#pragma once
#include "command.h"
namespace Kleo
{
class RefreshCertificateCommand : public Command
{
Q_OBJECT
public:
explicit RefreshCertificateCommand(const GpgME::Key &key);
~RefreshCertificateCommand() override;
private:
void doStart() override;
void doCancel() override;
private:
class Private;
inline Private *d_func();
inline const Private *d_func() const;
};
}
......@@ -23,6 +23,7 @@
#include "commands/changepassphrasecommand.h"
#include "commands/changeexpirycommand.h"
#include "commands/certifycertificatecommand.h"
#include "commands/refreshcertificatecommand.h"
#include "commands/revokecertificationcommand.h"
#include "commands/revokeuseridcommand.h"
#include "commands/adduseridcommand.h"
......@@ -89,6 +90,7 @@ public:
void revokeUserID(const GpgME::UserID &uid);
void genRevokeCert();
void refreshCertificate();
void certifyClicked();
void webOfTrustClicked();
void exportClicked();
......@@ -127,6 +129,7 @@ private:
QPushButton *changePassphraseBtn;
QPushButton *trustChainDetailsBtn;
QPushButton *genRevokeBtn;
QPushButton *refreshBtn;
QPushButton *certifyBtn;
QGroupBox *groupBox;
QGridLayout *gridLayout;
......@@ -187,6 +190,12 @@ private:
hboxLayout_1->addWidget(genRevokeBtn);
refreshBtn = new QPushButton{i18nc("@action:button", "Refresh"), parent};
#ifndef QGPGME_SUPPORTS_KEY_REFRESH
refreshBtn->setVisible(false);
#endif
hboxLayout_1->addWidget(refreshBtn);
certifyBtn = new QPushButton(i18nc("@action:button", "Certify"), parent);
hboxLayout_1->addWidget(certifyBtn);
......@@ -393,6 +402,8 @@ CertificateDetailsWidget::Private::Private(CertificateDetailsWidget *qq)
q, [this]() { showMoreDetails(); });
connect(ui.publishing, &QPushButton::pressed,
q, [this]() { publishCertificate(); });
connect(ui.refreshBtn, &QPushButton::clicked,
q, [this]() { refreshCertificate(); });
connect(ui.certifyBtn, &QPushButton::clicked,
q, [this]() { certifyClicked(); });
connect(ui.webOfTrustBtn, &QPushButton::clicked,
......@@ -585,6 +596,17 @@ void CertificateDetailsWidget::Private::genRevokeCert()
cmd->start();
}
void CertificateDetailsWidget::Private::refreshCertificate()
{
auto cmd = new Kleo::RefreshCertificateCommand{key};
QObject::connect(cmd, &Kleo::RefreshCertificateCommand::finished,
q, [this]() {
ui.refreshBtn->setEnabled(true);
});
ui.refreshBtn->setEnabled(false);
cmd->start();
}
void CertificateDetailsWidget::Private::certifyClicked()
{
auto cmd = new Kleo::Commands::CertifyCertificateCommand(key);
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment