Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F36623459
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
157 KB
Subscribers
None
View Options
diff --git a/src/crypto/certificateresolver.cpp b/src/crypto/certificateresolver.cpp
index 34891f15e..cc2d3cab9 100644
--- a/src/crypto/certificateresolver.cpp
+++ b/src/crypto/certificateresolver.cpp
@@ -1,228 +1,227 @@
/* -*- mode: c++; c-basic-offset:4 -*-
crypto/certificateresolver.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-kleopatra.h>
#include "certificateresolver.h"
#include <Libkleo/KeyCache>
#include <gpgme++/key.h>
#include <KConfig>
#include <KConfigGroup>
#include <QRegularExpression>
#include <QByteArray>
#include <QHash>
#include <QSet>
#include <algorithm>
#include <iterator>
using namespace Kleo;
using namespace Kleo::Crypto;
using namespace GpgME;
using namespace KMime::Types;
-using namespace KMime::HeaderParsing;
class KConfigBasedRecipientPreferences::Private
{
friend class ::Kleo::Crypto::KConfigBasedRecipientPreferences;
KConfigBasedRecipientPreferences *const q;
public:
explicit Private(const KSharedConfigPtr &config, KConfigBasedRecipientPreferences *qq);
~Private();
private:
void ensurePrefsParsed() const;
void writePrefs();
private:
KSharedConfigPtr m_config;
mutable QHash<QByteArray, QByteArray> pgpPrefs;
mutable QHash<QByteArray, QByteArray> cmsPrefs;
mutable bool m_parsed;
mutable bool m_dirty;
};
KConfigBasedRecipientPreferences::Private::Private(const KSharedConfigPtr &config, KConfigBasedRecipientPreferences *qq)
: q(qq)
, m_config(config)
, m_parsed(false)
, m_dirty(false)
{
Q_UNUSED(q);
Q_ASSERT(m_config);
}
KConfigBasedRecipientPreferences::Private::~Private()
{
writePrefs();
}
void KConfigBasedRecipientPreferences::Private::writePrefs()
{
if (!m_dirty) {
return;
}
const auto pgpPrefsKeys = pgpPrefs.keys();
const auto cmsPrefsKeys = cmsPrefs.keys();
const QSet<QByteArray> keys = QSet<QByteArray>(pgpPrefsKeys.begin(), pgpPrefsKeys.end()) + QSet<QByteArray>(cmsPrefsKeys.begin(), cmsPrefsKeys.end());
int n = 0;
for (const QByteArray &i : keys) {
KConfigGroup group(m_config, QStringLiteral("EncryptionPreference_%1").arg(n++));
group.writeEntry("email", i);
const QByteArray pgp = pgpPrefs.value(i);
if (!pgp.isEmpty()) {
group.writeEntry("pgpCertificate", pgp);
}
const QByteArray cms = cmsPrefs.value(i);
if (!cms.isEmpty()) {
group.writeEntry("cmsCertificate", cms);
}
}
m_config->sync();
m_dirty = false;
}
void KConfigBasedRecipientPreferences::Private::ensurePrefsParsed() const
{
if (m_parsed) {
return;
}
const QStringList groups = m_config->groupList().filter(QRegularExpression(QStringLiteral("^EncryptionPreference_\\d+$")));
for (const QString &i : groups) {
const KConfigGroup group(m_config, i);
const QByteArray id = group.readEntry("email", QByteArray());
if (id.isEmpty()) {
continue;
}
pgpPrefs.insert(id, group.readEntry("pgpCertificate", QByteArray()));
cmsPrefs.insert(id, group.readEntry("cmsCertificate", QByteArray()));
}
m_parsed = true;
}
KConfigBasedRecipientPreferences::KConfigBasedRecipientPreferences(const KSharedConfigPtr &config)
: d(new Private(config, this))
{
}
KConfigBasedRecipientPreferences::~KConfigBasedRecipientPreferences()
{
d->writePrefs();
}
Key KConfigBasedRecipientPreferences::preferredCertificate(const Mailbox &recipient, Protocol protocol)
{
d->ensurePrefsParsed();
const QByteArray keyId = (protocol == CMS ? d->cmsPrefs : d->pgpPrefs).value(recipient.address());
return KeyCache::instance()->findByKeyIDOrFingerprint(keyId.constData());
}
void KConfigBasedRecipientPreferences::setPreferredCertificate(const Mailbox &recipient, Protocol protocol, const Key &certificate)
{
d->ensurePrefsParsed();
if (!recipient.hasAddress()) {
return;
}
(protocol == CMS ? d->cmsPrefs : d->pgpPrefs).insert(recipient.address(), certificate.keyID());
d->m_dirty = true;
}
class KConfigBasedSigningPreferences::Private
{
friend class ::Kleo::Crypto::KConfigBasedSigningPreferences;
KConfigBasedSigningPreferences *const q;
public:
explicit Private(const KSharedConfigPtr &config, KConfigBasedSigningPreferences *qq);
~Private();
private:
void ensurePrefsParsed() const;
void writePrefs();
private:
KSharedConfigPtr m_config;
mutable QByteArray pgpSigningCertificate;
mutable QByteArray cmsSigningCertificate;
mutable bool m_parsed;
mutable bool m_dirty;
};
KConfigBasedSigningPreferences::Private::Private(const KSharedConfigPtr &config, KConfigBasedSigningPreferences *qq)
: q(qq)
, m_config(config)
, m_parsed(false)
, m_dirty(false)
{
Q_UNUSED(q);
Q_ASSERT(m_config);
}
void KConfigBasedSigningPreferences::Private::ensurePrefsParsed() const
{
if (m_parsed) {
return;
}
const KConfigGroup group(m_config, QStringLiteral("SigningPreferences"));
pgpSigningCertificate = group.readEntry("pgpSigningCertificate", QByteArray());
cmsSigningCertificate = group.readEntry("cmsSigningCertificate", QByteArray());
m_parsed = true;
}
void KConfigBasedSigningPreferences::Private::writePrefs()
{
if (!m_dirty) {
return;
}
KConfigGroup group(m_config, QStringLiteral("SigningPreferences"));
group.writeEntry("pgpSigningCertificate", pgpSigningCertificate);
group.writeEntry("cmsSigningCertificate", cmsSigningCertificate);
m_config->sync();
m_dirty = false;
}
KConfigBasedSigningPreferences::Private::~Private()
{
writePrefs();
}
KConfigBasedSigningPreferences::KConfigBasedSigningPreferences(const KSharedConfigPtr &config)
: d(new Private(config, this))
{
}
KConfigBasedSigningPreferences::~KConfigBasedSigningPreferences()
{
d->writePrefs();
}
Key KConfigBasedSigningPreferences::preferredCertificate(Protocol protocol)
{
d->ensurePrefsParsed();
const QByteArray keyId = (protocol == CMS ? d->cmsSigningCertificate : d->pgpSigningCertificate);
const Key key = KeyCache::instance()->findByKeyIDOrFingerprint(keyId.constData());
return key.hasSecret() ? key : Key::null;
}
void KConfigBasedSigningPreferences::setPreferredCertificate(Protocol protocol, const Key &certificate)
{
d->ensurePrefsParsed();
(protocol == CMS ? d->cmsSigningCertificate : d->pgpSigningCertificate) = certificate.keyID();
d->m_dirty = true;
}
diff --git a/src/crypto/certificateresolver.h b/src/crypto/certificateresolver.h
index 17c0b455d..4671161c3 100644
--- a/src/crypto/certificateresolver.h
+++ b/src/crypto/certificateresolver.h
@@ -1,79 +1,79 @@
/* -*- mode: c++; c-basic-offset:4 -*-
crypto/certificateresolver.h
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include <utils/pimpl_ptr.h>
-#include <KMime/HeaderParsing>
+#include <KMime/Types>
#include <gpgme++/key.h>
#include <KSharedConfig>
class KConfig;
namespace GpgME
{
class Key;
}
namespace Kleo
{
namespace Crypto
{
class SigningPreferences
{
public:
virtual ~SigningPreferences()
{
}
virtual GpgME::Key preferredCertificate(GpgME::Protocol protocol) = 0;
virtual void setPreferredCertificate(GpgME::Protocol protocol, const GpgME::Key &certificate) = 0;
};
class RecipientPreferences
{
public:
virtual ~RecipientPreferences()
{
}
virtual GpgME::Key preferredCertificate(const KMime::Types::Mailbox &recipient, GpgME::Protocol protocol) = 0;
virtual void setPreferredCertificate(const KMime::Types::Mailbox &recipient, GpgME::Protocol protocol, const GpgME::Key &certificate) = 0;
};
class KConfigBasedRecipientPreferences : public RecipientPreferences
{
public:
explicit KConfigBasedRecipientPreferences(const KSharedConfigPtr &config);
~KConfigBasedRecipientPreferences() override;
GpgME::Key preferredCertificate(const KMime::Types::Mailbox &recipient, GpgME::Protocol protocol) override;
void setPreferredCertificate(const KMime::Types::Mailbox &recipient, GpgME::Protocol protocol, const GpgME::Key &certificate) override;
private:
Q_DISABLE_COPY(KConfigBasedRecipientPreferences)
class Private;
kdtools::pimpl_ptr<Private> d;
};
class KConfigBasedSigningPreferences : public SigningPreferences
{
public:
explicit KConfigBasedSigningPreferences(const KSharedConfigPtr &config);
~KConfigBasedSigningPreferences() override;
GpgME::Key preferredCertificate(GpgME::Protocol protocol) override;
void setPreferredCertificate(GpgME::Protocol protocol, const GpgME::Key &certificate) override;
private:
Q_DISABLE_COPY(KConfigBasedSigningPreferences)
class Private;
kdtools::pimpl_ptr<Private> d;
};
}
}
diff --git a/src/crypto/decryptverifyemailcontroller.cpp b/src/crypto/decryptverifyemailcontroller.cpp
index 81fc27629..9da974cf2 100644
--- a/src/crypto/decryptverifyemailcontroller.cpp
+++ b/src/crypto/decryptverifyemailcontroller.cpp
@@ -1,471 +1,471 @@
/* -*- mode: c++; c-basic-offset:4 -*-
decryptverifyemailcontroller.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 "decryptverifyemailcontroller.h"
#include "kleopatra_debug.h"
#include "emailoperationspreferences.h"
#include <crypto/decryptverifytask.h>
#include <crypto/gui/newresultpage.h>
#include <crypto/taskcollection.h>
#include <Libkleo/GnuPG>
#include <utils/input.h>
#include <utils/kleo_assert.h>
#include <utils/output.h>
#include <QGpgME/Protocol>
#include <Libkleo/Classify>
#include <Libkleo/Formatting>
-#include <KMime/HeaderParsing>
+#include <KMime/Types>
#include <KLocalizedString>
#include <QPoint>
#include <QPointer>
#include <QTimer>
using namespace GpgME;
using namespace Kleo;
using namespace Kleo::Crypto;
using namespace Kleo::Crypto::Gui;
using namespace KMime::Types;
namespace
{
class DecryptVerifyEMailWizard : public QWizard
{
Q_OBJECT
public:
explicit DecryptVerifyEMailWizard(QWidget *parent = nullptr, Qt::WindowFlags f = {})
: QWizard(parent, f)
, m_resultPage(this)
{
KDAB_SET_OBJECT_NAME(m_resultPage);
m_resultPage.setSubTitle(i18n("Status and progress of the crypto operations is shown here."));
addPage(&m_resultPage);
}
void addTaskCollection(const std::shared_ptr<TaskCollection> &coll)
{
m_resultPage.addTaskCollection(coll);
}
public Q_SLOTS:
void accept() override
{
EMailOperationsPreferences prefs;
prefs.setDecryptVerifyPopupGeometry(geometry());
prefs.save();
QWizard::accept();
}
private:
NewResultPage m_resultPage;
};
}
class DecryptVerifyEMailController::Private
{
DecryptVerifyEMailController *const q;
public:
explicit Private(DecryptVerifyEMailController *qq);
void slotWizardCanceled();
void schedule();
std::vector<std::shared_ptr<AbstractDecryptVerifyTask>> buildTasks();
static DecryptVerifyEMailWizard *findOrCreateWizard(unsigned int id);
void ensureWizardCreated();
void ensureWizardVisible();
void reportError(int err, const QString &details)
{
q->setLastError(err, details);
q->emitDoneOrError();
}
void cancelAllTasks();
std::vector<std::shared_ptr<Input>> m_inputs, m_signedDatas;
std::vector<std::shared_ptr<Output>> m_outputs;
unsigned int m_sessionId;
QPointer<DecryptVerifyEMailWizard> m_wizard;
std::vector<std::shared_ptr<const DecryptVerifyResult>> m_results;
std::vector<std::shared_ptr<AbstractDecryptVerifyTask>> m_runnableTasks, m_completedTasks;
std::shared_ptr<AbstractDecryptVerifyTask> m_runningTask;
bool m_silent;
bool m_operationCompleted;
DecryptVerifyOperation m_operation;
Protocol m_protocol;
VerificationMode m_verificationMode;
std::vector<KMime::Types::Mailbox> m_informativeSenders;
};
DecryptVerifyEMailController::Private::Private(DecryptVerifyEMailController *qq)
: q(qq)
, m_sessionId(0)
, m_silent(false)
, m_operationCompleted(false)
, m_operation(DecryptVerify)
, m_protocol(UnknownProtocol)
, m_verificationMode(Detached)
{
qRegisterMetaType<VerificationResult>();
}
void DecryptVerifyEMailController::Private::slotWizardCanceled()
{
qCDebug(KLEOPATRA_LOG);
if (!m_operationCompleted) {
reportError(gpg_error(GPG_ERR_CANCELED), i18n("User canceled"));
}
}
void DecryptVerifyEMailController::doTaskDone(const Task *task, const std::shared_ptr<const Task::Result> &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->m_runningTask.get()) {
d->m_completedTasks.push_back(d->m_runningTask);
const std::shared_ptr<const DecryptVerifyResult> &dvr = std::dynamic_pointer_cast<const DecryptVerifyResult>(result);
Q_ASSERT(dvr);
d->m_results.push_back(dvr);
d->m_runningTask.reset();
}
QTimer::singleShot(0, this, SLOT(schedule()));
}
void DecryptVerifyEMailController::Private::schedule()
{
if (!m_runningTask && !m_runnableTasks.empty()) {
const std::shared_ptr<AbstractDecryptVerifyTask> t = m_runnableTasks.back();
m_runnableTasks.pop_back();
t->start();
m_runningTask = t;
}
if (!m_runningTask) {
kleo_assert(m_runnableTasks.empty());
for (const std::shared_ptr<const DecryptVerifyResult> &i : std::as_const(m_results)) {
Q_EMIT q->verificationResult(i->verificationResult());
}
// if there is a popup, wait for either the client cancel or the user closing the popup.
// Otherwise (silent case), finish immediately
m_operationCompleted = true;
q->emitDoneOrError();
}
}
void DecryptVerifyEMailController::Private::ensureWizardCreated()
{
if (m_wizard) {
return;
}
DecryptVerifyEMailWizard *w = findOrCreateWizard(m_sessionId);
connect(w, SIGNAL(destroyed()), q, SLOT(slotWizardCanceled()), Qt::QueuedConnection);
m_wizard = w;
}
namespace
{
template<typename C>
void collectGarbage(C &c)
{
auto it = c.begin();
while (it != c.end() /*sic!*/)
if (it->second) {
++it;
} else {
c.erase(it++ /*sic!*/);
}
}
}
// static
DecryptVerifyEMailWizard *DecryptVerifyEMailController::Private::findOrCreateWizard(unsigned int id)
{
static std::map<unsigned int, QPointer<DecryptVerifyEMailWizard>> s_wizards;
collectGarbage(s_wizards);
qCDebug(KLEOPATRA_LOG) << "id = " << id;
if (id != 0) {
const auto it = s_wizards.find(id);
if (it != s_wizards.end()) {
Q_ASSERT(it->second && "This should have been garbage-collected");
return it->second;
}
}
auto w = new DecryptVerifyEMailWizard;
w->setWindowTitle(i18nc("@title:window", "Decrypt/Verify E-Mail"));
w->setAttribute(Qt::WA_DeleteOnClose);
const QRect preferredGeometry = EMailOperationsPreferences().decryptVerifyPopupGeometry();
if (preferredGeometry.isValid()) {
w->setGeometry(preferredGeometry);
}
s_wizards[id] = w;
return w;
}
std::vector<std::shared_ptr<AbstractDecryptVerifyTask>> DecryptVerifyEMailController::Private::buildTasks()
{
const uint numInputs = m_inputs.size();
const uint numMessages = m_signedDatas.size();
const uint numOutputs = m_outputs.size();
const uint numInformativeSenders = m_informativeSenders.size();
// these are duplicated from DecryptVerifyCommandEMailBase::Private::checkForErrors with slightly modified error codes/messages
if (!numInputs)
throw Kleo::Exception(makeGnuPGError(GPG_ERR_CONFLICT), i18n("At least one input needs to be provided"));
if (numInformativeSenders > 0 && numInformativeSenders != numInputs)
throw Kleo::Exception(makeGnuPGError(GPG_ERR_CONFLICT), // TODO use better error code if possible
i18n("Informative sender/signed data count mismatch"));
if (numMessages) {
if (numMessages != numInputs)
throw Kleo::Exception(makeGnuPGError(GPG_ERR_CONFLICT), // TODO use better error code if possible
i18n("Signature/signed data count mismatch"));
else if (m_operation != Verify || m_verificationMode != Detached)
throw Kleo::Exception(makeGnuPGError(GPG_ERR_CONFLICT), i18n("Signed data can only be given for detached signature verification"));
}
if (numOutputs) {
if (numOutputs != numInputs)
throw Kleo::Exception(makeGnuPGError(GPG_ERR_CONFLICT), // TODO use better error code if possible
i18n("Input/Output count mismatch"));
else if (numMessages)
throw Kleo::Exception(makeGnuPGError(GPG_ERR_CONFLICT), i18n("Cannot use output and signed data simultaneously"));
}
kleo_assert(m_protocol != UnknownProtocol);
const QGpgME::Protocol *const backend = (m_protocol == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime();
if (!backend) {
throw Kleo::Exception(makeGnuPGError(GPG_ERR_UNSUPPORTED_PROTOCOL), i18n("No backend support for %1", Formatting::displayName(m_protocol)));
}
if (m_operation != Decrypt && !m_silent) {
ensureWizardVisible();
}
std::vector<std::shared_ptr<AbstractDecryptVerifyTask>> tasks;
for (unsigned int i = 0; i < numInputs; ++i) {
std::shared_ptr<AbstractDecryptVerifyTask> task;
switch (m_operation) {
case Decrypt: {
std::shared_ptr<DecryptTask> t(new DecryptTask);
t->setInput(m_inputs.at(i));
Q_ASSERT(numOutputs);
t->setOutput(m_outputs.at(i));
t->setProtocol(m_protocol);
task = t;
} break;
case Verify: {
if (m_verificationMode == Detached) {
std::shared_ptr<VerifyDetachedTask> t(new VerifyDetachedTask);
t->setInput(m_inputs.at(i));
t->setSignedData(m_signedDatas.at(i));
if (numInformativeSenders > 0) {
t->setInformativeSender(m_informativeSenders.at(i));
}
t->setProtocol(m_protocol);
task = t;
} else {
std::shared_ptr<VerifyOpaqueTask> t(new VerifyOpaqueTask);
t->setInput(m_inputs.at(i));
if (numOutputs) {
t->setOutput(m_outputs.at(i));
}
if (numInformativeSenders > 0) {
t->setInformativeSender(m_informativeSenders.at(i));
}
t->setProtocol(m_protocol);
task = t;
}
} break;
case DecryptVerify: {
std::shared_ptr<DecryptVerifyTask> t(new DecryptVerifyTask);
t->setInput(m_inputs.at(i));
Q_ASSERT(numOutputs);
t->setOutput(m_outputs.at(i));
if (numInformativeSenders > 0) {
t->setInformativeSender(m_informativeSenders.at(i));
}
t->setProtocol(m_protocol);
task = t;
}
}
Q_ASSERT(task);
tasks.push_back(task);
}
return tasks;
}
void DecryptVerifyEMailController::Private::ensureWizardVisible()
{
ensureWizardCreated();
q->bringToForeground(m_wizard);
}
DecryptVerifyEMailController::DecryptVerifyEMailController(QObject *parent)
: Controller(parent)
, d(new Private(this))
{
}
DecryptVerifyEMailController::DecryptVerifyEMailController(const std::shared_ptr<const ExecutionContext> &ctx, QObject *parent)
: Controller(ctx, parent)
, d(new Private(this))
{
}
DecryptVerifyEMailController::~DecryptVerifyEMailController()
{
qCDebug(KLEOPATRA_LOG);
}
void DecryptVerifyEMailController::start()
{
d->m_runnableTasks = d->buildTasks();
const std::shared_ptr<TaskCollection> coll(new TaskCollection);
std::vector<std::shared_ptr<Task>> tsks;
for (std::shared_ptr<Task> i : std::as_const(d->m_runnableTasks)) {
connectTask(i);
tsks.push_back(i);
}
coll->setTasks(tsks);
d->ensureWizardCreated();
d->m_wizard->addTaskCollection(coll);
d->ensureWizardVisible();
QTimer::singleShot(0, this, SLOT(schedule()));
}
void DecryptVerifyEMailController::setInput(const std::shared_ptr<Input> &input)
{
d->m_inputs.resize(1, input);
}
void DecryptVerifyEMailController::setInputs(const std::vector<std::shared_ptr<Input>> &inputs)
{
d->m_inputs = inputs;
}
void DecryptVerifyEMailController::setSignedData(const std::shared_ptr<Input> &data)
{
d->m_signedDatas.resize(1, data);
}
void DecryptVerifyEMailController::setSignedData(const std::vector<std::shared_ptr<Input>> &data)
{
d->m_signedDatas = data;
}
void DecryptVerifyEMailController::setOutput(const std::shared_ptr<Output> &output)
{
d->m_outputs.resize(1, output);
}
void DecryptVerifyEMailController::setOutputs(const std::vector<std::shared_ptr<Output>> &outputs)
{
d->m_outputs = outputs;
}
void DecryptVerifyEMailController::setInformativeSenders(const std::vector<KMime::Types::Mailbox> &senders)
{
d->m_informativeSenders = senders;
}
void DecryptVerifyEMailController::setWizardShown(bool shown)
{
d->m_silent = !shown;
if (d->m_wizard) {
d->m_wizard->setVisible(shown);
}
}
void DecryptVerifyEMailController::setOperation(DecryptVerifyOperation operation)
{
d->m_operation = operation;
}
void DecryptVerifyEMailController::setVerificationMode(VerificationMode vm)
{
d->m_verificationMode = vm;
}
void DecryptVerifyEMailController::setProtocol(Protocol prot)
{
d->m_protocol = prot;
}
void DecryptVerifyEMailController::setSessionId(unsigned int id)
{
qCDebug(KLEOPATRA_LOG) << "id = " << id;
d->m_sessionId = id;
}
void DecryptVerifyEMailController::cancel()
{
qCDebug(KLEOPATRA_LOG);
try {
if (d->m_wizard) {
disconnect(d->m_wizard);
d->m_wizard->close();
}
d->cancelAllTasks();
} catch (const std::exception &e) {
qCDebug(KLEOPATRA_LOG) << "Caught exception: " << e.what();
}
}
void DecryptVerifyEMailController::Private::cancelAllTasks()
{
// we just kill all runnable tasks - this will not result in
// signal emissions.
m_runnableTasks.clear();
// a cancel() will result in a call to
if (m_runningTask) {
m_runningTask->cancel();
}
}
#include "decryptverifyemailcontroller.moc"
#include "moc_decryptverifyemailcontroller.cpp"
diff --git a/src/crypto/decryptverifytask.cpp b/src/crypto/decryptverifytask.cpp
index 2e0fe228d..881a2e16e 100644
--- a/src/crypto/decryptverifytask.cpp
+++ b/src/crypto/decryptverifytask.cpp
@@ -1,1821 +1,1821 @@
/* -*- mode: c++; c-basic-offset:4 -*-
decryptverifytask.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 "decryptverifytask.h"
#include <QGpgME/DecryptJob>
#include <QGpgME/DecryptVerifyArchiveJob>
#include <QGpgME/DecryptVerifyJob>
#include <QGpgME/Protocol>
#include <QGpgME/VerifyDetachedJob>
#include <QGpgME/VerifyOpaqueJob>
#include <Libkleo/AuditLogEntry>
#include <Libkleo/Classify>
#include <Libkleo/Compliance>
#include <Libkleo/Dn>
#include <Libkleo/Formatting>
#include <Libkleo/KeyCache>
#include <Libkleo/KleoException>
#include <Libkleo/Predicates>
#include <Libkleo/Stl_Util>
#include <Libkleo/GnuPG>
#include <utils/detail_p.h>
#include <utils/input.h>
#include <utils/kleo_assert.h>
#include <utils/output.h>
-#include <KMime/HeaderParsing>
+#include <KMime/Types>
#include <gpgme++/context.h>
#include <gpgme++/decryptionresult.h>
#include <gpgme++/error.h>
#include <gpgme++/key.h>
#include <gpgme++/verificationresult.h>
#include <gpg-error.h>
#include "kleopatra_debug.h"
#include <KFileUtils>
#include <KLocalizedString>
#include <QByteArray>
#include <QDateTime>
#include <QDir>
#include <QFileInfo>
#include <QIODevice>
#include <QLocale>
#include <QMimeDatabase>
#include <QStringList>
#include <QTextDocument> // Qt::escape
#include <algorithm>
#include <sstream>
using namespace Kleo::Crypto;
using namespace Kleo;
using namespace GpgME;
using namespace KMime::Types;
namespace
{
static AuditLogEntry auditLogFromSender(QObject *sender)
{
return AuditLogEntry::fromJob(qobject_cast<const QGpgME::Job *>(sender));
}
static bool addrspec_equal(const AddrSpec &lhs, const AddrSpec &rhs, Qt::CaseSensitivity cs)
{
return lhs.localPart.compare(rhs.localPart, cs) == 0 && lhs.domain.compare(rhs.domain, Qt::CaseInsensitive) == 0;
}
static bool mailbox_equal(const Mailbox &lhs, const Mailbox &rhs, Qt::CaseSensitivity cs)
{
return addrspec_equal(lhs.addrSpec(), rhs.addrSpec(), cs);
}
static std::string stripAngleBrackets(const std::string &str)
{
if (str.empty()) {
return str;
}
if (str[0] == '<' && str[str.size() - 1] == '>') {
return str.substr(1, str.size() - 2);
}
return str;
}
static std::string email(const UserID &uid)
{
if (uid.parent().protocol() == OpenPGP) {
if (const char *const email = uid.email()) {
return stripAngleBrackets(email);
} else {
return std::string();
}
}
Q_ASSERT(uid.parent().protocol() == CMS);
if (const char *const id = uid.id())
if (*id == '<') {
return stripAngleBrackets(id);
} else {
return DN(id)[QStringLiteral("EMAIL")].trimmed().toUtf8().constData();
}
else {
return std::string();
}
}
static Mailbox mailbox(const UserID &uid)
{
const std::string e = email(uid);
Mailbox mbox;
if (!e.empty()) {
mbox.setAddress(e.c_str());
}
return mbox;
}
static std::vector<Mailbox> extractMailboxes(const Key &key)
{
std::vector<Mailbox> res;
const auto userIDs{key.userIDs()};
for (const UserID &id : userIDs) {
const Mailbox mbox = mailbox(id);
if (!mbox.addrSpec().isEmpty()) {
res.push_back(mbox);
}
}
return res;
}
static std::vector<Mailbox> extractMailboxes(const std::vector<Key> &signers)
{
std::vector<Mailbox> res;
for (const Key &i : signers) {
const std::vector<Mailbox> bxs = extractMailboxes(i);
res.insert(res.end(), bxs.begin(), bxs.end());
}
return res;
}
static bool keyContainsMailbox(const Key &key, const Mailbox &mbox)
{
const std::vector<Mailbox> mbxs = extractMailboxes(key);
return std::find_if(mbxs.cbegin(),
mbxs.cend(),
[mbox](const Mailbox &m) {
return mailbox_equal(mbox, m, Qt::CaseInsensitive);
})
!= mbxs.cend();
}
static bool keysContainMailbox(const std::vector<Key> &keys, const Mailbox &mbox)
{
return std::find_if(keys.cbegin(),
keys.cend(),
[mbox](const Key &key) {
return keyContainsMailbox(key, mbox);
})
!= keys.cend();
}
static bool relevantInDecryptVerifyContext(const VerificationResult &r)
{
// for D/V operations, we ignore verification results which are not errors and contain
// no signatures (which means that the data was just not signed)
return (r.error() && r.error().code() != GPG_ERR_DECRYPT_FAILED) || r.numSignatures() > 0;
}
static QString signatureSummaryToString(int summary)
{
if (summary & Signature::None) {
return i18n("Error: Signature not verified");
} else if (summary & Signature::Valid || summary & Signature::Green) {
return i18n("Good signature");
} else if (summary & Signature::KeyRevoked) {
return i18n("Signing certificate was revoked");
} else if (summary & Signature::KeyExpired) {
return i18n("Signing certificate is expired");
} else if (summary & Signature::KeyMissing) {
return i18n("Certificate is not available");
} else if (summary & Signature::SigExpired) {
return i18n("Signature expired");
} else if (summary & Signature::CrlMissing) {
return i18n("CRL missing");
} else if (summary & Signature::CrlTooOld) {
return i18n("CRL too old");
} else if (summary & Signature::BadPolicy) {
return i18n("Bad policy");
} else if (summary & Signature::SysError) {
return i18n("System error"); // ### retrieve system error details?
} else if (summary & Signature::Red) {
return i18n("Bad signature");
}
return QString();
}
static QString formatValidSignatureWithTrustLevel(const UserID &id)
{
if (id.isNull()) {
return QString();
}
switch (id.validity()) {
case UserID::Marginal:
return i18n("The signature is valid but the trust in the certificate's validity is only marginal.");
case UserID::Full:
return i18n("The signature is valid and the certificate's validity is fully trusted.");
case UserID::Ultimate:
return i18n("The signature is valid and the certificate's validity is ultimately trusted.");
case UserID::Never:
return i18n("The signature is valid but the certificate's validity is <em>not trusted</em>.");
case UserID::Unknown:
return i18n("The signature is valid but the certificate's validity is unknown.");
case UserID::Undefined:
default:
return i18n("The signature is valid but the certificate's validity is undefined.");
}
}
static QString renderKeyLink(const QString &fpr, const QString &text)
{
return QStringLiteral("<a href=\"key:%1\">%2</a>").arg(fpr, text);
}
static QString renderKey(const Key &key)
{
if (key.isNull()) {
return i18n("Unknown certificate");
}
if (key.primaryFingerprint() && strlen(key.primaryFingerprint()) > 16 && key.numUserIDs()) {
const QString text = QStringLiteral("%1 (%2)")
.arg(Formatting::prettyNameAndEMail(key).toHtmlEscaped())
.arg(Formatting::prettyID(QString::fromLocal8Bit(key.primaryFingerprint()).right(16).toLatin1().constData()));
return renderKeyLink(QLatin1String(key.primaryFingerprint()), text);
}
return renderKeyLink(QLatin1String(key.primaryFingerprint()), Formatting::prettyID(key.primaryFingerprint()));
}
static QString renderKeyEMailOnlyNameAsFallback(const Key &key)
{
if (key.isNull()) {
return i18n("Unknown certificate");
}
const QString email = Formatting::prettyEMail(key);
const QString user = !email.isEmpty() ? email : Formatting::prettyName(key);
return renderKeyLink(QLatin1String(key.primaryFingerprint()), user);
}
static QString formatDate(const QDateTime &dt)
{
return QLocale().toString(dt);
}
static QString formatSigningInformation(const Signature &sig)
{
if (sig.isNull()) {
return QString();
}
const QDateTime dt = sig.creationTime() != 0 ? QDateTime::fromSecsSinceEpoch(quint32(sig.creationTime())) : QDateTime();
QString text;
Key key = sig.key();
if (dt.isValid()) {
text = i18nc("1 is a date", "Signature created on %1", formatDate(dt)) + QStringLiteral("<br>");
}
if (key.isNull()) {
return text += i18n("With unavailable certificate:") + QStringLiteral("<br>ID: 0x%1").arg(QString::fromLatin1(sig.fingerprint()).toUpper());
}
text += i18n("With certificate:") + QStringLiteral("<br>") + renderKey(key);
if (DeVSCompliance::isCompliant()) {
text += (QStringLiteral("<br/>")
+ (sig.isDeVs() ? i18nc("%1 is a placeholder for the name of a compliance mode. E.g. NATO RESTRICTED compliant or VS-NfD compliant",
"The signature is %1",
DeVSCompliance::name(true))
: i18nc("%1 is a placeholder for the name of a compliance mode. E.g. NATO RESTRICTED compliant or VS-NfD compliant",
"The signature <b>is not</b> %1.",
DeVSCompliance::name(true))));
}
return text;
}
static QString strikeOut(const QString &str, bool strike)
{
return QString(strike ? QStringLiteral("<s>%1</s>") : QStringLiteral("%1")).arg(str.toHtmlEscaped());
}
static QString formatInputOutputLabel(const QString &input, const QString &output, bool inputDeleted, bool outputDeleted)
{
if (output.isEmpty()) {
return strikeOut(input, inputDeleted);
}
return i18nc("Input file --> Output file (rarr is arrow", "%1 → %2", strikeOut(input, inputDeleted), strikeOut(output, outputDeleted));
}
static bool IsErrorOrCanceled(const GpgME::Error &err)
{
return err || err.isCanceled();
}
static bool IsErrorOrCanceled(const Result &res)
{
return IsErrorOrCanceled(res.error());
}
static bool IsBad(const Signature &sig)
{
return sig.summary() & Signature::Red;
}
static bool IsGoodOrValid(const Signature &sig)
{
return (sig.summary() & Signature::Valid) || (sig.summary() & Signature::Green);
}
static UserID findUserIDByMailbox(const Key &key, const Mailbox &mbox)
{
const auto userIDs{key.userIDs()};
for (const UserID &id : userIDs)
if (mailbox_equal(mailbox(id), mbox, Qt::CaseInsensitive)) {
return id;
}
return UserID();
}
static void updateKeys(const VerificationResult &result)
{
// This little hack works around the problem that GnuPG / GpgME does not
// provide Key information in a verification result. The Key object is
// a dummy just holding the KeyID. This hack ensures that all available
// keys are fetched from the backend and are populated
for (const auto &sig : result.signatures()) {
// Update key information
sig.key(true, true);
}
}
static QString ensureUniqueDirectory(const QString &path)
{
// make sure that we don't use an existing directory
QString uniquePath = path;
const QFileInfo outputInfo{path};
if (outputInfo.exists()) {
const auto uniqueName = KFileUtils::suggestName(QUrl::fromLocalFile(outputInfo.absolutePath()), outputInfo.fileName());
uniquePath = outputInfo.dir().filePath(uniqueName);
}
if (!QDir{}.mkpath(uniquePath)) {
return {};
}
return uniquePath;
}
static bool mimeTypeInherits(const QMimeType &mimeType, const QString &mimeTypeName)
{
// inherits is expensive on an invalid mimeType
return mimeType.isValid() && mimeType.inherits(mimeTypeName);
}
}
class DecryptVerifyResult::SenderInfo
{
public:
explicit SenderInfo(const Mailbox &infSender, const std::vector<Key> &signers_)
: informativeSender(infSender)
, signers(signers_)
{
}
const Mailbox informativeSender;
const std::vector<Key> signers;
bool hasInformativeSender() const
{
return !informativeSender.addrSpec().isEmpty();
}
bool conflicts() const
{
return hasInformativeSender() && hasKeys() && !keysContainMailbox(signers, informativeSender);
}
bool hasKeys() const
{
return std::any_of(signers.cbegin(), signers.cend(), [](const Key &key) {
return !key.isNull();
});
}
std::vector<Mailbox> signerMailboxes() const
{
return extractMailboxes(signers);
}
};
namespace
{
static Task::Result::VisualCode codeForVerificationResult(const VerificationResult &res)
{
if (res.isNull()) {
return Task::Result::NeutralSuccess;
}
const std::vector<Signature> sigs = res.signatures();
if (sigs.empty()) {
return Task::Result::Warning;
}
if (std::find_if(sigs.begin(), sigs.end(), IsBad) != sigs.end()) {
return Task::Result::Danger;
}
if ((size_t)std::count_if(sigs.begin(), sigs.end(), IsGoodOrValid) == sigs.size()) {
return Task::Result::AllGood;
}
return Task::Result::Warning;
}
static QString formatVerificationResultOverview(const VerificationResult &res, const DecryptVerifyResult::SenderInfo &info)
{
if (res.isNull()) {
return QString();
}
const Error err = res.error();
if (err.isCanceled()) {
return i18n("<b>Verification canceled.</b>");
} else if (err) {
return i18n("<b>Verification failed: %1.</b>", Formatting::errorAsString(err).toHtmlEscaped());
}
const std::vector<Signature> sigs = res.signatures();
if (sigs.empty()) {
return i18n("<b>No signatures found.</b>");
}
const uint bad = std::count_if(sigs.cbegin(), sigs.cend(), IsBad);
if (bad > 0) {
return i18np("<b>Invalid signature.</b>", "<b>%1 invalid signatures.</b>", bad);
}
const uint warn = std::count_if(sigs.cbegin(), sigs.cend(), [](const Signature &sig) {
return !IsGoodOrValid(sig);
});
if (warn == sigs.size()) {
return i18np("<b>The data could not be verified.</b>", "<b>%1 signatures could not be verified.</b>", warn);
}
// Good signature:
QString text;
if (sigs.size() == 1) {
text = i18n("<b>Valid signature by %1</b>", renderKeyEMailOnlyNameAsFallback(sigs[0].key()));
if (info.conflicts())
text += i18n("<br/><b>Warning:</b> The sender's mail address is not stored in the %1 used for signing.",
renderKeyLink(QLatin1String(sigs[0].key().primaryFingerprint()), i18n("certificate")));
} else {
text = i18np("<b>Valid signature.</b>", "<b>%1 valid signatures.</b>", sigs.size());
if (info.conflicts()) {
text += i18n("<br/><b>Warning:</b> The sender's mail address is not stored in the certificates used for signing.");
}
}
return text;
}
static QString formatDecryptionResultOverview(const DecryptionResult &result, const QString &errorString = QString())
{
const Error err = result.error();
if (err.isCanceled()) {
return i18n("<b>Decryption canceled.</b>");
} else if (result.isLegacyCipherNoMDC()) {
return i18n("<b>Decryption failed: %1.</b>", i18n("No integrity protection (MDC)."));
} else if (!errorString.isEmpty()) {
return i18n("<b>Decryption failed: %1.</b>", errorString.toHtmlEscaped());
} else if (err) {
return i18n("<b>Decryption failed: %1.</b>", Formatting::errorAsString(err).toHtmlEscaped());
}
return i18n("<b>Decryption succeeded.</b>");
}
static QString formatSignature(const Signature &sig, const DecryptVerifyResult::SenderInfo &info)
{
if (sig.isNull()) {
return QString();
}
const QString text = formatSigningInformation(sig) + QLatin1String("<br/>");
const Key key = sig.key();
// Green
if (sig.summary() & Signature::Valid) {
const UserID id = findUserIDByMailbox(key, info.informativeSender);
return text + formatValidSignatureWithTrustLevel(!id.isNull() ? id : key.userID(0));
}
// Red
if ((sig.summary() & Signature::Red)) {
const QString ret = text + i18n("The signature is invalid: %1", signatureSummaryToString(sig.summary()));
if (sig.summary() & Signature::SysError) {
return ret + QStringLiteral(" (%1)").arg(Formatting::errorAsString(sig.status()));
}
return ret;
}
// Key missing
if ((sig.summary() & Signature::KeyMissing)) {
return text + i18n("You can search the certificate on a keyserver or import it from a file.");
}
// Yellow
if ((sig.validity() & Signature::Validity::Undefined) //
|| (sig.validity() & Signature::Validity::Unknown) //
|| (sig.summary() == Signature::Summary::None)) {
return text
+ (key.protocol() == OpenPGP
? i18n("The used key is not certified by you or any trusted person.")
: i18n("The used certificate is not certified by a trustworthy Certificate Authority or the Certificate Authority is unknown."));
}
// Catch all fall through
const QString ret = text + i18n("The signature is invalid: %1", signatureSummaryToString(sig.summary()));
if (sig.summary() & Signature::SysError) {
return ret + QStringLiteral(" (%1)").arg(Formatting::errorAsString(sig.status()));
}
return ret;
}
static QStringList format(const std::vector<Mailbox> &mbxs)
{
QStringList res;
std::transform(mbxs.cbegin(), mbxs.cend(), std::back_inserter(res), [](const Mailbox &mbox) {
return mbox.prettyAddress();
});
return res;
}
static QString formatVerificationResultDetails(const VerificationResult &res, const DecryptVerifyResult::SenderInfo &info, const QString &errorString)
{
if ((res.error().code() == GPG_ERR_EIO || res.error().code() == GPG_ERR_NO_DATA) && !errorString.isEmpty()) {
return i18n("Input error: %1", errorString);
}
const std::vector<Signature> sigs = res.signatures();
QString details;
for (const Signature &sig : sigs) {
details += formatSignature(sig, info) + QLatin1Char('\n');
}
details = details.trimmed();
details.replace(QLatin1Char('\n'), QStringLiteral("<br/><br/>"));
if (info.conflicts()) {
details += i18n("<p>The sender's address %1 is not stored in the certificate. Stored: %2</p>",
info.informativeSender.prettyAddress(),
format(info.signerMailboxes()).join(i18nc("separator for a list of e-mail addresses", ", ")));
}
return details;
}
static QString formatRecipientsDetails(const std::vector<Key> &knownRecipients, unsigned int numRecipients)
{
if (numRecipients == 0) {
return {};
}
if (knownRecipients.empty()) {
return QLatin1String("<i>") + i18np("One unknown recipient.", "%1 unknown recipients.", numRecipients) + QLatin1String("</i>");
}
QString details = i18np("Recipient:", "Recipients:", numRecipients);
if (numRecipients == 1) {
details += QLatin1Char(' ') + renderKey(knownRecipients.front());
} else {
details += QLatin1String("<ul>");
for (const Key &key : knownRecipients) {
details += QLatin1String("<li>") + renderKey(key) + QLatin1String("</li>");
}
if (knownRecipients.size() < numRecipients) {
details += QLatin1String("<li><i>") + i18np("One unknown recipient", "%1 unknown recipients", numRecipients - knownRecipients.size())
+ QLatin1String("</i></li>");
}
details += QLatin1String("</ul>");
}
return details;
}
static QString formatDecryptionResultDetails(const DecryptionResult &res,
const std::vector<Key> &recipients,
const QString &errorString,
bool isSigned,
const QPointer<Task> &task)
{
if ((res.error().code() == GPG_ERR_EIO || res.error().code() == GPG_ERR_NO_DATA) && !errorString.isEmpty()) {
return i18n("Input error: %1", errorString);
}
if (res.isNull() || res.error() || res.error().isCanceled()) {
return formatRecipientsDetails(recipients, res.numRecipients());
}
QString details;
if (DeVSCompliance::isCompliant()) {
details += ((res.isDeVs() ? i18nc("%1 is a placeholder for the name of a compliance mode. E.g. NATO RESTRICTED compliant or VS-NfD compliant",
"The decryption is %1.",
DeVSCompliance::name(true))
: i18nc("%1 is a placeholder for the name of a compliance mode. E.g. NATO RESTRICTED compliant or VS-NfD compliant",
"The decryption <b>is not</b> %1.",
DeVSCompliance::name(true)))
+ QStringLiteral("<br/>"));
}
if (res.fileName()) {
const auto decVerifyTask = qobject_cast<AbstractDecryptVerifyTask *>(task.data());
if (decVerifyTask) {
const auto embedFileName = QString::fromUtf8(res.fileName()).toHtmlEscaped();
if (embedFileName != decVerifyTask->outputLabel()) {
details += i18n("Embedded file name: '%1'", embedFileName);
details += QStringLiteral("<br/>");
}
}
}
if (!isSigned) {
details += i18n("<b>Note:</b> You cannot be sure who encrypted this message as it is not signed.") + QLatin1String("<br/>");
}
if (res.isLegacyCipherNoMDC()) {
details += i18nc("Integrity protection was missing because an old cipher was used.",
"<b>Hint:</b> If this file was encrypted before the year 2003 it is "
"likely that the file is legitimate. This is because back "
"then integrity protection was not widely used.")
+ QStringLiteral("<br/><br/>")
+ i18nc("The user is offered to force decrypt a non integrity protected message. With the strong advice to re-encrypt it.",
"If you are confident that the file was not manipulated you should re-encrypt it after you have forced the decryption.")
+ QStringLiteral("<br/><br/>");
}
details += formatRecipientsDetails(recipients, res.numRecipients());
return details;
}
static QString formatDecryptVerifyResultOverview(const DecryptionResult &dr, const VerificationResult &vr, const DecryptVerifyResult::SenderInfo &info)
{
if (IsErrorOrCanceled(dr) || !relevantInDecryptVerifyContext(vr)) {
return formatDecryptionResultOverview(dr);
}
return formatVerificationResultOverview(vr, info);
}
static QString formatDecryptVerifyResultDetails(const DecryptionResult &dr,
const VerificationResult &vr,
const std::vector<Key> &recipients,
const DecryptVerifyResult::SenderInfo &info,
const QString &errorString,
const QPointer<Task> &task)
{
const QString drDetails = formatDecryptionResultDetails(dr, recipients, errorString, relevantInDecryptVerifyContext(vr), task);
if (IsErrorOrCanceled(dr) || !relevantInDecryptVerifyContext(vr)) {
return drDetails;
}
return drDetails + (drDetails.isEmpty() ? QString() : QStringLiteral("<br/>")) + formatVerificationResultDetails(vr, info, errorString);
}
} // anon namespace
class DecryptVerifyResult::Private
{
DecryptVerifyResult *const q;
public:
Private(DecryptVerifyOperation type,
const VerificationResult &vr,
const DecryptionResult &dr,
const QByteArray &stuff,
const QString &fileName,
const GpgME::Error &error,
const QString &errString,
const QString &input,
const QString &output,
const AuditLogEntry &auditLog,
Task *parentTask,
const Mailbox &informativeSender,
DecryptVerifyResult *qq)
: q(qq)
, m_type(type)
, m_verificationResult(vr)
, m_decryptionResult(dr)
, m_stuff(stuff)
, m_fileName(fileName)
, m_error(error)
, m_errorString(errString)
, m_inputLabel(input)
, m_outputLabel(output)
, m_auditLog(auditLog)
, m_parentTask(QPointer<Task>(parentTask))
, m_informativeSender(informativeSender)
{
}
QString label() const
{
return formatInputOutputLabel(m_inputLabel, m_outputLabel, false, q->hasError());
}
DecryptVerifyResult::SenderInfo makeSenderInfo() const;
bool isDecryptOnly() const
{
return m_type == Decrypt;
}
bool isVerifyOnly() const
{
return m_type == Verify;
}
bool isDecryptVerify() const
{
return m_type == DecryptVerify;
}
DecryptVerifyOperation m_type;
VerificationResult m_verificationResult;
DecryptionResult m_decryptionResult;
QByteArray m_stuff;
QString m_fileName;
GpgME::Error m_error;
QString m_errorString;
QString m_inputLabel;
QString m_outputLabel;
const AuditLogEntry m_auditLog;
QPointer<Task> m_parentTask;
const Mailbox m_informativeSender;
};
DecryptVerifyResult::SenderInfo DecryptVerifyResult::Private::makeSenderInfo() const
{
return SenderInfo(m_informativeSender, KeyCache::instance()->findSigners(m_verificationResult));
}
std::shared_ptr<DecryptVerifyResult>
AbstractDecryptVerifyTask::fromDecryptResult(const DecryptionResult &dr, const QByteArray &plaintext, const AuditLogEntry &auditLog)
{
return std::shared_ptr<DecryptVerifyResult>(new DecryptVerifyResult(Decrypt, //
VerificationResult(),
dr,
plaintext,
{},
{},
QString(),
inputLabel(),
outputLabel(),
auditLog,
this,
informativeSender()));
}
std::shared_ptr<DecryptVerifyResult> AbstractDecryptVerifyTask::fromDecryptResult(const GpgME::Error &err, const QString &what, const AuditLogEntry &auditLog)
{
return std::shared_ptr<DecryptVerifyResult>(new DecryptVerifyResult(Decrypt, //
VerificationResult(),
DecryptionResult(err),
QByteArray(),
{},
err,
what,
inputLabel(),
outputLabel(),
auditLog,
this,
informativeSender()));
}
std::shared_ptr<DecryptVerifyResult> AbstractDecryptVerifyTask::fromDecryptVerifyResult(const DecryptionResult &dr,
const VerificationResult &vr,
const QByteArray &plaintext,
const QString &fileName,
const AuditLogEntry &auditLog)
{
const auto err = dr.error().code() ? dr.error() : vr.error();
return std::shared_ptr<DecryptVerifyResult>(new DecryptVerifyResult(DecryptVerify, //
vr,
dr,
plaintext,
fileName,
err,
QString(),
inputLabel(),
outputLabel(),
auditLog,
this,
informativeSender()));
}
std::shared_ptr<DecryptVerifyResult>
AbstractDecryptVerifyTask::fromDecryptVerifyResult(const GpgME::Error &err, const QString &details, const AuditLogEntry &auditLog)
{
return std::shared_ptr<DecryptVerifyResult>(new DecryptVerifyResult(DecryptVerify, //
VerificationResult(),
DecryptionResult(err),
QByteArray(),
{},
err,
details,
inputLabel(),
outputLabel(),
auditLog,
this,
informativeSender()));
}
std::shared_ptr<DecryptVerifyResult>
AbstractDecryptVerifyTask::fromVerifyOpaqueResult(const VerificationResult &vr, const QByteArray &plaintext, const AuditLogEntry &auditLog)
{
return std::shared_ptr<DecryptVerifyResult>(new DecryptVerifyResult(Verify, //
vr,
DecryptionResult(),
plaintext,
{},
{},
QString(),
inputLabel(),
outputLabel(),
auditLog,
this,
informativeSender()));
}
std::shared_ptr<DecryptVerifyResult>
AbstractDecryptVerifyTask::fromVerifyOpaqueResult(const GpgME::Error &err, const QString &details, const AuditLogEntry &auditLog)
{
return std::shared_ptr<DecryptVerifyResult>(new DecryptVerifyResult(Verify, //
VerificationResult(err),
DecryptionResult(),
QByteArray(),
{},
err,
details,
inputLabel(),
outputLabel(),
auditLog,
this,
informativeSender()));
}
std::shared_ptr<DecryptVerifyResult> AbstractDecryptVerifyTask::fromVerifyDetachedResult(const VerificationResult &vr, const AuditLogEntry &auditLog)
{
return std::shared_ptr<DecryptVerifyResult>(new DecryptVerifyResult(Verify, //
vr,
DecryptionResult(),
QByteArray(),
{},
{},
QString(),
inputLabel(),
outputLabel(),
auditLog,
this,
informativeSender()));
}
std::shared_ptr<DecryptVerifyResult>
AbstractDecryptVerifyTask::fromVerifyDetachedResult(const GpgME::Error &err, const QString &details, const AuditLogEntry &auditLog)
{
return std::shared_ptr<DecryptVerifyResult>(new DecryptVerifyResult(Verify, //
VerificationResult(err),
DecryptionResult(),
QByteArray(),
{},
err,
details,
inputLabel(),
outputLabel(),
auditLog,
this,
informativeSender()));
}
DecryptVerifyResult::DecryptVerifyResult(DecryptVerifyOperation type,
const VerificationResult &vr,
const DecryptionResult &dr,
const QByteArray &stuff,
const QString &fileName,
const GpgME::Error &error,
const QString &errString,
const QString &inputLabel,
const QString &outputLabel,
const AuditLogEntry &auditLog,
Task *parentTask,
const Mailbox &informativeSender)
: Task::Result()
, d(new Private(type, vr, dr, stuff, fileName, error, errString, inputLabel, outputLabel, auditLog, parentTask, informativeSender, this))
{
}
Task::Result::ContentType DecryptVerifyResult::viewableContentType() const
{
#if QGPGME_SUPPORTS_IS_MIME
if (decryptionResult().isMime()) {
return Task::Result::ContentType::Mime;
}
#endif
if (fileName().endsWith(QStringLiteral("openpgp-encrypted-message"))) {
return Task::Result::ContentType::Mime;
}
QMimeDatabase mimeDatabase;
const auto mimeType = mimeDatabase.mimeTypeForFile(fileName());
if (mimeTypeInherits(mimeType, QStringLiteral("message/rfc822"))) {
return Task::Result::ContentType::Mime;
}
if (mimeTypeInherits(mimeType, QStringLiteral("application/mbox"))) {
return Task::Result::ContentType::Mbox;
}
return Task::Result::ContentType::None;
}
QString DecryptVerifyResult::overview() const
{
QString ov;
if (d->isDecryptOnly()) {
ov += formatDecryptionResultOverview(d->m_decryptionResult);
} else if (d->isVerifyOnly()) {
ov += formatVerificationResultOverview(d->m_verificationResult, d->makeSenderInfo());
} else {
ov += formatDecryptVerifyResultOverview(d->m_decryptionResult, d->m_verificationResult, d->makeSenderInfo());
}
if (ov.size() + d->label().size() > 120) {
// Avoid ugly breaks
ov = QStringLiteral("<br>") + ov;
}
return i18nc("label: result example: foo.sig: Verification failed. ", "%1: %2", d->label(), ov);
}
QString DecryptVerifyResult::details() const
{
if (d->isDecryptOnly()) {
return formatDecryptionResultDetails(d->m_decryptionResult,
KeyCache::instance()->findRecipients(d->m_decryptionResult),
errorString(),
false,
d->m_parentTask);
}
if (d->isVerifyOnly()) {
return formatVerificationResultDetails(d->m_verificationResult, d->makeSenderInfo(), errorString());
}
return formatDecryptVerifyResultDetails(d->m_decryptionResult,
d->m_verificationResult,
KeyCache::instance()->findRecipients(d->m_decryptionResult),
d->makeSenderInfo(),
errorString(),
d->m_parentTask);
}
GpgME::Error DecryptVerifyResult::error() const
{
return d->m_error;
}
QString DecryptVerifyResult::errorString() const
{
return d->m_errorString;
}
AuditLogEntry DecryptVerifyResult::auditLog() const
{
return d->m_auditLog;
}
QPointer<Task> DecryptVerifyResult::parentTask() const
{
return d->m_parentTask;
}
Task::Result::VisualCode DecryptVerifyResult::code() const
{
if ((d->m_type == DecryptVerify || d->m_type == Verify) && relevantInDecryptVerifyContext(verificationResult())) {
return codeForVerificationResult(verificationResult());
}
return hasError() ? NeutralError : NeutralSuccess;
}
GpgME::VerificationResult DecryptVerifyResult::verificationResult() const
{
return d->m_verificationResult;
}
GpgME::DecryptionResult DecryptVerifyResult::decryptionResult() const
{
return d->m_decryptionResult;
}
QString DecryptVerifyResult::fileName() const
{
return d->m_fileName;
}
class AbstractDecryptVerifyTask::Private
{
public:
Mailbox informativeSender;
QPointer<QGpgME::Job> job;
};
AbstractDecryptVerifyTask::AbstractDecryptVerifyTask(QObject *parent)
: Task(parent)
, d(new Private)
{
}
AbstractDecryptVerifyTask::~AbstractDecryptVerifyTask()
{
}
void AbstractDecryptVerifyTask::cancel()
{
qCDebug(KLEOPATRA_LOG) << this << __func__;
if (d->job) {
d->job->slotCancel();
}
}
Mailbox AbstractDecryptVerifyTask::informativeSender() const
{
return d->informativeSender;
}
void AbstractDecryptVerifyTask::setInformativeSender(const Mailbox &sender)
{
d->informativeSender = sender;
}
QGpgME::Job *AbstractDecryptVerifyTask::job() const
{
return d->job;
}
void AbstractDecryptVerifyTask::setJob(QGpgME::Job *job)
{
d->job = job;
}
class DecryptVerifyTask::Private
{
DecryptVerifyTask *const q;
public:
explicit Private(DecryptVerifyTask *qq)
: q{qq}
{
}
void startDecryptVerifyJob();
void startDecryptVerifyArchiveJob();
void slotResult(const DecryptionResult &, const VerificationResult &, const QByteArray & = {});
std::shared_ptr<Input> m_input;
std::shared_ptr<Output> m_output;
const QGpgME::Protocol *m_backend = nullptr;
Protocol m_protocol = UnknownProtocol;
bool m_ignoreMDCError = false;
bool m_extractArchive = false;
QString m_inputFilePath;
QString m_outputDirectory;
};
void DecryptVerifyTask::Private::slotResult(const DecryptionResult &dr, const VerificationResult &vr, const QByteArray &plainText)
{
updateKeys(vr);
{
std::stringstream ss;
ss << dr << '\n' << vr;
qCDebug(KLEOPATRA_LOG) << ss.str().c_str();
}
const AuditLogEntry auditLog = auditLogFromSender(q->sender());
if (m_output) {
if (dr.error().code() || vr.error().code()) {
m_output->cancel();
} else {
try {
kleo_assert(!dr.isNull() || !vr.isNull());
m_output->finalize();
} catch (const GpgME::Exception &e) {
q->emitResult(q->fromDecryptResult(e.error(), QString::fromLocal8Bit(e.what()), auditLog));
return;
} catch (const std::exception &e) {
q->emitResult(
q->fromDecryptResult(Error::fromCode(GPG_ERR_INTERNAL), i18n("Caught exception: %1", QString::fromLocal8Bit(e.what())), auditLog));
return;
} catch (...) {
q->emitResult(q->fromDecryptResult(Error::fromCode(GPG_ERR_INTERNAL), i18n("Caught unknown exception"), auditLog));
return;
}
}
}
const int drErr = dr.error().code();
const QString errorString = m_output ? m_output->errorString() : QString{};
if (((drErr == GPG_ERR_EIO || drErr == GPG_ERR_NO_DATA) && !errorString.isEmpty()) || (m_output && m_output->failed())) {
q->emitResult(q->fromDecryptResult(drErr ? dr.error() : Error::fromCode(GPG_ERR_EIO), errorString, auditLog));
return;
}
q->emitResult(q->fromDecryptVerifyResult(dr, vr, plainText, m_output ? m_output->fileName() : QString{}, auditLog));
}
DecryptVerifyTask::DecryptVerifyTask(QObject *parent)
: AbstractDecryptVerifyTask(parent)
, d(new Private(this))
{
}
DecryptVerifyTask::~DecryptVerifyTask()
{
}
void DecryptVerifyTask::setInput(const std::shared_ptr<Input> &input)
{
d->m_input = input;
kleo_assert(d->m_input && d->m_input->ioDevice());
}
void DecryptVerifyTask::setOutput(const std::shared_ptr<Output> &output)
{
d->m_output = output;
kleo_assert(d->m_output && d->m_output->ioDevice());
}
void DecryptVerifyTask::setProtocol(Protocol prot)
{
kleo_assert(prot != UnknownProtocol);
d->m_protocol = prot;
d->m_backend = prot == GpgME::OpenPGP ? QGpgME::openpgp() : QGpgME::smime();
kleo_assert(d->m_backend);
}
void DecryptVerifyTask::autodetectProtocolFromInput()
{
if (!d->m_input) {
return;
}
const Protocol p = findProtocol(d->m_input->classification());
if (p == UnknownProtocol) {
throw Exception(
gpg_error(GPG_ERR_NOTHING_FOUND),
i18n("Could not determine whether this is an S/MIME or an OpenPGP signature/ciphertext - maybe it is neither ciphertext nor a signature?"),
Exception::MessageOnly);
}
setProtocol(p);
}
QString DecryptVerifyTask::label() const
{
return i18n("Decrypting: %1...", d->m_input->label());
}
unsigned long long DecryptVerifyTask::inputSize() const
{
return d->m_input ? d->m_input->size() : 0;
}
QString DecryptVerifyTask::inputLabel() const
{
return d->m_input ? d->m_input->label() : QString();
}
QString DecryptVerifyTask::outputLabel() const
{
return d->m_output ? d->m_output->label() : d->m_outputDirectory;
}
Protocol DecryptVerifyTask::protocol() const
{
return d->m_protocol;
}
static void ensureIOOpen(QIODevice *input, QIODevice *output)
{
if (input && !input->isOpen()) {
input->open(QIODevice::ReadOnly);
}
if (output && !output->isOpen()) {
output->open(QIODevice::WriteOnly);
}
}
void DecryptVerifyTask::setIgnoreMDCError(bool value)
{
d->m_ignoreMDCError = value;
}
void DecryptVerifyTask::setExtractArchive(bool extract)
{
d->m_extractArchive = extract;
}
void DecryptVerifyTask::setInputFile(const QString &path)
{
d->m_inputFilePath = path;
}
void DecryptVerifyTask::setOutputDirectory(const QString &directory)
{
d->m_outputDirectory = directory;
}
static bool archiveJobsCanBeUsed(GpgME::Protocol protocol)
{
return (protocol == GpgME::OpenPGP) && QGpgME::DecryptVerifyArchiveJob::isSupported();
}
void DecryptVerifyTask::doStart()
{
kleo_assert(d->m_backend);
if (d->m_extractArchive && archiveJobsCanBeUsed(d->m_protocol)) {
d->startDecryptVerifyArchiveJob();
} else {
d->startDecryptVerifyJob();
}
}
static void setIgnoreMDCErrorFlag(QGpgME::Job *job, bool ignoreMDCError)
{
if (ignoreMDCError) {
qCDebug(KLEOPATRA_LOG) << "Modifying job to ignore MDC errors.";
auto ctx = QGpgME::Job::context(job);
if (!ctx) {
qCWarning(KLEOPATRA_LOG) << "Failed to get context for job";
} else {
const auto err = ctx->setFlag("ignore-mdc-error", "1");
if (err) {
qCWarning(KLEOPATRA_LOG) << "Failed to set ignore mdc errors" << Formatting::errorAsString(err);
}
}
}
}
void DecryptVerifyTask::Private::startDecryptVerifyJob()
{
try {
std::unique_ptr<QGpgME::DecryptVerifyJob> job{m_backend->decryptVerifyJob()};
kleo_assert(job);
setIgnoreMDCErrorFlag(job.get(), m_ignoreMDCError);
QObject::connect(job.get(),
&QGpgME::DecryptVerifyJob::result,
q,
[this](const GpgME::DecryptionResult &decryptResult, const GpgME::VerificationResult &verifyResult, const QByteArray &plainText) {
slotResult(decryptResult, verifyResult, plainText);
});
connect(job.get(), &QGpgME::Job::jobProgress, q, &DecryptVerifyTask::setProgress);
ensureIOOpen(m_input->ioDevice().get(), m_output->ioDevice().get());
job->start(m_input->ioDevice(), m_output->ioDevice());
q->setJob(job.release());
} catch (const GpgME::Exception &e) {
q->emitResult(q->fromDecryptVerifyResult(e.error(), QString::fromLocal8Bit(e.what()), AuditLogEntry()));
} catch (const std::exception &e) {
q->emitResult(
q->fromDecryptVerifyResult(Error::fromCode(GPG_ERR_INTERNAL), i18n("Caught exception: %1", QString::fromLocal8Bit(e.what())), AuditLogEntry()));
} catch (...) {
q->emitResult(q->fromDecryptVerifyResult(Error::fromCode(GPG_ERR_INTERNAL), i18n("Caught unknown exception"), AuditLogEntry()));
}
}
void DecryptVerifyTask::Private::startDecryptVerifyArchiveJob()
{
std::unique_ptr<QGpgME::DecryptVerifyArchiveJob> job{m_backend->decryptVerifyArchiveJob()};
kleo_assert(job);
setIgnoreMDCErrorFlag(job.get(), m_ignoreMDCError);
connect(job.get(),
&QGpgME::DecryptVerifyArchiveJob::result,
q,
[this](const GpgME::DecryptionResult &decryptResult, const GpgME::VerificationResult &verifyResult) {
slotResult(decryptResult, verifyResult);
});
connect(job.get(), &QGpgME::Job::jobProgress, q, &DecryptVerifyTask::setProgress);
#if QGPGME_ARCHIVE_JOBS_SUPPORT_INPUT_FILENAME
// make sure that we don't use an existing output directory
const auto outputDirectory = ensureUniqueDirectory(m_outputDirectory);
if (outputDirectory.isEmpty()) {
q->emitResult(q->fromDecryptVerifyResult(Error::fromCode(GPG_ERR_GENERAL), {}, {}));
return;
}
m_outputDirectory = outputDirectory;
job->setInputFile(m_inputFilePath);
job->setOutputDirectory(m_outputDirectory);
const auto err = job->startIt();
#else
ensureIOOpen(m_input->ioDevice().get(), nullptr);
job->setOutputDirectory(m_outputDirectory);
const auto err = job->start(m_input->ioDevice());
#endif
q->setJob(job.release());
if (err) {
q->emitResult(q->fromDecryptVerifyResult(err, {}, {}));
}
}
class DecryptTask::Private
{
DecryptTask *const q;
public:
explicit Private(DecryptTask *qq)
: q{qq}
{
}
void slotResult(const DecryptionResult &, const QByteArray &);
void registerJob(QGpgME::DecryptJob *job)
{
q->connect(job, SIGNAL(result(GpgME::DecryptionResult, QByteArray)), q, SLOT(slotResult(GpgME::DecryptionResult, QByteArray)));
q->connect(job, &QGpgME::Job::jobProgress, q, &DecryptTask::setProgress);
}
std::shared_ptr<Input> m_input;
std::shared_ptr<Output> m_output;
const QGpgME::Protocol *m_backend = nullptr;
Protocol m_protocol = UnknownProtocol;
};
void DecryptTask::Private::slotResult(const DecryptionResult &result, const QByteArray &plainText)
{
{
std::stringstream ss;
ss << result;
qCDebug(KLEOPATRA_LOG) << ss.str().c_str();
}
const AuditLogEntry auditLog = auditLogFromSender(q->sender());
if (result.error().code()) {
m_output->cancel();
} else {
try {
kleo_assert(!result.isNull());
m_output->finalize();
} catch (const GpgME::Exception &e) {
q->emitResult(q->fromDecryptResult(e.error(), QString::fromLocal8Bit(e.what()), auditLog));
return;
} catch (const std::exception &e) {
q->emitResult(q->fromDecryptResult(Error::fromCode(GPG_ERR_INTERNAL), i18n("Caught exception: %1", QString::fromLocal8Bit(e.what())), auditLog));
return;
} catch (...) {
q->emitResult(q->fromDecryptResult(Error::fromCode(GPG_ERR_INTERNAL), i18n("Caught unknown exception"), auditLog));
return;
}
}
const int drErr = result.error().code();
const QString errorString = m_output->errorString();
if (((drErr == GPG_ERR_EIO || drErr == GPG_ERR_NO_DATA) && !errorString.isEmpty()) || m_output->failed()) {
q->emitResult(q->fromDecryptResult(result.error() ? result.error() : Error::fromCode(GPG_ERR_EIO), errorString, auditLog));
return;
}
q->emitResult(q->fromDecryptResult(result, plainText, auditLog));
}
DecryptTask::DecryptTask(QObject *parent)
: AbstractDecryptVerifyTask(parent)
, d(new Private(this))
{
}
DecryptTask::~DecryptTask()
{
}
void DecryptTask::setInput(const std::shared_ptr<Input> &input)
{
d->m_input = input;
kleo_assert(d->m_input && d->m_input->ioDevice());
}
void DecryptTask::setOutput(const std::shared_ptr<Output> &output)
{
d->m_output = output;
kleo_assert(d->m_output && d->m_output->ioDevice());
}
void DecryptTask::setProtocol(Protocol prot)
{
kleo_assert(prot != UnknownProtocol);
d->m_protocol = prot;
d->m_backend = (prot == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime();
kleo_assert(d->m_backend);
}
void DecryptTask::autodetectProtocolFromInput()
{
if (!d->m_input) {
return;
}
const Protocol p = findProtocol(d->m_input->classification());
if (p == UnknownProtocol) {
throw Exception(gpg_error(GPG_ERR_NOTHING_FOUND),
i18n("Could not determine whether this was S/MIME- or OpenPGP-encrypted - maybe it is not ciphertext at all?"),
Exception::MessageOnly);
}
setProtocol(p);
}
QString DecryptTask::label() const
{
return i18n("Decrypting: %1...", d->m_input->label());
}
unsigned long long DecryptTask::inputSize() const
{
return d->m_input ? d->m_input->size() : 0;
}
QString DecryptTask::inputLabel() const
{
return d->m_input ? d->m_input->label() : QString();
}
QString DecryptTask::outputLabel() const
{
return d->m_output ? d->m_output->label() : QString();
}
Protocol DecryptTask::protocol() const
{
return d->m_protocol;
}
void DecryptTask::doStart()
{
kleo_assert(d->m_backend);
try {
std::unique_ptr<QGpgME::DecryptJob> job{d->m_backend->decryptJob()};
kleo_assert(job);
d->registerJob(job.get());
ensureIOOpen(d->m_input->ioDevice().get(), d->m_output->ioDevice().get());
job->start(d->m_input->ioDevice(), d->m_output->ioDevice());
setJob(job.release());
} catch (const GpgME::Exception &e) {
emitResult(fromDecryptResult(e.error(), QString::fromLocal8Bit(e.what()), AuditLogEntry()));
} catch (const std::exception &e) {
emitResult(fromDecryptResult(Error::fromCode(GPG_ERR_INTERNAL), i18n("Caught exception: %1", QString::fromLocal8Bit(e.what())), AuditLogEntry()));
} catch (...) {
emitResult(fromDecryptResult(Error::fromCode(GPG_ERR_INTERNAL), i18n("Caught unknown exception"), AuditLogEntry()));
}
}
class VerifyOpaqueTask::Private
{
VerifyOpaqueTask *const q;
public:
explicit Private(VerifyOpaqueTask *qq)
: q{qq}
{
}
void startVerifyOpaqueJob();
void startDecryptVerifyArchiveJob();
void slotResult(const VerificationResult &, const QByteArray & = {});
std::shared_ptr<Input> m_input;
std::shared_ptr<Output> m_output;
const QGpgME::Protocol *m_backend = nullptr;
Protocol m_protocol = UnknownProtocol;
bool m_extractArchive = false;
QString m_inputFilePath;
QString m_outputDirectory;
};
void VerifyOpaqueTask::Private::slotResult(const VerificationResult &result, const QByteArray &plainText)
{
updateKeys(result);
{
std::stringstream ss;
ss << result;
qCDebug(KLEOPATRA_LOG) << ss.str().c_str();
}
const AuditLogEntry auditLog = auditLogFromSender(q->sender());
if (m_output) {
if (result.error().code()) {
m_output->cancel();
} else {
try {
kleo_assert(!result.isNull());
m_output->finalize();
} catch (const GpgME::Exception &e) {
q->emitResult(q->fromVerifyOpaqueResult(e.error(), QString::fromLocal8Bit(e.what()), auditLog));
return;
} catch (const std::exception &e) {
q->emitResult(
q->fromVerifyOpaqueResult(Error::fromCode(GPG_ERR_INTERNAL), i18n("Caught exception: %1", QString::fromLocal8Bit(e.what())), auditLog));
return;
} catch (...) {
q->emitResult(q->fromVerifyOpaqueResult(Error::fromCode(GPG_ERR_INTERNAL), i18n("Caught unknown exception"), auditLog));
return;
}
}
}
const int drErr = result.error().code();
const QString errorString = m_output ? m_output->errorString() : QString{};
if (((drErr == GPG_ERR_EIO || drErr == GPG_ERR_NO_DATA) && !errorString.isEmpty()) || (m_output && m_output->failed())) {
q->emitResult(q->fromVerifyOpaqueResult(result.error() ? result.error() : Error::fromCode(GPG_ERR_EIO), errorString, auditLog));
return;
}
q->emitResult(q->fromVerifyOpaqueResult(result, plainText, auditLog));
}
VerifyOpaqueTask::VerifyOpaqueTask(QObject *parent)
: AbstractDecryptVerifyTask(parent)
, d(new Private(this))
{
}
VerifyOpaqueTask::~VerifyOpaqueTask()
{
}
void VerifyOpaqueTask::setInput(const std::shared_ptr<Input> &input)
{
d->m_input = input;
kleo_assert(d->m_input && d->m_input->ioDevice());
}
void VerifyOpaqueTask::setOutput(const std::shared_ptr<Output> &output)
{
d->m_output = output;
kleo_assert(d->m_output && d->m_output->ioDevice());
}
void VerifyOpaqueTask::setProtocol(Protocol prot)
{
kleo_assert(prot != UnknownProtocol);
d->m_protocol = prot;
d->m_backend = (prot == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime();
kleo_assert(d->m_backend);
}
void VerifyOpaqueTask::autodetectProtocolFromInput()
{
if (!d->m_input) {
return;
}
const Protocol p = findProtocol(d->m_input->classification());
if (p == UnknownProtocol) {
throw Exception(gpg_error(GPG_ERR_NOTHING_FOUND),
i18n("Could not determine whether this is an S/MIME or an OpenPGP signature - maybe it is not a signature at all?"),
Exception::MessageOnly);
}
setProtocol(p);
}
QString VerifyOpaqueTask::label() const
{
return i18n("Verifying: %1...", d->m_input->label());
}
unsigned long long VerifyOpaqueTask::inputSize() const
{
return d->m_input ? d->m_input->size() : 0;
}
QString VerifyOpaqueTask::inputLabel() const
{
return d->m_input ? d->m_input->label() : QString();
}
QString VerifyOpaqueTask::outputLabel() const
{
return d->m_output ? d->m_output->label() : d->m_outputDirectory;
}
Protocol VerifyOpaqueTask::protocol() const
{
return d->m_protocol;
}
void VerifyOpaqueTask::setExtractArchive(bool extract)
{
d->m_extractArchive = extract;
}
void VerifyOpaqueTask::setInputFile(const QString &path)
{
d->m_inputFilePath = path;
}
void VerifyOpaqueTask::setOutputDirectory(const QString &directory)
{
d->m_outputDirectory = directory;
}
void VerifyOpaqueTask::doStart()
{
kleo_assert(d->m_backend);
if (d->m_extractArchive && archiveJobsCanBeUsed(d->m_protocol)) {
d->startDecryptVerifyArchiveJob();
} else {
d->startVerifyOpaqueJob();
}
}
void VerifyOpaqueTask::Private::startVerifyOpaqueJob()
{
try {
std::unique_ptr<QGpgME::VerifyOpaqueJob> job{m_backend->verifyOpaqueJob()};
kleo_assert(job);
connect(job.get(), &QGpgME::VerifyOpaqueJob::result, q, [this](const GpgME::VerificationResult &result, const QByteArray &plainText) {
slotResult(result, plainText);
});
connect(job.get(), &QGpgME::Job::jobProgress, q, &VerifyOpaqueTask::setProgress);
ensureIOOpen(m_input->ioDevice().get(), m_output ? m_output->ioDevice().get() : nullptr);
job->start(m_input->ioDevice(), m_output ? m_output->ioDevice() : std::shared_ptr<QIODevice>());
q->setJob(job.release());
} catch (const GpgME::Exception &e) {
q->emitResult(q->fromVerifyOpaqueResult(e.error(), QString::fromLocal8Bit(e.what()), AuditLogEntry()));
} catch (const std::exception &e) {
q->emitResult(
q->fromVerifyOpaqueResult(Error::fromCode(GPG_ERR_INTERNAL), i18n("Caught exception: %1", QString::fromLocal8Bit(e.what())), AuditLogEntry()));
} catch (...) {
q->emitResult(q->fromVerifyOpaqueResult(Error::fromCode(GPG_ERR_INTERNAL), i18n("Caught unknown exception"), AuditLogEntry()));
}
}
void VerifyOpaqueTask::Private::startDecryptVerifyArchiveJob()
{
std::unique_ptr<QGpgME::DecryptVerifyArchiveJob> job{m_backend->decryptVerifyArchiveJob()};
kleo_assert(job);
connect(job.get(), &QGpgME::DecryptVerifyArchiveJob::result, q, [this](const DecryptionResult &, const VerificationResult &verifyResult) {
slotResult(verifyResult);
});
connect(job.get(), &QGpgME::DecryptVerifyArchiveJob::dataProgress, q, &VerifyOpaqueTask::setProgress);
#if QGPGME_ARCHIVE_JOBS_SUPPORT_INPUT_FILENAME
// make sure that we don't use an existing output directory
const auto outputDirectory = ensureUniqueDirectory(m_outputDirectory);
if (outputDirectory.isEmpty()) {
q->emitResult(q->fromDecryptVerifyResult(Error::fromCode(GPG_ERR_GENERAL), {}, {}));
return;
}
m_outputDirectory = outputDirectory;
job->setInputFile(m_inputFilePath);
job->setOutputDirectory(m_outputDirectory);
const auto err = job->startIt();
#else
ensureIOOpen(m_input->ioDevice().get(), nullptr);
job->setOutputDirectory(m_outputDirectory);
const auto err = job->start(m_input->ioDevice());
#endif
q->setJob(job.release());
if (err) {
q->emitResult(q->fromVerifyOpaqueResult(err, {}, {}));
}
}
class VerifyDetachedTask::Private
{
VerifyDetachedTask *const q;
public:
explicit Private(VerifyDetachedTask *qq)
: q{qq}
{
}
void slotResult(const VerificationResult &);
void registerJob(QGpgME::VerifyDetachedJob *job)
{
q->connect(job, SIGNAL(result(GpgME::VerificationResult)), q, SLOT(slotResult(GpgME::VerificationResult)));
q->connect(job, &QGpgME::Job::jobProgress, q, &VerifyDetachedTask::setProgress);
}
std::shared_ptr<Input> m_input, m_signedData;
const QGpgME::Protocol *m_backend = nullptr;
Protocol m_protocol = UnknownProtocol;
};
void VerifyDetachedTask::Private::slotResult(const VerificationResult &result)
{
updateKeys(result);
{
std::stringstream ss;
ss << result;
qCDebug(KLEOPATRA_LOG) << ss.str().c_str();
}
const AuditLogEntry auditLog = auditLogFromSender(q->sender());
try {
kleo_assert(!result.isNull());
q->emitResult(q->fromVerifyDetachedResult(result, auditLog));
} catch (const GpgME::Exception &e) {
q->emitResult(q->fromVerifyDetachedResult(e.error(), QString::fromLocal8Bit(e.what()), auditLog));
} catch (const std::exception &e) {
q->emitResult(q->fromVerifyDetachedResult(Error::fromCode(GPG_ERR_INTERNAL), i18n("Caught exception: %1", QString::fromLocal8Bit(e.what())), auditLog));
} catch (...) {
q->emitResult(q->fromVerifyDetachedResult(Error::fromCode(GPG_ERR_INTERNAL), i18n("Caught unknown exception"), auditLog));
}
}
VerifyDetachedTask::VerifyDetachedTask(QObject *parent)
: AbstractDecryptVerifyTask(parent)
, d(new Private(this))
{
}
VerifyDetachedTask::~VerifyDetachedTask()
{
}
void VerifyDetachedTask::setInput(const std::shared_ptr<Input> &input)
{
d->m_input = input;
kleo_assert(d->m_input && d->m_input->ioDevice());
}
void VerifyDetachedTask::setSignedData(const std::shared_ptr<Input> &signedData)
{
d->m_signedData = signedData;
kleo_assert(d->m_signedData && d->m_signedData->ioDevice());
}
void VerifyDetachedTask::setProtocol(Protocol prot)
{
kleo_assert(prot != UnknownProtocol);
d->m_protocol = prot;
d->m_backend = (prot == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime();
kleo_assert(d->m_backend);
}
void VerifyDetachedTask::autodetectProtocolFromInput()
{
if (!d->m_input) {
return;
}
const Protocol p = findProtocol(d->m_input->classification());
if (p == UnknownProtocol) {
throw Exception(gpg_error(GPG_ERR_NOTHING_FOUND),
i18n("Could not determine whether this is an S/MIME or an OpenPGP signature - maybe it is not a signature at all?"),
Exception::MessageOnly);
}
setProtocol(p);
}
unsigned long long VerifyDetachedTask::inputSize() const
{
return d->m_signedData ? d->m_signedData->size() : 0;
}
QString VerifyDetachedTask::label() const
{
if (d->m_signedData) {
return xi18nc(
"Verification of a detached signature in progress. The first file contains the data."
"The second file is the signature file.",
"Verifying: <filename>%1</filename> with <filename>%2</filename>...",
d->m_signedData->label(),
d->m_input->label());
}
return i18n("Verifying signature: %1...", d->m_input->label());
}
QString VerifyDetachedTask::inputLabel() const
{
if (d->m_signedData && d->m_input) {
return xi18nc(
"Verification of a detached signature summary. The first file contains the data."
"The second file is signature.",
"Verified <filename>%1</filename> with <filename>%2</filename>",
d->m_signedData->label(),
d->m_input->label());
}
return d->m_input ? d->m_input->label() : QString();
}
QString VerifyDetachedTask::outputLabel() const
{
return QString();
}
Protocol VerifyDetachedTask::protocol() const
{
return d->m_protocol;
}
void VerifyDetachedTask::doStart()
{
kleo_assert(d->m_backend);
try {
std::unique_ptr<QGpgME::VerifyDetachedJob> job{d->m_backend->verifyDetachedJob()};
kleo_assert(job);
d->registerJob(job.get());
ensureIOOpen(d->m_input->ioDevice().get(), nullptr);
ensureIOOpen(d->m_signedData->ioDevice().get(), nullptr);
job->start(d->m_input->ioDevice(), d->m_signedData->ioDevice());
setJob(job.release());
} catch (const GpgME::Exception &e) {
emitResult(fromVerifyDetachedResult(e.error(), QString::fromLocal8Bit(e.what()), AuditLogEntry()));
} catch (const std::exception &e) {
emitResult(
fromVerifyDetachedResult(Error::fromCode(GPG_ERR_INTERNAL), i18n("Caught exception: %1", QString::fromLocal8Bit(e.what())), AuditLogEntry()));
} catch (...) {
emitResult(fromVerifyDetachedResult(Error::fromCode(GPG_ERR_INTERNAL), i18n("Caught unknown exception"), AuditLogEntry()));
}
}
#include "moc_decryptverifytask.cpp"
diff --git a/src/crypto/gui/resolverecipientspage_p.h b/src/crypto/gui/resolverecipientspage_p.h
index 68faa4211..7cbadb87c 100644
--- a/src/crypto/gui/resolverecipientspage_p.h
+++ b/src/crypto/gui/resolverecipientspage_p.h
@@ -1,105 +1,105 @@
/* -*- mode: c++; c-basic-offset:4 -*-
crypto/gui/resolverecipientspage_p.h
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include <crypto/gui/resolverecipientspage.h>
-#include <KMime/HeaderParsing>
+#include <KMime/Types>
#include <QHash>
class QComboBox;
class QLabel;
class QListWidget;
class QListWidgetItem;
#include <QStringList>
class QToolButton;
class Kleo::Crypto::Gui::ResolveRecipientsPage::ListWidget : public QWidget
{
Q_OBJECT
public:
explicit ListWidget(QWidget *parent = nullptr, Qt::WindowFlags flags = {});
~ListWidget() override;
void addEntry(const QString &id, const QString &name);
void addEntry(const KMime::Types::Mailbox &mbox);
void addEntry(const QString &id, const QString &name, const KMime::Types::Mailbox &mbox);
void removeEntry(const QString &id);
QStringList selectedEntries() const;
void setCertificates(const QString &id, const std::vector<GpgME::Key> &pgpCerts, const std::vector<GpgME::Key> &cmsCerts);
GpgME::Key selectedCertificate(const QString &id) const;
GpgME::Key selectedCertificate(const QString &id, GpgME::Protocol prot) const;
KMime::Types::Mailbox mailbox(const QString &id) const;
QStringList identifiers() const;
void setProtocol(GpgME::Protocol prot);
void showSelectionDialog(const QString &id);
enum Role {
IdRole = Qt::UserRole,
};
Q_SIGNALS:
void selectionChanged();
void completeChanged();
private Q_SLOTS:
void onSelectionChange();
private:
QListWidget *m_listWidget;
QHash<QString, ItemWidget *> widgets;
QHash<QString, QListWidgetItem *> items;
GpgME::Protocol m_protocol;
};
class Kleo::Crypto::Gui::ResolveRecipientsPage::ItemWidget : public QWidget
{
Q_OBJECT
public:
explicit ItemWidget(const QString &id, const QString &name, const KMime::Types::Mailbox &mbox, QWidget *parent = nullptr, Qt::WindowFlags flags = {});
~ItemWidget() override;
QString id() const;
KMime::Types::Mailbox mailbox() const;
void setCertificates(const std::vector<GpgME::Key> &pgp, const std::vector<GpgME::Key> &cms);
GpgME::Key selectedCertificate() const;
GpgME::Key selectedCertificate(GpgME::Protocol prot) const;
std::vector<GpgME::Key> certificates() const;
void setProtocol(GpgME::Protocol protocol);
void setSelected(bool selected);
bool isSelected() const;
public Q_SLOTS:
void showSelectionDialog();
Q_SIGNALS:
void changed();
private:
void addCertificateToComboBox(const GpgME::Key &key);
void resetCertificates();
void selectCertificateInComboBox(const GpgME::Key &key);
void updateVisibility();
private:
QString m_id;
KMime::Types::Mailbox m_mailbox;
QLabel *m_nameLabel;
QLabel *m_certLabel;
QComboBox *m_certCombo;
QToolButton *m_selectButton;
GpgME::Protocol m_protocol;
QHash<GpgME::Protocol, GpgME::Key> m_selectedCertificates;
std::vector<GpgME::Key> m_pgp, m_cms;
bool m_selected;
};
diff --git a/src/crypto/gui/signencryptemailconflictdialog.cpp b/src/crypto/gui/signencryptemailconflictdialog.cpp
index 463dbd689..f075674e9 100644
--- a/src/crypto/gui/signencryptemailconflictdialog.cpp
+++ b/src/crypto/gui/signencryptemailconflictdialog.cpp
@@ -1,628 +1,628 @@
/* -*- mode: c++; c-basic-offset:4 -*-
crypto/gui/signencryptemailconflictdialog.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2009 Klarälvdalens Datakonsult AB
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-kleopatra.h>
#include "signencryptemailconflictdialog.h"
#include <crypto/recipient.h>
#include <crypto/sender.h>
#include "certificateselectionline.h"
#include "dialogs/certificateselectiondialog.h"
#include "utils/gui-helper.h"
#include "utils/kleo_assert.h"
#include <Libkleo/GnuPG>
#include <Libkleo/Compliance>
#include <Libkleo/Formatting>
#include <Libkleo/Stl_Util>
#include <Libkleo/SystemInfo>
#include <gpgme++/key.h>
-#include <KMime/HeaderParsing>
+#include <KMime/Types>
#include <KColorScheme>
#include <KLocalizedString>
#include <QCheckBox>
#include <QDialogButtonBox>
#include <QGroupBox>
#include <QLabel>
#include <QLayout>
#include <QPointer>
#include <QPushButton>
#include <QRadioButton>
#include <QSignalBlocker>
#include <QToolButton>
#include <algorithm>
#include <iterator>
using namespace Kleo;
using namespace Kleo::Crypto;
using namespace Kleo::Crypto::Gui;
using namespace Kleo::Dialogs;
using namespace GpgME;
Q_DECLARE_METATYPE(GpgME::Key)
Q_DECLARE_METATYPE(GpgME::UserID)
static CertificateSelectionDialog *create_certificate_selection_dialog(QWidget *parent, Protocol proto)
{
auto const dlg = new CertificateSelectionDialog(parent);
dlg->setOptions(proto == OpenPGP ? CertificateSelectionDialog::OpenPGPFormat
: proto == CMS ? CertificateSelectionDialog::CMSFormat
: CertificateSelectionDialog::AnyFormat);
return dlg;
}
static CertificateSelectionDialog *create_encryption_certificate_selection_dialog(QWidget *parent, Protocol proto, const QString &mailbox)
{
CertificateSelectionDialog *const dlg = create_certificate_selection_dialog(parent, proto);
dlg->setCustomLabelText(i18n("Please select an encryption certificate for recipient \"%1\"", mailbox));
dlg->setOptions(CertificateSelectionDialog::SingleSelection | //
CertificateSelectionDialog::EncryptOnly | //
dlg->options());
return dlg;
}
static CertificateSelectionDialog *create_signing_certificate_selection_dialog(QWidget *parent, Protocol proto, const QString &mailbox)
{
CertificateSelectionDialog *const dlg = create_certificate_selection_dialog(parent, proto);
dlg->setCustomLabelText(i18n("Please select a signing certificate for sender \"%1\"", mailbox));
dlg->setOptions(CertificateSelectionDialog::SingleSelection | //
CertificateSelectionDialog::SignOnly | //
CertificateSelectionDialog::SecretKeys | //
dlg->options());
return dlg;
}
static QString make_top_label_conflict_text(bool sign, bool enc)
{
return sign && enc ? i18n(
"Kleopatra cannot unambiguously determine matching certificates "
"for all recipients/senders of the message.\n"
"Please select the correct certificates for each recipient:")
: sign ? i18n(
"Kleopatra cannot unambiguously determine matching certificates "
"for the sender of the message.\n"
"Please select the correct certificates for the sender:")
: enc ? i18n(
"Kleopatra cannot unambiguously determine matching certificates "
"for all recipients of the message.\n"
"Please select the correct certificates for each recipient:")
: (kleo_assert_fail(sign || enc), QString());
}
static QString make_top_label_quickmode_text(bool sign, bool enc)
{
return enc ? i18n("Please verify that correct certificates have been selected for each recipient:")
: sign ? i18n("Please verify that the correct certificate has been selected for the sender:")
: (kleo_assert_fail(sign || enc), QString());
}
class SignEncryptEMailConflictDialog::Private
{
friend class ::Kleo::Crypto::Gui::SignEncryptEMailConflictDialog;
SignEncryptEMailConflictDialog *const q;
public:
explicit Private(SignEncryptEMailConflictDialog *qq)
: q(qq)
, senders()
, recipients()
, sign(true)
, encrypt(true)
, presetProtocol(UnknownProtocol)
, ui(q)
{
}
private:
void updateTopLabelText()
{
ui.conflictTopLB.setText(make_top_label_conflict_text(sign, encrypt));
ui.quickModeTopLB.setText(make_top_label_quickmode_text(sign, encrypt));
}
void showHideWidgets()
{
const Protocol proto = q->selectedProtocol();
const bool quickMode = q->isQuickMode();
const bool needProtocolSelection = presetProtocol == UnknownProtocol;
const bool needShowAllRecipientsCB = quickMode ? false
: needProtocolSelection ? needShowAllRecipients(OpenPGP) || needShowAllRecipients(CMS)
: needShowAllRecipients(proto);
ui.showAllRecipientsCB.setVisible(needShowAllRecipientsCB);
ui.pgpRB.setVisible(needProtocolSelection);
ui.cmsRB.setVisible(needProtocolSelection);
const bool showAll = !needShowAllRecipientsCB || ui.showAllRecipientsCB.isChecked();
bool first;
first = true;
for (const CertificateSelectionLine &line : std::as_const(ui.signers)) {
line.showHide(proto, first, showAll, sign);
}
ui.selectSigningCertificatesGB.setVisible(sign && (showAll || !first));
first = true;
for (const CertificateSelectionLine &line : std::as_const(ui.recipients)) {
line.showHide(proto, first, showAll, encrypt);
}
ui.selectEncryptionCertificatesGB.setVisible(encrypt && (showAll || !first));
}
bool needShowAllRecipients(Protocol proto) const
{
if (sign) {
if (const unsigned int num = std::count_if(ui.signers.cbegin(), ui.signers.cend(), [proto](const CertificateSelectionLine &l) {
return l.wasInitiallyAmbiguous(proto);
})) {
if (num != ui.signers.size()) {
return true;
}
}
}
if (encrypt) {
if (const unsigned int num = std::count_if(ui.recipients.cbegin(), ui.recipients.cend(), [proto](const CertificateSelectionLine &l) {
return l.wasInitiallyAmbiguous(proto);
})) {
if (num != ui.recipients.size()) {
return true;
}
}
}
return false;
}
void createSendersAndRecipients()
{
ui.clearSendersAndRecipients();
ui.addSelectSigningCertificatesGB();
for (const Sender &s : std::as_const(senders)) {
addSigner(s);
}
ui.addSelectEncryptionCertificatesGB();
for (const Sender &s : std::as_const(senders)) {
addRecipient(s);
}
for (const Recipient &r : std::as_const(recipients)) {
addRecipient(r);
}
}
void addSigner(const Sender &s)
{
ui.addSigner(s.mailbox().prettyAddress(),
s.signingCertificateCandidates(OpenPGP),
s.isSigningAmbiguous(OpenPGP),
s.signingCertificateCandidates(CMS),
s.isSigningAmbiguous(CMS),
q);
}
void addRecipient(const Sender &s)
{
ui.addRecipient(s.mailbox().prettyAddress(),
s.encryptToSelfCertificateCandidates(OpenPGP),
s.isEncryptionAmbiguous(OpenPGP),
s.encryptToSelfCertificateCandidates(CMS),
s.isEncryptionAmbiguous(CMS),
q);
}
void addRecipient(const Recipient &r)
{
ui.addRecipient(r.mailbox().prettyAddress(),
r.encryptionCertificateCandidates(OpenPGP),
r.isEncryptionAmbiguous(OpenPGP),
r.encryptionCertificateCandidates(CMS),
r.isEncryptionAmbiguous(CMS),
q);
}
bool isComplete(Protocol proto) const;
private:
void updateComplianceStatus()
{
if (!DeVSCompliance::isCompliant()) {
return;
}
if (q->selectedProtocol() == UnknownProtocol || (q->resolvedSigningKeys().empty() && q->resolvedEncryptionKeys().empty())) {
return;
}
// Handle compliance
bool de_vs = true;
for (const auto &key : q->resolvedSigningKeys()) {
if (!DeVSCompliance::keyIsCompliant(key)) {
de_vs = false;
break;
}
}
if (de_vs) {
for (const auto &key : q->resolvedEncryptionKeys()) {
if (!DeVSCompliance::keyIsCompliant(key)) {
de_vs = false;
break;
}
}
}
auto btn = ui.buttonBox.button(QDialogButtonBox::Ok);
DeVSCompliance::decorate(btn, de_vs);
ui.complianceLB.setText(DeVSCompliance::name(de_vs));
ui.complianceLB.setVisible(true);
}
void updateDialogStatus()
{
ui.setOkButtonEnabled(q->isComplete());
updateComplianceStatus();
}
void slotCompleteChanged()
{
updateDialogStatus();
}
void slotShowAllRecipientsToggled(bool)
{
showHideWidgets();
}
void slotProtocolChanged()
{
showHideWidgets();
updateDialogStatus();
}
void slotCertificateSelectionDialogRequested()
{
const QObject *const s = q->sender();
const Protocol proto = q->selectedProtocol();
QPointer<CertificateSelectionDialog> dlg;
for (const CertificateSelectionLine &l : std::as_const(ui.signers))
if (s == l.toolButton()) {
dlg = create_signing_certificate_selection_dialog(q, proto, l.mailboxText());
if (dlg->exec()) {
l.addAndSelectCertificate(dlg->selectedCertificate());
}
// ### switch to key.protocol(), in case proto == UnknownProtocol
break;
}
for (const CertificateSelectionLine &l : std::as_const(ui.recipients))
if (s == l.toolButton()) {
dlg = create_encryption_certificate_selection_dialog(q, proto, l.mailboxText());
if (dlg->exec()) {
l.addAndSelectCertificate(dlg->selectedCertificate());
}
// ### switch to key.protocol(), in case proto == UnknownProtocol
break;
}
#ifndef Q_OS_WIN
// This leads to a crash on Windows. We don't really
// leak memory here anyway because the destruction of the
// dialog happens when the parent (q) is destroyed anyway.
delete dlg;
#endif
}
private:
std::vector<Sender> senders;
std::vector<Recipient> recipients;
bool sign : 1;
bool encrypt : 1;
Protocol presetProtocol;
private:
struct Ui {
QLabel conflictTopLB, quickModeTopLB;
QCheckBox showAllRecipientsCB;
QRadioButton pgpRB, cmsRB;
QGroupBox selectSigningCertificatesGB;
QGroupBox selectEncryptionCertificatesGB;
QCheckBox quickModeCB;
QDialogButtonBox buttonBox;
QVBoxLayout vlay;
QHBoxLayout hlay;
QHBoxLayout hlay2;
QGridLayout glay;
std::vector<CertificateSelectionLine> signers, recipients;
QLabel complianceLB;
void setOkButtonEnabled(bool enable)
{
return buttonBox.button(QDialogButtonBox::Ok)->setEnabled(enable);
}
explicit Ui(SignEncryptEMailConflictDialog *q)
: conflictTopLB(make_top_label_conflict_text(true, true), q)
, quickModeTopLB(make_top_label_quickmode_text(true, true), q)
, showAllRecipientsCB(i18n("Show all recipients"), q)
, pgpRB(i18n("OpenPGP"), q)
, cmsRB(i18n("S/MIME"), q)
, selectSigningCertificatesGB(i18n("Select Signing Certificate"), q)
, selectEncryptionCertificatesGB(i18n("Select Encryption Certificate"), q)
, quickModeCB(i18n("Only show this dialog in case of conflicts (experimental)"), q)
, buttonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, q)
, vlay(q)
, hlay()
, glay()
, signers()
, recipients()
{
KDAB_SET_OBJECT_NAME(conflictTopLB);
KDAB_SET_OBJECT_NAME(quickModeTopLB);
KDAB_SET_OBJECT_NAME(showAllRecipientsCB);
KDAB_SET_OBJECT_NAME(pgpRB);
KDAB_SET_OBJECT_NAME(cmsRB);
KDAB_SET_OBJECT_NAME(selectSigningCertificatesGB);
KDAB_SET_OBJECT_NAME(selectEncryptionCertificatesGB);
KDAB_SET_OBJECT_NAME(quickModeCB);
KDAB_SET_OBJECT_NAME(buttonBox);
KDAB_SET_OBJECT_NAME(hlay);
KDAB_SET_OBJECT_NAME(glay);
KDAB_SET_OBJECT_NAME(vlay);
q->setWindowTitle(i18nc("@title:window", "Select Certificates for Message"));
conflictTopLB.hide();
selectSigningCertificatesGB.setFlat(true);
selectEncryptionCertificatesGB.setFlat(true);
selectSigningCertificatesGB.setAlignment(Qt::AlignCenter);
selectEncryptionCertificatesGB.setAlignment(Qt::AlignCenter);
glay.setColumnStretch(2, 1);
glay.setColumnStretch(3, 1);
vlay.setSizeConstraint(QLayout::SetMinimumSize);
vlay.addWidget(&conflictTopLB);
vlay.addWidget(&quickModeTopLB);
hlay.addWidget(&showAllRecipientsCB);
hlay.addStretch(1);
hlay.addWidget(&pgpRB);
hlay.addWidget(&cmsRB);
vlay.addLayout(&hlay);
addSelectSigningCertificatesGB();
addSelectEncryptionCertificatesGB();
vlay.addLayout(&glay);
vlay.addStretch(1);
complianceLB.setVisible(false);
hlay2.addStretch(1);
hlay2.addWidget(&complianceLB, 0, Qt::AlignRight);
hlay2.addWidget(&buttonBox, 0, Qt::AlignRight);
vlay.addWidget(&quickModeCB, 0, Qt::AlignRight);
vlay.addLayout(&hlay2);
connect(&buttonBox, &QDialogButtonBox::accepted, q, &SignEncryptEMailConflictDialog::accept);
connect(&buttonBox, &QDialogButtonBox::rejected, q, &SignEncryptEMailConflictDialog::reject);
connect(&showAllRecipientsCB, SIGNAL(toggled(bool)), q, SLOT(slotShowAllRecipientsToggled(bool)));
connect(&pgpRB, SIGNAL(toggled(bool)), q, SLOT(slotProtocolChanged()));
connect(&cmsRB, SIGNAL(toggled(bool)), q, SLOT(slotProtocolChanged()));
}
void clearSendersAndRecipients()
{
std::vector<CertificateSelectionLine> sig, enc;
sig.swap(signers);
enc.swap(recipients);
std::for_each(sig.begin(), sig.end(), std::mem_fn(&CertificateSelectionLine::kill));
std::for_each(enc.begin(), enc.end(), std::mem_fn(&CertificateSelectionLine::kill));
glay.removeWidget(&selectSigningCertificatesGB);
glay.removeWidget(&selectEncryptionCertificatesGB);
}
void addSelectSigningCertificatesGB()
{
glay.addWidget(&selectSigningCertificatesGB, glay.rowCount(), 0, 1, CertificateSelectionLine::NumColumns);
}
void addSelectEncryptionCertificatesGB()
{
glay.addWidget(&selectEncryptionCertificatesGB, glay.rowCount(), 0, 1, CertificateSelectionLine::NumColumns);
}
void addSigner(const QString &mailbox, const std::vector<Key> &pgp, bool pgpAmbiguous, const std::vector<Key> &cms, bool cmsAmbiguous, QWidget *q)
{
CertificateSelectionLine line(i18n("From:"), mailbox, pgp, pgpAmbiguous, cms, cmsAmbiguous, q, glay);
signers.push_back(line);
}
void addRecipient(const QString &mailbox, const std::vector<Key> &pgp, bool pgpAmbiguous, const std::vector<Key> &cms, bool cmsAmbiguous, QWidget *q)
{
CertificateSelectionLine line(i18n("To:"), mailbox, pgp, pgpAmbiguous, cms, cmsAmbiguous, q, glay);
recipients.push_back(line);
}
} ui;
};
SignEncryptEMailConflictDialog::SignEncryptEMailConflictDialog(QWidget *parent)
: QDialog(parent)
, d(new Private(this))
{
}
SignEncryptEMailConflictDialog::~SignEncryptEMailConflictDialog()
{
}
void SignEncryptEMailConflictDialog::setPresetProtocol(Protocol p)
{
if (p == d->presetProtocol) {
return;
}
const QSignalBlocker pgpBlocker(d->ui.pgpRB);
const QSignalBlocker cmsBlocker(d->ui.cmsRB);
really_check(d->ui.pgpRB, p == OpenPGP);
really_check(d->ui.cmsRB, p == CMS);
d->presetProtocol = p;
d->showHideWidgets();
d->updateDialogStatus();
}
Protocol SignEncryptEMailConflictDialog::selectedProtocol() const
{
if (d->presetProtocol != UnknownProtocol) {
return d->presetProtocol;
}
if (d->ui.pgpRB.isChecked()) {
return OpenPGP;
}
if (d->ui.cmsRB.isChecked()) {
return CMS;
}
return UnknownProtocol;
}
void SignEncryptEMailConflictDialog::setSubject(const QString &subject)
{
setWindowTitle(i18nc("@title:window", "Select Certificates for Message \"%1\"", subject));
}
void SignEncryptEMailConflictDialog::setSign(bool sign)
{
if (sign == d->sign) {
return;
}
d->sign = sign;
d->updateTopLabelText();
d->showHideWidgets();
d->updateDialogStatus();
}
void SignEncryptEMailConflictDialog::setEncrypt(bool encrypt)
{
if (encrypt == d->encrypt) {
return;
}
d->encrypt = encrypt;
d->updateTopLabelText();
d->showHideWidgets();
d->updateDialogStatus();
}
void SignEncryptEMailConflictDialog::setSenders(const std::vector<Sender> &senders)
{
if (senders == d->senders) {
return;
}
d->senders = senders;
d->createSendersAndRecipients();
d->showHideWidgets();
d->updateDialogStatus();
}
void SignEncryptEMailConflictDialog::setRecipients(const std::vector<Recipient> &recipients)
{
if (d->recipients == recipients) {
return;
}
d->recipients = recipients;
d->createSendersAndRecipients();
d->showHideWidgets();
d->updateDialogStatus();
}
void SignEncryptEMailConflictDialog::pickProtocol()
{
if (selectedProtocol() != UnknownProtocol) {
return; // already picked
}
const bool pgp = d->isComplete(OpenPGP);
const bool cms = d->isComplete(CMS);
if (pgp && !cms) {
d->ui.pgpRB.setChecked(true);
} else if (cms && !pgp) {
d->ui.cmsRB.setChecked(true);
}
}
bool SignEncryptEMailConflictDialog::isComplete() const
{
const Protocol proto = selectedProtocol();
return proto != UnknownProtocol && d->isComplete(proto);
}
bool SignEncryptEMailConflictDialog::Private::isComplete(Protocol proto) const
{
return (!sign
|| std::none_of(ui.signers.cbegin(), //
ui.signers.cend(),
[proto](const CertificateSelectionLine &l) {
return l.isStillAmbiguous(proto);
}))
&& (!encrypt
|| std::none_of(ui.recipients.cbegin(), //
ui.recipients.cend(),
[proto](const CertificateSelectionLine &l) {
return l.isStillAmbiguous(proto);
}));
}
static std::vector<Key> get_keys(const std::vector<CertificateSelectionLine> &lines, Protocol proto)
{
if (proto == UnknownProtocol) {
return std::vector<Key>();
}
Q_ASSERT(proto == OpenPGP || proto == CMS);
std::vector<Key> keys;
keys.reserve(lines.size());
std::transform(lines.cbegin(), lines.cend(), std::back_inserter(keys), [proto](const CertificateSelectionLine &l) {
return l.key(proto);
});
return keys;
}
std::vector<Key> SignEncryptEMailConflictDialog::resolvedSigningKeys() const
{
return d->sign ? get_keys(d->ui.signers, selectedProtocol()) : std::vector<Key>();
}
std::vector<Key> SignEncryptEMailConflictDialog::resolvedEncryptionKeys() const
{
return d->encrypt ? get_keys(d->ui.recipients, selectedProtocol()) : std::vector<Key>();
}
void SignEncryptEMailConflictDialog::setQuickMode(bool on)
{
d->ui.quickModeCB.setChecked(on);
}
bool SignEncryptEMailConflictDialog::isQuickMode() const
{
return d->ui.quickModeCB.isChecked();
}
void SignEncryptEMailConflictDialog::setConflict(bool conflict)
{
d->ui.conflictTopLB.setVisible(conflict);
d->ui.quickModeTopLB.setVisible(!conflict);
}
#include "moc_signencryptemailconflictdialog.cpp"
diff --git a/src/crypto/gui/signencryptwizard.h b/src/crypto/gui/signencryptwizard.h
index bbbdb5f54..303d7519a 100644
--- a/src/crypto/gui/signencryptwizard.h
+++ b/src/crypto/gui/signencryptwizard.h
@@ -1,138 +1,138 @@
/* -*- mode: c++; c-basic-offset:4 -*-
crypto/gui/signencryptwizard.h
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include <crypto/gui/wizard.h>
#include <crypto/gui/signerresolvepage.h>
#include <utils/pimpl_ptr.h>
-#include <KMime/HeaderParsing>
+#include <KMime/Types>
#include <gpgme++/global.h>
#include <memory>
#include <vector>
namespace GpgME
{
class Key;
}
class QFileInfo;
template<typename T>
class QList;
using QFileInfoList = QList<QFileInfo>;
namespace Kleo
{
namespace Crypto
{
class Task;
class TaskCollection;
namespace Gui
{
class ObjectsPage;
class ResolveRecipientsPage;
class ResultPage;
class SignerResolvePage;
class SignEncryptWizard : public Wizard
{
Q_OBJECT
public:
explicit SignEncryptWizard(QWidget *parent = nullptr, Qt::WindowFlags f = {});
~SignEncryptWizard() override;
struct Page {
enum Id {
ResolveSigner = 0,
Objects,
ResolveRecipients,
Result,
};
};
void setCommitPage(Page::Id);
GpgME::Protocol presetProtocol() const;
void setPresetProtocol(GpgME::Protocol proto);
GpgME::Protocol selectedProtocol() const;
/// SignOrEncryptFiles mode subinterface
//@{
QFileInfoList resolvedFiles() const;
void setFiles(const QStringList &files);
bool signingSelected() const;
void setSigningSelected(bool selected);
bool encryptionSelected() const;
void setEncryptionSelected(bool selected);
bool isSigningUserMutable() const;
void setSigningUserMutable(bool isMutable);
bool isEncryptionUserMutable() const;
void setEncryptionUserMutable(bool isMutable);
bool isMultipleProtocolsAllowed() const;
void setMultipleProtocolsAllowed(bool allowed);
//@}
/** if true, the user is allowed to remove/add recipients via the UI.
* Defaults to @p false.
*/
bool recipientsUserMutable() const;
void setRecipientsUserMutable(bool isMutable);
void setSignersAndCandidates(const std::vector<KMime::Types::Mailbox> &signers, const std::vector<std::vector<GpgME::Key>> &keys);
void setTaskCollection(const std::shared_ptr<TaskCollection> &tasks);
std::vector<GpgME::Key> resolvedCertificates() const;
std::vector<GpgME::Key> resolvedSigners() const;
bool isAsciiArmorEnabled() const;
void setAsciiArmorEnabled(bool enabled);
bool keepResultPageOpenWhenDone() const;
void setKeepResultPageOpenWhenDone(bool keep);
void onNext(int currentId) override;
Q_SIGNALS:
void signersResolved();
void objectsResolved();
void recipientsResolved();
protected:
SignerResolvePage *signerResolvePage();
const SignerResolvePage *signerResolvePage() const;
ObjectsPage *objectsPage();
ResultPage *resultPage();
ResolveRecipientsPage *resolveRecipientsPage();
void setSignerResolvePageValidator(const std::shared_ptr<SignerResolvePage::Validator> &validator);
private:
class Private;
kdtools::pimpl_ptr<Private> d;
};
}
}
}
diff --git a/src/crypto/gui/signerresolvepage.h b/src/crypto/gui/signerresolvepage.h
index a80faef71..aeb29d302 100644
--- a/src/crypto/gui/signerresolvepage.h
+++ b/src/crypto/gui/signerresolvepage.h
@@ -1,123 +1,123 @@
/* -*- mode: c++; c-basic-offset:4 -*-
crypto/gui/signerresolvepage.h
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include <crypto/gui/wizardpage.h>
#include <utils/pimpl_ptr.h>
-#include <KMime/HeaderParsing>
+#include <KMime/Types>
#include <gpgme++/global.h>
#include <memory>
#include <set>
#include <vector>
namespace GpgME
{
class Key;
}
namespace Kleo
{
namespace Crypto
{
class SigningPreferences;
namespace Gui
{
class SignerResolvePage : public WizardPage
{
Q_OBJECT
public:
explicit SignerResolvePage(QWidget *parent = nullptr, Qt::WindowFlags f = {});
~SignerResolvePage() override;
void setSignersAndCandidates(const std::vector<KMime::Types::Mailbox> &signers, const std::vector<std::vector<GpgME::Key>> &keys);
std::vector<GpgME::Key> resolvedSigners() const;
std::vector<GpgME::Key> signingCertificates(GpgME::Protocol protocol = GpgME::UnknownProtocol) const;
bool isComplete() const override;
bool encryptionSelected() const;
void setEncryptionSelected(bool selected);
bool signingSelected() const;
void setSigningSelected(bool selected);
bool isEncryptionUserMutable() const;
void setEncryptionUserMutable(bool ismutable);
bool isSigningUserMutable() const;
void setSigningUserMutable(bool ismutable);
bool isAsciiArmorEnabled() const;
void setAsciiArmorEnabled(bool enabled);
void setPresetProtocol(GpgME::Protocol protocol);
void setPresetProtocols(const std::vector<GpgME::Protocol> &protocols);
std::set<GpgME::Protocol> selectedProtocols() const;
std::set<GpgME::Protocol> selectedProtocolsWithoutSigningCertificate() const;
void setMultipleProtocolsAllowed(bool allowed);
bool multipleProtocolsAllowed() const;
void setProtocolSelectionUserMutable(bool ismutable);
bool protocolSelectionUserMutable() const;
enum Operation {
SignAndEncrypt = 0,
SignOnly,
EncryptOnly,
};
Operation operation() const;
class Validator
{
public:
virtual ~Validator()
{
}
virtual bool isComplete() const = 0;
virtual QString explanation() const = 0;
/**
* returns a custom window title, or a null string if no custom
* title is required.
* (use this if the title needs dynamic adaption
* depending on the user's selection)
*/
virtual QString customWindowTitle() const = 0;
};
void setValidator(const std::shared_ptr<Validator> &);
std::shared_ptr<Validator> validator() const;
void setSigningPreferences(const std::shared_ptr<SigningPreferences> &prefs);
std::shared_ptr<SigningPreferences> signingPreferences() const;
private:
void onNext() override;
private:
class Private;
kdtools::pimpl_ptr<Private> d;
Q_PRIVATE_SLOT(d, void operationButtonClicked(int))
Q_PRIVATE_SLOT(d, void selectCertificates())
Q_PRIVATE_SLOT(d, void updateUi())
};
}
}
}
diff --git a/src/crypto/newsignencryptemailcontroller.cpp b/src/crypto/newsignencryptemailcontroller.cpp
index e90bdfb54..0922c4c59 100644
--- a/src/crypto/newsignencryptemailcontroller.cpp
+++ b/src/crypto/newsignencryptemailcontroller.cpp
@@ -1,436 +1,436 @@
/* -*- mode: c++; c-basic-offset:4 -*-
crypto/newsignencryptemailcontroller.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2009, 2010 Klarälvdalens Datakonsult AB
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-kleopatra.h>
#include "encryptemailtask.h"
#include "kleopatra_debug.h"
#include "newsignencryptemailcontroller.h"
#include "recipient.h"
#include "sender.h"
#include "signemailtask.h"
#include "taskcollection.h"
#include "emailoperationspreferences.h"
#include <crypto/gui/signencryptemailconflictdialog.h>
#include "utils/input.h"
#include "utils/kleo_assert.h"
#include "utils/output.h"
#include <Libkleo/GnuPG>
#include <Libkleo/Compliance>
#include <Libkleo/KleoException>
#include <Libkleo/Stl_Util>
#include <gpgme++/key.h>
-#include <KMime/HeaderParsing>
+#include <KMime/Types>
#include <KLocalizedString>
#include <KMessageBox>
#include <QPointer>
#include <QTimer>
using namespace Kleo;
using namespace Kleo::Crypto;
using namespace Kleo::Crypto::Gui;
using namespace GpgME;
using namespace KMime::Types;
static std::vector<Sender> mailbox2sender(const std::vector<Mailbox> &mbs)
{
std::vector<Sender> senders;
senders.reserve(mbs.size());
for (const Mailbox &mb : mbs) {
senders.push_back(Sender(mb));
}
return senders;
}
static std::vector<Recipient> mailbox2recipient(const std::vector<Mailbox> &mbs)
{
std::vector<Recipient> recipients;
recipients.reserve(mbs.size());
for (const Mailbox &mb : mbs) {
recipients.push_back(Recipient(mb));
}
return recipients;
}
class NewSignEncryptEMailController::Private
{
friend class ::Kleo::Crypto::NewSignEncryptEMailController;
NewSignEncryptEMailController *const q;
public:
explicit Private(NewSignEncryptEMailController *qq);
~Private();
private:
void slotDialogAccepted();
void slotDialogRejected();
private:
void ensureDialogVisible();
void cancelAllTasks();
void startSigning();
void startEncryption();
void schedule();
std::shared_ptr<Task> takeRunnable(GpgME::Protocol proto);
private:
bool sign : 1;
bool encrypt : 1;
bool resolvingInProgress : 1;
bool certificatesResolved : 1;
bool detached : 1;
Protocol presetProtocol;
std::vector<Key> signers, recipients;
std::vector<std::shared_ptr<Task>> runnable, completed;
std::shared_ptr<Task> cms, openpgp;
QPointer<SignEncryptEMailConflictDialog> dialog;
};
NewSignEncryptEMailController::Private::Private(NewSignEncryptEMailController *qq)
: q(qq)
, sign(false)
, encrypt(false)
, resolvingInProgress(false)
, certificatesResolved(false)
, detached(false)
, presetProtocol(UnknownProtocol)
, signers()
, recipients()
, runnable()
, cms()
, openpgp()
, dialog(new SignEncryptEMailConflictDialog)
{
connect(dialog, SIGNAL(accepted()), q, SLOT(slotDialogAccepted()));
connect(dialog, SIGNAL(rejected()), q, SLOT(slotDialogRejected()));
}
NewSignEncryptEMailController::Private::~Private()
{
delete dialog;
}
NewSignEncryptEMailController::NewSignEncryptEMailController(const std::shared_ptr<ExecutionContext> &xc, QObject *p)
: Controller(xc, p)
, d(new Private(this))
{
}
NewSignEncryptEMailController::NewSignEncryptEMailController(QObject *p)
: Controller(p)
, d(new Private(this))
{
}
NewSignEncryptEMailController::~NewSignEncryptEMailController()
{
qCDebug(KLEOPATRA_LOG);
}
void NewSignEncryptEMailController::setSubject(const QString &subject)
{
d->dialog->setSubject(subject);
}
void NewSignEncryptEMailController::setProtocol(Protocol proto)
{
d->presetProtocol = proto;
d->dialog->setPresetProtocol(proto);
}
Protocol NewSignEncryptEMailController::protocol() const
{
return d->dialog->selectedProtocol();
}
const char *NewSignEncryptEMailController::protocolAsString() const
{
switch (protocol()) {
case OpenPGP:
return "OpenPGP";
case CMS:
return "CMS";
default:
throw Kleo::Exception(gpg_error(GPG_ERR_INTERNAL), i18n("Call to NewSignEncryptEMailController::protocolAsString() is ambiguous."));
}
}
void NewSignEncryptEMailController::setSigning(bool sign)
{
d->sign = sign;
d->dialog->setSign(sign);
}
bool NewSignEncryptEMailController::isSigning() const
{
return d->sign;
}
void NewSignEncryptEMailController::setEncrypting(bool encrypt)
{
d->encrypt = encrypt;
d->dialog->setEncrypt(encrypt);
}
bool NewSignEncryptEMailController::isEncrypting() const
{
return d->encrypt;
}
void NewSignEncryptEMailController::setDetachedSignature(bool detached)
{
d->detached = detached;
}
bool NewSignEncryptEMailController::isResolvingInProgress() const
{
return d->resolvingInProgress;
}
bool NewSignEncryptEMailController::areCertificatesResolved() const
{
return d->certificatesResolved;
}
void NewSignEncryptEMailController::startResolveCertificates(const std::vector<Mailbox> &r, const std::vector<Mailbox> &s)
{
d->certificatesResolved = false;
d->resolvingInProgress = true;
const std::vector<Sender> senders = mailbox2sender(s);
const std::vector<Recipient> recipients = mailbox2recipient(r);
d->dialog->setQuickMode(false);
d->dialog->setSenders(senders);
d->dialog->setRecipients(recipients);
d->dialog->pickProtocol();
d->dialog->setConflict(false);
d->ensureDialogVisible();
}
void NewSignEncryptEMailController::Private::slotDialogAccepted()
{
resolvingInProgress = false;
certificatesResolved = true;
signers = dialog->resolvedSigningKeys();
recipients = dialog->resolvedEncryptionKeys();
QMetaObject::invokeMethod(q, "certificatesResolved", Qt::QueuedConnection);
}
void NewSignEncryptEMailController::Private::slotDialogRejected()
{
resolvingInProgress = false;
certificatesResolved = false;
QMetaObject::invokeMethod(q, "error", Qt::QueuedConnection, Q_ARG(int, gpg_error(GPG_ERR_CANCELED)), Q_ARG(QString, i18n("User cancel")));
}
void NewSignEncryptEMailController::startEncryption(const std::vector<std::shared_ptr<Input>> &inputs, const std::vector<std::shared_ptr<Output>> &outputs)
{
kleo_assert(d->encrypt);
kleo_assert(!d->resolvingInProgress);
kleo_assert(!inputs.empty());
kleo_assert(outputs.size() == inputs.size());
std::vector<std::shared_ptr<Task>> tasks;
tasks.reserve(inputs.size());
kleo_assert(!d->recipients.empty());
for (unsigned int i = 0, end = inputs.size(); i < end; ++i) {
const std::shared_ptr<EncryptEMailTask> task(new EncryptEMailTask);
task->setInput(inputs[i]);
task->setOutput(outputs[i]);
task->setRecipients(d->recipients);
tasks.push_back(task);
}
// append to runnable stack
d->runnable.insert(d->runnable.end(), tasks.begin(), tasks.end());
d->startEncryption();
}
void NewSignEncryptEMailController::Private::startEncryption()
{
std::shared_ptr<TaskCollection> coll(new TaskCollection);
std::vector<std::shared_ptr<Task>> tmp;
tmp.reserve(runnable.size());
std::copy(runnable.cbegin(), runnable.cend(), std::back_inserter(tmp));
coll->setTasks(tmp);
#if 0
#warning use a new result dialog
// ### use a new result dialog
dialog->setTaskCollection(coll);
#endif
for (const std::shared_ptr<Task> &t : std::as_const(tmp)) {
q->connectTask(t);
}
schedule();
}
void NewSignEncryptEMailController::startSigning(const std::vector<std::shared_ptr<Input>> &inputs, const std::vector<std::shared_ptr<Output>> &outputs)
{
kleo_assert(d->sign);
kleo_assert(!d->resolvingInProgress);
kleo_assert(!inputs.empty());
kleo_assert(!outputs.empty());
std::vector<std::shared_ptr<Task>> tasks;
tasks.reserve(inputs.size());
kleo_assert(!d->signers.empty());
kleo_assert(std::none_of(d->signers.cbegin(), d->signers.cend(), std::mem_fn(&Key::isNull)));
for (unsigned int i = 0, end = inputs.size(); i < end; ++i) {
const std::shared_ptr<SignEMailTask> task(new SignEMailTask);
task->setInput(inputs[i]);
task->setOutput(outputs[i]);
task->setSigners(d->signers);
task->setDetachedSignature(d->detached);
tasks.push_back(task);
}
// append to runnable stack
d->runnable.insert(d->runnable.end(), tasks.begin(), tasks.end());
d->startSigning();
}
void NewSignEncryptEMailController::Private::startSigning()
{
std::shared_ptr<TaskCollection> coll(new TaskCollection);
std::vector<std::shared_ptr<Task>> tmp;
tmp.reserve(runnable.size());
std::copy(runnable.cbegin(), runnable.cend(), std::back_inserter(tmp));
coll->setTasks(tmp);
#if 0
#warning use a new result dialog
// ### use a new result dialog
dialog->setTaskCollection(coll);
#endif
for (const std::shared_ptr<Task> &t : std::as_const(tmp)) {
q->connectTask(t);
}
schedule();
}
void NewSignEncryptEMailController::Private::schedule()
{
if (!cms)
if (const std::shared_ptr<Task> t = takeRunnable(CMS)) {
t->start();
cms = t;
}
if (!openpgp)
if (const std::shared_ptr<Task> t = takeRunnable(OpenPGP)) {
t->start();
openpgp = t;
}
if (cms || openpgp) {
return;
}
kleo_assert(runnable.empty());
q->emitDoneOrError();
}
std::shared_ptr<Task> NewSignEncryptEMailController::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<Task>();
}
const std::shared_ptr<Task> result = *it;
runnable.erase(it);
return result;
}
void NewSignEncryptEMailController::doTaskDone(const Task *task, const std::shared_ptr<const Task::Result> &result)
{
Q_ASSERT(task);
if (result && result->hasError()) {
QPointer<QObject> that = this;
if (result->details().isEmpty())
KMessageBox::error(nullptr, result->overview(), i18nc("@title:window", "Error"));
else
KMessageBox::detailedError(nullptr, result->overview(), result->details(), i18nc("@title:window", "Error"));
if (!that) {
return;
}
}
// 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 NewSignEncryptEMailController::cancel()
{
try {
d->dialog->close();
d->cancelAllTasks();
} catch (const std::exception &e) {
qCDebug(KLEOPATRA_LOG) << "Caught exception: " << e.what();
}
}
void NewSignEncryptEMailController::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 NewSignEncryptEMailController::Private::ensureDialogVisible()
{
q->bringToForeground(dialog, true);
}
#include "moc_newsignencryptemailcontroller.cpp"
diff --git a/src/crypto/recipient.cpp b/src/crypto/recipient.cpp
index d7cbaacc6..0a3bc1cdb 100644
--- a/src/crypto/recipient.cpp
+++ b/src/crypto/recipient.cpp
@@ -1,175 +1,175 @@
/* -*- mode: c++; c-basic-offset:4 -*-
crypto/recipient.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2009 Klarälvdalens Datakonsult AB
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-kleopatra.h>
#include "recipient.h"
#include <Libkleo/KeyCache>
#include <Libkleo/Predicates>
#include <Libkleo/Stl_Util>
#include <utils/cached.h>
#include <utils/kleo_assert.h>
-#include <KMime/HeaderParsing>
+#include <KMime/Types>
#include <gpgme++/key.h>
using namespace Kleo;
using namespace Kleo::Crypto;
using namespace KMime::Types;
using namespace GpgME;
namespace KMime
{
namespace Types
{
static bool operator==(const AddrSpec &lhs, const AddrSpec &rhs)
{
return lhs.localPart == rhs.localPart && lhs.domain == rhs.domain;
}
static bool operator==(const Mailbox &lhs, const Mailbox &rhs)
{
return lhs.name() == rhs.name() && lhs.addrSpec() == rhs.addrSpec();
}
static bool determine_ambiguous(const Mailbox &mb, const std::vector<Key> &keys)
{
Q_UNUSED(mb)
// ### really do check when we don't only show matching keys
return keys.size() != 1;
}
} // namespace Types
} // namespace KMime
class Recipient::Private
{
friend class ::Kleo::Crypto::Recipient;
public:
explicit Private(const Mailbox &mb)
: mailbox(mb)
{
// ### also fill up to a certain number of keys with those
// ### that don't match, for the case where there's a low
// ### total number of keys
const std::vector<Key> encrypt = KeyCache::instance()->findEncryptionKeysByMailbox(mb.addrSpec().asString());
kdtools::separate_if(encrypt.cbegin(),
encrypt.cend(),
std::back_inserter(pgpEncryptionKeys),
std::back_inserter(cmsEncryptionKeys),
[](const Key &key) {
return key.protocol() == OpenPGP;
});
}
private:
const Mailbox mailbox;
std::vector<Key> pgpEncryptionKeys, cmsEncryptionKeys;
Key cmsEncryptionKey;
UserID pgpEncryptionUid;
cached<bool> encryptionAmbiguous[2];
};
Recipient::Recipient(const Mailbox &mb)
: d(new Private(mb))
{
}
void Recipient::detach()
{
if (d && !d.unique()) {
d.reset(new Private(*d));
}
}
bool Recipient::deepEquals(const Recipient &other) const
{
static const _detail::ByFingerprint<std::equal_to> compare = {};
return mailbox() == other.mailbox() //
&& compare(d->cmsEncryptionKey, other.d->cmsEncryptionKey) //
&& compare(d->pgpEncryptionUid.parent(), other.d->pgpEncryptionUid.parent()) //
&& strcmp(d->pgpEncryptionUid.id(), other.d->pgpEncryptionUid.id()) //
&& std::equal(d->pgpEncryptionKeys.cbegin(), d->pgpEncryptionKeys.cend(), other.d->pgpEncryptionKeys.cbegin(), compare)
&& std::equal(d->cmsEncryptionKeys.cbegin(), d->pgpEncryptionKeys.cend(), other.d->cmsEncryptionKeys.cbegin(), compare);
}
bool Recipient::isEncryptionAmbiguous(GpgME::Protocol proto) const
{
if (d->encryptionAmbiguous[proto].dirty()) {
d->encryptionAmbiguous[proto] = determine_ambiguous(d->mailbox, encryptionCertificateCandidates(proto));
}
return d->encryptionAmbiguous[proto];
}
const Mailbox &Recipient::mailbox() const
{
return d->mailbox;
}
const std::vector<Key> &Recipient::encryptionCertificateCandidates(GpgME::Protocol proto) const
{
if (proto == OpenPGP) {
return d->pgpEncryptionKeys;
}
if (proto == CMS) {
return d->cmsEncryptionKeys;
}
kleo_assert_fail(proto == OpenPGP || proto == CMS);
#if 0
return
proto == OpenPGP ? d->pgpEncryptionKeys :
proto == CMS ? d->cmsEncryptionKeys :
// even though gcc warns about this line, it's completely ok, promise:
kleo_assert_fail(proto == OpenPGP || proto == CMS);
#endif
}
void Recipient::setResolvedEncryptionKey(const Key &key)
{
if (key.isNull()) {
return;
}
const Protocol proto = key.protocol();
kleo_assert(proto == OpenPGP || proto == CMS);
detach();
if (proto == OpenPGP) {
d->pgpEncryptionUid = key.userID(0);
} else {
d->cmsEncryptionKey = key;
}
d->encryptionAmbiguous[proto] = false;
}
Key Recipient::resolvedEncryptionKey(GpgME::Protocol proto) const
{
kleo_assert(proto == OpenPGP || proto == CMS);
if (proto == OpenPGP) {
return d->pgpEncryptionUid.parent();
} else {
return d->cmsEncryptionKey;
}
}
void Recipient::setResolvedOpenPGPEncryptionUserID(const UserID &uid)
{
if (uid.isNull()) {
return;
}
detach();
d->pgpEncryptionUid = uid;
}
UserID Recipient::resolvedOpenPGPEncryptionUserID() const
{
return d->pgpEncryptionUid;
}
diff --git a/src/crypto/sender.cpp b/src/crypto/sender.cpp
index 9a73e77c3..1e3cd53bf 100644
--- a/src/crypto/sender.cpp
+++ b/src/crypto/sender.cpp
@@ -1,228 +1,228 @@
/* -*- mode: c++; c-basic-offset:4 -*-
crypto/sender.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2009 Klarälvdalens Datakonsult AB
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-kleopatra.h>
#include "sender.h"
#include <Libkleo/KeyCache>
#include <Libkleo/Predicates>
#include <Libkleo/Stl_Util>
#include <utils/cached.h>
#include <utils/kleo_assert.h>
-#include <KMime/HeaderParsing>
+#include <KMime/Types>
#include <gpgme++/key.h>
#include <algorithm>
using namespace Kleo;
using namespace Kleo::Crypto;
using namespace KMime::Types;
using namespace GpgME;
namespace KMime
{
namespace Types
{
static bool operator==(const AddrSpec &lhs, const AddrSpec &rhs)
{
return lhs.localPart == rhs.localPart && lhs.domain == rhs.domain;
}
static bool operator==(const Mailbox &lhs, const Mailbox &rhs)
{
return lhs.name() == rhs.name() && lhs.addrSpec() == rhs.addrSpec();
}
static bool determine_ambiguous(const Mailbox &mb, const std::vector<Key> &keys)
{
Q_UNUSED(mb)
// ### really do check when we don't only show matching keys
return keys.size() != 1;
}
} // namespace Types
} // namespace KMime
class Sender::Private
{
friend class ::Kleo::Crypto::Sender;
public:
explicit Private(const Mailbox &mb)
: mailbox(mb)
{
// ### also fill up to a certain number of keys with those
// ### that don't match, for the case where there's a low
// ### total number of keys
const QString email = mb.addrSpec().asString();
const std::vector<Key> signers = KeyCache::instance()->findSigningKeysByMailbox(email);
const std::vector<Key> encrypt = KeyCache::instance()->findEncryptionKeysByMailbox(email);
kdtools::separate_if(signers.cbegin(), signers.cend(), std::back_inserter(pgpSigners), std::back_inserter(cmsSigners), [](const Key &key) {
return key.protocol() == OpenPGP;
});
kdtools::separate_if(encrypt.cbegin(),
encrypt.cend(),
std::back_inserter(pgpEncryptToSelfKeys),
std::back_inserter(cmsEncryptToSelfKeys),
[](const Key &key) {
return key.protocol() == OpenPGP;
});
}
private:
const Mailbox mailbox;
std::vector<Key> pgpSigners, cmsSigners, pgpEncryptToSelfKeys, cmsEncryptToSelfKeys;
cached<bool> signingAmbiguous[2], encryptionAmbiguous[2];
Key signingKey[2], cmsEncryptionKey;
UserID pgpEncryptionUid;
};
Sender::Sender(const Mailbox &mb)
: d(new Private(mb))
{
}
void Sender::detach()
{
if (d && !d.unique()) {
d.reset(new Private(*d));
}
}
bool Sender::deepEquals(const Sender &other) const
{
static const _detail::ByFingerprint<std::equal_to> compare = {};
return mailbox() == other.mailbox() //
&& compare(d->signingKey[CMS], other.d->signingKey[CMS]) //
&& compare(d->signingKey[OpenPGP], other.d->signingKey[OpenPGP]) //
&& compare(d->cmsEncryptionKey, other.d->cmsEncryptionKey) //
&& compare(d->pgpEncryptionUid.parent(), other.d->pgpEncryptionUid.parent()) && strcmp(d->pgpEncryptionUid.id(), other.d->pgpEncryptionUid.id()) == 0
&& std::equal(d->pgpSigners.cbegin(), d->pgpSigners.cend(), other.d->pgpSigners.cbegin(), compare)
&& std::equal(d->cmsSigners.cbegin(), d->cmsSigners.cend(), other.d->cmsSigners.cbegin(), compare)
&& std::equal(d->pgpEncryptToSelfKeys.cbegin(), d->pgpEncryptToSelfKeys.cend(), other.d->pgpEncryptToSelfKeys.cbegin(), compare)
&& std::equal(d->cmsEncryptToSelfKeys.cbegin(), d->cmsEncryptToSelfKeys.cend(), other.d->cmsEncryptToSelfKeys.cbegin(), compare);
}
bool Sender::isSigningAmbiguous(GpgME::Protocol proto) const
{
if (d->signingAmbiguous[proto].dirty()) {
d->signingAmbiguous[proto] = determine_ambiguous(d->mailbox, signingCertificateCandidates(proto));
}
return d->signingAmbiguous[proto];
}
bool Sender::isEncryptionAmbiguous(GpgME::Protocol proto) const
{
if (d->encryptionAmbiguous[proto].dirty()) {
d->encryptionAmbiguous[proto] = determine_ambiguous(d->mailbox, encryptToSelfCertificateCandidates(proto));
}
return d->encryptionAmbiguous[proto];
}
const Mailbox &Sender::mailbox() const
{
return d->mailbox;
}
const std::vector<Key> &Sender::signingCertificateCandidates(GpgME::Protocol proto) const
{
if (proto == OpenPGP) {
return d->pgpSigners;
}
if (proto == CMS) {
return d->cmsSigners;
}
kleo_assert_fail(proto == OpenPGP || proto == CMS);
#if 0
return
proto == OpenPGP ? d->pgpSigners :
proto == CMS ? d->cmsSigners :
// even though gcc warns about this line, it's completely ok, promise:
kleo_assert_fail(proto == OpenPGP || proto == CMS);
#endif
}
const std::vector<Key> &Sender::encryptToSelfCertificateCandidates(GpgME::Protocol proto) const
{
if (proto == OpenPGP) {
return d->pgpEncryptToSelfKeys;
}
if (proto == CMS) {
return d->cmsEncryptToSelfKeys;
}
kleo_assert_fail(proto == OpenPGP || proto == CMS);
#if 0
return
proto == OpenPGP ? d->pgpEncryptToSelfKeys :
proto == CMS ? d->cmsEncryptToSelfKeys :
// even though gcc warns about this line, it's completely ok, promise:
kleo_assert_fail(proto == OpenPGP || proto == CMS);
#endif
}
void Sender::setResolvedSigningKey(const Key &key)
{
if (key.isNull()) {
return;
}
const Protocol proto = key.protocol();
kleo_assert(proto == OpenPGP || proto == CMS);
detach();
d->signingKey[proto] = key;
d->signingAmbiguous[proto] = false;
}
Key Sender::resolvedSigningKey(GpgME::Protocol proto) const
{
kleo_assert(proto == OpenPGP || proto == CMS);
return d->signingKey[proto];
}
void Sender::setResolvedEncryptionKey(const Key &key)
{
if (key.isNull()) {
return;
}
const Protocol proto = key.protocol();
kleo_assert(proto == OpenPGP || proto == CMS);
detach();
if (proto == OpenPGP) {
d->pgpEncryptionUid = key.userID(0);
} else {
d->cmsEncryptionKey = key;
}
d->encryptionAmbiguous[proto] = false;
}
Key Sender::resolvedEncryptionKey(GpgME::Protocol proto) const
{
kleo_assert(proto == OpenPGP || proto == CMS);
if (proto == OpenPGP) {
return d->pgpEncryptionUid.parent();
} else {
return d->cmsEncryptionKey;
}
}
void Sender::setResolvedOpenPGPEncryptionUserID(const UserID &uid)
{
if (uid.isNull()) {
return;
}
detach();
d->pgpEncryptionUid = uid;
}
UserID Sender::resolvedOpenPGPEncryptionUserID() const
{
return d->pgpEncryptionUid;
}
diff --git a/src/uiserver/assuancommand.h b/src/uiserver/assuancommand.h
index 34b97219f..0c0b677f1 100644
--- a/src/uiserver/assuancommand.h
+++ b/src/uiserver/assuancommand.h
@@ -1,378 +1,378 @@
/* -*- mode: c++; c-basic-offset:4 -*-
uiserver/assuancommand.h
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include <utils/pimpl_ptr.h>
#include <utils/types.h>
#include <gpgme++/error.h>
#include <gpgme++/global.h>
#include <gpg-error.h>
-#include <KMime/HeaderParsing>
+#include <KMime/Types>
#include <qwindowdefs.h> // for WId
#include <map>
#include <memory>
#include <string>
#include <vector>
class QVariant;
class QObject;
#include <QStringList>
struct assuan_context_s;
namespace Kleo
{
class Input;
class Output;
class AssuanCommandFactory;
/*!
\brief Base class for GnuPG UI Server commands
\note large parts of this are outdated by now!
<h3>Implementing a new AssuanCommand</h3>
You do not directly inherit AssuanCommand, unless you want to
deal with implementing low-level, repetitive things like name()
in terms of staticName(). Assuming you don't, then you inherit
your command class from AssuanCommandMixin, passing your class
as the template argument to AssuanCommandMixin, like this:
\code
class MyFooCommand : public AssuanCommandMixin<MyFooCommand> {
\endcode
(http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern)
You then choose a command name, and return that from the static
method staticName(), which is by convention queried by both
AssuanCommandMixin<> and GenericAssuanCommandFactory<>:
\code
static const char * staticName() { return "MYFOO"; }
\endcode
The string should be all-uppercase by convention, but the
UiServer implementation doesn't enforce this.
The next step is to implement start(), the starting point of
command execution:
<h3>Executing the command</h3>
\code
int start( const std::string & line ) {
\endcode
This should set everything up and check the parameters in \a
line and any options this command understands. If there's an
error, choose one of the gpg-error codes and create a
gpg_error_t from it using the protected makeError() function:
\code
return makeError( GPG_ERR_NOT_IMPLEMENTED );
\endcode
But usually, you will want to create a dialog, or call some
GpgME function from here. In case of errors from GpgME, you
shouldn't pipe them through makeError(), but return them
as-is. This will preserve the error source. Error created using
makeError() will have Kleopatra as their error source, so watch
out what you're doing :)
In addition to options and the command line, your command might
require <em>bulk data</em> input or output. That's what the bulk
input and output channels are for. You can check whether the
client handed you an input channel by checking that
bulkInputDevice() isn't NULL, likewise for bulkOutputDevice().
If everything is ok, you return 0. This indicates to the client
that the command has been accepted and is now in progress.
In this mode (start() returned 0), there are a bunch of options
for your command to do. Some commands may require additional
information from the client. The options passed to start() are
designed to be persistent across commands, and rather limited in
length (there's a strict line length limit in the assuan
protocol with no line continuation mechanism). The same is true
for command line arguments, which, in addition, you have to
parse yourself. Those usually apply only to this command, and
not to following ones.
If you need data that might be larger than the line length
limit, you can either expect it on the bulkInputDevice(), or, if
you have the need for more than one such data channel, or the
data is optional or conditional on some condition that can only
be determined during command execution, you can \em inquire the
missing information from the client.
As an example, a VERIFY command would expect the signed data on
the bulkInputDevice(). But if the input stream doesn't contain
an embedded (opaque) signature, indicating a \em detached
signature, it would go and inquire that data from the
client. Here's how it works:
\code
const int err = inquire( "DETACHED_SIGNATURE",
this, SLOT(slotDetachedSignature(int,QByteArray,QByteArray)) );
if ( err )
done( err );
\endcode
This should be self-explanatory: You give a slot to call when
the data has arrived. The slot's first argument is an error
code. The second the data (if any), and the third is just
repeating what you gave as inquire()'s first argument. As usual,
you can leave argument off of the end, if you are not interested
in them.
You can do as many inquiries as you want, but only one at a
time.
You should periodically send status updates to the client. You do
that by calling sendStatus().
Once your command has finished executing, call done(). If it's
with an error code, call done(err) like above. <b>Do not
forget to call done() when done!</b>. It will close
bulkInputDevice(), bulkOutputDevice(), and send an OK or ERR
message back to the client.
At that point, your command has finished executing, and a new
one can be accepted, or the connection closed.
Apropos connection closed. The only way for the client to cancel
an operation is to shut down the connection. In this case, the
canceled() function will be called. At that point, the
connection to the client will have been broken already, and all
you can do is pack your things and go down gracefully.
If _you_ detect that the user has canceled (your dialog contains
a cancel button, doesn't it?), then you should instead call
done( GPG_ERR_CANCELED ), like for normal operation.
<h3>Registering the command with UiServer</h3>
To register a command, you implement a AssuanCommandFactory for
your AssuanCommand subclass, and register it with the
UiServer. This can be made considerably easier using
GenericAssuanCommandFactory:
\code
UiServer server;
server.registerCommandFactory( shared_ptr<AssuanCommandFactory>( new GenericAssuanCommandFactory<MyFooCommand> ) );
// more registerCommandFactory calls...
server.start();
\endcode
*/
class AssuanCommand : public ExecutionContext, public std::enable_shared_from_this<AssuanCommand>
{
// defined in assuanserverconnection.cpp!
public:
AssuanCommand();
~AssuanCommand() override;
int start();
void canceled();
virtual const char *name() const = 0;
class Memento
{
public:
virtual ~Memento()
{
}
};
template<typename T>
class TypedMemento : public Memento
{
T m_t;
public:
explicit TypedMemento(const T &t)
: m_t(t)
{
}
const T &get() const
{
return m_t;
}
T &get()
{
return m_t;
}
};
template<typename T>
static std::shared_ptr<TypedMemento<T>> make_typed_memento(const T &t)
{
return std::shared_ptr<TypedMemento<T>>(new TypedMemento<T>(t));
}
static int makeError(int code);
// convenience methods:
enum Mode { NoMode, EMail, FileManager };
Mode checkMode() const;
enum CheckProtocolOption {
AllowProtocolMissing = 0x01,
};
GpgME::Protocol checkProtocol(Mode mode, int options = 0) const;
void applyWindowID(QWidget *w) const override
{
doApplyWindowID(w);
}
WId parentWId() const;
void setNohup(bool on);
bool isNohup() const;
bool isDone() const;
QString sessionTitle() const;
unsigned int sessionId() const;
bool informativeRecipients() const;
bool informativeSenders() const;
const std::vector<KMime::Types::Mailbox> &recipients() const;
const std::vector<KMime::Types::Mailbox> &senders() const;
bool hasMemento(const QByteArray &tag) const;
std::shared_ptr<Memento> memento(const QByteArray &tag) const;
template<typename T>
std::shared_ptr<T> mementoAs(const QByteArray &tag) const
{
return std::dynamic_pointer_cast<T>(this->memento(tag));
}
QByteArray registerMemento(const std::shared_ptr<Memento> &mem);
QByteArray registerMemento(const QByteArray &tag, const std::shared_ptr<Memento> &mem);
void removeMemento(const QByteArray &tag);
template<typename T>
T mementoContent(const QByteArray &tag) const
{
if (std::shared_ptr<TypedMemento<T>> m = mementoAs<TypedMemento<T>>(tag)) {
return m->get();
} else {
return T();
}
}
bool hasOption(const char *opt) const;
QVariant option(const char *opt) const;
const std::map<std::string, QVariant> &options() const;
const std::vector<std::shared_ptr<Input>> &inputs() const;
const std::vector<std::shared_ptr<Input>> &messages() const;
const std::vector<std::shared_ptr<Output>> &outputs() const;
QStringList fileNames() const;
unsigned int numFiles() const;
void sendStatus(const char *keyword, const QString &text);
void sendStatusEncoded(const char *keyword, const std::string &text);
void sendData(const QByteArray &data, bool moreToCome = false);
int inquire(const char *keyword, QObject *receiver, const char *slot, unsigned int maxSize = 0);
void done(const GpgME::Error &err = GpgME::Error());
void done(const GpgME::Error &err, const QString &details);
void done(int err)
{
done(GpgME::Error(err));
}
void done(int err, const QString &details)
{
done(GpgME::Error(err), details);
}
private:
virtual void doCanceled() = 0;
virtual int doStart() = 0;
private:
void doApplyWindowID(QWidget *w) const;
private:
const std::map<QByteArray, std::shared_ptr<Memento>> &mementos() const;
private:
friend class ::Kleo::AssuanCommandFactory;
class Private;
kdtools::pimpl_ptr<Private> d;
};
class AssuanCommandFactory
{
public:
virtual ~AssuanCommandFactory()
{
}
virtual std::shared_ptr<AssuanCommand> create() const = 0;
virtual const char *name() const = 0;
using _Handler = gpg_error_t (*)(assuan_context_s *, char *);
virtual _Handler _handler() const = 0;
static gpg_error_t _handle(assuan_context_s *, char *, const char *);
};
template<typename Command>
class GenericAssuanCommandFactory : public AssuanCommandFactory
{
AssuanCommandFactory::_Handler _handler() const override
{
return &GenericAssuanCommandFactory::_handle;
}
static gpg_error_t _handle(assuan_context_s *_ctx, char *_line)
{
return AssuanCommandFactory::_handle(_ctx, _line, Command::staticName());
}
std::shared_ptr<AssuanCommand> create() const override
{
return make();
}
const char *name() const override
{
return Command::staticName();
}
public:
static std::shared_ptr<Command> make()
{
return std::shared_ptr<Command>(new Command);
}
};
template<typename Derived, typename Base = AssuanCommand>
class AssuanCommandMixin : public Base
{
protected:
/* reimp */ const char *name() const override
{
return Derived::staticName();
}
};
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Thu, Feb 26, 6:53 PM (1 d, 15 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
ba/ab/88a6bb398534676f2b12d35f79e1
Attached To
rKLEOPATRA Kleopatra
Event Timeline
Log In to Comment