Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F34113460
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
14 KB
Subscribers
None
View Options
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
Details
Attached
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
Attached To
rKLEOPATRA Kleopatra
Event Timeline
Log In to Comment