Page MenuHome GnuPG

No OneTemporary

diff --git a/src/crypto/decryptverifytask.cpp b/src/crypto/decryptverifytask.cpp
index 2dafad135..212956f0e 100644
--- a/src/crypto/decryptverifytask.cpp
+++ b/src/crypto/decryptverifytask.cpp
@@ -1,1698 +1,1708 @@
/* -*- 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/Protocol>
#include <QGpgME/VerifyOpaqueJob>
#include <QGpgME/VerifyDetachedJob>
#include <QGpgME/DecryptJob>
#include <QGpgME/DecryptVerifyJob>
#if QGPGME_SUPPORTS_ARCHIVE_JOBS
#include <QGpgME/DecryptVerifyArchiveJob>
#endif
#include <Libkleo/AuditLogEntry>
#include <Libkleo/Compliance>
#include <Libkleo/Dn>
#include <Libkleo/KleoException>
#include <Libkleo/Stl_Util>
#include <Libkleo/KeyCache>
#include <Libkleo/Predicates>
#include <Libkleo/Formatting>
#include <Libkleo/Classify>
#include <utils/detail_p.h>
#include <utils/input.h>
#include <utils/output.h>
#include <utils/kleo_assert.h>
#include <Libkleo/GnuPG>
#include <KMime/HeaderParsing>
#include <gpgme++/error.h>
#include <gpgme++/key.h>
#include <gpgme++/verificationresult.h>
#include <gpgme++/decryptionresult.h>
#include <gpgme++/context.h>
#include <gpg-error.h>
#include "kleopatra_debug.h"
#include <KLocalizedString>
#include <QLocale>
#include <QByteArray>
#include <QDateTime>
#include <QIODevice>
#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 &rarr; %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);
}
}
}
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>", QString::fromLocal8Bit(err.asString()).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>", QString::fromLocal8Bit(err.asString()).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(QString::fromLocal8Bit(sig.status().asString()));
}
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(QString::fromLocal8Bit(sig.status().asString()));
}
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))
{
}
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;
};
AbstractDecryptVerifyTask::AbstractDecryptVerifyTask(QObject *parent) : Task(parent), d(new Private) {}
AbstractDecryptVerifyTask::~AbstractDecryptVerifyTask() {}
Mailbox AbstractDecryptVerifyTask::informativeSender() const
{
return d->informativeSender;
}
void AbstractDecryptVerifyTask::setInformativeSender(const Mailbox &sender)
{
d->informativeSender = sender;
}
class DecryptVerifyTask::Private
{
DecryptVerifyTask *const q;
public:
explicit Private(DecryptVerifyTask *qq)
: q{qq}
{
}
void startDecryptVerifyJob();
#if QGPGME_SUPPORTS_ARCHIVE_JOBS
void startDecryptVerifyArchiveJob();
#endif
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_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;
}
void DecryptVerifyTask::cancel()
{
}
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::setOutputDirectory(const QString &directory)
{
d->m_outputDirectory = directory;
}
#if QGPGME_SUPPORTS_ARCHIVE_JOBS
static bool archiveJobsCanBeUsed(GpgME::Protocol protocol)
{
return (protocol == GpgME::OpenPGP) && QGpgME::DecryptVerifyArchiveJob::isSupported();
}
#endif
void DecryptVerifyTask::doStart()
{
kleo_assert(d->m_backend);
#if QGPGME_SUPPORTS_ARCHIVE_JOBS
if (d->m_extractArchive && archiveJobsCanBeUsed(d->m_protocol)) {
d->startDecryptVerifyArchiveJob();
} else
#endif
{
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" << err.asString();
}
}
}
}
void DecryptVerifyTask::Private::startDecryptVerifyJob()
{
try {
QGpgME::DecryptVerifyJob *const job = m_backend->decryptVerifyJob();
kleo_assert(job);
setIgnoreMDCErrorFlag(job, m_ignoreMDCError);
QObject::connect(job, &QGpgME::DecryptVerifyJob::result, q, [this](const GpgME::DecryptionResult &decryptResult, const GpgME::VerificationResult &verifyResult, const QByteArray &plainText) {
slotResult(decryptResult, verifyResult, plainText);
});
- QObject::connect(job, &QGpgME::DecryptVerifyJob::progress, q, &DecryptVerifyTask::setProgress);
+ QObject::connect(job, &QGpgME::Job::progress, q, [this](const QString &, int processed, int total) {
+ q->setProgress(processed, total);
+ });
ensureIOOpen(m_input->ioDevice().get(), m_output->ioDevice().get());
job->start(m_input->ioDevice(), m_output->ioDevice());
} 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()));
}
}
#if QGPGME_SUPPORTS_ARCHIVE_JOBS
void DecryptVerifyTask::Private::startDecryptVerifyArchiveJob()
{
auto *const job = m_backend->decryptVerifyArchiveJob();
kleo_assert(job);
setIgnoreMDCErrorFlag(job, m_ignoreMDCError);
job->setOutputDirectory(m_outputDirectory);
connect(job, &QGpgME::DecryptVerifyArchiveJob::result, q, [this](const GpgME::DecryptionResult &decryptResult, const GpgME::VerificationResult &verifyResult) {
slotResult(decryptResult, verifyResult);
});
- connect(job, &QGpgME::DecryptVerifyJob::progress, q, &DecryptVerifyTask::setProgress);
+ connect(job, &QGpgME::Job::progress, q, [this](const QString &, int processed, int total) {
+ q->setProgress(processed, total);
+ });
ensureIOOpen(m_input->ioDevice().get(), nullptr);
const auto err = job->start(m_input->ioDevice());
if (err) {
q->emitResult(q->fromDecryptVerifyResult(err, {}, {}));
}
}
#endif
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, SIGNAL(progress(QString,int,int)),
- q, SLOT(setProgress(QString,int,int)));
+ q->connect(job, &QGpgME::Job::progress, q, [this](const QString &, int processed, int total) {
+ q->setProgress(processed, total);
+ });
}
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::cancel()
{
}
void DecryptTask::doStart()
{
kleo_assert(d->m_backend);
try {
QGpgME::DecryptJob *const job = d->m_backend->decryptJob();
kleo_assert(job);
d->registerJob(job);
ensureIOOpen(d->m_input->ioDevice().get(), d->m_output->ioDevice().get());
job->start(d->m_input->ioDevice(), d->m_output->ioDevice());
} 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();
#if QGPGME_SUPPORTS_ARCHIVE_JOBS
void startDecryptVerifyArchiveJob();
#endif
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_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::setOutputDirectory(const QString &directory)
{
d->m_outputDirectory = directory;
}
void VerifyOpaqueTask::cancel()
{
}
void VerifyOpaqueTask::doStart()
{
kleo_assert(d->m_backend);
#if QGPGME_SUPPORTS_ARCHIVE_JOBS
if (d->m_extractArchive && archiveJobsCanBeUsed(d->m_protocol)) {
d->startDecryptVerifyArchiveJob();
} else
#endif
{
d->startVerifyOpaqueJob();
}
}
void VerifyOpaqueTask::Private::startVerifyOpaqueJob()
{
try {
QGpgME::VerifyOpaqueJob *const job = m_backend->verifyOpaqueJob();
kleo_assert(job);
connect(job, &QGpgME::VerifyOpaqueJob::result, q, [this](const GpgME::VerificationResult &result, const QByteArray &plainText) {
slotResult(result, plainText);
});
- connect(job, &QGpgME::VerifyOpaqueJob::progress, q, &VerifyOpaqueTask::setProgress);
+ connect(job, &QGpgME::Job::progress, q, [this](const QString &, int processed, int total) {
+ q->setProgress(processed, total);
+ });
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>());
} 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()));
}
}
#if QGPGME_SUPPORTS_ARCHIVE_JOBS
void VerifyOpaqueTask::Private::startDecryptVerifyArchiveJob()
{
auto *const job = m_backend->decryptVerifyArchiveJob();
kleo_assert(job);
job->setOutputDirectory(m_outputDirectory);
connect(job, &QGpgME::DecryptVerifyArchiveJob::result, q, [this](const DecryptionResult &, const VerificationResult &verifyResult) {
slotResult(verifyResult);
});
- connect(job, &QGpgME::DecryptVerifyJob::progress, q, &VerifyOpaqueTask::setProgress);
+ connect(job, &QGpgME::Job::progress, q, [this](const QString &, int processed, int total) {
+ q->setProgress(processed, total);
+ });
ensureIOOpen(m_input->ioDevice().get(), nullptr);
const auto err = job->start(m_input->ioDevice());
if (err) {
q->emitResult(q->fromVerifyOpaqueResult(err, {}, {}));
}
}
#endif
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, SIGNAL(progress(QString,int,int)),
- q, SLOT(setProgress(QString,int,int)));
+ q->connect(job, &QGpgME::Job::progress, q, [this](const QString &, int processed, int total) {
+ q->setProgress(processed, total);
+ });
}
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::cancel()
{
}
void VerifyDetachedTask::doStart()
{
kleo_assert(d->m_backend);
try {
QGpgME::VerifyDetachedJob *const job = d->m_backend->verifyDetachedJob();
kleo_assert(job);
d->registerJob(job);
ensureIOOpen(d->m_input->ioDevice().get(), nullptr);
ensureIOOpen(d->m_signedData->ioDevice().get(), nullptr);
job->start(d->m_input->ioDevice(), d->m_signedData->ioDevice());
} 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/encryptemailtask.cpp b/src/crypto/encryptemailtask.cpp
index 28e6f7870..a014c915f 100644
--- a/src/crypto/encryptemailtask.cpp
+++ b/src/crypto/encryptemailtask.cpp
@@ -1,232 +1,233 @@
/* -*- mode: c++; c-basic-offset:4 -*-
crypto/encryptemailtask.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 "encryptemailtask.h"
#include <utils/input.h>
#include <utils/output.h>
#include <utils/kleo_assert.h>
#include <Libkleo/AuditLogEntry>
#include <Libkleo/Stl_Util>
#include <QGpgME/Protocol>
#include <QGpgME/EncryptJob>
#include <gpgme++/encryptionresult.h>
#include <gpgme++/key.h>
#include <KLocalizedString>
#include <QPointer>
#include <QTextDocument> // for Qt::escape
using namespace Kleo;
using namespace Kleo::Crypto;
using namespace GpgME;
namespace
{
class EncryptEMailResult : public Task::Result
{
const EncryptionResult m_result;
const AuditLogEntry m_auditLog;
public:
EncryptEMailResult(const EncryptionResult &r, const AuditLogEntry &auditLog)
: Task::Result(), m_result(r), m_auditLog(auditLog) {}
QString overview() const override;
QString details() const override;
GpgME::Error error() const override;
QString errorString() const override;
VisualCode code() const override;
AuditLogEntry auditLog() const override;
};
QString makeResultString(const EncryptionResult &res)
{
const Error err = res.error();
if (err.isCanceled()) {
return i18n("Encryption canceled.");
}
if (err) {
return i18n("Encryption failed: %1", QString::fromLocal8Bit(err.asString()).toHtmlEscaped());
}
return i18n("Encryption succeeded.");
}
}
class EncryptEMailTask::Private
{
friend class ::Kleo::Crypto::EncryptEMailTask;
EncryptEMailTask *const q;
public:
explicit Private(EncryptEMailTask *qq);
private:
std::unique_ptr<QGpgME::EncryptJob> createJob(GpgME::Protocol proto);
private:
void slotResult(const EncryptionResult &);
private:
std::shared_ptr<Input> input;
std::shared_ptr<Output> output;
std::vector<Key> recipients;
QPointer<QGpgME::EncryptJob> job;
};
EncryptEMailTask::Private::Private(EncryptEMailTask *qq)
: q(qq),
input(),
output(),
job(nullptr)
{
}
EncryptEMailTask::EncryptEMailTask(QObject *p)
: Task(p), d(new Private(this))
{
}
EncryptEMailTask::~EncryptEMailTask() {}
void EncryptEMailTask::setInput(const std::shared_ptr<Input> &input)
{
kleo_assert(!d->job);
kleo_assert(input);
d->input = input;
}
void EncryptEMailTask::setOutput(const std::shared_ptr<Output> &output)
{
kleo_assert(!d->job);
kleo_assert(output);
d->output = output;
}
void EncryptEMailTask::setRecipients(const std::vector<Key> &recipients)
{
kleo_assert(!d->job);
kleo_assert(!recipients.empty());
d->recipients = recipients;
}
Protocol EncryptEMailTask::protocol() const
{
kleo_assert(!d->recipients.empty());
return d->recipients.front().protocol();
}
QString EncryptEMailTask::label() const
{
return d->input ? d->input->label() : QString();
}
unsigned long long EncryptEMailTask::inputSize() const
{
return d->input ? d->input->size() : 0;
}
void EncryptEMailTask::doStart()
{
kleo_assert(!d->job);
kleo_assert(d->input);
kleo_assert(d->output);
kleo_assert(!d->recipients.empty());
std::unique_ptr<QGpgME::EncryptJob> job = d->createJob(protocol());
kleo_assert(job.get());
job->start(d->recipients,
d->input->ioDevice(), d->output->ioDevice(),
/*alwaysTrust=*/true);
d->job = job.release();
}
void EncryptEMailTask::cancel()
{
if (d->job) {
d->job->slotCancel();
}
}
std::unique_ptr<QGpgME::EncryptJob> EncryptEMailTask::Private::createJob(GpgME::Protocol proto)
{
const QGpgME::Protocol *const backend = (proto == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime();
kleo_assert(backend);
bool shouldArmor = (proto == OpenPGP || q->asciiArmor()) && !output->binaryOpt();
std::unique_ptr<QGpgME::EncryptJob> encryptJob(backend->encryptJob(shouldArmor, /*textmode=*/false));
kleo_assert(encryptJob.get());
if (proto == CMS && !q->asciiArmor() && !output->binaryOpt()) {
encryptJob->setOutputIsBase64Encoded(true);
}
- connect(encryptJob.get(), SIGNAL(progress(QString,int,int)),
- q, SLOT(setProgress(QString,int,int)));
+ connect(encryptJob.get(), &QGpgME::Job::progress, q, [this](const QString &, int processed, int total) {
+ q->setProgress(processed, total);
+ });
connect(encryptJob.get(), SIGNAL(result(GpgME::EncryptionResult,QByteArray)),
q, SLOT(slotResult(GpgME::EncryptionResult)));
return encryptJob;
}
void EncryptEMailTask::Private::slotResult(const EncryptionResult &result)
{
const auto *const job = qobject_cast<const QGpgME::Job *>(q->sender());
if (result.error().code()) {
output->cancel();
} else {
output->finalize();
}
q->emitResult(std::shared_ptr<Result>(new EncryptEMailResult(result, AuditLogEntry::fromJob(job))));
}
QString EncryptEMailResult::overview() const
{
return makeOverview(makeResultString(m_result));
}
QString EncryptEMailResult::details() const
{
return QString();
}
GpgME::Error EncryptEMailResult::error() const
{
return m_result.error();
}
QString EncryptEMailResult::errorString() const
{
return hasError() ? makeResultString(m_result) : QString();
}
AuditLogEntry EncryptEMailResult::auditLog() const
{
return m_auditLog;
}
Task::Result::VisualCode EncryptEMailResult::code() const
{
if (m_result.error().isCanceled()) {
return Warning;
}
return m_result.error().code() ? NeutralError : NeutralSuccess;
}
#include "moc_encryptemailtask.cpp"
diff --git a/src/crypto/gui/decryptverifyfilesdialog.cpp b/src/crypto/gui/decryptverifyfilesdialog.cpp
index 7df1a1fee..544f9d6d7 100644
--- a/src/crypto/gui/decryptverifyfilesdialog.cpp
+++ b/src/crypto/gui/decryptverifyfilesdialog.cpp
@@ -1,243 +1,242 @@
/* crypto/gui/decryptverifyfilesdialog.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB
SPDX-FileCopyrightText: 2016 Bundesamt für Sicherheit in der Informationstechnik
SPDX-FileContributor: Intevation GmbH
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "decryptverifyfilesdialog.h"
#include "kleopatra_debug.h"
#include "crypto/taskcollection.h"
#include "crypto/decryptverifytask.h"
#include "crypto/gui/resultpage.h"
#include "crypto/gui/resultlistwidget.h"
#include <Libkleo/FileNameRequester>
#include <QWindow>
#include <QVBoxLayout>
#include <QProgressBar>
#include <QLabel>
#include <QPushButton>
#include <vector>
#include <KLocalizedString>
#include <KMessageBox>
#include <KConfigGroup>
#include <KSharedConfig>
#include <KWindowConfig>
using namespace Kleo;
using namespace Kleo::Crypto;
using namespace Kleo::Crypto::Gui;
DecryptVerifyFilesDialog::DecryptVerifyFilesDialog(const std::shared_ptr<TaskCollection> &coll, QWidget *parent)
: QDialog(parent), m_tasks(coll), m_buttonBox(new QDialogButtonBox)
{
readConfig();
auto vLay = new QVBoxLayout(this);
auto labels = new QWidget;
auto outputLayout = new QHBoxLayout;
m_outputLocationFNR = new FileNameRequester;
auto outLabel = new QLabel(i18n("&Output folder:"));
outLabel->setBuddy(m_outputLocationFNR);
outputLayout->addWidget(outLabel);
outputLayout->addWidget(m_outputLocationFNR);
m_outputLocationFNR->setFilter(QDir::Dirs);
vLay->addLayout(outputLayout);
m_progressLabelLayout = new QVBoxLayout(labels);
vLay->addWidget(labels);
m_progressBar = new QProgressBar;
vLay->addWidget(m_progressBar);
m_resultList = new ResultListWidget;
vLay->addWidget(m_resultList);
m_tasks = coll;
Q_ASSERT(m_tasks);
m_resultList->setTaskCollection(coll);
connect(m_tasks.get(), &TaskCollection::progress, this, &DecryptVerifyFilesDialog::progress);
connect(m_tasks.get(), &TaskCollection::done, this, &DecryptVerifyFilesDialog::allDone);
connect(m_tasks.get(), &TaskCollection::started, this, &DecryptVerifyFilesDialog::started);
connect(m_buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
connect(m_buttonBox, &QDialogButtonBox::clicked, this, &DecryptVerifyFilesDialog::btnClicked);
layout()->addWidget(m_buttonBox);
bool hasOutputs = false;
for (const auto &t: coll->tasks()) {
if (!qobject_cast<VerifyDetachedTask *>(t.get())) {
hasOutputs = true;
break;
}
}
if (hasOutputs) {
setWindowTitle(i18nc("@title:window", "Decrypt/Verify Files"));
m_saveButton = QDialogButtonBox::SaveAll;
m_buttonBox->addButton(QDialogButtonBox::Discard);
connect(m_buttonBox, &QDialogButtonBox::accepted, this, &DecryptVerifyFilesDialog::checkAccept);
} else {
outLabel->setVisible(false);
m_outputLocationFNR->setVisible(false);
setWindowTitle(i18nc("@title:window", "Verify Files"));
m_buttonBox->addButton(QDialogButtonBox::Close);
connect(m_buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
}
if (m_saveButton) {
m_buttonBox->addButton(m_saveButton);
m_buttonBox->button(m_saveButton)->setEnabled(false);
}
}
DecryptVerifyFilesDialog::~DecryptVerifyFilesDialog()
{
qCDebug(KLEOPATRA_LOG);
writeConfig();
}
void DecryptVerifyFilesDialog::allDone()
{
qCDebug(KLEOPATRA_LOG) << "All done";
Q_ASSERT(m_tasks);
m_progressBar->setRange(0, 100);
m_progressBar->setValue(100);
for (const auto &i: m_progressLabelByTag.keys()) {
if (!i.isEmpty()) {
m_progressLabelByTag.value(i)->setText(i18n("%1: All operations completed.", i));
} else {
m_progressLabelByTag.value(i)->setText(i18n("All operations completed."));
}
}
if (m_tasks->allTasksHaveErrors()) {
return;
}
if (m_saveButton != QDialogButtonBox::NoButton) {
m_buttonBox->button(m_saveButton)->setEnabled(true);
} else {
m_buttonBox->removeButton(m_buttonBox->button(QDialogButtonBox::Close));
m_buttonBox->addButton(QDialogButtonBox::Ok);
}
}
void DecryptVerifyFilesDialog::started(const std::shared_ptr<Task> &task)
{
Q_ASSERT(task);
const auto tag = task->tag();
auto label = labelForTag(tag);
Q_ASSERT(label);
if (tag.isEmpty()) {
label->setText(i18nc("number, operation description", "Operation %1: %2", m_tasks->numberOfCompletedTasks() + 1, task->label()));
} else {
label->setText(i18nc(R"(tag( "OpenPGP" or "CMS"), operation description)", "%1: %2", tag, task->label()));
}
if (m_saveButton != QDialogButtonBox::NoButton) {
m_buttonBox->button(m_saveButton)->setEnabled(false);
} else if (m_buttonBox->button(QDialogButtonBox::Ok)) {
m_buttonBox->removeButton(m_buttonBox->button(QDialogButtonBox::Ok));
m_buttonBox->addButton(QDialogButtonBox::Close);
}
}
QLabel *DecryptVerifyFilesDialog::labelForTag(const QString &tag)
{
if (QLabel *const label = m_progressLabelByTag.value(tag)) {
return label;
}
auto label = new QLabel;
label->setTextFormat(Qt::RichText);
label->setWordWrap(true);
m_progressLabelLayout->addWidget(label);
m_progressLabelByTag.insert(tag, label);
return label;
}
-void DecryptVerifyFilesDialog::progress(const QString &msg, int progress, int total)
+void DecryptVerifyFilesDialog::progress(int progress, int total)
{
- Q_UNUSED(msg)
Q_ASSERT(progress >= 0);
Q_ASSERT(total >= 0);
m_progressBar->setRange(0, total);
m_progressBar->setValue(progress);
}
void DecryptVerifyFilesDialog::setOutputLocation(const QString &dir)
{
m_outputLocationFNR->setFileName(dir);
}
QString DecryptVerifyFilesDialog::outputLocation() const
{
return m_outputLocationFNR->fileName();
}
void DecryptVerifyFilesDialog::btnClicked(QAbstractButton *btn)
{
if (m_buttonBox->buttonRole(btn) == QDialogButtonBox::DestructiveRole) {
close();
}
}
void DecryptVerifyFilesDialog::checkAccept() {
const auto outLoc = outputLocation();
if (outLoc.isEmpty()) {
KMessageBox::information(this, i18n("Please select an output folder."),
i18n("No output folder."));
return;
}
const QFileInfo fi(outLoc);
if (fi.exists() && fi.isDir() && fi.isWritable()) {
accept();
return;
}
if (!fi.exists()) {
qCDebug(KLEOPATRA_LOG) << "Output dir does not exist. Trying to create.";
const QDir dir(outLoc);
if (!dir.mkdir(outLoc)) {
KMessageBox::information(this, i18n("Please select a different output folder."),
i18n("Failed to create output folder."));
return;
} else {
accept();
return;
}
}
KMessageBox::information(this, i18n("Please select a different output folder."),
i18n("Invalid output folder."));
}
void DecryptVerifyFilesDialog::readConfig()
{
winId(); // ensure there's a window created
// set default window size
windowHandle()->resize(640, 480);
// restore size from config file
KConfigGroup cfgGroup(KSharedConfig::openStateConfig(), "DecryptVerifyFilesDialog");
KWindowConfig::restoreWindowSize(windowHandle(), cfgGroup);
// NOTICE: QWindow::setGeometry() does NOT impact the backing QWidget geometry even if the platform
// window was created -> QTBUG-40584. We therefore copy the size here.
// TODO: remove once this was resolved in QWidget QPA
resize(windowHandle()->size());
}
void DecryptVerifyFilesDialog::writeConfig()
{
KConfigGroup cfgGroup(KSharedConfig::openStateConfig(), "DecryptVerifyFilesDialog");
KWindowConfig::saveWindowSize(windowHandle(), cfgGroup);
cfgGroup.sync();
}
diff --git a/src/crypto/gui/decryptverifyfilesdialog.h b/src/crypto/gui/decryptverifyfilesdialog.h
index 457d0fc0b..c752e3164 100644
--- a/src/crypto/gui/decryptverifyfilesdialog.h
+++ b/src/crypto/gui/decryptverifyfilesdialog.h
@@ -1,76 +1,76 @@
/* crypto/gui/decryptverifyfilesdialog.h
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB
SPDX-FileCopyrightText: 2016 Bundesamt für Sicherheit in der Informationstechnik
SPDX-FileContributor: Intevation GmbH
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include <QDialog>
#include <QString>
#include <QDialogButtonBox>
#include <QHash>
#include "crypto/task.h"
#include <memory>
class QVBoxLayout;
class QProgressBar;
template <typename K, typename U> class QHash;
class QLabel;
namespace Kleo
{
class FileNameRequester;
namespace Crypto
{
class TaskCollection;
namespace Gui
{
class ResultListWidget;
class DecryptVerifyFilesDialog : public QDialog
{
Q_OBJECT
public:
explicit DecryptVerifyFilesDialog(const std::shared_ptr<TaskCollection> &coll,
QWidget *parent = nullptr);
~DecryptVerifyFilesDialog() override;
void setOutputLocation(const QString &dir);
QString outputLocation() const;
protected Q_SLOTS:
- void progress(const QString &msg, int progress, int total);
+ void progress(int progress, int total);
void started(const std::shared_ptr<Task> &result);
void allDone();
void btnClicked(QAbstractButton *btn);
void checkAccept();
protected:
void readConfig();
void writeConfig();
protected:
QLabel *labelForTag(const QString &tag);
private:
std::shared_ptr<TaskCollection> m_tasks;
QProgressBar *m_progressBar;
QHash<QString, QLabel *> m_progressLabelByTag;
QVBoxLayout *m_progressLabelLayout;
int m_lastErrorItemIndex;
ResultListWidget *m_resultList;
FileNameRequester *m_outputLocationFNR;
QDialogButtonBox::StandardButton m_saveButton = QDialogButtonBox::NoButton;
QDialogButtonBox *const m_buttonBox;
};
} // namespace Gui
} //namespace Crypto;
} // namespace Kleo
diff --git a/src/crypto/gui/newresultpage.cpp b/src/crypto/gui/newresultpage.cpp
index 478eb8ce3..9c73fbae3 100644
--- a/src/crypto/gui/newresultpage.cpp
+++ b/src/crypto/gui/newresultpage.cpp
@@ -1,186 +1,186 @@
/* -*- mode: c++; c-basic-offset:4 -*-
crypto/gui/resultpage.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 "newresultpage.h"
#include "resultlistwidget.h"
#include "resultitemwidget.h"
#include <crypto/taskcollection.h>
#include <Libkleo/Stl_Util>
#include <KLocalizedString>
#include <QCheckBox>
#include <QHash>
#include <QLabel>
#include <QProgressBar>
#include <QVBoxLayout>
#include <QTimer>
static const int ProgressBarHideDelay = 2000; // 2 secs
using namespace Kleo;
using namespace Kleo::Crypto;
using namespace Kleo::Crypto::Gui;
class NewResultPage::Private
{
NewResultPage *const q;
public:
explicit Private(NewResultPage *qq);
- void progress(const QString &msg, int progress, int total);
+ void progress(int progress, int total);
void result(const std::shared_ptr<const Task::Result> &result);
void started(const std::shared_ptr<Task> &result);
void allDone();
QLabel *labelForTag(const QString &tag);
std::vector< std::shared_ptr<TaskCollection> > m_collections;
QTimer m_hideProgressTimer;
QProgressBar *m_progressBar;
QHash<QString, QLabel *> m_progressLabelByTag;
QVBoxLayout *m_progressLabelLayout;
int m_lastErrorItemIndex;
ResultListWidget *m_resultList;
};
NewResultPage::Private::Private(NewResultPage *qq) : q(qq), m_lastErrorItemIndex(0)
{
m_hideProgressTimer.setInterval(ProgressBarHideDelay);
m_hideProgressTimer.setSingleShot(true);
QBoxLayout *const layout = new QVBoxLayout(q);
auto const labels = new QWidget;
m_progressLabelLayout = new QVBoxLayout(labels);
layout->addWidget(labels);
m_progressBar = new QProgressBar;
layout->addWidget(m_progressBar);
m_resultList = new ResultListWidget;
connect(m_resultList, &ResultListWidget::linkActivated, q, &NewResultPage::linkActivated);
layout->addWidget(m_resultList, 1);
connect(&m_hideProgressTimer, &QTimer::timeout, m_progressBar, &QProgressBar::hide);
}
-void NewResultPage::Private::progress(const QString &msg, int progress, int total)
+void NewResultPage::Private::progress(int progress, int total)
{
- Q_UNUSED(msg)
Q_ASSERT(progress >= 0);
Q_ASSERT(total >= 0);
m_progressBar->setRange(0, total);
m_progressBar->setValue(progress);
}
void NewResultPage::Private::allDone()
{
Q_ASSERT(!m_collections.empty());
if (!m_resultList->isComplete()) {
return;
}
m_progressBar->setRange(0, 100);
m_progressBar->setValue(100);
m_collections.clear();
const auto progressLabelByTagKeys{m_progressLabelByTag.keys()};
for (const QString &i : progressLabelByTagKeys) {
if (!i.isEmpty()) {
m_progressLabelByTag.value(i)->setText(i18n("%1: All operations completed.", i));
} else {
m_progressLabelByTag.value(i)->setText(i18n("All operations completed."));
}
}
if (QAbstractButton *cancel = q->wizard()->button(QWizard::CancelButton)) {
cancel->setEnabled(false);
}
Q_EMIT q->completeChanged();
m_hideProgressTimer.start();
}
void NewResultPage::Private::result(const std::shared_ptr<const Task::Result> &)
{
}
void NewResultPage::Private::started(const std::shared_ptr<Task> &task)
{
Q_ASSERT(task);
const QString tag = task->tag();
QLabel *const label = labelForTag(tag);
Q_ASSERT(label);
if (tag.isEmpty()) {
label->setText(i18nc("number, operation description", "Operation %1: %2", m_resultList->numberOfCompletedTasks() + 1, task->label()));
} else {
label->setText(i18nc(R"(tag( "OpenPGP" or "CMS"), operation description)", "%1: %2", tag, task->label()));
}
}
NewResultPage::NewResultPage(QWidget *parent) : QWizardPage(parent), d(new Private(this))
{
setTitle(i18n("<b>Results</b>"));
}
NewResultPage::~NewResultPage()
{
}
void NewResultPage::setTaskCollection(const std::shared_ptr<TaskCollection> &coll)
{
//clear(); ### PENDING(marc) implement
addTaskCollection(coll);
}
void NewResultPage::addTaskCollection(const std::shared_ptr<TaskCollection> &coll)
{
Q_ASSERT(coll);
if (std::find(d->m_collections.cbegin(), d->m_collections.cend(), coll) != d->m_collections.cend()) {
return;
}
d->m_hideProgressTimer.stop();
d->m_progressBar->show();
d->m_collections.push_back(coll);
d->m_resultList->addTaskCollection(coll);
- connect(coll.get(), SIGNAL(progress(QString,int,int)),
- this, SLOT(progress(QString,int,int)));
+ connect(coll.get(), &TaskCollection::progress, this, [this](int current, int total) {
+ d->progress(current, total);
+ });
connect(coll.get(), SIGNAL(done()),
this, SLOT(allDone()));
connect(coll.get(), SIGNAL(result(std::shared_ptr<const Kleo::Crypto::Task::Result>)),
this, SLOT(result(std::shared_ptr<const Kleo::Crypto::Task::Result>)));
connect(coll.get(), SIGNAL(started(std::shared_ptr<Kleo::Crypto::Task>)),
this, SLOT(started(std::shared_ptr<Kleo::Crypto::Task>)));
for (const std::shared_ptr<Task> &i : coll->tasks()) { // create labels for all tags in collection
Q_ASSERT(i);
QLabel *l = d->labelForTag(i->tag());
Q_ASSERT(l); (void)l;
}
Q_EMIT completeChanged();
}
QLabel *NewResultPage::Private::labelForTag(const QString &tag)
{
if (QLabel *const label = m_progressLabelByTag.value(tag)) {
return label;
}
auto label = new QLabel;
label->setTextFormat(Qt::RichText);
label->setWordWrap(true);
m_progressLabelLayout->addWidget(label);
m_progressLabelByTag.insert(tag, label);
return label;
}
bool NewResultPage::isComplete() const
{
return d->m_resultList->isComplete();
}
#include "moc_newresultpage.cpp"
diff --git a/src/crypto/gui/newresultpage.h b/src/crypto/gui/newresultpage.h
index 236c1723d..75591c3f7 100644
--- a/src/crypto/gui/newresultpage.h
+++ b/src/crypto/gui/newresultpage.h
@@ -1,61 +1,60 @@
/* -*- mode: c++; c-basic-offset:4 -*-
crypto/gui/newresultpage.h
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2008, 2009 Klarälvdalens Datakonsult AB
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include <QWizardPage>
#include <utils/pimpl_ptr.h>
#include <memory>
namespace Kleo
{
namespace Crypto
{
class TaskCollection;
class Task;
}
}
namespace Kleo
{
namespace Crypto
{
namespace Gui
{
class NewResultPage : public QWizardPage
{
Q_OBJECT
public:
explicit NewResultPage(QWidget *parent = nullptr);
~NewResultPage() override;
void setTaskCollection(const std::shared_ptr<TaskCollection> &coll);
void addTaskCollection(const std::shared_ptr<TaskCollection> &coll);
bool isComplete() const override;
Q_SIGNALS:
void linkActivated(const QString &link);
private:
class Private;
kdtools::pimpl_ptr<Private> d;
- Q_PRIVATE_SLOT(d, void progress(QString, int, int))
Q_PRIVATE_SLOT(d, void result(std::shared_ptr<const Kleo::Crypto::Task::Result>))
Q_PRIVATE_SLOT(d, void started(std::shared_ptr<Kleo::Crypto::Task>))
Q_PRIVATE_SLOT(d, void allDone())
};
}
}
}
diff --git a/src/crypto/gui/resultpage.cpp b/src/crypto/gui/resultpage.cpp
index 5ee5718f3..b69c6e45f 100644
--- a/src/crypto/gui/resultpage.cpp
+++ b/src/crypto/gui/resultpage.cpp
@@ -1,176 +1,176 @@
/* -*- mode: c++; c-basic-offset:4 -*-
crypto/gui/resultpage.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 "resultpage.h"
#include "resultlistwidget.h"
#include "resultitemwidget.h"
#include <crypto/taskcollection.h>
#include <utils/scrollarea.h>
#include <KLocalizedString>
#include <QCheckBox>
#include <QHash>
#include <QLabel>
#include <QProgressBar>
#include <QVBoxLayout>
using namespace Kleo;
using namespace Kleo::Crypto;
using namespace Kleo::Crypto::Gui;
class ResultPage::Private
{
ResultPage *const q;
public:
explicit Private(ResultPage *qq);
- void progress(const QString &msg, int progress, int total);
+ void progress(int progress, int total);
void result(const std::shared_ptr<const Task::Result> &result);
void started(const std::shared_ptr<Task> &result);
void allDone();
QLabel *labelForTag(const QString &tag);
std::shared_ptr<TaskCollection> m_tasks;
QProgressBar *m_progressBar;
QHash<QString, QLabel *> m_progressLabelByTag;
QVBoxLayout *m_progressLabelLayout;
int m_lastErrorItemIndex = 0;
ResultListWidget *m_resultList;
QCheckBox *m_keepOpenCB;
};
ResultPage::Private::Private(ResultPage *qq) : q(qq)
{
QBoxLayout *const layout = new QVBoxLayout(q);
auto const labels = new QWidget;
m_progressLabelLayout = new QVBoxLayout(labels);
layout->addWidget(labels);
m_progressBar = new QProgressBar;
layout->addWidget(m_progressBar);
m_resultList = new ResultListWidget;
layout->addWidget(m_resultList);
m_keepOpenCB = new QCheckBox;
m_keepOpenCB->setText(i18n("Keep open after operation completed"));
m_keepOpenCB->setChecked(true);
layout->addWidget(m_keepOpenCB);
}
-void ResultPage::Private::progress(const QString &msg, int progress, int total)
+void ResultPage::Private::progress(int progress, int total)
{
- Q_UNUSED(msg)
Q_ASSERT(progress >= 0);
Q_ASSERT(total >= 0);
m_progressBar->setRange(0, total);
m_progressBar->setValue(progress);
}
void ResultPage::Private::allDone()
{
Q_ASSERT(m_tasks);
q->setAutoAdvance(!m_keepOpenCB->isChecked() && !m_tasks->errorOccurred());
m_progressBar->setRange(0, 100);
m_progressBar->setValue(100);
m_tasks.reset();
const auto progressLabelByTagKeys{m_progressLabelByTag.keys()};
for (const QString &i : progressLabelByTagKeys) {
if (!i.isEmpty()) {
m_progressLabelByTag.value(i)->setText(i18n("%1: All operations completed.", i));
} else {
m_progressLabelByTag.value(i)->setText(i18n("All operations completed."));
}
}
Q_EMIT q->completeChanged();
}
void ResultPage::Private::result(const std::shared_ptr<const Task::Result> &)
{
}
void ResultPage::Private::started(const std::shared_ptr<Task> &task)
{
Q_ASSERT(task);
const QString tag = task->tag();
QLabel *const label = labelForTag(tag);
Q_ASSERT(label);
if (tag.isEmpty()) {
label->setText(i18nc("number, operation description", "Operation %1: %2", m_tasks->numberOfCompletedTasks() + 1, task->label()));
} else {
label->setText(i18nc(R"(tag( "OpenPGP" or "CMS"), operation description)", "%1: %2", tag, task->label()));
}
}
ResultPage::ResultPage(QWidget *parent, Qt::WindowFlags flags) : WizardPage(parent, flags), d(new Private(this))
{
setTitle(i18n("<b>Results</b>"));
}
ResultPage::~ResultPage()
{
}
bool ResultPage::keepOpenWhenDone() const
{
return d->m_keepOpenCB->isChecked();
}
void ResultPage::setKeepOpenWhenDone(bool keep)
{
d->m_keepOpenCB->setChecked(keep);
}
void ResultPage::setTaskCollection(const std::shared_ptr<TaskCollection> &coll)
{
Q_ASSERT(!d->m_tasks);
if (d->m_tasks == coll) {
return;
}
d->m_tasks = coll;
Q_ASSERT(d->m_tasks);
d->m_resultList->setTaskCollection(coll);
- connect(d->m_tasks.get(), SIGNAL(progress(QString,int,int)),
- this, SLOT(progress(QString,int,int)));
+ connect(d->m_tasks.get(), &TaskCollection::progress, this, [this](int current, int total) {
+ d->progress(current, total);
+ });
connect(d->m_tasks.get(), SIGNAL(done()),
this, SLOT(allDone()));
connect(d->m_tasks.get(), SIGNAL(result(std::shared_ptr<const Kleo::Crypto::Task::Result>)),
this, SLOT(result(std::shared_ptr<const Kleo::Crypto::Task::Result>)));
connect(d->m_tasks.get(), SIGNAL(started(std::shared_ptr<Kleo::Crypto::Task>)),
this, SLOT(started(std::shared_ptr<Kleo::Crypto::Task>)));
for (const std::shared_ptr<Task> &i : d->m_tasks->tasks()) { // create labels for all tags in collection
Q_ASSERT(i && d->labelForTag(i->tag()));
Q_UNUSED(i)
}
Q_EMIT completeChanged();
}
QLabel *ResultPage::Private::labelForTag(const QString &tag)
{
if (QLabel *const label = m_progressLabelByTag.value(tag)) {
return label;
}
auto label = new QLabel;
label->setTextFormat(Qt::RichText);
label->setWordWrap(true);
m_progressLabelLayout->addWidget(label);
m_progressLabelByTag.insert(tag, label);
return label;
}
bool ResultPage::isComplete() const
{
return d->m_tasks ? d->m_tasks->allTasksCompleted() : true;
}
#include "moc_resultpage.cpp"
diff --git a/src/crypto/gui/resultpage.h b/src/crypto/gui/resultpage.h
index 63d0c89e0..751f3830e 100644
--- a/src/crypto/gui/resultpage.h
+++ b/src/crypto/gui/resultpage.h
@@ -1,56 +1,55 @@
/* -*- mode: c++; c-basic-offset:4 -*-
crypto/gui/resultpage.h
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include <crypto/gui/wizardpage.h>
#include <crypto/task.h>
#include <utils/pimpl_ptr.h>
#include <memory>
namespace Kleo
{
namespace Crypto
{
class TaskCollection;
namespace Gui
{
class ResultPage : public WizardPage
{
Q_OBJECT
public:
explicit ResultPage(QWidget *parent = nullptr, Qt::WindowFlags flags = {});
~ResultPage() override;
void setTaskCollection(const std::shared_ptr<TaskCollection> &coll);
bool isComplete() const override;
bool keepOpenWhenDone() const;
void setKeepOpenWhenDone(bool keep);
private:
class Private;
kdtools::pimpl_ptr<Private> d;
- Q_PRIVATE_SLOT(d, void progress(QString, int, int))
Q_PRIVATE_SLOT(d, void result(std::shared_ptr<const Kleo::Crypto::Task::Result>))
Q_PRIVATE_SLOT(d, void started(std::shared_ptr<Kleo::Crypto::Task>))
Q_PRIVATE_SLOT(d, void allDone())
};
}
}
}
diff --git a/src/crypto/signemailtask.cpp b/src/crypto/signemailtask.cpp
index f6ce40c09..5a7fed171 100644
--- a/src/crypto/signemailtask.cpp
+++ b/src/crypto/signemailtask.cpp
@@ -1,284 +1,285 @@
/* -*- mode: c++; c-basic-offset:4 -*-
crypto/signemailtask.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 "signemailtask.h"
#include <utils/input.h>
#include <utils/output.h>
#include <utils/kleo_assert.h>
#include <Libkleo/AuditLogEntry>
#include <Libkleo/Stl_Util>
#include <QGpgME/Protocol>
#include <QGpgME/SignJob>
#include <gpgme++/signingresult.h>
#include <gpgme++/key.h>
#include <KLocalizedString>
#include <QPointer>
#include <QTextDocument> // for Qt::escape
#include <algorithm>
#include <functional>
using namespace Kleo;
using namespace Kleo::Crypto;
using namespace GpgME;
namespace
{
class SignEMailResult : public Task::Result
{
const SigningResult m_result;
const AuditLogEntry m_auditLog;
public:
explicit SignEMailResult(const SigningResult &r, const AuditLogEntry &auditLog)
: Task::Result(), m_result(r), m_auditLog(auditLog) {}
QString overview() const override;
QString details() const override;
GpgME::Error error() const override;
QString errorString() const override;
VisualCode code() const override;
AuditLogEntry auditLog() const override;
};
QString makeResultString(const SigningResult &res)
{
const Error err = res.error();
if (err.isCanceled()) {
return i18n("Signing canceled.");
}
if (err) {
return i18n("Signing failed: %1", QString::fromLocal8Bit(err.asString()).toHtmlEscaped());
}
return i18n("Signing succeeded.");
}
}
class SignEMailTask::Private
{
friend class ::Kleo::Crypto::SignEMailTask;
SignEMailTask *const q;
public:
explicit Private(SignEMailTask *qq);
private:
std::unique_ptr<QGpgME::SignJob> createJob(GpgME::Protocol proto);
private:
void slotResult(const SigningResult &);
private:
std::shared_ptr<Input> input;
std::shared_ptr<Output> output;
std::vector<Key> signers;
bool detached;
bool clearsign;
QString micAlg;
QPointer<QGpgME::SignJob> job;
};
SignEMailTask::Private::Private(SignEMailTask *qq)
: q(qq),
input(),
output(),
signers(),
detached(false),
clearsign(false),
micAlg(),
job(nullptr)
{
}
SignEMailTask::SignEMailTask(QObject *p)
: Task(p), d(new Private(this))
{
}
SignEMailTask::~SignEMailTask() {}
void SignEMailTask::setInput(const std::shared_ptr<Input> &input)
{
kleo_assert(!d->job);
kleo_assert(input);
d->input = input;
}
void SignEMailTask::setOutput(const std::shared_ptr<Output> &output)
{
kleo_assert(!d->job);
kleo_assert(output);
d->output = output;
}
void SignEMailTask::setSigners(const std::vector<Key> &signers)
{
kleo_assert(!d->job);
kleo_assert(!signers.empty());
kleo_assert(std::none_of(signers.cbegin(), signers.cend(), std::mem_fn(&Key::isNull)));
d->signers = signers;
}
void SignEMailTask::setDetachedSignature(bool detached)
{
kleo_assert(!d->job);
d->detached = detached;
d->clearsign = false;
}
void SignEMailTask::setClearsign(bool clear)
{
kleo_assert(!d->job);
d->clearsign = clear;
d->detached = false;
}
Protocol SignEMailTask::protocol() const
{
kleo_assert(!d->signers.empty());
return d->signers.front().protocol();
}
QString SignEMailTask::label() const
{
return d->input ? d->input->label() : QString();
}
unsigned long long SignEMailTask::inputSize() const
{
return d->input ? d->input->size() : 0;
}
void SignEMailTask::doStart()
{
kleo_assert(!d->job);
kleo_assert(d->input);
kleo_assert(d->output);
kleo_assert(!d->signers.empty());
d->micAlg.clear();
std::unique_ptr<QGpgME::SignJob> job = d->createJob(protocol());
kleo_assert(job.get());
job->start(d->signers,
d->input->ioDevice(), d->output->ioDevice(),
d->clearsign ? GpgME::Clearsigned : d->detached ? GpgME::Detached : GpgME::NormalSignatureMode);
d->job = job.release();
}
void SignEMailTask::cancel()
{
if (d->job) {
d->job->slotCancel();
}
}
std::unique_ptr<QGpgME::SignJob> SignEMailTask::Private::createJob(GpgME::Protocol proto)
{
const QGpgME::Protocol *const backend = (proto == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime();
kleo_assert(backend);
bool shouldArmor = (proto == OpenPGP || q->asciiArmor()) && !output->binaryOpt();
std::unique_ptr<QGpgME::SignJob> signJob(backend->signJob(/*armor=*/ shouldArmor, /*textmode=*/false));
kleo_assert(signJob.get());
if (proto == CMS && !q->asciiArmor() && !output->binaryOpt()) {
signJob->setOutputIsBase64Encoded(true);
}
- connect(signJob.get(), SIGNAL(progress(QString,int,int)),
- q, SLOT(setProgress(QString,int,int)));
+ connect(signJob.get(), &QGpgME::Job::progress, q, [this](const QString &, int processed, int total) {
+ q->setProgress(processed, total);
+ });
connect(signJob.get(), SIGNAL(result(GpgME::SigningResult,QByteArray)),
q, SLOT(slotResult(GpgME::SigningResult)));
return signJob;
}
static QString collect_micalgs(const GpgME::SigningResult &result, GpgME::Protocol proto)
{
const std::vector<GpgME::CreatedSignature> css = result.createdSignatures();
QStringList micalgs;
std::transform(css.begin(), css.end(),
std::back_inserter(micalgs),
[](const GpgME::CreatedSignature &sig) {
return QString::fromLatin1(sig.hashAlgorithmAsString()).toLower();
});
if (proto == GpgME::OpenPGP)
for (QStringList::iterator it = micalgs.begin(), end = micalgs.end(); it != end; ++it) {
it->prepend(QLatin1String("pgp-"));
}
micalgs.sort();
micalgs.erase(std::unique(micalgs.begin(), micalgs.end()), micalgs.end());
return micalgs.join(QLatin1Char(','));
}
void SignEMailTask::Private::slotResult(const SigningResult &result)
{
const auto *const job = qobject_cast<const QGpgME::Job *>(q->sender());
if (result.error().code()) {
output->cancel();
} else {
output->finalize();
micAlg = collect_micalgs(result, q->protocol());
}
q->emitResult(std::shared_ptr<Result>(new SignEMailResult(result, AuditLogEntry::fromJob(job))));
}
QString SignEMailTask::micAlg() const
{
return d->micAlg;
}
QString SignEMailResult::overview() const
{
return makeOverview(makeResultString(m_result));
}
QString SignEMailResult::details() const
{
return QString();
}
GpgME::Error SignEMailResult::error() const
{
return m_result.error();
}
QString SignEMailResult::errorString() const
{
return hasError() ? makeResultString(m_result) : QString();
}
Task::Result::VisualCode SignEMailResult::code() const
{
if (m_result.error().isCanceled()) {
return Warning;
}
return m_result.error().code() ? NeutralError : NeutralSuccess;
}
AuditLogEntry SignEMailResult::auditLog() const
{
return m_auditLog;
}
#include "moc_signemailtask.cpp"
diff --git a/src/crypto/signencrypttask.cpp b/src/crypto/signencrypttask.cpp
index ef47c7d15..444f7b5f0 100644
--- a/src/crypto/signencrypttask.cpp
+++ b/src/crypto/signencrypttask.cpp
@@ -1,797 +1,806 @@
/* -*- mode: c++; c-basic-offset:4 -*-
crypto/signencrypttask.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 "signencrypttask.h"
#include <utils/input.h>
#include <utils/output.h>
#include <utils/path-helper.h>
#include <utils/kleo_assert.h>
#include <Libkleo/AuditLogEntry>
#include <Libkleo/Formatting>
#include <Libkleo/Stl_Util>
#include <Libkleo/KleoException>
#include <QGpgME/Protocol>
#include <QGpgME/SignJob>
#include <QGpgME/SignEncryptJob>
#include <QGpgME/EncryptJob>
#if QGPGME_SUPPORTS_ARCHIVE_JOBS
#include <QGpgME/EncryptArchiveJob>
#include <QGpgME/SignArchiveJob>
#include <QGpgME/SignEncryptArchiveJob>
#endif
#include <gpgme++/signingresult.h>
#include <gpgme++/encryptionresult.h>
#include <gpgme++/key.h>
#include <KLocalizedString>
#include "kleopatra_debug.h"
#include <QFileInfo>
#include <QPointer>
#include <QTextDocument> // for Qt::escape
using namespace Kleo;
using namespace Kleo::Crypto;
using namespace GpgME;
namespace
{
QString formatInputOutputLabel(const QString &input, const QString &output, bool outputDeleted)
{
return i18nc("Input file --> Output file (rarr is arrow", "%1 &rarr; %2",
input.toHtmlEscaped(),
outputDeleted ? QStringLiteral("<s>%1</s>").arg(output.toHtmlEscaped()) : output.toHtmlEscaped());
}
class ErrorResult : public Task::Result
{
public:
ErrorResult(bool sign, bool encrypt, const Error &err, const QString &errStr, const QString &input, const QString &output, const AuditLogEntry &auditLog)
: Task::Result(), m_sign(sign), m_encrypt(encrypt), m_error(err), m_errString(errStr), m_inputLabel(input), m_outputLabel(output), m_auditLog(auditLog) {}
QString overview() const override;
QString details() const override;
GpgME::Error error() const override
{
return m_error;
}
QString errorString() const override
{
return m_errString;
}
VisualCode code() const override
{
return NeutralError;
}
AuditLogEntry auditLog() const override
{
return m_auditLog;
}
private:
const bool m_sign;
const bool m_encrypt;
const Error m_error;
const QString m_errString;
const QString m_inputLabel;
const QString m_outputLabel;
const AuditLogEntry m_auditLog;
};
namespace
{
struct LabelAndError
{
QString label;
QString errorString;
};
}
class SignEncryptFilesResult : public Task::Result
{
public:
SignEncryptFilesResult(const SigningResult &sr, const LabelAndError &input, const LabelAndError &output, bool outputCreated, const AuditLogEntry &auditLog)
: Task::Result(),
m_sresult(sr),
m_input{input},
m_output{output},
m_outputCreated(outputCreated),
m_auditLog(auditLog)
{
qCDebug(KLEOPATRA_LOG) << "\ninputError :" << m_input.errorString
<< "\noutputError:" << m_output.errorString;
Q_ASSERT(!m_sresult.isNull());
}
SignEncryptFilesResult(const EncryptionResult &er, const LabelAndError &input, const LabelAndError &output, bool outputCreated, const AuditLogEntry &auditLog)
: Task::Result(),
m_eresult(er),
m_input{input},
m_output{output},
m_outputCreated(outputCreated),
m_auditLog(auditLog)
{
qCDebug(KLEOPATRA_LOG) << "\ninputError :" << m_input.errorString
<< "\noutputError:" << m_output.errorString;
Q_ASSERT(!m_eresult.isNull());
}
SignEncryptFilesResult(const SigningResult &sr, const EncryptionResult &er, const LabelAndError &input, const LabelAndError &output, bool outputCreated, const AuditLogEntry &auditLog)
: Task::Result(),
m_sresult(sr),
m_eresult(er),
m_input{input},
m_output{output},
m_outputCreated(outputCreated),
m_auditLog(auditLog)
{
qCDebug(KLEOPATRA_LOG) << "\ninputError :" << m_input.errorString
<< "\noutputError:" << m_output.errorString;
Q_ASSERT(!m_sresult.isNull() || !m_eresult.isNull());
}
QString overview() const override;
QString details() const override;
GpgME::Error error() const override;
QString errorString() const override;
VisualCode code() const override;
AuditLogEntry auditLog() const override;
private:
const SigningResult m_sresult;
const EncryptionResult m_eresult;
const LabelAndError m_input;
const LabelAndError m_output;
const bool m_outputCreated;
const AuditLogEntry m_auditLog;
};
static QString makeSigningOverview(const Error &err)
{
if (err.isCanceled()) {
return i18n("Signing canceled.");
}
if (err) {
return i18n("Signing failed.");
}
return i18n("Signing succeeded.");
}
static QString makeResultOverview(const SigningResult &result)
{
return makeSigningOverview(result.error());
}
static QString makeEncryptionOverview(const Error &err)
{
if (err.isCanceled()) {
return i18n("Encryption canceled.");
}
if (err) {
return i18n("Encryption failed.");
}
return i18n("Encryption succeeded.");
}
static QString makeResultOverview(const EncryptionResult &result)
{
return makeEncryptionOverview(result.error());
}
static QString makeResultOverview(const SigningResult &sr, const EncryptionResult &er)
{
if (er.isNull() && sr.isNull()) {
return QString();
}
if (er.isNull()) {
return makeResultOverview(sr);
}
if (sr.isNull()) {
return makeResultOverview(er);
}
if (sr.error().isCanceled() || sr.error()) {
return makeResultOverview(sr);
}
if (er.error().isCanceled() || er.error()) {
return makeResultOverview(er);
}
return i18n("Signing and encryption succeeded.");
}
static QString escape(QString s)
{
s = s.toHtmlEscaped();
s.replace(QLatin1Char('\n'), QStringLiteral("<br>"));
return s;
}
static QString makeResultDetails(const SigningResult &result, const QString &inputError, const QString &outputError)
{
const Error err = result.error();
if (err.code() == GPG_ERR_EIO) {
if (!inputError.isEmpty()) {
return i18n("Input error: %1", escape(inputError));
} else if (!outputError.isEmpty()) {
return i18n("Output error: %1", escape(outputError));
}
}
if (err || err.isCanceled()) {
return QString::fromLocal8Bit(err.asString()).toHtmlEscaped();
}
return QString();
}
static QString makeResultDetails(const EncryptionResult &result, const QString &inputError, const QString &outputError)
{
const Error err = result.error();
if (err.code() == GPG_ERR_EIO) {
if (!inputError.isEmpty()) {
return i18n("Input error: %1", escape(inputError));
} else if (!outputError.isEmpty()) {
return i18n("Output error: %1", escape(outputError));
}
}
if (err || err.isCanceled()) {
return QString::fromLocal8Bit(err.asString()).toHtmlEscaped();
}
return i18n(" Encryption succeeded.");
}
}
QString ErrorResult::overview() const
{
Q_ASSERT(m_error || m_error.isCanceled());
Q_ASSERT(m_sign || m_encrypt);
const QString label = formatInputOutputLabel(m_inputLabel, m_outputLabel, true);
const bool canceled = m_error.isCanceled();
if (m_sign && m_encrypt) {
return canceled ? i18n("%1: <b>Sign/encrypt canceled.</b>", label) : i18n(" %1: Sign/encrypt failed.", label);
}
return i18nc("label: result. Example: foo -> foo.gpg: Encryption failed.", "%1: <b>%2</b>", label,
m_sign ? makeSigningOverview(m_error) : makeEncryptionOverview(m_error));
}
QString ErrorResult::details() const
{
return m_errString;
}
class SignEncryptTask::Private
{
friend class ::Kleo::Crypto::SignEncryptTask;
SignEncryptTask *const q;
public:
explicit Private(SignEncryptTask *qq);
private:
void startSignEncryptJob(GpgME::Protocol proto);
std::unique_ptr<QGpgME::SignJob> createSignJob(GpgME::Protocol proto);
std::unique_ptr<QGpgME::SignEncryptJob> createSignEncryptJob(GpgME::Protocol proto);
std::unique_ptr<QGpgME::EncryptJob> createEncryptJob(GpgME::Protocol proto);
#if QGPGME_SUPPORTS_ARCHIVE_JOBS
void startSignEncryptArchiveJob(GpgME::Protocol proto);
std::unique_ptr<QGpgME::SignArchiveJob> createSignArchiveJob(GpgME::Protocol proto);
std::unique_ptr<QGpgME::SignEncryptArchiveJob> createSignEncryptArchiveJob(GpgME::Protocol proto);
std::unique_ptr<QGpgME::EncryptArchiveJob> createEncryptArchiveJob(GpgME::Protocol proto);
#endif
std::shared_ptr<const Task::Result> makeErrorResult(const Error &err, const QString &errStr, const AuditLogEntry &auditLog);
private:
void slotResult(const SigningResult &);
void slotResult(const SigningResult &, const EncryptionResult &);
void slotResult(const EncryptionResult &);
void slotResult(const QGpgME::Job *, const SigningResult &, const EncryptionResult &);
private:
std::shared_ptr<Input> input;
std::shared_ptr<Output> output;
QStringList inputFileNames;
QString outputFileName;
std::vector<Key> signers;
std::vector<Key> recipients;
bool sign : 1;
bool encrypt : 1;
bool detached : 1;
bool symmetric: 1;
bool clearsign: 1;
bool archive : 1;
QPointer<QGpgME::Job> job;
std::shared_ptr<OverwritePolicy> m_overwritePolicy;
};
SignEncryptTask::Private::Private(SignEncryptTask *qq)
: q{qq}
, sign{true}
, encrypt{true}
, detached{false}
, clearsign{false}
, archive{false}
, m_overwritePolicy{new OverwritePolicy{nullptr}}
{
q->setAsciiArmor(true);
}
std::shared_ptr<const Task::Result> SignEncryptTask::Private::makeErrorResult(const Error &err, const QString &errStr, const AuditLogEntry &auditLog)
{
return std::shared_ptr<const ErrorResult>(new ErrorResult(sign, encrypt, err, errStr, q->label(), output ? output->label() : QString{}, auditLog));
}
SignEncryptTask::SignEncryptTask(QObject *p)
: Task(p), d(new Private(this))
{
}
SignEncryptTask::~SignEncryptTask() {}
void SignEncryptTask::setInputFileName(const QString &fileName)
{
kleo_assert(!d->job);
kleo_assert(!fileName.isEmpty());
d->inputFileNames = QStringList(fileName);
}
void SignEncryptTask::setInputFileNames(const QStringList &fileNames)
{
kleo_assert(!d->job);
kleo_assert(!fileNames.empty());
d->inputFileNames = fileNames;
}
void SignEncryptTask::setInput(const std::shared_ptr<Input> &input)
{
kleo_assert(!d->job);
kleo_assert(input);
d->input = input;
}
void SignEncryptTask::setOutput(const std::shared_ptr<Output> &output)
{
kleo_assert(!d->job);
kleo_assert(output);
d->output = output;
}
void SignEncryptTask::setOutputFileName(const QString &fileName)
{
kleo_assert(!d->job);
kleo_assert(!fileName.isEmpty());
d->outputFileName = fileName;
}
void SignEncryptTask::setSigners(const std::vector<Key> &signers)
{
kleo_assert(!d->job);
d->signers = signers;
}
void SignEncryptTask::setRecipients(const std::vector<Key> &recipients)
{
kleo_assert(!d->job);
d->recipients = recipients;
}
void SignEncryptTask::setOverwritePolicy(const std::shared_ptr<OverwritePolicy> &policy)
{
kleo_assert(!d->job);
d->m_overwritePolicy = policy;
}
void SignEncryptTask::setSign(bool sign)
{
kleo_assert(!d->job);
d->sign = sign;
}
void SignEncryptTask::setEncrypt(bool encrypt)
{
kleo_assert(!d->job);
d->encrypt = encrypt;
}
void SignEncryptTask::setDetachedSignature(bool detached)
{
kleo_assert(!d->job);
d->detached = detached;
}
void SignEncryptTask::setEncryptSymmetric(bool symmetric)
{
kleo_assert(!d->job);
d->symmetric = symmetric;
}
void SignEncryptTask::setClearsign(bool clearsign)
{
kleo_assert(!d->job);
d->clearsign = clearsign;
}
void SignEncryptTask::setCreateArchive(bool archive)
{
kleo_assert(!d->job);
d->archive = archive;
}
Protocol SignEncryptTask::protocol() const
{
if (d->sign && !d->signers.empty()) {
return d->signers.front().protocol();
}
if (d->encrypt || d->symmetric) {
if (!d->recipients.empty()) {
return d->recipients.front().protocol();
} else {
return GpgME::OpenPGP; // symmetric OpenPGP encryption
}
}
throw Kleo::Exception(gpg_error(GPG_ERR_INTERNAL),
i18n("Cannot determine protocol for task"));
}
QString SignEncryptTask::label() const
{
if (d->input) {
return d->input->label();
} else if (!d->inputFileNames.empty()) {
const auto firstFile = QFileInfo{d->inputFileNames.front()}.fileName();
return d->inputFileNames.size() == 1 ? firstFile : i18nc("<name of first file>, ...", "%1, ...", firstFile);
}
return {};
}
QString SignEncryptTask::tag() const
{
return Formatting::displayName(protocol());
}
unsigned long long SignEncryptTask::inputSize() const
{
return d->input ? d->input->size() : 0U;
}
#if QGPGME_SUPPORTS_ARCHIVE_JOBS
static bool archiveJobsCanBeUsed(GpgME::Protocol protocol)
{
return (protocol == GpgME::OpenPGP) && QGpgME::SignEncryptArchiveJob::isSupported();
}
#endif
void SignEncryptTask::doStart()
{
kleo_assert(!d->job);
if (d->sign) {
kleo_assert(!d->signers.empty());
if (d->archive) {
kleo_assert(!d->detached && !d->clearsign);
}
}
if (!d->output) {
d->output = Output::createFromFile(d->outputFileName, d->m_overwritePolicy);
}
const auto proto = protocol();
#if QGPGME_SUPPORTS_ARCHIVE_JOBS
if (d->archive && archiveJobsCanBeUsed(proto)) {
d->startSignEncryptArchiveJob(proto);
} else
#endif
{
d->startSignEncryptJob(proto);
}
}
void SignEncryptTask::Private::startSignEncryptJob(GpgME::Protocol proto)
{
kleo_assert(input);
if (encrypt || symmetric) {
Context::EncryptionFlags flags = Context::AlwaysTrust;
if (symmetric) {
flags = static_cast<Context::EncryptionFlags>(flags | Context::Symmetric);
qCDebug(KLEOPATRA_LOG) << "Adding symmetric flag";
}
if (sign) {
std::unique_ptr<QGpgME::SignEncryptJob> job = createSignEncryptJob(proto);
kleo_assert(job.get());
#if QGPGME_SUPPORTS_SET_FILENAME
if (inputFileNames.size() == 1) {
job->setFileName(inputFileNames.front());
}
#endif
job->start(signers, recipients,
input->ioDevice(), output->ioDevice(), flags);
this->job = job.release();
} else {
std::unique_ptr<QGpgME::EncryptJob> job = createEncryptJob(proto);
kleo_assert(job.get());
#if QGPGME_SUPPORTS_SET_FILENAME
if (inputFileNames.size() == 1) {
job->setFileName(inputFileNames.front());
}
#endif
job->start(recipients, input->ioDevice(), output->ioDevice(), flags);
this->job = job.release();
}
} else if (sign) {
std::unique_ptr<QGpgME::SignJob> job = createSignJob(proto);
kleo_assert(job.get());
kleo_assert(! (detached && clearsign));
job->start(signers,
input->ioDevice(), output->ioDevice(),
detached ? GpgME::Detached : clearsign ?
GpgME::Clearsigned : GpgME::NormalSignatureMode);
this->job = job.release();
} else {
kleo_assert(!"Either 'sign' or 'encrypt' or 'symmetric' must be set!");
}
}
void SignEncryptTask::cancel()
{
if (d->job) {
d->job->slotCancel();
}
}
std::unique_ptr<QGpgME::SignJob> SignEncryptTask::Private::createSignJob(GpgME::Protocol proto)
{
const QGpgME::Protocol *const backend = (proto == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime();
kleo_assert(backend);
std::unique_ptr<QGpgME::SignJob> signJob(backend->signJob(q->asciiArmor(), /*textmode=*/false));
kleo_assert(signJob.get());
- connect(signJob.get(), SIGNAL(progress(QString,int,int)),
- q, SLOT(setProgress(QString,int,int)));
+ connect(signJob.get(), &QGpgME::Job::progress, q, [this](const QString &, int processed, int total) {
+ q->setProgress(processed, total);
+ });
connect(signJob.get(), SIGNAL(result(GpgME::SigningResult,QByteArray)),
q, SLOT(slotResult(GpgME::SigningResult)));
return signJob;
}
std::unique_ptr<QGpgME::SignEncryptJob> SignEncryptTask::Private::createSignEncryptJob(GpgME::Protocol proto)
{
const QGpgME::Protocol *const backend = (proto == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime();
kleo_assert(backend);
std::unique_ptr<QGpgME::SignEncryptJob> signEncryptJob(backend->signEncryptJob(q->asciiArmor(), /*textmode=*/false));
kleo_assert(signEncryptJob.get());
- connect(signEncryptJob.get(), SIGNAL(progress(QString,int,int)),
- q, SLOT(setProgress(QString,int,int)));
+ connect(signEncryptJob.get(), &QGpgME::Job::progress, q, [this](const QString &, int processed, int total) {
+ q->setProgress(processed, total);
+ });
connect(signEncryptJob.get(), SIGNAL(result(GpgME::SigningResult,GpgME::EncryptionResult,QByteArray)),
q, SLOT(slotResult(GpgME::SigningResult,GpgME::EncryptionResult)));
return signEncryptJob;
}
std::unique_ptr<QGpgME::EncryptJob> SignEncryptTask::Private::createEncryptJob(GpgME::Protocol proto)
{
const QGpgME::Protocol *const backend = (proto == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime();
kleo_assert(backend);
std::unique_ptr<QGpgME::EncryptJob> encryptJob(backend->encryptJob(q->asciiArmor(), /*textmode=*/false));
kleo_assert(encryptJob.get());
- connect(encryptJob.get(), SIGNAL(progress(QString,int,int)),
- q, SLOT(setProgress(QString,int,int)));
+ connect(encryptJob.get(), &QGpgME::Job::progress, q, [this](const QString &, int processed, int total) {
+ q->setProgress(processed, total);
+ });
connect(encryptJob.get(), SIGNAL(result(GpgME::EncryptionResult,QByteArray)),
q, SLOT(slotResult(GpgME::EncryptionResult)));
return encryptJob;
}
#if QGPGME_SUPPORTS_ARCHIVE_JOBS
void SignEncryptTask::Private::startSignEncryptArchiveJob(GpgME::Protocol proto)
{
kleo_assert(!input);
const auto baseDirectory = heuristicBaseDirectory(inputFileNames);
if (baseDirectory.isEmpty()) {
throw Kleo::Exception(GPG_ERR_CONFLICT, i18n("Cannot find common base directory for these files:\n%1", inputFileNames.join(QLatin1Char('\n'))));
}
qCDebug(KLEOPATRA_LOG) << "heuristicBaseDirectory(" << inputFileNames << ") ->" << baseDirectory;
const auto tempPaths = makeRelativeTo(baseDirectory, inputFileNames);
const auto relativePaths = std::vector<QString>{tempPaths.begin(), tempPaths.end()};
qCDebug(KLEOPATRA_LOG) << "relative paths:" << relativePaths;
if (encrypt || symmetric) {
Context::EncryptionFlags flags = Context::AlwaysTrust;
if (symmetric) {
flags = static_cast<Context::EncryptionFlags>(flags | Context::Symmetric);
qCDebug(KLEOPATRA_LOG) << "Adding symmetric flag";
}
if (sign) {
std::unique_ptr<QGpgME::SignEncryptArchiveJob> job = createSignEncryptArchiveJob(proto);
kleo_assert(job.get());
job->setBaseDirectory(baseDirectory);
job->start(signers, recipients, relativePaths, output->ioDevice(), flags);
this->job = job.release();
} else {
std::unique_ptr<QGpgME::EncryptArchiveJob> job = createEncryptArchiveJob(proto);
kleo_assert(job.get());
job->setBaseDirectory(baseDirectory);
job->start(recipients, relativePaths, output->ioDevice(), flags);
this->job = job.release();
}
} else if (sign) {
std::unique_ptr<QGpgME::SignArchiveJob> job = createSignArchiveJob(proto);
kleo_assert(job.get());
job->setBaseDirectory(baseDirectory);
job->start(signers, relativePaths, output->ioDevice());
this->job = job.release();
} else {
kleo_assert(!"Either 'sign' or 'encrypt' or 'symmetric' must be set!");
}
}
std::unique_ptr<QGpgME::SignArchiveJob> SignEncryptTask::Private::createSignArchiveJob(GpgME::Protocol proto)
{
const QGpgME::Protocol *const backend = (proto == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime();
kleo_assert(backend);
std::unique_ptr<QGpgME::SignArchiveJob> signJob(backend->signArchiveJob(q->asciiArmor()));
auto job = signJob.get();
kleo_assert(job);
- connect(job, &QGpgME::SignArchiveJob::progress, q, &SignEncryptTask::setProgress);
+ connect(job, &QGpgME::Job::progress, q, [this](const QString &, int processed, int total) {
+ q->setProgress(processed, total);
+ });
connect(job, &QGpgME::SignArchiveJob::result, q, [this, job](const GpgME::SigningResult &signResult) {
slotResult(job, signResult, EncryptionResult{});
});
return signJob;
}
std::unique_ptr<QGpgME::SignEncryptArchiveJob> SignEncryptTask::Private::createSignEncryptArchiveJob(GpgME::Protocol proto)
{
const QGpgME::Protocol *const backend = (proto == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime();
kleo_assert(backend);
std::unique_ptr<QGpgME::SignEncryptArchiveJob> signEncryptJob(backend->signEncryptArchiveJob(q->asciiArmor()));
auto job = signEncryptJob.get();
kleo_assert(job);
- connect(job, &QGpgME::SignEncryptArchiveJob::progress, q, &SignEncryptTask::setProgress);
+ connect(job, &QGpgME::Job::progress, q, [this](const QString &, int processed, int total) {
+ q->setProgress(processed, total);
+ });
connect(job, &QGpgME::SignEncryptArchiveJob::result, q, [this, job](const GpgME::SigningResult &signResult, const GpgME::EncryptionResult &encryptResult) {
slotResult(job, signResult, encryptResult);
});
return signEncryptJob;
}
std::unique_ptr<QGpgME::EncryptArchiveJob> SignEncryptTask::Private::createEncryptArchiveJob(GpgME::Protocol proto)
{
const QGpgME::Protocol *const backend = (proto == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime();
kleo_assert(backend);
std::unique_ptr<QGpgME::EncryptArchiveJob> encryptJob(backend->encryptArchiveJob(q->asciiArmor()));
auto job = encryptJob.get();
kleo_assert(job);
- connect(job, &QGpgME::EncryptArchiveJob::progress, q, &SignEncryptTask::setProgress);
+ connect(job, &QGpgME::Job::progress, q, [this](const QString &, int processed, int total) {
+ q->setProgress(processed, total);
+ });
connect(job, &QGpgME::EncryptArchiveJob::result, q, [this, job](const GpgME::EncryptionResult &encryptResult) {
slotResult(job, SigningResult{}, encryptResult);
});
return encryptJob;
}
#endif
void SignEncryptTask::Private::slotResult(const SigningResult &result)
{
slotResult(qobject_cast<const QGpgME::Job *>(q->sender()), result, EncryptionResult{});
}
void SignEncryptTask::Private::slotResult(const SigningResult &sresult, const EncryptionResult &eresult)
{
slotResult(qobject_cast<const QGpgME::Job *>(q->sender()), sresult, eresult);
}
void SignEncryptTask::Private::slotResult(const EncryptionResult &result)
{
slotResult(qobject_cast<const QGpgME::Job *>(q->sender()), SigningResult{}, result);
}
void SignEncryptTask::Private::slotResult(const QGpgME::Job *job, const SigningResult &sresult, const EncryptionResult &eresult)
{
const AuditLogEntry auditLog = AuditLogEntry::fromJob(job);
bool outputCreated = false;
if (input && input->failed()) {
output->cancel();
q->emitResult(makeErrorResult(Error::fromCode(GPG_ERR_EIO),
i18n("Input error: %1", escape( input->errorString())),
auditLog));
return;
} else if (sresult.error().code() || eresult.error().code()) {
output->cancel();
} else {
try {
kleo_assert(!sresult.isNull() || !eresult.isNull());
output->finalize();
outputCreated = true;
if (input) {
input->finalize();
}
} catch (const GpgME::Exception &e) {
q->emitResult(makeErrorResult(e.error(), QString::fromLocal8Bit(e.what()), auditLog));
return;
}
}
const LabelAndError inputInfo{q->label(), input ? input->errorString() : QString{}};
const LabelAndError outputInfo{output->label(), output->errorString()};
q->emitResult(std::shared_ptr<Result>(new SignEncryptFilesResult(sresult, eresult, inputInfo, outputInfo, outputCreated, auditLog)));
}
QString SignEncryptFilesResult::overview() const
{
const QString files = formatInputOutputLabel(m_input.label, m_output.label, !m_outputCreated);
return files + QLatin1String(": ") + makeOverview(makeResultOverview(m_sresult, m_eresult));
}
QString SignEncryptFilesResult::details() const
{
return errorString();
}
GpgME::Error SignEncryptFilesResult::error() const
{
if (m_sresult.error().code()) {
return m_sresult.error();
}
if (m_eresult.error().code()) {
return m_eresult.error();
}
return {};
}
QString SignEncryptFilesResult::errorString() const
{
const bool sign = !m_sresult.isNull();
const bool encrypt = !m_eresult.isNull();
kleo_assert(sign || encrypt);
if (sign && encrypt) {
return
m_sresult.error().code() ? makeResultDetails(m_sresult, m_input.errorString, m_output.errorString) :
m_eresult.error().code() ? makeResultDetails(m_eresult, m_input.errorString, m_output.errorString) :
QString();
}
return
sign ? makeResultDetails(m_sresult, m_input.errorString, m_output.errorString) :
/*else*/ makeResultDetails(m_eresult, m_input.errorString, m_output.errorString);
}
Task::Result::VisualCode SignEncryptFilesResult::code() const
{
if (m_sresult.error().isCanceled() || m_eresult.error().isCanceled()) {
return Warning;
}
return (m_sresult.error().code() || m_eresult.error().code()) ? NeutralError : NeutralSuccess;
}
AuditLogEntry SignEncryptFilesResult::auditLog() const
{
return m_auditLog;
}
#include "moc_signencrypttask.cpp"
diff --git a/src/crypto/task.cpp b/src/crypto/task.cpp
index 26925663c..b1eed7f7b 100644
--- a/src/crypto/task.cpp
+++ b/src/crypto/task.cpp
@@ -1,235 +1,228 @@
/* -*- mode: c++; c-basic-offset:4 -*-
crypto/task.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 "task.h"
#include "task_p.h"
#include "kleopatra_debug.h"
#include <Libkleo/KleoException>
#include <Libkleo/AuditLogEntry>
#include <Libkleo/GnuPG>
#include <gpgme++/exception.h>
#include <gpg-error.h>
#include <KIconLoader>
#include <KLocalizedString>
using namespace Kleo;
using namespace Kleo::Crypto;
using namespace GpgME;
namespace
{
class ErrorResult : public Task::Result
{
public:
ErrorResult(const GpgME::Error &error, const QString &details)
: Task::Result(), m_error(error), m_details(details) {}
QString overview() const override
{
return makeOverview(m_details);
}
QString details() const override
{
return QString();
}
GpgME::Error error() const override
{
return m_error;
}
QString errorString() const override
{
return m_details;
}
VisualCode code() const override
{
return NeutralError;
}
AuditLogEntry auditLog() const override
{
return AuditLogEntry();
}
private:
const GpgME::Error m_error;
const QString m_details;
};
}
class Task::Private
{
friend class ::Kleo::Crypto::Task;
Task *const q;
public:
explicit Private(Task *qq);
private:
- QString m_progressLabel;
int m_progress;
int m_totalProgress;
bool m_asciiArmor;
int m_id;
};
namespace
{
static int nextTaskId = 0;
}
Task::Private::Private(Task *qq)
- : q(qq), m_progressLabel(), m_progress(0), m_totalProgress(0), m_asciiArmor(false), m_id(nextTaskId++)
+ : q(qq), m_progress(0), m_totalProgress(0), m_asciiArmor(false), m_id(nextTaskId++)
{
}
Task::Task(QObject *p)
: QObject(p), d(new Private(this))
{
}
Task::~Task() {}
void Task::setAsciiArmor(bool armor)
{
d->m_asciiArmor = armor;
}
bool Task::asciiArmor() const
{
return d->m_asciiArmor;
}
std::shared_ptr<Task> Task::makeErrorTask(const GpgME::Error &error, const QString &details, const QString &label)
{
const std::shared_ptr<SimpleTask> t(new SimpleTask(label));
t->setResult(t->makeErrorResult(error, details));
return t;
}
int Task::id() const
{
return d->m_id;
}
int Task::currentProgress() const
{
return d->m_progress;
}
int Task::totalProgress() const
{
return d->m_totalProgress;
}
QString Task::tag() const
{
return QString();
}
-QString Task::progressLabel() const
-{
- return d->m_progressLabel;
-}
-
-void Task::setProgress(const QString &label, int processed, int total)
+void Task::setProgress(int processed, int total)
{
d->m_progress = processed;
d->m_totalProgress = total;
- d->m_progressLabel = label;
- Q_EMIT progress(label, processed, total, QPrivateSignal());
+ Q_EMIT progress(processed, total, QPrivateSignal());
}
void Task::start()
{
try {
doStart();
} catch (const Kleo::Exception &e) {
QMetaObject::invokeMethod(this, "emitError", Qt::QueuedConnection, Q_ARG(GpgME::Error, e.error()), Q_ARG(QString, e.message()));
} catch (const GpgME::Exception &e) {
QMetaObject::invokeMethod(this, "emitError", Qt::QueuedConnection, Q_ARG(GpgME::Error, e.error()), Q_ARG(QString, QString::fromLocal8Bit(e.what())));
} catch (const std::exception &e) {
QMetaObject::invokeMethod(this, "emitError", Qt::QueuedConnection, Q_ARG(GpgME::Error, Error::fromCode(GPG_ERR_UNEXPECTED)), Q_ARG(QString, QString::fromLocal8Bit(e.what())));
} catch (...) {
QMetaObject::invokeMethod(this, "emitError", Qt::QueuedConnection, Q_ARG(GpgME::Error, Error::fromCode(GPG_ERR_UNEXPECTED)), Q_ARG(QString, i18n("Unknown exception in Task::start()")));
}
Q_EMIT started(QPrivateSignal());
}
void Task::emitError(const GpgME::Error &error, const QString &details)
{
emitResult(makeErrorResult(error, details));
}
void Task::emitResult(const std::shared_ptr<const Task::Result> &r)
{
d->m_progress = d->m_totalProgress;
- Q_EMIT progress(progressLabel(), currentProgress(), totalProgress(), QPrivateSignal());
+ Q_EMIT progress(currentProgress(), totalProgress(), QPrivateSignal());
Q_EMIT result(r, QPrivateSignal());
}
std::shared_ptr<Task::Result> Task::makeErrorResult(const GpgME::Error &error, const QString &details)
{
return std::shared_ptr<Task::Result>(new ErrorResult(error, details));
}
class Task::Result::Private
{
public:
Private() {}
};
Task::Result::Result() : d(new Private()) {}
Task::Result::~Result() {}
bool Task::Result::hasError() const
{
return error().code() != 0;
}
static QString image(const char *img)
{
// ### escape?
return KIconLoader::global()->iconPath(QLatin1String(img), KIconLoader::Small);
}
QString Task::Result::makeOverview(const QString &msg)
{
return QLatin1String("<b>") + msg + QLatin1String("</b>");
}
QString Task::Result::iconPath(VisualCode code)
{
switch (code) {
case Danger:
return image("dialog-error");
case AllGood:
return image("dialog-ok");
case Warning:
return image("dialog-warning");
case NeutralError:
case NeutralSuccess:
default:
return QString();
}
}
QString Task::Result::icon() const
{
return iconPath(code());
}
#include "moc_task_p.cpp"
diff --git a/src/crypto/task.h b/src/crypto/task.h
index 7f6dfbae5..664a063f3 100644
--- a/src/crypto/task.h
+++ b/src/crypto/task.h
@@ -1,131 +1,131 @@
/* -*- mode: c++; c-basic-offset:4 -*-
crypto/task.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 <QObject>
#include <QString>
#include <utils/pimpl_ptr.h>
#include <gpgme++/global.h>
#include <memory>
#include <QPointer>
namespace Kleo
{
class AuditLogEntry;
}
namespace Kleo
{
namespace Crypto
{
class Task : public QObject
{
Q_OBJECT
public:
explicit Task(QObject *parent = nullptr);
~Task() override;
class Result;
void setAsciiArmor(bool armor);
bool asciiArmor() const;
virtual GpgME::Protocol protocol() const = 0;
void start();
virtual QString label() const = 0;
virtual QString tag() const;
QString progressLabel() const;
int currentProgress() const;
int totalProgress() const;
int id() const;
static std::shared_ptr<Task> makeErrorTask(const GpgME::Error &error, const QString &details, const QString &label);
public Q_SLOTS:
virtual void cancel() = 0;
Q_SIGNALS:
- void progress(const QString &what, int processed, int total, QPrivateSignal);
+ void progress(int processed, int total, QPrivateSignal);
void result(const std::shared_ptr<const Kleo::Crypto::Task::Result> &, QPrivateSignal);
void started(QPrivateSignal);
protected:
std::shared_ptr<Result> makeErrorResult(const GpgME::Error &error, const QString &details);
void emitResult(const std::shared_ptr<const Task::Result> &result);
protected Q_SLOTS:
- void setProgress(const QString &msg, int processed, int total);
+ void setProgress(int processed, int total);
private Q_SLOTS:
void emitError(const GpgME::Error &error, const QString &details);
private:
virtual void doStart() = 0;
virtual unsigned long long inputSize() const = 0;
private:
class Private;
kdtools::pimpl_ptr<Private> d;
};
class Task::Result
{
const QString m_nonce;
public:
Result();
virtual ~Result();
const QString &nonce() const
{
return m_nonce;
}
bool hasError() const;
enum VisualCode {
AllGood,
Warning,
Danger,
NeutralSuccess,
NeutralError
};
virtual QString icon() const;
virtual QString overview() const = 0;
virtual QString details() const = 0;
virtual GpgME::Error error() const = 0;
virtual QString errorString() const = 0;
virtual VisualCode code() const = 0;
virtual AuditLogEntry auditLog() const = 0;
virtual QPointer<Task> parentTask() const {return QPointer<Task>();}
protected:
static QString iconPath(VisualCode code);
static QString makeOverview(const QString &msg);
private:
class Private;
kdtools::pimpl_ptr<Private> d;
};
}
}
diff --git a/src/crypto/taskcollection.cpp b/src/crypto/taskcollection.cpp
index e183c033e..8753b0b03 100644
--- a/src/crypto/taskcollection.cpp
+++ b/src/crypto/taskcollection.cpp
@@ -1,227 +1,225 @@
/* -*- mode: c++; c-basic-offset:4 -*-
crypto/taskcollection.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
SPDX-FileCopyrightText: 2016 Bundesamt für Sicherheit in der Informationstechnik
SPDX-FileContributor: Intevation GmbH
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-kleopatra.h>
#include "taskcollection.h"
#include "task.h"
#include "kleopatra_debug.h"
#include <Libkleo/GnuPG>
#include <algorithm>
#include <map>
#include <cmath>
#include <KLocalizedString>
using namespace Kleo;
using namespace Kleo::Crypto;
class TaskCollection::Private
{
TaskCollection *const q;
public:
explicit Private(TaskCollection *qq);
- void taskProgress(const QString &, int, int);
+ void taskProgress();
void taskResult(const std::shared_ptr<const Task::Result> &);
void taskStarted();
void calculateAndEmitProgress();
std::map<int, std::shared_ptr<Task> > m_tasks;
mutable quint64 m_totalProgress;
mutable quint64 m_progress;
unsigned int m_nCompleted;
unsigned int m_nErrors;
- QString m_lastProgressMessage;
bool m_errorOccurred;
bool m_doneEmitted;
};
TaskCollection::Private::Private(TaskCollection *qq):
q(qq),
m_totalProgress(0),
m_progress(0),
m_nCompleted(0),
m_nErrors(0),
m_errorOccurred(false),
m_doneEmitted(false)
{
}
int TaskCollection::numberOfCompletedTasks() const
{
return d->m_nCompleted;
}
size_t TaskCollection::size() const
{
return d->m_tasks.size();
}
bool TaskCollection::allTasksCompleted() const
{
Q_ASSERT(d->m_nCompleted <= d->m_tasks.size());
return d->m_nCompleted == d->m_tasks.size();
}
-void TaskCollection::Private::taskProgress(const QString &msg, int, int)
+void TaskCollection::Private::taskProgress()
{
- m_lastProgressMessage = msg;
calculateAndEmitProgress();
}
void TaskCollection::Private::taskResult(const std::shared_ptr<const Task::Result> &result)
{
Q_ASSERT(result);
++m_nCompleted;
if (result->hasError()) {
m_errorOccurred = true;
++m_nErrors;
}
- m_lastProgressMessage.clear();
calculateAndEmitProgress();
Q_EMIT q->result(result);
if (!m_doneEmitted && q->allTasksCompleted()) {
Q_EMIT q->done();
m_doneEmitted = true;
}
}
void TaskCollection::Private::taskStarted()
{
const Task *const task = qobject_cast<Task *>(q->sender());
Q_ASSERT(task);
Q_ASSERT(m_tasks.find(task->id()) != m_tasks.end());
Q_EMIT q->started(m_tasks[task->id()]);
calculateAndEmitProgress(); // start Knight-Rider-Mode right away (gpgsm doesn't report _any_ progress).
if (m_doneEmitted) {
// We are not done anymore, one task restarted.
m_nCompleted--;
m_nErrors--;
m_doneEmitted = false;
}
}
void TaskCollection::Private::calculateAndEmitProgress()
{
quint64 total = 0;
quint64 processed = 0;
static bool haveWorkingProgress = engineIsVersion(2, 1, 15);
if (!haveWorkingProgress) {
// GnuPG before 2.1.15 would overflow on progress values > max int.
// and did not emit a total for our Qt data types.
// As we can't know if it overflowed or what the total is we just knight
// rider in that case
if (m_doneEmitted) {
- Q_EMIT q->progress(m_lastProgressMessage, 1, 1);
+ Q_EMIT q->progress(1, 1);
} else {
- Q_EMIT q->progress(m_lastProgressMessage, 0, 0);
+ Q_EMIT q->progress(0, 0);
}
return;
}
bool unknowable = false;
for (auto it = m_tasks.begin(), end = m_tasks.end(); it != end; ++it) {
// Sum up progress and totals
const std::shared_ptr<Task> &i = it->second;
Q_ASSERT(i);
if (!i->totalProgress()) {
// There still might be jobs for which we don't know the progress.
qCDebug(KLEOPATRA_LOG) << "Task: " << i->label() << " has no total progress set. ";
unknowable = true;
break;
}
processed += i->currentProgress();
total += i->totalProgress();
}
m_totalProgress = total;
m_progress = processed;
if (!unknowable && processed && total >= processed) {
// Scale down to avoid range issues.
int scaled = 1000 * (m_progress / static_cast<double>(m_totalProgress));
qCDebug(KLEOPATRA_LOG) << "Collection Progress: " << scaled << " total: " << 1000;
if (total == processed) {
// This can happen when an output is finalizing, e.g. extracting an
// archive.
- Q_EMIT q->progress(i18n("Finalizing output..."), 0, 0);
+ Q_EMIT q->progress(0, 0);
} else {
- Q_EMIT q->progress(m_lastProgressMessage, scaled, 1000);
+ Q_EMIT q->progress(scaled, 1000);
}
} else {
if (total < processed) {
qCDebug(KLEOPATRA_LOG) << "Total progress is smaller then current progress.";
}
// Knight rider.
- Q_EMIT q->progress(m_lastProgressMessage, 0, 0);
+ Q_EMIT q->progress(0, 0);
}
}
TaskCollection::TaskCollection(QObject *parent) : QObject(parent), d(new Private(this))
{
}
TaskCollection::~TaskCollection()
{
}
bool TaskCollection::isEmpty() const
{
return d->m_tasks.empty();
}
bool TaskCollection::errorOccurred() const
{
return d->m_errorOccurred;
}
bool TaskCollection::allTasksHaveErrors() const
{
return d->m_nErrors == d->m_nCompleted;
}
std::shared_ptr<Task> TaskCollection::taskById(int id) const
{
const auto it = d->m_tasks.find(id);
return it != d->m_tasks.end() ? it->second : std::shared_ptr<Task>();
}
std::vector<std::shared_ptr<Task> > TaskCollection::tasks() const
{
std::vector<std::shared_ptr<Task> > res;
res.reserve(d->m_tasks.size());
for (auto it = d->m_tasks.begin(), end = d->m_tasks.end(); it != end; ++it) {
res.push_back(it->second);
}
return res;
}
void TaskCollection::setTasks(const std::vector<std::shared_ptr<Task> > &tasks)
{
for (const std::shared_ptr<Task> &i : tasks) {
Q_ASSERT(i);
d->m_tasks[i->id()] = i;
- connect(i.get(), SIGNAL(progress(QString,int,int)),
- this, SLOT(taskProgress(QString,int,int)));
+ connect(i.get(), &Task::progress, this, [this]() {
+ d->taskProgress();
+ });
connect(i.get(), SIGNAL(result(std::shared_ptr<const Kleo::Crypto::Task::Result>)),
this, SLOT(taskResult(std::shared_ptr<const Kleo::Crypto::Task::Result>)));
connect(i.get(), SIGNAL(started()),
this, SLOT(taskStarted()));
}
}
#include "moc_taskcollection.cpp"
diff --git a/src/crypto/taskcollection.h b/src/crypto/taskcollection.h
index 444c323e4..3b4ac5996 100644
--- a/src/crypto/taskcollection.h
+++ b/src/crypto/taskcollection.h
@@ -1,62 +1,61 @@
/* -*- mode: c++; c-basic-offset:4 -*-
crypto/taskcollection.h
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include <QObject>
#include <crypto/task.h>
#include <utils/pimpl_ptr.h>
#include <memory>
#include <vector>
namespace Kleo
{
namespace Crypto
{
class TaskCollection : public QObject
{
Q_OBJECT
public:
explicit TaskCollection(QObject *parent = nullptr);
~TaskCollection() override;
std::vector<std::shared_ptr<Task> > tasks() const;
std::shared_ptr<Task> taskById(int id) const;
void setTasks(const std::vector<std::shared_ptr<Task> > &tasks);
bool isEmpty() const;
size_t size() const;
int numberOfCompletedTasks() const;
bool allTasksCompleted() const;
bool errorOccurred() const;
bool allTasksHaveErrors() const;
Q_SIGNALS:
- void progress(const QString &msg, int processed, int total);
+ void progress(int processed, int total);
void result(const std::shared_ptr<const Kleo::Crypto::Task::Result> &result);
void started(const std::shared_ptr<Kleo::Crypto::Task> &task);
void done();
private:
class Private;
kdtools::pimpl_ptr<Private> d;
- Q_PRIVATE_SLOT(d, void taskProgress(QString, int, int))
Q_PRIVATE_SLOT(d, void taskResult(std::shared_ptr<const Kleo::Crypto::Task::Result>))
Q_PRIVATE_SLOT(d, void taskStarted())
};
}
}

File Metadata

Mime Type
text/x-diff
Expires
Tue, Sep 16, 12:42 AM (3 h, 59 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
be/34/721c91b47d268e8050386855adc8

Event Timeline