Page MenuHome GnuPG

No OneTemporary

diff --git a/src/commands/certifygroupcommand.cpp b/src/commands/certifygroupcommand.cpp
index 7c1d7a6a2..5e05074f1 100644
--- a/src/commands/certifygroupcommand.cpp
+++ b/src/commands/certifygroupcommand.cpp
@@ -1,311 +1,328 @@
/*
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/keys.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 <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 startNextCertification();
void createJob();
void slotResult(const Error &err);
void wrapUp();
private:
KeyGroup group;
std::vector<Key> certificates;
QPointer<CertifyCertificateDialog> dialog;
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();
startNextCertification();
}
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,
});
if (err.isCanceled()) {
finished();
return;
}
- if (err) {
- // for now we only deal with primary user IDs
- Q_ASSERT(jobData.userIds.size() == 1);
- const Key key = jobData.userIds.front().parent();
- const QString message = i18nc("@info", "<p>Certifying the certificate <b>%1</b> failed.</p>", Formatting::formatForComboBox(key))
- + xi18nc("@info", "<para>Error: <message>%1</message></para>", Formatting::errorAsString(err));
- error(message);
- }
if (!userIdsToCertify.empty()) {
job.clear();
jobData.userIds.clear();
startNextCertification();
return;
}
wrapUp();
}
-static QString resultSummary(int successCount, int totalCount)
+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) {
- return i18nc("@info", "The certification of all certificates failed.");
+ // 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(successCount, results.size()) + QLatin1String{"</p>"};
+ 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) {
- information(message, i18nc("@title:window", "Certification Completed"));
+ 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"
diff --git a/src/commands/command_p.h b/src/commands/command_p.h
index 3509d0b91..c5e364a0d 100644
--- a/src/commands/command_p.h
+++ b/src/commands/command_p.h
@@ -1,129 +1,141 @@
/* -*- mode: c++; c-basic-offset:4 -*-
commands/command_p.h
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include "command.h"
#include "view/keylistcontroller.h"
#include <Libkleo/KeyListModel>
#include <KLocalizedString>
#include <KMessageBox>
#include <QAbstractItemView>
#include <QPointer>
#include <gpgme++/key.h>
#include <algorithm>
#include <iterator>
class Kleo::Command::Private
{
friend class ::Kleo::Command;
protected:
Command *const q;
public:
explicit Private(Command *qq);
explicit Private(Command *qq, KeyListController *controller);
explicit Private(Command *qq, QWidget *parent);
virtual ~Private();
QAbstractItemView *view() const
{
return view_;
}
QWidget *parentWidgetOrView() const
{
if (parentWidget_) {
return parentWidget_;
} else {
return view_;
}
}
WId parentWId() const
{
return parentWId_;
}
GpgME::Key key() const
{
return keys_.empty() ? GpgME::Key{} : keys_.front();
}
std::vector<GpgME::Key> keys() const
{
return keys_;
}
void finished()
{
Q_EMIT q->finished();
doFinish();
if (autoDelete) {
q->deleteLater();
}
}
void canceled()
{
Q_EMIT q->canceled();
finished();
}
void error(const QString &text, const QString &caption = QString(), KMessageBox::Options options = KMessageBox::Notify) const
{
if (parentWId_) {
KMessageBox::errorWId(parentWId_, text, caption, options);
} else {
KMessageBox::error(parentWidgetOrView(), text, caption, options);
}
}
void success(const QString &text, const QString &caption = {}, KMessageBox::Options options = KMessageBox::Notify) const
{
static const QString noDontShowAgainName{};
const QString title = caption.isEmpty() ? i18nc("@title:window", "Success") : caption;
if (parentWId_) {
KMessageBox::informationWId(parentWId_, text, title, noDontShowAgainName, options);
} else {
KMessageBox::information(parentWidgetOrView(), text, title, noDontShowAgainName, options);
}
}
void information(const QString &text,
const QString &caption = QString(),
const QString &dontShowAgainName = QString(),
KMessageBox::Options options = KMessageBox::Notify) const
{
if (parentWId_) {
KMessageBox::informationWId(parentWId_, text, caption, dontShowAgainName, options);
} else {
KMessageBox::information(parentWidgetOrView(), text, caption, dontShowAgainName, options);
}
}
+ void informationList(const QString &text,
+ const QStringList &strlist,
+ const QString &title = {},
+ const QString &dontShowAgainName = {},
+ KMessageBox::Options options = KMessageBox::Notify) const
+ {
+ if (parentWId_) {
+ KMessageBox::informationListWId(parentWId_, text, strlist, title, dontShowAgainName, options);
+ } else {
+ KMessageBox::informationList(parentWidgetOrView(), text, strlist, title, dontShowAgainName, options);
+ }
+ }
void applyWindowID(QWidget *w) const
{
q->applyWindowID(w);
}
private:
virtual void doFinish()
{
}
private:
bool autoDelete : 1;
bool warnWhenRunningAtShutdown : 1;
std::vector<GpgME::Key> keys_;
QPointer<QAbstractItemView> view_;
QPointer<QWidget> parentWidget_;
WId parentWId_ = 0;
QPointer<KeyListController> controller_;
};

File Metadata

Mime Type
text/x-diff
Expires
Fri, Dec 5, 5:43 PM (1 d, 4 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
4e/17/17dbf0ea3578d5b026f05fa0cb1f

Event Timeline