Page MenuHome GnuPG

No OneTemporary

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

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

Event Timeline