Page MenuHome GnuPG

No OneTemporary

diff --git a/src/commands/certifygroupcommand.cpp b/src/commands/certifygroupcommand.cpp
index 48757392a..1fab287a1 100644
--- a/src/commands/certifygroupcommand.cpp
+++ b/src/commands/certifygroupcommand.cpp
@@ -1,351 +1,359 @@
/*
commands/certifygroupcommand.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2023 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "certifygroupcommand.h"
#include "command_p.h"
#include <commands/exportopenpgpcertstoservercommand.h>
#include <dialogs/certifycertificatedialog.h>
#include <utils/tags.h>
#include <Libkleo/Algorithm>
#include <Libkleo/Formatting>
#include <Libkleo/KeyGroup>
#include <Libkleo/KeyHelpers>
#include <QGpgME/Protocol>
#include <QGpgME/SignKeyJob>
#include <QDate>
#include <QProgressDialog>
#include <gpgme++/key.h>
using namespace Kleo;
using namespace Kleo::Commands;
using namespace GpgME;
namespace
{
struct CertificationResultData {
std::vector<UserID> userIds;
GpgME::Error error;
};
}
class CertifyGroupCommand::Private : public Command::Private
{
friend class ::Kleo::CertifyGroupCommand;
CertifyGroupCommand *q_func() const
{
return static_cast<CertifyGroupCommand *>(q);
}
public:
explicit Private(CertifyGroupCommand *qq);
~Private() override;
void start();
private:
void showDialog();
void certifyCertificates();
void setUpProgressDialog(int numberOfKeysToCertify);
void startNextCertification();
void createJob();
void slotResult(const Error &err);
void wrapUp();
private:
KeyGroup group;
std::vector<Key> certificates;
QPointer<CertifyCertificateDialog> dialog;
QPointer<QProgressDialog> progressDialog;
std::vector<UserID> userIdsToCertify;
struct {
Key certificationKey;
QDate expirationDate;
QString tags;
bool exportable = false;
bool sendToServer = false;
} certificationOptions;
struct {
std::vector<UserID> userIds;
} jobData;
QPointer<QGpgME::SignKeyJob> job;
std::vector<CertificationResultData> results;
};
CertifyGroupCommand::Private *CertifyGroupCommand::d_func()
{
return static_cast<Private *>(d.get());
}
const CertifyGroupCommand::Private *CertifyGroupCommand::d_func() const
{
return static_cast<const Private *>(d.get());
}
#define d d_func()
#define q q_func()
CertifyGroupCommand::Private::Private(CertifyGroupCommand *qq)
: Command::Private(qq)
{
}
CertifyGroupCommand::Private::~Private() = default;
void CertifyGroupCommand::Private::start()
{
if (!group.isNull()) {
const auto &groupKeys = group.keys();
certificates = std::vector<GpgME::Key>(groupKeys.begin(), groupKeys.end());
}
if (certificates.empty()) {
finished();
return;
}
if (!allKeysHaveProtocol(certificates, GpgME::OpenPGP)) {
const auto title = i18nc("@title:window", "Group Cannot Be Certified");
const auto message = i18nc("@info", "This group contains S/MIME certificates which cannot be certified.");
information(message, title);
finished();
return;
}
showDialog();
}
void CertifyGroupCommand::Private::showDialog()
{
dialog = new CertifyCertificateDialog;
dialog->setAttribute(Qt::WA_DeleteOnClose);
applyWindowID(dialog);
connect(dialog, &QDialog::accepted, q, [this]() {
certifyCertificates();
});
connect(dialog, &QDialog::rejected, q, [this]() {
canceled();
});
if (!group.isNull()) {
dialog->setGroupName(group.name());
}
dialog->setCertificatesToCertify(certificates);
dialog->show();
}
void CertifyGroupCommand::Private::certifyCertificates()
{
userIdsToCertify = dialog->selectedUserIDs();
if (userIdsToCertify.empty()) {
canceled();
return;
}
certificationOptions.certificationKey = dialog->selectedSecretKey();
certificationOptions.expirationDate = dialog->expirationDate();
certificationOptions.tags = dialog->tags();
certificationOptions.exportable = dialog->exportableCertificationSelected();
certificationOptions.sendToServer = dialog->sendToServer();
setUpProgressDialog(userIdsToCertify.size());
startNextCertification();
}
void CertifyGroupCommand::Private::setUpProgressDialog(int numberOfKeysToCertify)
{
if (progressDialog) {
return;
}
progressDialog = new QProgressDialog{parentWidgetOrView()};
progressDialog->setAttribute(Qt::WA_DeleteOnClose);
progressDialog->setModal(true);
progressDialog->setWindowTitle(i18nc("@title:window", "Certify Certificates"));
progressDialog->setLabelText(i18nc("@info:progress", "Certifying certificates ..."));
progressDialog->setMinimumDuration(1000);
progressDialog->setMaximum(numberOfKeysToCertify);
progressDialog->setValue(0);
connect(progressDialog, &QProgressDialog::canceled, q, &Command::cancel);
connect(q, &Command::finished, progressDialog, [this]() {
progressDialog->accept();
});
}
void CertifyGroupCommand::Private::startNextCertification()
{
Q_ASSERT(!userIdsToCertify.empty());
const auto nextKey = userIdsToCertify.front().parent();
// for now we only deal with primary user IDs
jobData.userIds = {userIdsToCertify.front()};
userIdsToCertify.erase(userIdsToCertify.begin());
const std::vector<unsigned int> userIdIndexes = {0};
createJob();
job->setUserIDsToSign(userIdIndexes);
if (const Error err = job->start(nextKey)) {
QMetaObject::invokeMethod(
q,
[this, err]() {
slotResult(err);
},
Qt::QueuedConnection);
}
}
void CertifyGroupCommand::Private::createJob()
{
Q_ASSERT(!job);
std::unique_ptr<QGpgME::SignKeyJob> newJob{QGpgME::openpgp()->signKeyJob()};
newJob->setDupeOk(true);
newJob->setSigningKey(certificationOptions.certificationKey);
newJob->setExportable(certificationOptions.exportable);
if (!certificationOptions.tags.isEmpty()) {
// do not set an empty remark to avoid an empty signature notation (GnuPG bug T5142)
newJob->setRemark(certificationOptions.tags);
}
if (!certificationOptions.expirationDate.isNull()) {
newJob->setExpirationDate(certificationOptions.expirationDate);
}
connect(newJob.get(), &QGpgME::SignKeyJob::result, q, [this](const GpgME::Error &result) {
slotResult(result);
});
job = newJob.release();
}
void CertifyGroupCommand::Private::slotResult(const Error &err)
{
results.push_back({
jobData.userIds,
err,
});
progressDialog->setValue(results.size());
if (err.isCanceled()) {
finished();
- return;
- }
-
- if (!userIdsToCertify.empty()) {
+ } else if (err && (results.size() == 1) //
+ && ((err.sourceID() == GPG_ERR_SOURCE_PINENTRY) //
+ || (err.code() == GPG_ERR_BAD_PASSPHRASE))) {
+ // abort the certification process on certain errors during the first
+ // certification, e.g. bad passphrase or pinentry timeout, where it's
+ // better to restart the whole process instead of continuing with the
+ // next certificate
+ error(xi18nc("@info",
+ "<para>The certification of the certificates failed.</para>"
+ "<para>Error: <message>%1</message></para>",
+ Formatting::errorAsString(results.front().error)));
+ finished();
+ } else if (!userIdsToCertify.empty()) {
job.clear();
jobData.userIds.clear();
startNextCertification();
- return;
+ } else {
+ wrapUp();
}
-
- wrapUp();
}
static QString resultSummary(const std::vector<CertificationResultData> &results)
{
Q_ASSERT(!results.empty());
const int totalCount = results.size();
const int successCount = Kleo::count_if(results, [](const auto &result) {
return !result.error;
});
if (successCount == totalCount) {
return i18nc("@info", "All certificates were certified successfully.");
}
if (successCount == 0) {
// we assume that all attempted certifications failed for the same reason
return xi18nc("@info",
"<para>The certification of all certificates failed.</para>"
"<para>Error: <message>%1</message></para>",
Formatting::errorAsString(results.front().error));
}
return i18ncp("@info", //
"1 of %2 certificates was certified successfully.",
"%1 of %2 certificates were certified successfully.",
successCount,
totalCount);
}
void CertifyGroupCommand::Private::wrapUp()
{
Q_ASSERT(userIdsToCertify.empty());
Q_ASSERT(!results.empty());
const int successCount = Kleo::count_if(results, [](const auto &result) {
return !result.error;
});
const bool sendToServer = (successCount > 0) && certificationOptions.exportable && certificationOptions.sendToServer;
QString message = QLatin1String{"<p>"} + resultSummary(results) + QLatin1String{"</p>"};
if (sendToServer) {
message += i18nc("@info", "<p>Next the certified certificates will be uploaded to the configured certificate directory.</p>");
}
const auto failedUserIdsInfo = std::accumulate(results.cbegin(), results.cend(), QStringList{}, [](auto failedUserIds, const auto &result) {
if (result.error) {
failedUserIds.push_back(i18nc("A user ID (an error description)",
"%1 (%2)",
Formatting::formatForComboBox(result.userIds.front().parent()),
Formatting::errorAsString(result.error)));
}
return failedUserIds;
});
if (successCount > 0) {
if (failedUserIdsInfo.size() > 0) {
message += i18nc("@info", "<p>Certifying the following certificates failed:</p>");
}
informationList(message, failedUserIdsInfo, i18nc("@title:window", "Certification Completed"));
} else {
error(message);
}
if (sendToServer) {
const auto certificatesToSendToServer = std::accumulate(results.cbegin(), results.cend(), std::vector<Key>{}, [](auto keys, const auto &result) {
if (!result.error) {
keys.push_back(result.userIds.front().parent());
}
return keys;
});
const auto cmd = new ExportOpenPGPCertsToServerCommand(certificatesToSendToServer);
cmd->start();
}
if (!certificationOptions.tags.isEmpty()) {
Tags::enableTags();
}
finished();
}
CertifyGroupCommand::CertifyGroupCommand(const KeyGroup &group)
: Command{new Private{this}}
{
d->group = group;
}
CertifyGroupCommand::~CertifyGroupCommand() = default;
void CertifyGroupCommand::doStart()
{
d->start();
}
void CertifyGroupCommand::doCancel()
{
if (d->dialog) {
d->dialog->close();
}
if (d->job) {
d->job->slotCancel();
}
}
#undef d
#undef q
#include "moc_certifygroupcommand.cpp"

File Metadata

Mime Type
text/x-diff
Expires
Tue, Apr 22, 3:56 AM (8 h, 42 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
8b/f6/d537a87b57b2abc7f11cedc8dcd0

Event Timeline