Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F20065054
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
37 KB
Subscribers
None
View Options
diff --git a/src/commands/exportcertificatecommand.cpp b/src/commands/exportcertificatecommand.cpp
index e89dd89fd..c22f227aa 100644
--- a/src/commands/exportcertificatecommand.cpp
+++ b/src/commands/exportcertificatecommand.cpp
@@ -1,368 +1,417 @@
/* -*- mode: c++; c-basic-offset:4 -*-
exportcertificatecommand.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB
SPDX-FileCopyrightText: 2021 g10 Code GmbH
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-kleopatra.h>
#include "exportcertificatecommand.h"
#include "fileoperationspreferences.h"
#include "command_p.h"
#include <utils/applicationstate.h>
#include <utils/filedialog.h>
+#include <Libkleo/Algorithm>
#include <Libkleo/Classify>
#include <Libkleo/Formatting>
+#include <Libkleo/KeyHelpers>
#include <QGpgME/ExportJob>
#include <QGpgME/Protocol>
#include <gpgme++/key.h>
#include <KLocalizedString>
#include <QSaveFile>
#include <QFileInfo>
#include <QMap>
#include <QPointer>
#include <algorithm>
#include <vector>
using namespace Kleo;
using namespace GpgME;
using namespace QGpgME;
class ExportCertificateCommand::Private : public Command::Private
{
friend class ::ExportCertificateCommand;
ExportCertificateCommand *q_func() const
{
return static_cast<ExportCertificateCommand *>(q);
}
public:
explicit Private(ExportCertificateCommand *qq, KeyListController *c);
~Private() override;
void startExportJob(GpgME::Protocol protocol, const std::vector<Key> &keys);
void cancelJobs();
void exportResult(const GpgME::Error &, const QByteArray &);
void showError(const GpgME::Error &error);
+ bool confirmExport(const std::vector<Key> &pgpKeys);
bool requestFileNames(GpgME::Protocol prot);
void finishedIfLastJob();
private:
QMap<GpgME::Protocol, QString> fileNames;
uint jobsPending = 0;
QMap<QObject *, QString> outFileForSender;
QPointer<ExportJob> cmsJob;
QPointer<ExportJob> pgpJob;
};
ExportCertificateCommand::Private *ExportCertificateCommand::d_func()
{
return static_cast<Private *>(d.get());
}
const ExportCertificateCommand::Private *ExportCertificateCommand::d_func() const
{
return static_cast<const Private *>(d.get());
}
#define d d_func()
#define q q_func()
ExportCertificateCommand::Private::Private(ExportCertificateCommand *qq, KeyListController *c)
: Command::Private(qq, c)
{
}
ExportCertificateCommand::Private::~Private()
{
}
ExportCertificateCommand::ExportCertificateCommand(KeyListController *p)
: Command(new Private(this, p))
{
}
ExportCertificateCommand::ExportCertificateCommand(QAbstractItemView *v, KeyListController *p)
: Command(v, new Private(this, p))
{
}
ExportCertificateCommand::ExportCertificateCommand(const Key &key)
: Command(key, new Private(this, nullptr))
{
}
ExportCertificateCommand::~ExportCertificateCommand()
{
}
void ExportCertificateCommand::setOpenPGPFileName(const QString &fileName)
{
if (!d->jobsPending) {
d->fileNames[OpenPGP] = fileName;
}
}
QString ExportCertificateCommand::openPGPFileName() const
{
return d->fileNames[OpenPGP];
}
void ExportCertificateCommand::setX509FileName(const QString &fileName)
{
if (!d->jobsPending) {
d->fileNames[CMS] = fileName;
}
}
QString ExportCertificateCommand::x509FileName() const
{
return d->fileNames[CMS];
}
void ExportCertificateCommand::doStart()
{
- std::vector<Key> keys = d->keys();
- if (keys.empty()) {
+ if (d->keys().empty()) {
+ d->finished();
return;
}
- const auto firstCms = std::partition(keys.begin(), keys.end(), [](const GpgME::Key &key) {
- return key.protocol() != GpgME::CMS;
- });
- std::vector<Key> openpgp, cms;
- std::copy(keys.begin(), firstCms, std::back_inserter(openpgp));
- std::copy(firstCms, keys.end(), std::back_inserter(cms));
- Q_ASSERT(!openpgp.empty() || !cms.empty());
- const bool haveBoth = !cms.empty() && !openpgp.empty();
- const GpgME::Protocol prot = haveBoth ? UnknownProtocol : (!cms.empty() ? CMS : OpenPGP);
+ const auto keys = Kleo::partitionKeysByProtocol(d->keys());
+
+ if (!keys.openpgp.empty() && !d->confirmExport(keys.openpgp)) {
+ d->canceled();
+ return;
+ }
+
+ const bool haveBoth = !keys.cms.empty() && !keys.openpgp.empty();
+ const GpgME::Protocol prot = haveBoth ? UnknownProtocol : (!keys.cms.empty() ? CMS : OpenPGP);
if (!d->requestFileNames(prot)) {
- Q_EMIT canceled();
- d->finished();
- } else {
- if (!openpgp.empty()) {
- d->startExportJob(GpgME::OpenPGP, openpgp);
+ d->canceled();
+ return;
+ }
+
+ if (!keys.openpgp.empty()) {
+ d->startExportJob(GpgME::OpenPGP, keys.openpgp);
+ }
+ if (!keys.cms.empty()) {
+ d->startExportJob(GpgME::CMS, keys.cms);
+ }
+}
+
+bool ExportCertificateCommand::Private::confirmExport(const std::vector<Key> &pgpKeys)
+{
+ auto notCertifiedKeys = std::accumulate(pgpKeys.cbegin(), pgpKeys.cend(), QStringList{}, [](auto keyNames, const auto &key) {
+ const bool allValidUserIDsAreCertifiedByUser = Kleo::all_of(key.userIDs(), [](const UserID &userId) {
+ return userId.isBad() || Kleo::userIDIsCertifiedByUser(userId);
+ });
+ if (!allValidUserIDsAreCertifiedByUser) {
+ keyNames.push_back(Formatting::formatForComboBox(key));
}
- if (!cms.empty()) {
- d->startExportJob(GpgME::CMS, cms);
+ return keyNames;
+ });
+ if (!notCertifiedKeys.empty()) {
+ if (pgpKeys.size() == 1) {
+ const auto answer = KMessageBox::warningContinueCancel( //
+ parentWidgetOrView(),
+ xi18nc("@info",
+ "<para>You haven't certified all valid user IDs of this certificate "
+ "with an exportable certification. People relying on your certifications "
+ "may not be able to verify the certificate.</para>"
+ "<para>Do you want to continue the export?</para>"),
+ i18nc("@title:window", "Confirm Certificate Export"),
+ KGuiItem{i18nc("@action:button", "Export Certificate")},
+ KStandardGuiItem::cancel(),
+ QStringLiteral("confirm-export-of-uncertified-keys"));
+ return answer == KMessageBox::Continue;
+ } else {
+ std::sort(notCertifiedKeys.begin(), notCertifiedKeys.end());
+ const auto answer = KMessageBox::warningContinueCancelList( //
+ parentWidgetOrView(),
+ xi18nc("@info",
+ "<para>You haven't certified all valid user IDs of the certificates listed below "
+ "with exportable certifications. People relying on your certifications "
+ "may not be able to verify the certificates.</para>"
+ "<para>Do you want to continue the export?</para>"),
+ notCertifiedKeys,
+ i18nc("@title:window", "Confirm Certificate Export"),
+ KGuiItem{i18nc("@action:button", "Export Certificates")},
+ KStandardGuiItem::cancel(),
+ QStringLiteral("confirm-export-of-uncertified-keys"));
+ return answer == KMessageBox::Continue;
}
}
+
+ return true;
}
bool ExportCertificateCommand::Private::requestFileNames(GpgME::Protocol protocol)
{
if (protocol == UnknownProtocol) {
if (!fileNames[GpgME::OpenPGP].isEmpty() && !fileNames[GpgME::CMS].isEmpty()) {
return true;
}
/* Unknown protocol ask for first PGP Export file name */
if (fileNames[GpgME::OpenPGP].isEmpty() && !requestFileNames(GpgME::OpenPGP)) {
return false;
}
/* And then for CMS */
return requestFileNames(GpgME::CMS);
}
if (!fileNames[protocol].isEmpty()) {
return true;
}
const auto lastDir = ApplicationState::lastUsedExportDirectory();
QString proposedFileName = lastDir + QLatin1Char('/');
if (keys().size() == 1) {
const bool usePGPFileExt = FileOperationsPreferences().usePGPFileExt();
const auto key = keys().front();
auto name = Formatting::prettyName(key);
if (name.isEmpty()) {
name = Formatting::prettyEMail(key);
}
const auto asciiArmoredCertificateClass = (protocol == OpenPGP ? Class::OpenPGP : Class::CMS) | Class::Ascii | Class::Certificate;
/* Not translated so it's better to use in tutorials etc. */
proposedFileName += QStringLiteral("%1_%2_public.%3")
.arg(name)
.arg(Formatting::prettyKeyID(key.shortKeyID()))
.arg(outputFileExtension(asciiArmoredCertificateClass, usePGPFileExt));
}
if (protocol == GpgME::CMS) {
if (!fileNames[GpgME::OpenPGP].isEmpty()) {
/* If the user has already selected a PGP file name then use that as basis
* for a proposal for the S/MIME file. */
proposedFileName = fileNames[GpgME::OpenPGP];
const int idx = proposedFileName.size() - 4;
if (proposedFileName.endsWith(QLatin1String(".asc"))) {
proposedFileName.replace(idx, 4, QLatin1String(".pem"));
}
if (proposedFileName.endsWith(QLatin1String(".gpg")) || proposedFileName.endsWith(QLatin1String(".pgp"))) {
proposedFileName.replace(idx, 4, QLatin1String(".der"));
}
}
}
if (proposedFileName.isEmpty()) {
proposedFileName = lastDir;
proposedFileName += i18nc("A generic filename for exported certificates", "certificates");
proposedFileName += protocol == GpgME::OpenPGP ? QStringLiteral(".asc") : QStringLiteral(".pem");
}
auto fname = FileDialog::getSaveFileNameEx(parentWidgetOrView(),
i18nc("1 is protocol", "Export %1 Certificates", Formatting::displayName(protocol)),
QStringLiteral("imp"),
proposedFileName,
protocol == GpgME::OpenPGP ? i18n("OpenPGP Certificates") + QLatin1String(" (*.asc *.gpg *.pgp)")
: i18n("S/MIME Certificates") + QLatin1String(" (*.pem *.der)"));
if (!fname.isEmpty() && protocol == GpgME::CMS && fileNames[GpgME::OpenPGP] == fname) {
KMessageBox::error(parentWidgetOrView(),
i18n("You have to select different filenames for different protocols."),
i18nc("@title:window", "Export Error"));
return false;
}
const QFileInfo fi(fname);
if (fi.suffix().isEmpty()) {
fname += protocol == GpgME::OpenPGP ? QStringLiteral(".asc") : QStringLiteral(".pem");
}
fileNames[protocol] = fname;
ApplicationState::setLastUsedExportDirectory(fi.absolutePath());
return !fname.isEmpty();
}
void ExportCertificateCommand::Private::startExportJob(GpgME::Protocol protocol, const std::vector<Key> &keys)
{
Q_ASSERT(protocol != GpgME::UnknownProtocol);
const QGpgME::Protocol *const backend = (protocol == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime();
Q_ASSERT(backend);
const QString fileName = fileNames[protocol];
const bool binary = protocol == GpgME::OpenPGP
? fileName.endsWith(QLatin1String(".gpg"), Qt::CaseInsensitive) || fileName.endsWith(QLatin1String(".pgp"), Qt::CaseInsensitive)
: fileName.endsWith(QLatin1String(".der"), Qt::CaseInsensitive);
std::unique_ptr<ExportJob> job(backend->publicKeyExportJob(!binary));
Q_ASSERT(job.get());
connect(job.get(), &QGpgME::ExportJob::result, q, [this](const GpgME::Error &result, const QByteArray &keyData) {
exportResult(result, keyData);
});
#if QGPGME_JOB_HAS_NEW_PROGRESS_SIGNALS
connect(job.get(), &QGpgME::Job::jobProgress, q, &Command::progress);
#else
connect(job.get(), &QGpgME::Job::progress, q, [this](const QString &, int current, int total) {
Q_EMIT q->progress(current, total);
});
#endif
QStringList fingerprints;
fingerprints.reserve(keys.size());
for (const Key &i : keys) {
fingerprints << QLatin1String(i.primaryFingerprint());
}
const GpgME::Error err = job->start(fingerprints);
if (err) {
showError(err);
finished();
return;
}
Q_EMIT q->info(i18n("Exporting certificates..."));
++jobsPending;
const QPointer<ExportJob> exportJob(job.release());
outFileForSender[exportJob.data()] = fileName;
(protocol == CMS ? cmsJob : pgpJob) = exportJob;
}
void ExportCertificateCommand::Private::showError(const GpgME::Error &err)
{
Q_ASSERT(err);
const QString msg = i18n(
"<qt><p>An error occurred while trying to export "
"the certificate:</p>"
"<p><b>%1</b></p></qt>",
Formatting::errorAsString(err));
error(msg, i18n("Certificate Export Failed"));
}
void ExportCertificateCommand::doCancel()
{
d->cancelJobs();
}
void ExportCertificateCommand::Private::finishedIfLastJob()
{
if (jobsPending <= 0) {
finished();
}
}
static bool write_complete(QIODevice &iod, const QByteArray &data)
{
qint64 total = 0;
qint64 toWrite = data.size();
while (total < toWrite) {
const qint64 written = iod.write(data.data() + total, toWrite);
if (written < 0) {
return false;
}
total += written;
toWrite -= written;
}
return true;
}
void ExportCertificateCommand::Private::exportResult(const GpgME::Error &err, const QByteArray &data)
{
Q_ASSERT(jobsPending > 0);
--jobsPending;
Q_ASSERT(outFileForSender.contains(q->sender()));
const QString outFile = outFileForSender[q->sender()];
if (err) {
showError(err);
finishedIfLastJob();
return;
}
QSaveFile savefile(outFile);
// TODO: use KIO
const QString writeErrorMsg = i18n("Could not write to file %1.", outFile);
const QString errorCaption = i18n("Certificate Export Failed");
if (!savefile.open(QIODevice::WriteOnly)) {
error(writeErrorMsg, errorCaption);
finishedIfLastJob();
return;
}
if (!write_complete(savefile, data) || !savefile.commit()) {
error(writeErrorMsg, errorCaption);
}
finishedIfLastJob();
}
void ExportCertificateCommand::Private::cancelJobs()
{
if (cmsJob) {
cmsJob->slotCancel();
}
if (pgpJob) {
pgpJob->slotCancel();
}
}
#undef d
#undef q
#include "moc_exportcertificatecommand.cpp"
diff --git a/src/commands/exportgroupscommand.cpp b/src/commands/exportgroupscommand.cpp
index c7d53ec40..c89925637 100644
--- a/src/commands/exportgroupscommand.cpp
+++ b/src/commands/exportgroupscommand.cpp
@@ -1,339 +1,341 @@
/* -*- mode: c++; c-basic-offset:4 -*-
exportgroupscommand.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2021 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 "command_p.h"
#include "exportgroupscommand.h"
#include "utils/filedialog.h"
#include <utils/applicationstate.h>
#include <Libkleo/Algorithm>
#include <Libkleo/Formatting>
#include <Libkleo/KeyGroup>
#include <Libkleo/KeyGroupImportExport>
#include <Libkleo/KeyHelpers>
#include <KLocalizedString>
#include <KSharedConfig>
#include <QGpgME/ExportJob>
#include <QGpgME/Protocol>
#include <QFileInfo>
#include <QStandardPaths>
#include <memory>
#include <vector>
using namespace Kleo;
using namespace GpgME;
using namespace QGpgME;
namespace
{
static const QString certificateGroupFileExtension{QLatin1String{".kgrp"}};
QString proposeFilename(const std::vector<KeyGroup> &groups)
{
QString filename;
filename = ApplicationState::lastUsedExportDirectory() + QLatin1Char{'/'};
if (groups.size() == 1) {
filename += groups.front().name().replace(QLatin1Char{'/'}, QLatin1Char{'_'});
} else {
filename += i18nc("A generic filename for exported certificate groups", "certificate groups");
}
return filename + certificateGroupFileExtension;
}
QString requestFilename(QWidget *parent, const std::vector<KeyGroup> &groups)
{
const QString proposedFilename = proposeFilename(groups);
auto filename =
FileDialog::getSaveFileNameEx(parent,
i18ncp("@title:window", "Export Certificate Group", "Export Certificate Groups", groups.size()),
QStringLiteral("imp"),
proposedFilename,
i18nc("filename filter like Certificate Groups (*.foo)", "Certificate Groups (*%1)", certificateGroupFileExtension));
if (!filename.isEmpty()) {
const QFileInfo fi{filename};
if (fi.suffix().isEmpty()) {
filename += certificateGroupFileExtension;
}
ApplicationState::setLastUsedExportDirectory(filename);
}
return filename;
}
}
class ExportGroupsCommand::Private : public Command::Private
{
friend class ::ExportGroupsCommand;
ExportGroupsCommand *q_func() const
{
return static_cast<ExportGroupsCommand *>(q);
}
public:
explicit Private(ExportGroupsCommand *qq);
~Private() override;
void start();
bool confirmExport();
bool exportGroups();
bool startExportJob(GpgME::Protocol protocol, const std::vector<Key> &keys);
void onExportJobResult(const QGpgME::Job *job, const GpgME::Error &err, const QByteArray &keyData);
void cancelJobs();
void showError(const GpgME::Error &err);
void finishedIfLastJob(const QGpgME::Job *job);
private:
std::vector<KeyGroup> groups;
QString filename;
std::vector<QPointer<QGpgME::Job>> exportJobs;
};
ExportGroupsCommand::Private *ExportGroupsCommand::d_func()
{
return static_cast<Private *>(d.get());
}
const ExportGroupsCommand::Private *ExportGroupsCommand::d_func() const
{
return static_cast<const Private *>(d.get());
}
#define d d_func()
#define q q_func()
ExportGroupsCommand::Private::Private(ExportGroupsCommand *qq)
: Command::Private(qq)
{
}
ExportGroupsCommand::Private::~Private() = default;
void ExportGroupsCommand::Private::start()
{
if (groups.empty()) {
finished();
return;
}
if (!confirmExport()) {
- finished();
+ canceled();
return;
}
filename = requestFilename(parentWidgetOrView(), groups);
if (filename.isEmpty()) {
canceled();
return;
}
const auto groupKeys = std::accumulate(std::begin(groups), std::end(groups), KeyGroup::Keys{}, [](auto allKeys, const auto &group) {
const auto keys = group.keys();
allKeys.insert(std::begin(keys), std::end(keys));
return allKeys;
});
-
- std::vector<Key> openpgpKeys;
- std::vector<Key> cmsKeys;
- std::partition_copy(std::begin(groupKeys), std::end(groupKeys), std::back_inserter(openpgpKeys), std::back_inserter(cmsKeys), [](const GpgME::Key &key) {
- return key.protocol() == GpgME::OpenPGP;
- });
+ const auto keys = Kleo::partitionKeysByProtocol(groupKeys);
// remove/overwrite existing file
if (QFile::exists(filename) && !QFile::remove(filename)) {
error(xi18n("Cannot overwrite existing <filename>%1</filename>.", filename), i18nc("@title:window", "Export Failed"));
finished();
return;
}
if (!exportGroups()) {
finished();
return;
}
- if (!openpgpKeys.empty()) {
- if (!startExportJob(GpgME::OpenPGP, openpgpKeys)) {
+ if (!keys.openpgp.empty()) {
+ if (!startExportJob(GpgME::OpenPGP, keys.openpgp)) {
finished();
return;
}
}
- if (!cmsKeys.empty()) {
- if (!startExportJob(GpgME::CMS, cmsKeys)) {
+ if (!keys.cms.empty()) {
+ if (!startExportJob(GpgME::CMS, keys.cms)) {
finishedIfLastJob(nullptr);
}
}
}
bool ExportGroupsCommand::Private::confirmExport()
{
auto notFullyCertifiedGroups = std::accumulate(groups.cbegin(), groups.cend(), QStringList{}, [](auto groupNames, const auto &group) {
const bool allOpenPGPKeysAreCertifiedByUser = Kleo::all_of(group.keys(), [](const Key &key) {
// we only check the primary user ID of OpenPGP keys because currently group certification only certifies the primary user ID
return key.protocol() != GpgME::OpenPGP || Kleo::userIDIsCertifiedByUser(key.userID(0));
});
if (!allOpenPGPKeysAreCertifiedByUser) {
groupNames.push_back(group.name());
}
return groupNames;
});
if (!notFullyCertifiedGroups.empty()) {
if (groups.size() == 1) {
- const auto answer = KMessageBox::questionTwoActions(parentWidgetOrView(),
- xi18nc("@info",
- "<para>You haven't certified all OpenPGP certificates in this group.</para>"
- "<para>Do you want to continue the export?</para>"),
- i18nc("@title:window", "Confirm Group Export"),
- KGuiItem{i18nc("@action:button", "Export Group")},
- KStandardGuiItem::cancel());
- return answer == KMessageBox::PrimaryAction;
+ const auto answer = KMessageBox::warningContinueCancel( //
+ parentWidgetOrView(),
+ xi18nc("@info",
+ "<para>You haven't certified all OpenPGP certificates in this group "
+ "with an exportable certification. People relying on your certifications "
+ "may not be able to verify the certificates.</para>"
+ "<para>Do you want to continue the export?</para>"),
+ i18nc("@title:window", "Confirm Group Export"),
+ KGuiItem{i18nc("@action:button", "Export Group")},
+ KStandardGuiItem::cancel(),
+ QStringLiteral("confirm-export-of-uncertified-groups"));
+ return answer == KMessageBox::Continue;
} else {
std::sort(notFullyCertifiedGroups.begin(), notFullyCertifiedGroups.end());
- const auto answer =
- KMessageBox::questionTwoActionsList(parentWidgetOrView(),
- xi18nc("@info",
- "<para>You haven't certified all OpenPGP certificates in the groups listed below.</para>"
- "<para>Do you want to continue the export?</para>"),
- notFullyCertifiedGroups,
- i18nc("@title:window", "Confirm Group Export"),
- KGuiItem{i18nc("@action:button", "Export Groups")},
- KStandardGuiItem::cancel());
- return answer == KMessageBox::PrimaryAction;
+ const auto answer = KMessageBox::warningContinueCancelList( //
+ parentWidgetOrView(),
+ xi18nc("@info",
+ "<para>You haven't certified all OpenPGP certificates in the groups listed below "
+ "with exportable certifications. People relying on your certifications "
+ "may not be able to verify the certificates.</para>"
+ "<para>Do you want to continue the export?</para>"),
+ notFullyCertifiedGroups,
+ i18nc("@title:window", "Confirm Group Export"),
+ KGuiItem{i18nc("@action:button", "Export Groups")},
+ KStandardGuiItem::cancel(),
+ QStringLiteral("confirm-export-of-uncertified-groups"));
+ return answer == KMessageBox::Continue;
}
}
return true;
}
bool ExportGroupsCommand::Private::exportGroups()
{
const auto result = writeKeyGroups(filename, groups);
if (result != WriteKeyGroups::Success) {
error(xi18n("Writing groups to file <filename>%1</filename> failed.", filename), i18nc("@title:window", "Export Failed"));
}
return result == WriteKeyGroups::Success;
}
bool ExportGroupsCommand::Private::startExportJob(GpgME::Protocol protocol, const std::vector<Key> &keys)
{
const QGpgME::Protocol *const backend = (protocol == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime();
Q_ASSERT(backend);
std::unique_ptr<ExportJob> jobOwner(backend->publicKeyExportJob(/*armor=*/true));
auto job = jobOwner.get();
Q_ASSERT(job);
connect(job, &ExportJob::result, q, [this, job](const GpgME::Error &err, const QByteArray &keyData) {
onExportJobResult(job, err, keyData);
});
#if QGPGME_JOB_HAS_NEW_PROGRESS_SIGNALS
connect(job, &QGpgME::Job::jobProgress, q, &Command::progress);
#else
connect(job, &QGpgME::Job::progress, q, [this](const QString &, int current, int total) {
Q_EMIT q->progress(current, total);
});
#endif
const GpgME::Error err = job->start(Kleo::getFingerprints(keys));
if (err) {
showError(err);
return false;
}
Q_EMIT q->info(i18n("Exporting certificate groups..."));
exportJobs.push_back(jobOwner.release());
return true;
}
void ExportGroupsCommand::Private::onExportJobResult(const QGpgME::Job *job, const GpgME::Error &err, const QByteArray &keyData)
{
Q_ASSERT(Kleo::contains(exportJobs, job));
if (err) {
showError(err);
finishedIfLastJob(job);
return;
}
QFile f{filename};
if (!f.open(QIODevice::WriteOnly | QIODevice::Append)) {
error(xi18n("Cannot open file <filename>%1</filename> for writing.", filename), i18nc("@title:window", "Export Failed"));
finishedIfLastJob(job);
return;
}
const auto bytesWritten = f.write(keyData);
if (bytesWritten != keyData.size()) {
error(xi18n("Writing certificates to file <filename>%1</filename> failed.", filename), i18nc("@title:window", "Export Failed"));
}
finishedIfLastJob(job);
}
void ExportGroupsCommand::Private::showError(const GpgME::Error &err)
{
error(xi18n("<para>An error occurred during the export:</para>"
"<para><message>%1</message></para>",
Formatting::errorAsString(err)),
i18nc("@title:window", "Export Failed"));
}
void ExportGroupsCommand::Private::finishedIfLastJob(const QGpgME::Job *job)
{
if (job) {
exportJobs.erase(std::remove(exportJobs.begin(), exportJobs.end(), job), exportJobs.end());
}
if (exportJobs.size() == 0) {
finished();
}
}
void ExportGroupsCommand::Private::cancelJobs()
{
std::for_each(std::cbegin(exportJobs), std::cend(exportJobs), [](const auto &job) {
if (job) {
job->slotCancel();
}
});
exportJobs.clear();
}
ExportGroupsCommand::ExportGroupsCommand(const std::vector<KeyGroup> &groups)
: Command{new Private{this}}
{
d->groups = groups;
}
ExportGroupsCommand::~ExportGroupsCommand() = default;
void ExportGroupsCommand::doStart()
{
d->start();
}
void ExportGroupsCommand::doCancel()
{
d->cancelJobs();
}
#undef d
#undef q
#include "moc_exportgroupscommand.cpp"
diff --git a/src/commands/exportopenpgpcertstoservercommand.cpp b/src/commands/exportopenpgpcertstoservercommand.cpp
index 14ce1f63b..9e1ea05e9 100644
--- a/src/commands/exportopenpgpcertstoservercommand.cpp
+++ b/src/commands/exportopenpgpcertstoservercommand.cpp
@@ -1,138 +1,191 @@
/* -*- mode: c++; c-basic-offset:4 -*-
commands/exportopenpgpcertstoservercommand.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-kleopatra.h>
#include "exportopenpgpcertstoservercommand.h"
#include "command_p.h"
+#include <Libkleo/Algorithm>
+#include <Libkleo/Formatting>
#include <Libkleo/GnuPG>
+#include <Libkleo/KeyHelpers>
#include <gpgme++/key.h>
#include <KLocalizedString>
#include <KMessageBox>
using namespace Kleo;
using namespace Kleo::Commands;
using namespace GpgME;
ExportOpenPGPCertsToServerCommand::ExportOpenPGPCertsToServerCommand(KeyListController *c)
: GnuPGProcessCommand(c)
{
}
ExportOpenPGPCertsToServerCommand::ExportOpenPGPCertsToServerCommand(QAbstractItemView *v, KeyListController *c)
: GnuPGProcessCommand(v, c)
{
}
ExportOpenPGPCertsToServerCommand::ExportOpenPGPCertsToServerCommand(const Key &key)
: GnuPGProcessCommand(key)
{
}
ExportOpenPGPCertsToServerCommand::ExportOpenPGPCertsToServerCommand(const std::vector<Key> &keys)
: GnuPGProcessCommand(keys)
{
}
ExportOpenPGPCertsToServerCommand::~ExportOpenPGPCertsToServerCommand() = default;
+static bool confirmExport(const std::vector<Key> &pgpKeys, QWidget *parentWidget)
+{
+ auto notCertifiedKeys = std::accumulate(pgpKeys.cbegin(), pgpKeys.cend(), QStringList{}, [](auto keyNames, const auto &key) {
+ const bool allValidUserIDsAreCertifiedByUser = Kleo::all_of(key.userIDs(), [](const UserID &userId) {
+ return userId.isBad() || Kleo::userIDIsCertifiedByUser(userId);
+ });
+ if (!allValidUserIDsAreCertifiedByUser) {
+ keyNames.push_back(Formatting::formatForComboBox(key));
+ }
+ return keyNames;
+ });
+ if (!notCertifiedKeys.empty()) {
+ if (pgpKeys.size() == 1) {
+ const auto answer = KMessageBox::warningContinueCancel( //
+ parentWidget,
+ xi18nc("@info",
+ "<para>You haven't certified all valid user IDs of this certificate "
+ "with an exportable certification. People relying on your certifications "
+ "may not be able to verify the certificate.</para>"
+ "<para>Do you want to continue the export?</para>"),
+ i18nc("@title:window", "Confirm Certificate Export"),
+ KGuiItem{i18nc("@action:button", "Export Certificate")},
+ KStandardGuiItem::cancel(),
+ QStringLiteral("confirm-upload-of-uncertified-keys"));
+ return answer == KMessageBox::Continue;
+ } else {
+ std::sort(notCertifiedKeys.begin(), notCertifiedKeys.end());
+ const auto answer = KMessageBox::warningContinueCancelList( //
+ parentWidget,
+ xi18nc("@info",
+ "<para>You haven't certified all valid user IDs of the certificates listed below "
+ "with exportable certifications. People relying on your certifications "
+ "may not be able to verify the certificates.</para>"
+ "<para>Do you want to continue the export?</para>"),
+ notCertifiedKeys,
+ i18nc("@title:window", "Confirm Certificate Export"),
+ KGuiItem{i18nc("@action:button", "Export Certificates")},
+ KStandardGuiItem::cancel(),
+ QStringLiteral("confirm-upload-of-uncertified-keys"));
+ return answer == KMessageBox::Continue;
+ }
+ }
+
+ return true;
+}
+
bool ExportOpenPGPCertsToServerCommand::preStartHook(QWidget *parent) const
{
- if (!haveKeyserverConfigured())
+ if (!haveKeyserverConfigured()) {
if (KMessageBox::warningContinueCancel(parent,
xi18nc("@info",
"<para>No OpenPGP directory services have been configured.</para>"
"<para>Since none is configured, <application>Kleopatra</application> will use "
"<resource>keys.gnupg.net</resource> as the server to export to.</para>"
"<para>You can configure OpenPGP directory servers in <application>Kleopatra</application>'s "
"configuration dialog.</para>"
"<para>Do you want to continue with <resource>keys.gnupg.net</resource> "
"as the server to export to?</para>"),
i18nc("@title:window", "OpenPGP Certificate Export"),
- KStandardGuiItem::cont(),
+ KGuiItem{i18ncp("@action:button", "Export Certificate", "Export Certificates", d->keys().size())},
KStandardGuiItem::cancel(),
QStringLiteral("warn-export-openpgp-missing-keyserver"))
!= KMessageBox::Continue) {
return false;
}
+ }
+ if (!confirmExport(d->keys(), parent)) {
+ return false;
+ }
return KMessageBox::warningContinueCancel(parent,
xi18nc("@info",
"<para>When OpenPGP certificates have been exported to a public directory server, "
"it is nearly impossible to remove them again.</para>"
"<para>Before exporting your certificate to a public directory server, make sure that you "
"have created a revocation certificate so you can revoke the certificate if needed later.</para>"
"<para>Are you sure you want to continue?</para>"),
i18nc("@title:window", "OpenPGP Certificate Export"),
- KStandardGuiItem::cont(),
+ KGuiItem{i18ncp("@action:button", "Export Certificate", "Export Certificates", d->keys().size())},
KStandardGuiItem::cancel(),
QStringLiteral("warn-export-openpgp-nonrevocable"))
== KMessageBox::Continue;
}
QStringList ExportOpenPGPCertsToServerCommand::arguments() const
{
QStringList result;
result << gpgPath();
if (!haveKeyserverConfigured()) {
result << QStringLiteral("--keyserver") << QStringLiteral("keys.gnupg.net");
}
result << QStringLiteral("--send-keys");
const auto keys = d->keys();
for (const Key &key : keys) {
result << QLatin1String(key.primaryFingerprint());
}
return result;
}
QString ExportOpenPGPCertsToServerCommand::errorCaption() const
{
return i18nc("@title:window", "OpenPGP Certificate Export Error");
}
QString ExportOpenPGPCertsToServerCommand::successCaption() const
{
return i18nc("@title:window", "OpenPGP Certificate Export Finished");
}
QString ExportOpenPGPCertsToServerCommand::crashExitMessage(const QStringList &args) const
{
return xi18nc("@info",
"<para>The GPG process that tried to export OpenPGP certificates "
"ended prematurely because of an unexpected error.</para>"
"<para>Please check the output of <icode>%1</icode> for details.</para>",
args.join(QLatin1Char(' ')));
}
QString ExportOpenPGPCertsToServerCommand::errorExitMessage(const QStringList &args) const
{
// ki18n(" ") as initializer because initializing with empty string leads to
// (I18N_EMPTY_MESSAGE)
const auto errorLines = errorString().split(QLatin1Char{'\n'});
const auto errorText = std::accumulate(errorLines.begin(), errorLines.end(), KLocalizedString{ki18n(" ")}, [](KLocalizedString temp, const auto &line) {
return kxi18nc("@info used for concatenating multiple lines of text with line breaks; most likely this shouldn't be translated", "%1<nl />%2")
.subs(temp)
.subs(line);
});
return xi18nc("@info",
"<para>An error occurred while trying to export OpenPGP certificates.</para> "
"<para>The output of <command>%1</command> was:<nl /><message>%2</message></para>",
args[0],
errorText);
}
QString ExportOpenPGPCertsToServerCommand::successMessage(const QStringList &) const
{
return i18nc("@info", "OpenPGP certificates exported successfully.");
}
#include "moc_exportopenpgpcertstoservercommand.cpp"
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sun, Feb 23, 7:59 PM (5 h, 18 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
d4/73/f202d89f9f718d6c04636c481867
Attached To
rKLEOPATRA Kleopatra
Event Timeline
Log In to Comment