Page MenuHome GnuPG

No OneTemporary

diff --git a/src/commands/exportcertificatecommand.cpp b/src/commands/exportcertificatecommand.cpp
index 09194fa49..48332908b 100644
--- a/src/commands/exportcertificatecommand.cpp
+++ b/src/commands/exportcertificatecommand.cpp
@@ -1,366 +1,366 @@
/* -*- 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/Classify>
#include <Libkleo/Formatting>
#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 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()) {
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);
if (!d->requestFileNames(prot)) {
Q_EMIT canceled();
d->finished();
} else {
if (!openpgp.empty()) {
d->startExportJob(GpgME::OpenPGP, openpgp);
}
if (!cms.empty()) {
d->startExportJob(GpgME::CMS, cms);
}
}
}
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(QString::fromLatin1(outputFileExtension(asciiArmoredCertificateClass, usePGPFileExt)));
+ .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."), i18n("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/exportsecretkeycommand.cpp b/src/commands/exportsecretkeycommand.cpp
index a4ccd9fbc..3901d018c 100644
--- a/src/commands/exportsecretkeycommand.cpp
+++ b/src/commands/exportsecretkeycommand.cpp
@@ -1,310 +1,310 @@
/* -*- mode: c++; c-basic-offset:4 -*-
commands/exportsecretkeycommand.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2022 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 "exportsecretkeycommand.h"
#include "fileoperationspreferences.h"
#include "utils/filedialog.h"
#include <utils/applicationstate.h>
#include <Libkleo/Classify>
#include <Libkleo/Formatting>
#include <KLocalizedString>
#include <KSharedConfig>
#include <QGpgME/ExportJob>
#include <QGpgME/Protocol>
#include <QFileInfo>
#include <QStandardPaths>
#include <gpgme++/context.h>
#include <algorithm>
#include <memory>
#include <vector>
#include <kleopatra_debug.h>
using namespace Kleo;
using namespace Kleo::Commands;
using namespace GpgME;
namespace
{
QString openPGPCertificateFileExtension()
{
- return QLatin1String{outputFileExtension(Class::OpenPGP | Class::Ascii | Class::Certificate, FileOperationsPreferences().usePGPFileExt())};
+ return outputFileExtension(Class::OpenPGP | Class::Ascii | Class::Certificate, FileOperationsPreferences().usePGPFileExt());
}
QString cmsCertificateFileExtension()
{
- return QLatin1String{outputFileExtension(Class::CMS | Class::Binary | Class::ExportedPSM,
- /*usePGPFileExt=*/false)};
+ return outputFileExtension(Class::CMS | Class::Binary | Class::ExportedPSM,
+ /*usePGPFileExt=*/false);
}
QString certificateFileExtension(GpgME::Protocol protocol)
{
switch (protocol) {
case GpgME::OpenPGP:
return openPGPCertificateFileExtension();
case GpgME::CMS:
return cmsCertificateFileExtension();
default:
qCWarning(KLEOPATRA_LOG) << __func__ << "Error: Unknown protocol" << protocol;
return QStringLiteral("txt");
}
}
QString proposeFilename(const Key &key)
{
QString filename;
auto name = Formatting::prettyName(key);
if (name.isEmpty()) {
name = Formatting::prettyEMail(key);
}
const auto shortKeyID = Formatting::prettyKeyID(key.shortKeyID());
/* Not translated so it's better to use in tutorials etc. */
filename = QStringView{u"%1_%2_SECRET"}.arg(name, shortKeyID);
filename.replace(u'/', u'_');
return ApplicationState::lastUsedExportDirectory() + u'/' + filename + u'.' + certificateFileExtension(key.protocol());
}
QString secretKeyFileFilters(GpgME::Protocol protocol)
{
switch (protocol) {
case GpgME::OpenPGP:
return i18nc("description of filename filter", "Secret Key Files") + QLatin1String{" (*.asc *.gpg *.pgp)"};
case GpgME::CMS:
return i18nc("description of filename filter", "Secret Key Files") + QLatin1String{" (*.p12)"};
default:
qCWarning(KLEOPATRA_LOG) << __func__ << "Error: Unknown protocol" << protocol;
return i18nc("description of filename filter", "All Files") + QLatin1String{" (*)"};
}
}
QString requestFilename(const Key &key, const QString &proposedFilename, QWidget *parent)
{
auto filename = FileDialog::getSaveFileNameEx(parent,
i18nc("@title:window", "Secret Key Backup"),
QStringLiteral("imp"),
proposedFilename,
secretKeyFileFilters(key.protocol()));
if (!filename.isEmpty()) {
const QFileInfo fi{filename};
if (fi.suffix().isEmpty()) {
filename += u'.' + certificateFileExtension(key.protocol());
}
ApplicationState::setLastUsedExportDirectory(filename);
}
return filename;
}
QString errorCaption()
{
return i18nc("@title:window", "Secret Key Backup Error");
}
}
class ExportSecretKeyCommand::Private : public Command::Private
{
friend class ::ExportSecretKeyCommand;
ExportSecretKeyCommand *q_func() const
{
return static_cast<ExportSecretKeyCommand *>(q);
}
public:
explicit Private(ExportSecretKeyCommand *qq, KeyListController *c = nullptr);
~Private() override;
void start();
void cancel();
private:
std::unique_ptr<QGpgME::ExportJob> startExportJob(const Key &key);
void onExportJobResult(const Error &err, const QByteArray &keyData);
void showError(const Error &err);
private:
QString filename;
QPointer<QGpgME::ExportJob> job;
};
ExportSecretKeyCommand::Private *ExportSecretKeyCommand::d_func()
{
return static_cast<Private *>(d.get());
}
const ExportSecretKeyCommand::Private *ExportSecretKeyCommand::d_func() const
{
return static_cast<const Private *>(d.get());
}
#define d d_func()
#define q q_func()
ExportSecretKeyCommand::Private::Private(ExportSecretKeyCommand *qq, KeyListController *c)
: Command::Private{qq, c}
{
}
ExportSecretKeyCommand::Private::~Private() = default;
void ExportSecretKeyCommand::Private::start()
{
const Key key = this->key();
if (key.isNull()) {
finished();
return;
}
filename = requestFilename(key, proposeFilename(key), parentWidgetOrView());
if (filename.isEmpty()) {
canceled();
return;
}
auto exportJob = startExportJob(key);
if (!exportJob) {
finished();
return;
}
job = exportJob.release();
}
void ExportSecretKeyCommand::Private::cancel()
{
if (job) {
job->slotCancel();
}
job.clear();
}
std::unique_ptr<QGpgME::ExportJob> ExportSecretKeyCommand::Private::startExportJob(const Key &key)
{
#if QGPGME_SUPPORTS_SECRET_KEY_EXPORT
const bool armor = key.protocol() == GpgME::OpenPGP && filename.endsWith(u".asc", Qt::CaseInsensitive);
const QGpgME::Protocol *const backend = (key.protocol() == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime();
Q_ASSERT(backend);
std::unique_ptr<QGpgME::ExportJob> exportJob{backend->secretKeyExportJob(armor)};
Q_ASSERT(exportJob);
if (key.protocol() == GpgME::CMS) {
exportJob->setExportFlags(GpgME::Context::ExportPKCS12);
}
connect(exportJob.get(), &QGpgME::ExportJob::result, q, [this](const GpgME::Error &err, const QByteArray &keyData) {
onExportJobResult(err, keyData);
});
#if QGPGME_JOB_HAS_NEW_PROGRESS_SIGNALS
connect(exportJob.get(), &QGpgME::Job::jobProgress, q, &Command::progress);
#else
connect(exportJob.get(), &QGpgME::Job::progress, q, [this](const QString &, int current, int total) {
Q_EMIT q->progress(current, total);
});
#endif
const GpgME::Error err = exportJob->start({QLatin1String{key.primaryFingerprint()}});
if (err) {
showError(err);
return {};
}
Q_EMIT q->info(i18nc("@info:status", "Backing up secret key..."));
return exportJob;
#else
Q_UNUSED(key)
return {};
#endif
}
void ExportSecretKeyCommand::Private::onExportJobResult(const Error &err, const QByteArray &keyData)
{
if (err.isCanceled()) {
finished();
return;
}
if (err) {
showError(err);
finished();
return;
}
if (keyData.isEmpty()) {
error(i18nc("@info", "The result of the backup is empty. Maybe you entered an empty or a wrong passphrase."), errorCaption());
finished();
return;
}
QFile f{filename};
if (!f.open(QIODevice::WriteOnly)) {
error(xi18nc("@info", "Cannot open file <filename>%1</filename> for writing.", filename), errorCaption());
finished();
return;
}
const auto bytesWritten = f.write(keyData);
if (bytesWritten != keyData.size()) {
error(xi18nc("@info", "Writing key to file <filename>%1</filename> failed.", filename), errorCaption());
finished();
return;
}
information(i18nc("@info", "The backup of the secret key was created successfully."), i18nc("@title:window", "Secret Key Backup"));
finished();
}
void ExportSecretKeyCommand::Private::showError(const Error &err)
{
error(xi18nc("@info",
"<para>An error occurred during the backup of the secret key:</para>"
"<para><message>%1</message></para>",
Formatting::errorAsString(err)),
errorCaption());
}
ExportSecretKeyCommand::ExportSecretKeyCommand(QAbstractItemView *view, KeyListController *controller)
: Command{view, new Private{this, controller}}
{
}
ExportSecretKeyCommand::ExportSecretKeyCommand(const GpgME::Key &key)
: Command{key, new Private{this}}
{
}
ExportSecretKeyCommand::~ExportSecretKeyCommand() = default;
void ExportSecretKeyCommand::doStart()
{
d->start();
}
void ExportSecretKeyCommand::doCancel()
{
d->cancel();
}
#undef d
#undef q
#include "moc_exportsecretkeycommand.cpp"
diff --git a/src/commands/exportsecretkeycommand_old.cpp b/src/commands/exportsecretkeycommand_old.cpp
index cbb5daca4..43407b84b 100644
--- a/src/commands/exportsecretkeycommand_old.cpp
+++ b/src/commands/exportsecretkeycommand_old.cpp
@@ -1,174 +1,174 @@
/* -*- mode: c++; c-basic-offset:4 -*-
commands/exportsecretkeycommand_old.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 "exportsecretkeycommand_old.h"
#include "fileoperationspreferences.h"
#include "command_p.h"
#include <Libkleo/GnuPG>
#include <utils/filedialog.h>
#include <Libkleo/Classify>
#include <Libkleo/Formatting>
#include <gpgme++/key.h>
#include <KLocalizedString>
#include <QFile>
#include <QProcess>
using namespace Kleo;
using namespace Kleo::Commands;
using namespace GpgME;
namespace Kleo::Commands::Compat
{
ExportSecretKeyCommand::ExportSecretKeyCommand(QAbstractItemView *v, KeyListController *c)
: GnuPGProcessCommand(v, c)
{
}
ExportSecretKeyCommand::ExportSecretKeyCommand(const Key &key)
: GnuPGProcessCommand(key)
{
}
ExportSecretKeyCommand::~ExportSecretKeyCommand()
{
}
bool ExportSecretKeyCommand::preStartHook(QWidget *parent) const
{
if (!m_filename.isEmpty()) {
return true;
}
const auto key = d->key();
const auto protocol = key.protocol();
QString proposedFileName;
const bool usePGPFileExt = FileOperationsPreferences().usePGPFileExt();
auto name = Formatting::prettyName(key);
if (name.isEmpty()) {
name = Formatting::prettyEMail(key);
}
/* Not translated so it's better to use in tutorials etc. */
- proposedFileName = QStringLiteral("%1_%2_SECRET.%3")
- .arg(name)
- .arg(Formatting::prettyKeyID(key.shortKeyID()))
- .arg(QString::fromLatin1(outputFileExtension(protocol == OpenPGP ? Class::OpenPGP | Class::Ascii | Class::Certificate
- : Class::CMS | Class::Binary | Class::ExportedPSM,
- usePGPFileExt)));
+ proposedFileName =
+ QStringLiteral("%1_%2_SECRET.%3")
+ .arg(name)
+ .arg(Formatting::prettyKeyID(key.shortKeyID()))
+ .arg(outputFileExtension(protocol == OpenPGP ? Class::OpenPGP | Class::Ascii | Class::Certificate : Class::CMS | Class::Binary | Class::ExportedPSM,
+ usePGPFileExt));
m_filename = FileDialog::getSaveFileNameEx(parent ? parent : d->parentWidgetOrView(),
i18n("Backup Secret Key"),
QStringLiteral("imp"),
proposedFileName,
protocol == GpgME::OpenPGP ? i18n("Secret Key Files") + QLatin1String(" (*.asc *.gpg *.pgp)")
: i18n("Secret Key Files") + QLatin1String(" (*.p12)"));
m_armor = m_filename.endsWith(QLatin1String(".asc"));
return !m_filename.isEmpty();
}
QStringList ExportSecretKeyCommand::arguments() const
{
const Key key = d->key();
QStringList result;
if (key.protocol() == OpenPGP) {
result << gpgPath() << QStringLiteral("--batch");
} else {
result << gpgSmPath();
}
result << QStringLiteral("--yes") << QStringLiteral("--output") << QStringLiteral("-");
if (m_armor) {
result << QStringLiteral("--armor");
}
if (key.protocol() == CMS) {
result << QStringLiteral("--p12-charset") << QStringLiteral("utf-8");
}
if (key.protocol() == OpenPGP) {
result << QStringLiteral("--export-secret-key");
} else {
result << QStringLiteral("--export-secret-key-p12");
}
result << QLatin1String(key.primaryFingerprint());
return result;
}
QString ExportSecretKeyCommand::errorCaption() const
{
return i18nc("@title:window", "Secret Key Export Error");
}
QString ExportSecretKeyCommand::successCaption() const
{
return i18nc("@title:window", "Secret Key Export Finished");
}
QString ExportSecretKeyCommand::crashExitMessage(const QStringList &args) const
{
return xi18nc("@info",
"<para>The GPG or GpgSM process that tried to export the secret key "
"ended prematurely because of an unexpected error.</para>"
"<para>Please check the output of <icode>%1</icode> for details.</para>",
args.join(QLatin1Char(' ')));
}
QString ExportSecretKeyCommand::errorExitMessage(const QStringList &args) const
{
return xi18nc("@info",
"<para>An error occurred while trying to export the secret key.</para> "
"<para>The output from <command>%1</command> was: <message>%2</message></para>",
args[0],
errorString());
}
QString ExportSecretKeyCommand::successMessage(const QStringList &) const
{
if (mHasError) {
return QString();
}
return i18nc("@info", "Secret key successfully exported.");
}
void ExportSecretKeyCommand::postSuccessHook(QWidget *)
{
Q_ASSERT(process());
const auto data = process()->readAllStandardOutput();
if (!data.size()) {
d->error(i18nc("@info", "Possibly bad passphrase given."), errorCaption());
mHasError = true;
return;
}
QFile file(m_filename);
/* The filedialog already asked for replace ok. */
file.open(QIODevice::ReadWrite | QIODevice::Truncate);
if (file.write(data) != data.size()) {
d->error(i18nc("@info", "Failed to write data."), errorCaption());
mHasError = true;
}
file.close();
}
}
diff --git a/src/commands/exportsecretsubkeycommand.cpp b/src/commands/exportsecretsubkeycommand.cpp
index 8dfb0ae9e..91a7466e5 100644
--- a/src/commands/exportsecretsubkeycommand.cpp
+++ b/src/commands/exportsecretsubkeycommand.cpp
@@ -1,294 +1,294 @@
/* -*- mode: c++; c-basic-offset:4 -*-
commands/exportsecretsubkeycommand.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2022 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 "exportsecretsubkeycommand.h"
#include "fileoperationspreferences.h"
#include <utils/applicationstate.h>
#if QGPGME_SUPPORTS_SECRET_SUBKEY_EXPORT
#include "utils/filedialog.h"
#endif
#include <Libkleo/Classify>
#include <Libkleo/Formatting>
#include <KLocalizedString>
#include <KSharedConfig>
#include <QGpgME/ExportJob>
#include <QGpgME/Protocol>
#include <QFileInfo>
#include <QStandardPaths>
#include <algorithm>
#include <memory>
#include <vector>
using namespace Kleo;
using namespace GpgME;
namespace
{
#if QGPGME_SUPPORTS_SECRET_SUBKEY_EXPORT
QString openPGPCertificateFileExtension()
{
- return QLatin1String{outputFileExtension(Class::OpenPGP | Class::Ascii | Class::Certificate, FileOperationsPreferences().usePGPFileExt())};
+ return outputFileExtension(Class::OpenPGP | Class::Ascii | Class::Certificate, FileOperationsPreferences().usePGPFileExt());
}
QString proposeFilename(const std::vector<Subkey> &subkeys)
{
QString filename;
if (subkeys.size() == 1) {
const auto subkey = subkeys.front();
const auto key = subkey.parent();
auto name = Formatting::prettyName(key);
if (name.isEmpty()) {
name = Formatting::prettyEMail(key);
}
const auto shortKeyID = Formatting::prettyKeyID(key.shortKeyID());
const auto shortSubkeyID = Formatting::prettyKeyID(QByteArray{subkey.keyID()}.right(8).constData());
const auto usage = Formatting::usageString(subkey).replace(QLatin1String{", "}, QLatin1String{"_"});
/* Not translated so it's better to use in tutorials etc. */
filename = QStringView{u"%1_%2_SECRET_SUBKEY_%3_%4"}.arg(name, shortKeyID, shortSubkeyID, usage);
} else {
filename = i18nc("Generic filename for exported subkeys", "subkeys");
}
filename.replace(u'/', u'_');
return ApplicationState::lastUsedExportDirectory() + u'/' + filename + u'.' + openPGPCertificateFileExtension();
}
QString requestFilename(const std::vector<Subkey> &subkeys, const QString &proposedFilename, QWidget *parent)
{
auto filename = FileDialog::getSaveFileNameEx(parent,
i18ncp("@title:window", "Export Subkey", "Export Subkeys", subkeys.size()),
QStringLiteral("imp"),
proposedFilename,
i18nc("description of filename filter", "Secret Key Files") + QLatin1String{" (*.asc *.gpg *.pgp)"});
if (!filename.isEmpty()) {
const QFileInfo fi{filename};
if (fi.suffix().isEmpty()) {
filename += u'.' + openPGPCertificateFileExtension();
}
ApplicationState::setLastUsedExportDirectory(filename);
}
return filename;
}
template<typename SubkeyContainer>
QStringList getSubkeyFingerprints(const SubkeyContainer &subkeys)
{
QStringList fingerprints;
fingerprints.reserve(subkeys.size());
std::transform(std::begin(subkeys), std::end(subkeys), std::back_inserter(fingerprints), [](const auto &subkey) {
return QLatin1String{subkey.fingerprint()} + u'!';
});
return fingerprints;
}
#endif
}
class ExportSecretSubkeyCommand::Private : public Command::Private
{
friend class ::ExportSecretSubkeyCommand;
ExportSecretSubkeyCommand *q_func() const
{
return static_cast<ExportSecretSubkeyCommand *>(q);
}
public:
explicit Private(ExportSecretSubkeyCommand *qq);
~Private() override;
void start();
void cancel();
private:
std::unique_ptr<QGpgME::ExportJob> startExportJob(const std::vector<Subkey> &subkeys);
void onExportJobResult(const Error &err, const QByteArray &keyData);
void showError(const Error &err);
private:
std::vector<Subkey> subkeys;
QString filename;
QPointer<QGpgME::ExportJob> job;
};
ExportSecretSubkeyCommand::Private *ExportSecretSubkeyCommand::d_func()
{
return static_cast<Private *>(d.get());
}
const ExportSecretSubkeyCommand::Private *ExportSecretSubkeyCommand::d_func() const
{
return static_cast<const Private *>(d.get());
}
#define d d_func()
#define q q_func()
ExportSecretSubkeyCommand::Private::Private(ExportSecretSubkeyCommand *qq)
: Command::Private{qq}
{
}
ExportSecretSubkeyCommand::Private::~Private() = default;
void ExportSecretSubkeyCommand::Private::start()
{
#if QGPGME_SUPPORTS_SECRET_SUBKEY_EXPORT
if (subkeys.empty()) {
finished();
return;
}
filename = requestFilename(subkeys, proposeFilename(subkeys), parentWidgetOrView());
if (filename.isEmpty()) {
canceled();
return;
}
auto exportJob = startExportJob(subkeys);
if (!exportJob) {
finished();
return;
}
job = exportJob.release();
#else
Q_ASSERT(!"This command is not supported by the backend it was compiled against");
finished();
return;
#endif
}
void ExportSecretSubkeyCommand::Private::cancel()
{
if (job) {
job->slotCancel();
}
job.clear();
}
std::unique_ptr<QGpgME::ExportJob> ExportSecretSubkeyCommand::Private::startExportJob(const std::vector<Subkey> &subkeys)
{
#if QGPGME_SUPPORTS_SECRET_SUBKEY_EXPORT
const bool armor = filename.endsWith(u".asc", Qt::CaseInsensitive);
std::unique_ptr<QGpgME::ExportJob> exportJob{QGpgME::openpgp()->secretSubkeyExportJob(armor)};
Q_ASSERT(exportJob);
connect(exportJob.get(), &QGpgME::ExportJob::result, q, [this](const GpgME::Error &err, const QByteArray &keyData) {
onExportJobResult(err, keyData);
});
#if QGPGME_JOB_HAS_NEW_PROGRESS_SIGNALS
connect(exportJob.get(), &QGpgME::Job::jobProgress, q, &Command::progress);
#else
connect(exportJob.get(), &QGpgME::Job::progress, q, [this](const QString &, int current, int total) {
Q_EMIT q->progress(current, total);
});
#endif
const GpgME::Error err = exportJob->start(getSubkeyFingerprints(subkeys));
if (err) {
showError(err);
return {};
}
Q_EMIT q->info(i18nc("@info:status", "Exporting subkeys..."));
return exportJob;
#else
Q_UNUSED(subkeys)
return {};
#endif
}
void ExportSecretSubkeyCommand::Private::onExportJobResult(const Error &err, const QByteArray &keyData)
{
if (err) {
showError(err);
finished();
return;
}
if (err.isCanceled()) {
finished();
return;
}
if (keyData.isEmpty()) {
error(i18nc("@info", "The result of the export is empty."), i18nc("@title:window", "Export Failed"));
finished();
return;
}
QFile f{filename};
if (!f.open(QIODevice::WriteOnly)) {
error(xi18nc("@info", "Cannot open file <filename>%1</filename> for writing.", filename), i18nc("@title:window", "Export Failed"));
finished();
return;
}
const auto bytesWritten = f.write(keyData);
if (bytesWritten != keyData.size()) {
error(xi18ncp("@info",
"Writing subkey to file <filename>%2</filename> failed.",
"Writing subkeys to file <filename>%2</filename> failed.",
subkeys.size(),
filename),
i18nc("@title:window", "Export Failed"));
finished();
return;
}
information(i18ncp("@info", "The subkey was exported successfully.", "%1 subkeys were exported successfully.", subkeys.size()),
i18nc("@title:window", "Secret Key Backup"));
finished();
}
void ExportSecretSubkeyCommand::Private::showError(const Error &err)
{
error(xi18nc("@info",
"<para>An error occurred during the export:</para>"
"<para><message>%1</message></para>",
Formatting::errorAsString(err)),
i18nc("@title:window", "Export Failed"));
}
ExportSecretSubkeyCommand::ExportSecretSubkeyCommand(const std::vector<GpgME::Subkey> &subkeys)
: Command{new Private{this}}
{
d->subkeys = subkeys;
}
ExportSecretSubkeyCommand::~ExportSecretSubkeyCommand() = default;
void ExportSecretSubkeyCommand::doStart()
{
d->start();
}
void ExportSecretSubkeyCommand::doCancel()
{
d->cancel();
}
#undef d
#undef q
#include "moc_exportsecretsubkeycommand.cpp"
diff --git a/src/crypto/signencryptfilescontroller.cpp b/src/crypto/signencryptfilescontroller.cpp
index 31a314e95..0224a4327 100644
--- a/src/crypto/signencryptfilescontroller.cpp
+++ b/src/crypto/signencryptfilescontroller.cpp
@@ -1,790 +1,791 @@
/* -*- mode: c++; c-basic-offset:4 -*-
crypto/signencryptfilescontroller.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB
SPDX-FileCopyrightText: 2017 Bundesamt für Sicherheit in der Informationstechnik
SPDX-FileContributor: Intevation GmbH
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-kleopatra.h>
#include "signencryptfilescontroller.h"
#include "certificateresolver.h"
#include "signencrypttask.h"
#include "crypto/gui/signencryptfileswizard.h"
#include "crypto/taskcollection.h"
#include "fileoperationspreferences.h"
#include "utils/archivedefinition.h"
#include "utils/input.h"
#include "utils/kleo_assert.h"
#include "utils/output.h"
#include "utils/path-helper.h"
#include <Libkleo/Classify>
#include <Libkleo/KleoException>
#include "kleopatra_debug.h"
#include <KLocalizedString>
#if QGPGME_SUPPORTS_ARCHIVE_JOBS
#include <QGpgME/SignEncryptArchiveJob>
#endif
#include <QDir>
#include <QFileInfo>
#include <QPointer>
#include <QTimer>
using namespace Kleo;
using namespace Kleo::Crypto;
using namespace GpgME;
using namespace KMime::Types;
class SignEncryptFilesController::Private
{
friend class ::Kleo::Crypto::SignEncryptFilesController;
SignEncryptFilesController *const q;
public:
explicit Private(SignEncryptFilesController *qq);
~Private();
private:
void slotWizardOperationPrepared();
void slotWizardCanceled();
private:
void ensureWizardCreated();
void ensureWizardVisible();
void updateWizardMode();
void cancelAllTasks();
void reportError(int err, const QString &details)
{
q->setLastError(err, details);
q->emitDoneOrError();
}
void schedule();
std::shared_ptr<SignEncryptTask> takeRunnable(GpgME::Protocol proto);
static void assertValidOperation(unsigned int);
static QString titleForOperation(unsigned int op);
private:
std::vector<std::shared_ptr<SignEncryptTask>> runnable, completed;
std::shared_ptr<SignEncryptTask> cms, openpgp;
QPointer<SignEncryptFilesWizard> wizard;
QStringList files;
unsigned int operation;
Protocol protocol;
};
SignEncryptFilesController::Private::Private(SignEncryptFilesController *qq)
: q(qq)
, runnable()
, cms()
, openpgp()
, wizard()
, files()
, operation(SignAllowed | EncryptAllowed | ArchiveAllowed)
, protocol(UnknownProtocol)
{
}
SignEncryptFilesController::Private::~Private()
{
qCDebug(KLEOPATRA_LOG) << q << __func__;
}
QString SignEncryptFilesController::Private::titleForOperation(unsigned int op)
{
const bool signDisallowed = (op & SignMask) == SignDisallowed;
const bool encryptDisallowed = (op & EncryptMask) == EncryptDisallowed;
const bool archiveSelected = (op & ArchiveMask) == ArchiveForced;
kleo_assert(!signDisallowed || !encryptDisallowed);
if (!signDisallowed && encryptDisallowed) {
if (archiveSelected) {
return i18n("Archive and Sign Files");
} else {
return i18n("Sign Files");
}
}
if (signDisallowed && !encryptDisallowed) {
if (archiveSelected) {
return i18n("Archive and Encrypt Files");
} else {
return i18n("Encrypt Files");
}
}
if (archiveSelected) {
return i18n("Archive and Sign/Encrypt Files");
} else {
return i18n("Sign/Encrypt Files");
}
}
SignEncryptFilesController::SignEncryptFilesController(QObject *p)
: Controller(p)
, d(new Private(this))
{
}
SignEncryptFilesController::SignEncryptFilesController(const std::shared_ptr<const ExecutionContext> &ctx, QObject *p)
: Controller(ctx, p)
, d(new Private(this))
{
}
SignEncryptFilesController::~SignEncryptFilesController()
{
qCDebug(KLEOPATRA_LOG) << this << __func__;
if (d->wizard && !d->wizard->isVisible()) {
delete d->wizard;
}
// d->wizard->close(); ### ?
}
void SignEncryptFilesController::setProtocol(Protocol proto)
{
kleo_assert(d->protocol == UnknownProtocol || d->protocol == proto);
d->protocol = proto;
d->ensureWizardCreated();
}
Protocol SignEncryptFilesController::protocol() const
{
return d->protocol;
}
// static
void SignEncryptFilesController::Private::assertValidOperation(unsigned int op)
{
kleo_assert((op & SignMask) == SignDisallowed || //
(op & SignMask) == SignAllowed || //
(op & SignMask) == SignSelected);
kleo_assert((op & EncryptMask) == EncryptDisallowed || //
(op & EncryptMask) == EncryptAllowed || //
(op & EncryptMask) == EncryptSelected);
kleo_assert((op & ArchiveMask) == ArchiveDisallowed || //
(op & ArchiveMask) == ArchiveAllowed || //
(op & ArchiveMask) == ArchiveForced);
kleo_assert((op & ~(SignMask | EncryptMask | ArchiveMask)) == 0);
}
void SignEncryptFilesController::setOperationMode(unsigned int mode)
{
Private::assertValidOperation(mode);
d->operation = mode;
d->updateWizardMode();
}
void SignEncryptFilesController::Private::updateWizardMode()
{
if (!wizard) {
return;
}
wizard->setWindowTitle(titleForOperation(operation));
const unsigned int signOp = (operation & SignMask);
const unsigned int encrOp = (operation & EncryptMask);
const unsigned int archOp = (operation & ArchiveMask);
if (signOp == SignDisallowed) {
wizard->setSigningUserMutable(false);
wizard->setSigningPreset(false);
} else {
wizard->setSigningUserMutable(true);
wizard->setSigningPreset(signOp == SignSelected);
}
if (encrOp == EncryptDisallowed) {
wizard->setEncryptionPreset(false);
wizard->setEncryptionUserMutable(false);
} else {
wizard->setEncryptionUserMutable(true);
wizard->setEncryptionPreset(encrOp == EncryptSelected);
}
wizard->setArchiveForced(archOp == ArchiveForced);
wizard->setArchiveMutable(archOp == ArchiveAllowed);
}
unsigned int SignEncryptFilesController::operationMode() const
{
return d->operation;
}
-static const char *extension(bool pgp, bool sign, bool encrypt, bool ascii, bool detached)
+static QString extension(bool pgp, bool sign, bool encrypt, bool ascii, bool detached)
{
unsigned int cls = pgp ? Class::OpenPGP : Class::CMS;
if (encrypt) {
cls |= Class::CipherText;
} else if (sign) {
cls |= detached ? Class::DetachedSignature : Class::OpaqueSignature;
}
cls |= ascii ? Class::Ascii : Class::Binary;
const bool usePGPFileExt = FileOperationsPreferences().usePGPFileExt();
- if (const char *const ext = outputFileExtension(cls, usePGPFileExt)) {
+ const auto ext = outputFileExtension(cls, usePGPFileExt);
+ if (!ext.isEmpty()) {
return ext;
} else {
- return "out";
+ return QStringLiteral("out");
}
}
static std::shared_ptr<ArchiveDefinition> getDefaultAd()
{
const std::vector<std::shared_ptr<ArchiveDefinition>> ads = ArchiveDefinition::getArchiveDefinitions();
Q_ASSERT(!ads.empty());
std::shared_ptr<ArchiveDefinition> ad = ads.front();
const FileOperationsPreferences prefs;
const QString archiveCmd = prefs.archiveCommand();
auto it = std::find_if(ads.cbegin(), ads.cend(), [&archiveCmd](const std::shared_ptr<ArchiveDefinition> &toCheck) {
return toCheck->id() == archiveCmd;
});
if (it != ads.cend()) {
ad = *it;
}
return ad;
}
static QMap<int, QString> buildOutputNames(const QStringList &files, const bool archive)
{
QMap<int, QString> nameMap;
// Build the default names for the wizard.
QString baseNameCms;
QString baseNamePgp;
const QFileInfo firstFile(files.first());
if (archive) {
QString baseName = files.size() > 1 ? i18nc("base name of an archive file, e.g. archive.zip or archive.tar.gz", "archive") : firstFile.baseName();
baseName = QDir(heuristicBaseDirectory(files)).absoluteFilePath(baseName);
const auto ad = getDefaultAd();
baseNamePgp = baseName + QLatin1Char('.') + ad->extensions(GpgME::OpenPGP).first() + QLatin1Char('.');
baseNameCms = baseName + QLatin1Char('.') + ad->extensions(GpgME::CMS).first() + QLatin1Char('.');
} else {
baseNameCms = baseNamePgp = files.first() + QLatin1Char('.');
}
const FileOperationsPreferences prefs;
const bool ascii = prefs.addASCIIArmor();
- nameMap.insert(SignEncryptFilesWizard::SignatureCMS, baseNameCms + QString::fromLatin1(extension(false, true, false, ascii, true)));
- nameMap.insert(SignEncryptFilesWizard::EncryptedCMS, baseNameCms + QString::fromLatin1(extension(false, false, true, ascii, false)));
- nameMap.insert(SignEncryptFilesWizard::CombinedPGP, baseNamePgp + QString::fromLatin1(extension(true, true, true, ascii, false)));
- nameMap.insert(SignEncryptFilesWizard::EncryptedPGP, baseNamePgp + QString::fromLatin1(extension(true, false, true, ascii, false)));
- nameMap.insert(SignEncryptFilesWizard::SignaturePGP, baseNamePgp + QString::fromLatin1(extension(true, true, false, ascii, true)));
+ nameMap.insert(SignEncryptFilesWizard::SignatureCMS, baseNameCms + extension(false, true, false, ascii, true));
+ nameMap.insert(SignEncryptFilesWizard::EncryptedCMS, baseNameCms + extension(false, false, true, ascii, false));
+ nameMap.insert(SignEncryptFilesWizard::CombinedPGP, baseNamePgp + extension(true, true, true, ascii, false));
+ nameMap.insert(SignEncryptFilesWizard::EncryptedPGP, baseNamePgp + extension(true, false, true, ascii, false));
+ nameMap.insert(SignEncryptFilesWizard::SignaturePGP, baseNamePgp + extension(true, true, false, ascii, true));
nameMap.insert(SignEncryptFilesWizard::Directory, heuristicBaseDirectory(files));
return nameMap;
}
static QMap<int, QString> buildOutputNamesForDir(const QString &file, const QMap<int, QString> &orig)
{
QMap<int, QString> ret;
const QString dir = orig.value(SignEncryptFilesWizard::Directory);
if (dir.isEmpty()) {
return orig;
}
// Build the default names for the wizard.
const QFileInfo fi(file);
const QString baseName = dir + QLatin1Char('/') + fi.fileName() + QLatin1Char('.');
const FileOperationsPreferences prefs;
const bool ascii = prefs.addASCIIArmor();
- ret.insert(SignEncryptFilesWizard::SignatureCMS, baseName + QString::fromLatin1(extension(false, true, false, ascii, true)));
- ret.insert(SignEncryptFilesWizard::EncryptedCMS, baseName + QString::fromLatin1(extension(false, false, true, ascii, false)));
- ret.insert(SignEncryptFilesWizard::CombinedPGP, baseName + QString::fromLatin1(extension(true, true, true, ascii, false)));
- ret.insert(SignEncryptFilesWizard::EncryptedPGP, baseName + QString::fromLatin1(extension(true, false, true, ascii, false)));
- ret.insert(SignEncryptFilesWizard::SignaturePGP, baseName + QString::fromLatin1(extension(true, true, false, ascii, true)));
+ ret.insert(SignEncryptFilesWizard::SignatureCMS, baseName + extension(false, true, false, ascii, true));
+ ret.insert(SignEncryptFilesWizard::EncryptedCMS, baseName + extension(false, false, true, ascii, false));
+ ret.insert(SignEncryptFilesWizard::CombinedPGP, baseName + extension(true, true, true, ascii, false));
+ ret.insert(SignEncryptFilesWizard::EncryptedPGP, baseName + extension(true, false, true, ascii, false));
+ ret.insert(SignEncryptFilesWizard::SignaturePGP, baseName + extension(true, true, false, ascii, true));
return ret;
}
// strips all trailing slashes from the filename, but keeps filename "/"
static QString stripTrailingSlashes(const QString &fileName)
{
if (fileName.size() < 2 || !fileName.endsWith(QLatin1Char('/'))) {
return fileName;
}
auto tmp = QStringView{fileName}.chopped(1);
while (tmp.size() > 1 && tmp.endsWith(QLatin1Char('/'))) {
tmp.chop(1);
}
return tmp.toString();
}
static QStringList stripTrailingSlashesForAll(const QStringList &fileNames)
{
QStringList result;
result.reserve(fileNames.size());
std::transform(fileNames.begin(), fileNames.end(), std::back_inserter(result), &stripTrailingSlashes);
return result;
}
void SignEncryptFilesController::setFiles(const QStringList &files)
{
kleo_assert(!files.empty());
d->files = stripTrailingSlashesForAll(files);
bool archive = false;
if (d->files.size() > 1) {
setOperationMode((operationMode() & ~ArchiveMask) | ArchiveAllowed);
archive = true;
}
for (const auto &file : d->files) {
if (QFileInfo(file).isDir()) {
setOperationMode((operationMode() & ~ArchiveMask) | ArchiveForced);
archive = true;
break;
}
}
d->ensureWizardCreated();
d->wizard->setSingleFile(!archive);
d->wizard->setOutputNames(buildOutputNames(d->files, archive));
}
void SignEncryptFilesController::Private::slotWizardCanceled()
{
qCDebug(KLEOPATRA_LOG) << q << __func__;
q->cancel();
reportError(gpg_error(GPG_ERR_CANCELED), i18n("User cancel"));
}
void SignEncryptFilesController::start()
{
d->ensureWizardVisible();
}
static std::shared_ptr<SignEncryptTask> createSignEncryptTaskForFileInfo(const QFileInfo &fi,
bool ascii,
const std::vector<Key> &recipients,
const std::vector<Key> &signers,
const QString &outputName,
bool symmetric)
{
const std::shared_ptr<SignEncryptTask> task(new SignEncryptTask);
Q_ASSERT(!signers.empty() || !recipients.empty() || symmetric);
task->setAsciiArmor(ascii);
if (!signers.empty()) {
task->setSign(true);
task->setSigners(signers);
task->setDetachedSignature(true);
} else {
task->setSign(false);
}
if (!recipients.empty()) {
task->setEncrypt(true);
task->setRecipients(recipients);
task->setDetachedSignature(false);
} else {
task->setEncrypt(false);
}
task->setEncryptSymmetric(symmetric);
const QString input = fi.absoluteFilePath();
task->setInputFileName(input);
task->setInput(Input::createFromFile(input));
task->setOutputFileName(outputName);
return task;
}
static bool archiveJobsCanBeUsed([[maybe_unused]] GpgME::Protocol protocol)
{
#if QGPGME_SUPPORTS_ARCHIVE_JOBS
return (protocol == GpgME::OpenPGP) && QGpgME::SignEncryptArchiveJob::isSupported();
#else
return false;
#endif
}
static std::shared_ptr<SignEncryptTask> createArchiveSignEncryptTaskForFiles(const QStringList &files,
const std::shared_ptr<ArchiveDefinition> &ad,
bool pgp,
bool ascii,
const std::vector<Key> &recipients,
const std::vector<Key> &signers,
const QString &outputName,
bool symmetric)
{
const std::shared_ptr<SignEncryptTask> task(new SignEncryptTask);
task->setCreateArchive(true);
task->setEncryptSymmetric(symmetric);
Q_ASSERT(!signers.empty() || !recipients.empty() || symmetric);
task->setAsciiArmor(ascii);
if (!signers.empty()) {
task->setSign(true);
task->setSigners(signers);
task->setDetachedSignature(false);
} else {
task->setSign(false);
}
if (!recipients.empty()) {
task->setEncrypt(true);
task->setRecipients(recipients);
} else {
task->setEncrypt(false);
}
const Protocol proto = pgp ? OpenPGP : CMS;
task->setInputFileNames(files);
if (!archiveJobsCanBeUsed(proto)) {
// use legacy archive creation
kleo_assert(ad);
task->setInput(ad->createInputFromPackCommand(proto, files));
}
task->setOutputFileName(outputName);
return task;
}
static std::vector<std::shared_ptr<SignEncryptTask>> createSignEncryptTasksForFileInfo(const QFileInfo &fi,
bool ascii,
const std::vector<Key> &pgpRecipients,
const std::vector<Key> &pgpSigners,
const std::vector<Key> &cmsRecipients,
const std::vector<Key> &cmsSigners,
const QMap<int, QString> &outputNames,
bool symmetric)
{
std::vector<std::shared_ptr<SignEncryptTask>> result;
const bool pgp = !pgpSigners.empty() || !pgpRecipients.empty();
const bool cms = !cmsSigners.empty() || !cmsRecipients.empty();
result.reserve(pgp + cms);
if (pgp || symmetric) {
// Symmetric encryption is only supported for PGP
int outKind = 0;
if ((!pgpRecipients.empty() || symmetric) && !pgpSigners.empty()) {
outKind = SignEncryptFilesWizard::CombinedPGP;
} else if (!pgpRecipients.empty() || symmetric) {
outKind = SignEncryptFilesWizard::EncryptedPGP;
} else {
outKind = SignEncryptFilesWizard::SignaturePGP;
}
result.push_back(createSignEncryptTaskForFileInfo(fi, ascii, pgpRecipients, pgpSigners, outputNames[outKind], symmetric));
}
if (cms) {
// There is no combined sign / encrypt in gpgsm so we create one sign task
// and one encrypt task. Which leaves us with the age old dilemma, encrypt
// then sign, or sign then encrypt. Ugly.
if (!cmsSigners.empty()) {
result.push_back(
createSignEncryptTaskForFileInfo(fi, ascii, std::vector<Key>(), cmsSigners, outputNames[SignEncryptFilesWizard::SignatureCMS], false));
}
if (!cmsRecipients.empty()) {
result.push_back(
createSignEncryptTaskForFileInfo(fi, ascii, cmsRecipients, std::vector<Key>(), outputNames[SignEncryptFilesWizard::EncryptedCMS], false));
}
}
return result;
}
static std::vector<std::shared_ptr<SignEncryptTask>> createArchiveSignEncryptTasksForFiles(const QStringList &files,
const std::shared_ptr<ArchiveDefinition> &ad,
bool ascii,
const std::vector<Key> &pgpRecipients,
const std::vector<Key> &pgpSigners,
const std::vector<Key> &cmsRecipients,
const std::vector<Key> &cmsSigners,
const QMap<int, QString> &outputNames,
bool symmetric)
{
std::vector<std::shared_ptr<SignEncryptTask>> result;
const bool pgp = !pgpSigners.empty() || !pgpRecipients.empty();
const bool cms = !cmsSigners.empty() || !cmsRecipients.empty();
result.reserve(pgp + cms);
if (pgp || symmetric) {
int outKind = 0;
if ((!pgpRecipients.empty() || symmetric) && !pgpSigners.empty()) {
outKind = SignEncryptFilesWizard::CombinedPGP;
} else if (!pgpRecipients.empty() || symmetric) {
outKind = SignEncryptFilesWizard::EncryptedPGP;
} else {
outKind = SignEncryptFilesWizard::SignaturePGP;
}
result.push_back(createArchiveSignEncryptTaskForFiles(files, ad, true, ascii, pgpRecipients, pgpSigners, outputNames[outKind], symmetric));
}
if (cms) {
if (!cmsSigners.empty()) {
result.push_back(createArchiveSignEncryptTaskForFiles(files,
ad,
false,
ascii,
std::vector<Key>(),
cmsSigners,
outputNames[SignEncryptFilesWizard::SignatureCMS],
false));
}
if (!cmsRecipients.empty()) {
result.push_back(createArchiveSignEncryptTaskForFiles(files,
ad,
false,
ascii,
cmsRecipients,
std::vector<Key>(),
outputNames[SignEncryptFilesWizard::EncryptedCMS],
false));
}
}
return result;
}
namespace
{
static auto resolveFileNameConflicts(const std::vector<std::shared_ptr<SignEncryptTask>> &tasks, QWidget *parent)
{
std::vector<std::shared_ptr<SignEncryptTask>> resolvedTasks;
OverwritePolicy overwritePolicy{parent, tasks.size() > 1 ? OverwritePolicy::MultipleFiles : OverwritePolicy::Options{}};
for (auto &task : tasks) {
// by default, do not overwrite existing files
task->setOverwritePolicy(std::make_shared<OverwritePolicy>(OverwritePolicy::Skip));
const auto outputFileName = task->outputFileName();
if (QFile::exists(outputFileName)) {
const auto newFileName = overwritePolicy.obtainOverwritePermission(outputFileName);
if (newFileName.isEmpty()) {
if (overwritePolicy.policy() == OverwritePolicy::Cancel) {
resolvedTasks.clear();
break;
}
// else Skip -> do not add task to the final task list
continue;
} else if (newFileName != outputFileName) {
task->setOutputFileName(newFileName);
} else {
task->setOverwritePolicy(std::make_shared<OverwritePolicy>(OverwritePolicy::Overwrite));
}
}
resolvedTasks.push_back(task);
}
return resolvedTasks;
}
}
void SignEncryptFilesController::Private::slotWizardOperationPrepared()
{
try {
kleo_assert(wizard);
kleo_assert(!files.empty());
const bool archive = ((wizard->outputNames().value(SignEncryptFilesWizard::Directory).isNull() && files.size() > 1) //
|| ((operation & ArchiveMask) == ArchiveForced));
const std::vector<Key> recipients = wizard->resolvedRecipients();
const std::vector<Key> signers = wizard->resolvedSigners();
const FileOperationsPreferences prefs;
const bool ascii = prefs.addASCIIArmor();
std::vector<Key> pgpRecipients, cmsRecipients, pgpSigners, cmsSigners;
for (const Key &k : recipients) {
if (k.protocol() == GpgME::OpenPGP) {
pgpRecipients.push_back(k);
} else {
cmsRecipients.push_back(k);
}
}
for (const Key &k : signers) {
if (k.protocol() == GpgME::OpenPGP) {
pgpSigners.push_back(k);
} else {
cmsSigners.push_back(k);
}
}
std::vector<std::shared_ptr<SignEncryptTask>> tasks;
if (!archive) {
tasks.reserve(files.size());
}
if (archive) {
tasks = createArchiveSignEncryptTasksForFiles(files,
getDefaultAd(),
ascii,
pgpRecipients,
pgpSigners,
cmsRecipients,
cmsSigners,
wizard->outputNames(),
wizard->encryptSymmetric());
} else {
for (const QString &file : std::as_const(files)) {
const std::vector<std::shared_ptr<SignEncryptTask>> created =
createSignEncryptTasksForFileInfo(QFileInfo(file),
ascii,
pgpRecipients,
pgpSigners,
cmsRecipients,
cmsSigners,
buildOutputNamesForDir(file, wizard->outputNames()),
wizard->encryptSymmetric());
tasks.insert(tasks.end(), created.begin(), created.end());
}
}
tasks = resolveFileNameConflicts(tasks, wizard);
if (tasks.empty()) {
q->cancel();
return;
}
kleo_assert(runnable.empty());
runnable.swap(tasks);
for (const auto &task : std::as_const(runnable)) {
q->connectTask(task);
}
std::shared_ptr<TaskCollection> coll(new TaskCollection);
std::vector<std::shared_ptr<Task>> tmp;
std::copy(runnable.begin(), runnable.end(), std::back_inserter(tmp));
coll->setTasks(tmp);
wizard->setTaskCollection(coll);
QTimer::singleShot(0, q, SLOT(schedule()));
} catch (const Kleo::Exception &e) {
reportError(e.error().encodedError(), e.message());
} catch (const std::exception &e) {
reportError(
gpg_error(GPG_ERR_UNEXPECTED),
i18n("Caught unexpected exception in SignEncryptFilesController::Private::slotWizardOperationPrepared: %1", QString::fromLocal8Bit(e.what())));
} catch (...) {
reportError(gpg_error(GPG_ERR_UNEXPECTED), i18n("Caught unknown exception in SignEncryptFilesController::Private::slotWizardOperationPrepared"));
}
}
void SignEncryptFilesController::Private::schedule()
{
if (!cms)
if (const std::shared_ptr<SignEncryptTask> t = takeRunnable(CMS)) {
t->start();
cms = t;
}
if (!openpgp)
if (const std::shared_ptr<SignEncryptTask> t = takeRunnable(OpenPGP)) {
t->start();
openpgp = t;
}
if (!cms && !openpgp) {
kleo_assert(runnable.empty());
q->emitDoneOrError();
}
}
std::shared_ptr<SignEncryptTask> SignEncryptFilesController::Private::takeRunnable(GpgME::Protocol proto)
{
const auto it = std::find_if(runnable.begin(), runnable.end(), [proto](const std::shared_ptr<Task> &task) {
return task->protocol() == proto;
});
if (it == runnable.end()) {
return std::shared_ptr<SignEncryptTask>();
}
const std::shared_ptr<SignEncryptTask> result = *it;
runnable.erase(it);
return result;
}
void SignEncryptFilesController::doTaskDone(const Task *task, const std::shared_ptr<const Task::Result> &result)
{
Q_UNUSED(result)
Q_ASSERT(task);
// We could just delete the tasks here, but we can't use
// Qt::QueuedConnection here (we need sender()) and other slots
// might not yet have executed. Therefore, we push completed tasks
// into a burial container
if (task == d->cms.get()) {
d->completed.push_back(d->cms);
d->cms.reset();
} else if (task == d->openpgp.get()) {
d->completed.push_back(d->openpgp);
d->openpgp.reset();
}
QTimer::singleShot(0, this, SLOT(schedule()));
}
void SignEncryptFilesController::cancel()
{
qCDebug(KLEOPATRA_LOG) << this << __func__;
try {
if (d->wizard) {
d->wizard->close();
}
d->cancelAllTasks();
} catch (const std::exception &e) {
qCDebug(KLEOPATRA_LOG) << "Caught exception: " << e.what();
}
}
void SignEncryptFilesController::Private::cancelAllTasks()
{
// we just kill all runnable tasks - this will not result in
// signal emissions.
runnable.clear();
// a cancel() will result in a call to
if (cms) {
cms->cancel();
}
if (openpgp) {
openpgp->cancel();
}
}
void SignEncryptFilesController::Private::ensureWizardCreated()
{
if (wizard) {
return;
}
std::unique_ptr<SignEncryptFilesWizard> w(new SignEncryptFilesWizard);
w->setAttribute(Qt::WA_DeleteOnClose);
connect(w.get(), SIGNAL(operationPrepared()), q, SLOT(slotWizardOperationPrepared()), Qt::QueuedConnection);
connect(w.get(), SIGNAL(rejected()), q, SLOT(slotWizardCanceled()), Qt::QueuedConnection);
wizard = w.release();
updateWizardMode();
}
void SignEncryptFilesController::Private::ensureWizardVisible()
{
ensureWizardCreated();
q->bringToForeground(wizard);
}
#include "moc_signencryptfilescontroller.cpp"

File Metadata

Mime Type
text/x-diff
Expires
Mon, May 12, 6:40 PM (1 d, 21 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
b3/00/a58038784ae8cffb37fd7bac5b5b

Event Timeline