Page MenuHome GnuPG

No OneTemporary

diff --git a/src/crypto/decryptverifytask.cpp b/src/crypto/decryptverifytask.cpp
index ff7821f52..5722824d2 100644
--- a/src/crypto/decryptverifytask.cpp
+++ b/src/crypto/decryptverifytask.cpp
@@ -1,1741 +1,1765 @@
/* -*- mode: c++; c-basic-offset:4 -*-
decryptverifytask.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-kleopatra.h>
#include "decryptverifytask.h"
#include <QGpgME/DecryptJob>
#include <QGpgME/DecryptVerifyArchiveJob>
#include <QGpgME/DecryptVerifyJob>
#include <QGpgME/Protocol>
#include <QGpgME/VerifyDetachedJob>
#include <QGpgME/VerifyOpaqueJob>
#include <Libkleo/AuditLogEntry>
#include <Libkleo/Classify>
#include <Libkleo/Compliance>
#include <Libkleo/Dn>
#include <Libkleo/Formatting>
#include <Libkleo/KeyCache>
#include <Libkleo/KleoException>
#include <Libkleo/Predicates>
#include <Libkleo/Stl_Util>
#include <Libkleo/GnuPG>
#include <utils/detail_p.h>
#include <utils/input.h>
#include <utils/kleo_assert.h>
#include <utils/output.h>
#include <KEmailAddress>
#include <KMime/Types>
#include <gpgme++/context.h>
#include <gpgme++/decryptionresult.h>
#include <gpgme++/error.h>
#include <gpgme++/key.h>
#include <gpgme++/verificationresult.h>
#include <gpg-error.h>
#include "kleopatra_debug.h"
#include <KFileUtils>
#include <KLocalizedString>
#include <QByteArray>
#include <QDateTime>
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QIODevice>
#include <QLocale>
#include <QMimeDatabase>
#include <QStringList>
#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 std::vector<QString> extractEmails(const Key &key)
{
std::vector<QString> res;
const auto userIDs{key.userIDs()};
for (const UserID &id : userIDs) {
const auto email = Kleo::Formatting::email(id);
if (!email.isEmpty()) {
res.push_back(email);
}
}
return res;
}
static std::vector<QString> extractEmails(const std::vector<Key> &signers)
{
std::vector<QString> res;
for (const Key &i : signers) {
const std::vector<QString> bxs = extractEmails(i);
res.insert(res.end(), bxs.begin(), bxs.end());
}
return res;
}
static bool keyContainsEmail(const Key &key, const QString &email)
{
const std::vector<QString> emails = extractEmails(key);
return std::find_if(emails.cbegin(),
emails.cend(),
[email](const QString &emailItem) {
return emailItem.compare(email, Qt::CaseInsensitive);
})
!= emails.cend();
}
static bool keysContainEmail(const std::vector<Key> &keys, const QString &email)
{
return std::find_if(keys.cbegin(),
keys.cend(),
[email](const Key &key) {
return keyContainsEmail(key, email);
})
!= 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 renderKeyLink(const QString &fpr, const QString &text)
{
return QStringLiteral("<a href=\"key:%1\">%2</a>").arg(fpr, text);
}
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(QLatin1StringView(key.primaryFingerprint()), user);
}
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));
+ return i18nc("Decrypted <file> from <file>", "Decrypted %1 from %2", strikeOut(output, outputDeleted), strikeOut(input, inputDeleted));
}
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 void updateKeys(const VerificationResult &result)
{
// This little hack works around the problem that GnuPG / GpgME does not
// provide Key information in a verification result. The Key object is
// a dummy just holding the KeyID. This hack ensures that all available
// keys are fetched from the backend and are populated
for (const auto &sig : result.signatures()) {
// Update key information
sig.key(true, true);
}
}
static QString ensureUniqueDirectory(const QString &path)
{
// make sure that we don't use an existing directory
QString uniquePath = path;
const QFileInfo outputInfo{path};
if (outputInfo.exists()) {
const auto uniqueName = KFileUtils::suggestName(QUrl::fromLocalFile(outputInfo.absolutePath()), outputInfo.fileName());
uniquePath = outputInfo.dir().filePath(uniqueName);
}
if (!QDir{}.mkpath(uniquePath)) {
return {};
}
return uniquePath;
}
static bool mimeTypeInherits(const QMimeType &mimeType, const QString &mimeTypeName)
{
// inherits is expensive on an invalid mimeType
return mimeType.isValid() && mimeType.inherits(mimeTypeName);
}
}
class DecryptVerifyResult::SenderInfo
{
public:
explicit SenderInfo(const QString &infSender, const std::vector<Key> &signers_)
: informativeSender(infSender)
, signers(signers_)
{
}
const QString informativeSender;
const std::vector<Key> signers;
bool hasInformativeSender() const
{
return !informativeSender.isEmpty();
}
bool conflicts() const
{
return hasInformativeSender() && hasKeys() && !keysContainEmail(signers, informativeSender);
}
bool hasKeys() const
{
return std::any_of(signers.cbegin(), signers.cend(), [](const Key &key) {
return !key.isNull();
});
}
std::vector<QString> signerEmails() const
{
return extractEmails(signers);
}
};
namespace
{
static Task::Result::VisualCode codeForVerificationResult(const VerificationResult &res)
{
if (res.isNull()) {
return Task::Result::NeutralSuccess;
}
const std::vector<Signature> sigs = res.signatures();
if (sigs.empty()) {
return Task::Result::Warning;
}
if (std::find_if(sigs.begin(), sigs.end(), IsBad) != sigs.end()) {
return Task::Result::Danger;
}
if ((size_t)std::count_if(sigs.begin(), sigs.end(), IsGoodOrValid) == sigs.size()) {
return Task::Result::AllGood;
}
return Task::Result::Warning;
}
static QString formatVerificationResultOverview(const VerificationResult &res, const DecryptVerifyResult::SenderInfo &info)
{
if (res.isNull()) {
return QString();
}
const Error err = res.error();
if (err.isCanceled()) {
return i18n("<b>Verification canceled.</b>");
} else if (err) {
return i18n("<b>Verification failed: %1.</b>", Formatting::errorAsString(err).toHtmlEscaped());
}
const std::vector<Signature> sigs = res.signatures();
if (sigs.empty()) {
return i18n("<b>No signatures found.</b>");
}
const uint bad = std::count_if(sigs.cbegin(), sigs.cend(), IsBad);
if (bad > 0) {
return i18np("<b>Invalid signature.</b>", "<b>%1 invalid signatures.</b>", bad);
}
const uint warn = std::count_if(sigs.cbegin(), sigs.cend(), [](const Signature &sig) {
return !IsGoodOrValid(sig);
});
if (warn == sigs.size()) {
return i18np("<b>The data could not be verified.</b>", "<b>%1 signatures could not be verified.</b>", warn);
}
// Good signature:
QString text;
if (sigs.size() == 1) {
text = i18n("<b>Valid signature by %1</b>", renderKeyEMailOnlyNameAsFallback(sigs[0].key()));
if (info.conflicts())
text += i18n("<br/><b>Warning:</b> The sender's mail address is not stored in the %1 used for signing.",
renderKeyLink(QLatin1StringView(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.error().code() == GPG_ERR_NO_SECKEY) {
return i18nc("@info",
"<b>Decryption not possible: %1</b><br />The data was not encrypted for any secret key in your certificate list.",
Formatting::errorAsString(err));
} else if (result.isLegacyCipherNoMDC()) {
return i18n("<b>Decryption failed: %1.</b>", i18n("No integrity protection (MDC)."));
} else if (!errorString.isEmpty()) {
return i18n("<b>Decryption failed: %1.</b>", errorString.toHtmlEscaped());
} else if (err) {
return i18n("<b>Decryption failed: %1.</b>", Formatting::errorAsString(err));
}
return i18n("<b>Decryption succeeded.</b>");
}
static QString formatEmail(const QString &email_)
{
QString email;
QString name;
QString comment;
if (!email_.isEmpty() && KEmailAddress::splitAddress(email_, name, email, comment) == KEmailAddress::AddressOk) {
return email;
}
return email_;
}
static QStringList formatEmails(const std::vector<QString> &emails)
{
QStringList res;
std::transform(emails.cbegin(), emails.cend(), std::back_inserter(res), &formatEmail);
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 += Kleo::Formatting::prettySignature(sig, info.informativeSender) + 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>",
formatEmail(info.informativeSender),
formatEmails(info.signerEmails()).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 {};
}
QString details = i18np("Recipient:", "Recipients:", numRecipients);
if (numRecipients == 1) {
details += QLatin1Char(' ') + Formatting::summaryLine(knownRecipients.front()).toHtmlEscaped();
} else {
details += QLatin1StringView("<ul>");
for (const Key &key : knownRecipients) {
details += QLatin1StringView("<li>") + Formatting::summaryLine(key).toHtmlEscaped() + QLatin1StringView("</li>");
}
if (knownRecipients.size() < numRecipients) {
details += QLatin1StringView("<li>") + i18np("One unknown recipient", "%1 unknown recipients", numRecipients - knownRecipients.size())
+ QLatin1StringView("</li>");
}
details += QLatin1StringView("</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()) {
if (res.error().code() == GPG_ERR_NO_SECKEY && recipients.empty()) {
return {};
}
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.") + QLatin1StringView("<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(QString::fromUtf8(m_informativeSender.address()), KeyCache::instance()->findSigners(m_verificationResult));
}
std::shared_ptr<DecryptVerifyResult>
AbstractDecryptVerifyTask::fromDecryptResult(const DecryptionResult &dr, const QByteArray &plaintext, const AuditLogEntry &auditLog)
{
return std::shared_ptr<DecryptVerifyResult>(new DecryptVerifyResult(Decrypt, //
VerificationResult(),
dr,
plaintext,
{},
{},
QString(),
inputLabel(),
outputLabel(),
auditLog,
this,
informativeSender()));
}
std::shared_ptr<DecryptVerifyResult> AbstractDecryptVerifyTask::fromDecryptResult(const GpgME::Error &err, const QString &what, const AuditLogEntry &auditLog)
{
return std::shared_ptr<DecryptVerifyResult>(new DecryptVerifyResult(Decrypt, //
VerificationResult(),
DecryptionResult(err),
QByteArray(),
{},
err,
what,
inputLabel(),
outputLabel(),
auditLog,
this,
informativeSender()));
}
std::shared_ptr<DecryptVerifyResult> AbstractDecryptVerifyTask::fromDecryptVerifyResult(const DecryptionResult &dr,
const VerificationResult &vr,
const QByteArray &plaintext,
const QString &fileName,
const AuditLogEntry &auditLog)
{
const auto err = dr.error().code() ? dr.error() : vr.error();
return std::shared_ptr<DecryptVerifyResult>(new DecryptVerifyResult(DecryptVerify, //
vr,
dr,
plaintext,
fileName,
err,
QString(),
inputLabel(),
outputLabel(),
auditLog,
this,
informativeSender()));
}
std::shared_ptr<DecryptVerifyResult>
AbstractDecryptVerifyTask::fromDecryptVerifyResult(const GpgME::Error &err, const QString &details, const AuditLogEntry &auditLog)
{
return std::shared_ptr<DecryptVerifyResult>(new DecryptVerifyResult(DecryptVerify, //
VerificationResult(),
DecryptionResult(err),
QByteArray(),
{},
err,
details,
inputLabel(),
outputLabel(),
auditLog,
this,
informativeSender()));
}
std::shared_ptr<DecryptVerifyResult>
AbstractDecryptVerifyTask::fromVerifyOpaqueResult(const VerificationResult &vr, const QByteArray &plaintext, const AuditLogEntry &auditLog)
{
return std::shared_ptr<DecryptVerifyResult>(new DecryptVerifyResult(Verify, //
vr,
DecryptionResult(),
plaintext,
{},
{},
QString(),
inputLabel(),
outputLabel(),
auditLog,
this,
informativeSender()));
}
std::shared_ptr<DecryptVerifyResult>
AbstractDecryptVerifyTask::fromVerifyOpaqueResult(const GpgME::Error &err, const QString &details, const AuditLogEntry &auditLog)
{
return std::shared_ptr<DecryptVerifyResult>(new DecryptVerifyResult(Verify, //
VerificationResult(err),
DecryptionResult(),
QByteArray(),
{},
err,
details,
inputLabel(),
outputLabel(),
auditLog,
this,
informativeSender()));
}
std::shared_ptr<DecryptVerifyResult> AbstractDecryptVerifyTask::fromVerifyDetachedResult(const VerificationResult &vr, const AuditLogEntry &auditLog)
{
return std::shared_ptr<DecryptVerifyResult>(new DecryptVerifyResult(Verify, //
vr,
DecryptionResult(),
QByteArray(),
{},
{},
QString(),
inputLabel(),
outputLabel(),
auditLog,
this,
informativeSender()));
}
std::shared_ptr<DecryptVerifyResult>
AbstractDecryptVerifyTask::fromVerifyDetachedResult(const GpgME::Error &err, const QString &details, const AuditLogEntry &auditLog)
{
return std::shared_ptr<DecryptVerifyResult>(new DecryptVerifyResult(Verify, //
VerificationResult(err),
DecryptionResult(),
QByteArray(),
{},
err,
details,
inputLabel(),
outputLabel(),
auditLog,
this,
informativeSender()));
}
DecryptVerifyResult::DecryptVerifyResult(DecryptVerifyOperation type,
const VerificationResult &vr,
const DecryptionResult &dr,
const QByteArray &stuff,
const QString &fileName,
const GpgME::Error &error,
const QString &errString,
const QString &inputLabel,
const QString &outputLabel,
const AuditLogEntry &auditLog,
Task *parentTask,
const Mailbox &informativeSender)
: Task::Result()
, d(new Private(type, vr, dr, stuff, fileName, error, errString, inputLabel, outputLabel, auditLog, parentTask, informativeSender, this))
{
}
Task::Result::ContentType DecryptVerifyResult::viewableContentType() const
{
#if QGPGME_SUPPORTS_IS_MIME
if (decryptionResult().isMime()) {
return Task::Result::ContentType::Mime;
}
#endif
if (fileName().endsWith(QStringLiteral("openpgp-encrypted-message"))) {
return Task::Result::ContentType::Mime;
}
QMimeDatabase mimeDatabase;
const auto mimeType = mimeDatabase.mimeTypeForFile(fileName());
if (mimeTypeInherits(mimeType, QStringLiteral("message/rfc822"))) {
return Task::Result::ContentType::Mime;
}
if (mimeTypeInherits(mimeType, QStringLiteral("application/mbox"))) {
return Task::Result::ContentType::Mbox;
}
return Task::Result::ContentType::None;
}
QString DecryptVerifyResult::overview() const
{
QString ov;
if (d->isDecryptOnly()) {
ov += formatDecryptionResultOverview(d->m_decryptionResult);
} else if (d->isVerifyOnly()) {
ov += formatVerificationResultOverview(d->m_verificationResult, d->makeSenderInfo());
} else {
ov += formatDecryptVerifyResultOverview(d->m_decryptionResult, d->m_verificationResult, d->makeSenderInfo());
}
if (ov.size() + d->label().size() > 120) {
// Avoid ugly breaks
ov = QStringLiteral("<br>") + ov;
}
- return i18nc("label: result example: foo.sig: Verification failed. ", "%1: %2", d->label(), ov);
+ // TODO how does this behave for decrypt or decrpytverify?
+ return d->label();
+}
+
+static Task::Result::VisualCode codeForSignature(const Signature &signature)
+{
+ if (signature.summary() & Signature::Red) {
+ return Task::Result::VisualCode::Danger;
+ }
+ if (signature.summary() & Signature::Valid || signature.summary() & Signature::Green) {
+ return Task::Result::AllGood;
+ }
+ return Task::Result::Warning;
+}
+
+QList<Task::Result::ResultListItem> DecryptVerifyResult::detailsList() const
+{
+ QList<Task::Result::ResultListItem> details;
+ for (const Signature &sig : d->m_verificationResult.signatures()) {
+ details += Task::Result::ResultListItem{
+ .details = Kleo::Formatting::prettySignature(sig, d->makeSenderInfo().informativeSender),
+ .code = codeForSignature(sig),
+ };
+ }
+ return details;
}
QString DecryptVerifyResult::details() const
{
if (d->isDecryptOnly()) {
return formatDecryptionResultDetails(d->m_decryptionResult,
KeyCache::instance()->findRecipients(d->m_decryptionResult),
errorString(),
false,
d->m_parentTask);
}
if (d->isVerifyOnly()) {
return formatVerificationResultDetails(d->m_verificationResult, d->makeSenderInfo(), errorString());
}
return formatDecryptVerifyResultDetails(d->m_decryptionResult,
d->m_verificationResult,
KeyCache::instance()->findRecipients(d->m_decryptionResult),
d->makeSenderInfo(),
errorString(),
d->m_parentTask);
}
GpgME::Error DecryptVerifyResult::error() const
{
return d->m_error;
}
QString DecryptVerifyResult::errorString() const
{
return d->m_errorString;
}
AuditLogEntry DecryptVerifyResult::auditLog() const
{
return d->m_auditLog;
}
QPointer<Task> DecryptVerifyResult::parentTask() const
{
return d->m_parentTask;
}
Task::Result::VisualCode DecryptVerifyResult::code() const
{
if ((d->m_type == DecryptVerify || d->m_type == Verify) && relevantInDecryptVerifyContext(verificationResult())) {
return codeForVerificationResult(verificationResult());
}
return hasError() ? NeutralError : NeutralSuccess;
}
GpgME::VerificationResult DecryptVerifyResult::verificationResult() const
{
return d->m_verificationResult;
}
GpgME::DecryptionResult DecryptVerifyResult::decryptionResult() const
{
return d->m_decryptionResult;
}
QString DecryptVerifyResult::fileName() const
{
return d->m_fileName;
}
class AbstractDecryptVerifyTask::Private
{
public:
Mailbox informativeSender;
QPointer<QGpgME::Job> job;
};
AbstractDecryptVerifyTask::AbstractDecryptVerifyTask(QObject *parent)
: Task(parent)
, d(new Private)
{
}
AbstractDecryptVerifyTask::~AbstractDecryptVerifyTask()
{
}
void AbstractDecryptVerifyTask::cancel()
{
qCDebug(KLEOPATRA_LOG) << this << __func__;
if (d->job) {
d->job->slotCancel();
}
}
Mailbox AbstractDecryptVerifyTask::informativeSender() const
{
return d->informativeSender;
}
void AbstractDecryptVerifyTask::setInformativeSender(const Mailbox &sender)
{
d->informativeSender = sender;
}
QGpgME::Job *AbstractDecryptVerifyTask::job() const
{
return d->job;
}
void AbstractDecryptVerifyTask::setJob(QGpgME::Job *job)
{
d->job = job;
}
class DecryptVerifyTask::Private
{
DecryptVerifyTask *const q;
public:
explicit Private(DecryptVerifyTask *qq)
: q{qq}
{
}
void startDecryptVerifyJob();
void startDecryptVerifyArchiveJob();
void slotResult(const DecryptionResult &, const VerificationResult &, const QByteArray & = {});
std::shared_ptr<Input> m_input;
std::shared_ptr<Output> m_output;
const QGpgME::Protocol *m_backend = nullptr;
Protocol m_protocol = UnknownProtocol;
bool m_ignoreMDCError = false;
bool m_extractArchive = false;
QString m_inputFilePath;
QString m_outputFilePath;
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...", inputLabel());
}
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() : QFileInfo{d->m_inputFilePath}.fileName();
}
QString DecryptVerifyTask::outputLabel() const
{
if (d->m_output) {
return d->m_output->label();
} else if (!d->m_outputFilePath.isEmpty()) {
return QFileInfo{d->m_outputFilePath}.fileName();
} else {
return d->m_outputDirectory;
}
}
Protocol DecryptVerifyTask::protocol() const
{
return d->m_protocol;
}
static void ensureIOOpen(QIODevice *input, QIODevice *output)
{
if (input && !input->isOpen()) {
input->open(QIODevice::ReadOnly);
}
if (output && !output->isOpen()) {
output->open(QIODevice::WriteOnly);
}
}
void DecryptVerifyTask::setIgnoreMDCError(bool value)
{
d->m_ignoreMDCError = value;
}
void DecryptVerifyTask::setExtractArchive(bool extract)
{
d->m_extractArchive = extract;
}
void DecryptVerifyTask::setInputFile(const QString &path)
{
d->m_inputFilePath = path;
}
void DecryptVerifyTask::setOutputFile(const QString &path)
{
d->m_outputFilePath = path;
}
void DecryptVerifyTask::setOutputDirectory(const QString &directory)
{
d->m_outputDirectory = directory;
}
static bool archiveJobsCanBeUsed(GpgME::Protocol protocol)
{
return (protocol == GpgME::OpenPGP) && QGpgME::DecryptVerifyArchiveJob::isSupported();
}
void DecryptVerifyTask::doStart()
{
kleo_assert(d->m_backend);
if (d->m_extractArchive && archiveJobsCanBeUsed(d->m_protocol)) {
d->startDecryptVerifyArchiveJob();
} else {
d->startDecryptVerifyJob();
}
}
static void setIgnoreMDCErrorFlag(QGpgME::Job *job, bool ignoreMDCError)
{
if (ignoreMDCError) {
qCDebug(KLEOPATRA_LOG) << "Modifying job to ignore MDC errors.";
auto ctx = QGpgME::Job::context(job);
if (!ctx) {
qCWarning(KLEOPATRA_LOG) << "Failed to get context for job";
} else {
const auto err = ctx->setFlag("ignore-mdc-error", "1");
if (err) {
qCWarning(KLEOPATRA_LOG) << "Failed to set ignore mdc errors" << Formatting::errorAsString(err);
}
}
}
}
void DecryptVerifyTask::Private::startDecryptVerifyJob()
{
#if QGPGME_FILE_JOBS_SUPPORT_DIRECT_FILE_IO
if (!m_outputFilePath.isEmpty() && QFile::exists(m_outputFilePath)) {
// The output files are always written to a temporary location. Therefore, this can only occur
// if two signed/encrypted files with the same name in different folders are verified/decrypted
// because they would be written to the same temporary location.
QMetaObject::invokeMethod(
q,
[this]() {
slotResult(DecryptionResult{Error::fromCode(GPG_ERR_EEXIST)}, VerificationResult{});
},
Qt::QueuedConnection);
return;
}
#endif
try {
std::unique_ptr<QGpgME::DecryptVerifyJob> job{m_backend->decryptVerifyJob()};
kleo_assert(job);
setIgnoreMDCErrorFlag(job.get(), m_ignoreMDCError);
QObject::connect(job.get(),
&QGpgME::DecryptVerifyJob::result,
q,
[this](const GpgME::DecryptionResult &decryptResult, const GpgME::VerificationResult &verifyResult, const QByteArray &plainText) {
slotResult(decryptResult, verifyResult, plainText);
});
connect(job.get(), &QGpgME::Job::jobProgress, q, &DecryptVerifyTask::setProgress);
#if QGPGME_SUPPORTS_PROCESS_ALL_SIGNATURES
job->setProcessAllSignatures(true);
#endif
#if QGPGME_FILE_JOBS_SUPPORT_DIRECT_FILE_IO
if (!m_inputFilePath.isEmpty() && !m_outputFilePath.isEmpty()) {
job->setInputFile(m_inputFilePath);
job->setOutputFile(m_outputFilePath);
const auto err = job->startIt();
} else {
ensureIOOpen(m_input->ioDevice().get(), m_output->ioDevice().get());
job->start(m_input->ioDevice(), m_output->ioDevice());
}
#else
ensureIOOpen(m_input->ioDevice().get(), m_output->ioDevice().get());
job->start(m_input->ioDevice(), m_output->ioDevice());
#endif
q->setJob(job.release());
} catch (const GpgME::Exception &e) {
q->emitResult(q->fromDecryptVerifyResult(e.error(), QString::fromLocal8Bit(e.what()), AuditLogEntry()));
} catch (const std::exception &e) {
q->emitResult(
q->fromDecryptVerifyResult(Error::fromCode(GPG_ERR_INTERNAL), i18n("Caught exception: %1", QString::fromLocal8Bit(e.what())), AuditLogEntry()));
} catch (...) {
q->emitResult(q->fromDecryptVerifyResult(Error::fromCode(GPG_ERR_INTERNAL), i18n("Caught unknown exception"), AuditLogEntry()));
}
}
void DecryptVerifyTask::Private::startDecryptVerifyArchiveJob()
{
std::unique_ptr<QGpgME::DecryptVerifyArchiveJob> job{m_backend->decryptVerifyArchiveJob()};
kleo_assert(job);
setIgnoreMDCErrorFlag(job.get(), m_ignoreMDCError);
connect(job.get(),
&QGpgME::DecryptVerifyArchiveJob::result,
q,
[this](const GpgME::DecryptionResult &decryptResult, const GpgME::VerificationResult &verifyResult) {
slotResult(decryptResult, verifyResult);
});
connect(job.get(), &QGpgME::Job::jobProgress, q, &DecryptVerifyTask::setProgress);
// make sure that we don't use an existing output directory
const auto outputDirectory = ensureUniqueDirectory(m_outputDirectory);
if (outputDirectory.isEmpty()) {
q->emitResult(q->fromDecryptVerifyResult(Error::fromCode(GPG_ERR_GENERAL), {}, {}));
return;
}
m_outputDirectory = outputDirectory;
#if QGPGME_SUPPORTS_PROCESS_ALL_SIGNATURES
job->setProcessAllSignatures(true);
#endif
job->setInputFile(m_inputFilePath);
job->setOutputDirectory(m_outputDirectory);
const auto err = job->startIt();
q->setJob(job.release());
if (err) {
q->emitResult(q->fromDecryptVerifyResult(err, {}, {}));
}
}
class DecryptTask::Private
{
DecryptTask *const q;
public:
explicit Private(DecryptTask *qq)
: q{qq}
{
}
void slotResult(const DecryptionResult &, const QByteArray &);
void registerJob(QGpgME::DecryptJob *job)
{
q->connect(job, SIGNAL(result(GpgME::DecryptionResult, QByteArray)), q, SLOT(slotResult(GpgME::DecryptionResult, QByteArray)));
q->connect(job, &QGpgME::Job::jobProgress, q, &DecryptTask::setProgress);
}
std::shared_ptr<Input> m_input;
std::shared_ptr<Output> m_output;
const QGpgME::Protocol *m_backend = nullptr;
Protocol m_protocol = UnknownProtocol;
};
void DecryptTask::Private::slotResult(const DecryptionResult &result, const QByteArray &plainText)
{
{
std::stringstream ss;
ss << result;
qCDebug(KLEOPATRA_LOG) << ss.str().c_str();
}
const AuditLogEntry auditLog = auditLogFromSender(q->sender());
if (result.error().code()) {
m_output->cancel();
} else {
try {
kleo_assert(!result.isNull());
m_output->finalize();
} catch (const GpgME::Exception &e) {
q->emitResult(q->fromDecryptResult(e.error(), QString::fromLocal8Bit(e.what()), auditLog));
return;
} catch (const std::exception &e) {
q->emitResult(q->fromDecryptResult(Error::fromCode(GPG_ERR_INTERNAL), i18n("Caught exception: %1", QString::fromLocal8Bit(e.what())), auditLog));
return;
} catch (...) {
q->emitResult(q->fromDecryptResult(Error::fromCode(GPG_ERR_INTERNAL), i18n("Caught unknown exception"), auditLog));
return;
}
}
const int drErr = result.error().code();
const QString errorString = m_output->errorString();
if (((drErr == GPG_ERR_EIO || drErr == GPG_ERR_NO_DATA) && !errorString.isEmpty()) || m_output->failed()) {
q->emitResult(q->fromDecryptResult(result.error() ? result.error() : Error::fromCode(GPG_ERR_EIO), errorString, auditLog));
return;
}
q->emitResult(q->fromDecryptResult(result, plainText, auditLog));
}
DecryptTask::DecryptTask(QObject *parent)
: AbstractDecryptVerifyTask(parent)
, d(new Private(this))
{
}
DecryptTask::~DecryptTask()
{
}
void DecryptTask::setInput(const std::shared_ptr<Input> &input)
{
d->m_input = input;
kleo_assert(d->m_input && d->m_input->ioDevice());
}
void DecryptTask::setOutput(const std::shared_ptr<Output> &output)
{
d->m_output = output;
kleo_assert(d->m_output && d->m_output->ioDevice());
}
void DecryptTask::setProtocol(Protocol prot)
{
kleo_assert(prot != UnknownProtocol);
d->m_protocol = prot;
d->m_backend = (prot == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime();
kleo_assert(d->m_backend);
}
void DecryptTask::autodetectProtocolFromInput()
{
if (!d->m_input) {
return;
}
const Protocol p = findProtocol(d->m_input->classification());
if (p == UnknownProtocol) {
throw Exception(gpg_error(GPG_ERR_NOTHING_FOUND),
i18n("Could not determine whether this was S/MIME- or OpenPGP-encrypted - maybe it is not ciphertext at all?"),
Exception::MessageOnly);
}
setProtocol(p);
}
QString DecryptTask::label() const
{
return i18n("Decrypting: %1...", d->m_input->label());
}
unsigned long long DecryptTask::inputSize() const
{
return d->m_input ? d->m_input->size() : 0;
}
QString DecryptTask::inputLabel() const
{
return d->m_input ? d->m_input->label() : QString();
}
QString DecryptTask::outputLabel() const
{
return d->m_output ? d->m_output->label() : QString();
}
Protocol DecryptTask::protocol() const
{
return d->m_protocol;
}
void DecryptTask::doStart()
{
kleo_assert(d->m_backend);
try {
std::unique_ptr<QGpgME::DecryptJob> job{d->m_backend->decryptJob()};
kleo_assert(job);
d->registerJob(job.get());
ensureIOOpen(d->m_input->ioDevice().get(), d->m_output->ioDevice().get());
job->start(d->m_input->ioDevice(), d->m_output->ioDevice());
setJob(job.release());
} catch (const GpgME::Exception &e) {
emitResult(fromDecryptResult(e.error(), QString::fromLocal8Bit(e.what()), AuditLogEntry()));
} catch (const std::exception &e) {
emitResult(fromDecryptResult(Error::fromCode(GPG_ERR_INTERNAL), i18n("Caught exception: %1", QString::fromLocal8Bit(e.what())), AuditLogEntry()));
} catch (...) {
emitResult(fromDecryptResult(Error::fromCode(GPG_ERR_INTERNAL), i18n("Caught unknown exception"), AuditLogEntry()));
}
}
class VerifyOpaqueTask::Private
{
VerifyOpaqueTask *const q;
public:
explicit Private(VerifyOpaqueTask *qq)
: q{qq}
{
}
void startVerifyOpaqueJob();
void startDecryptVerifyArchiveJob();
void slotResult(const VerificationResult &, const QByteArray & = {});
std::shared_ptr<Input> m_input;
std::shared_ptr<Output> m_output;
const QGpgME::Protocol *m_backend = nullptr;
Protocol m_protocol = UnknownProtocol;
bool m_extractArchive = false;
QString m_inputFilePath;
QString m_outputFilePath;
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...", inputLabel());
}
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() : QFileInfo{d->m_inputFilePath}.fileName();
}
QString VerifyOpaqueTask::outputLabel() const
{
if (d->m_output) {
return d->m_output->label();
} else if (!d->m_outputFilePath.isEmpty()) {
return QFileInfo{d->m_outputFilePath}.fileName();
} else {
return d->m_outputDirectory;
}
}
Protocol VerifyOpaqueTask::protocol() const
{
return d->m_protocol;
}
void VerifyOpaqueTask::setExtractArchive(bool extract)
{
d->m_extractArchive = extract;
}
void VerifyOpaqueTask::setInputFile(const QString &path)
{
d->m_inputFilePath = path;
}
void VerifyOpaqueTask::setOutputFile(const QString &path)
{
d->m_outputFilePath = path;
}
void VerifyOpaqueTask::setOutputDirectory(const QString &directory)
{
d->m_outputDirectory = directory;
}
void VerifyOpaqueTask::doStart()
{
kleo_assert(d->m_backend);
if (d->m_extractArchive && archiveJobsCanBeUsed(d->m_protocol)) {
d->startDecryptVerifyArchiveJob();
} else {
d->startVerifyOpaqueJob();
}
}
void VerifyOpaqueTask::Private::startVerifyOpaqueJob()
{
#if QGPGME_FILE_JOBS_SUPPORT_DIRECT_FILE_IO
if (!m_outputFilePath.isEmpty() && QFile::exists(m_outputFilePath)) {
// The output files are always written to a temporary location. Therefore, this can only occur
// if two signed/encrypted files with the same name in different folders are verified/decrypted
// because they would be written to the same temporary location.
QMetaObject::invokeMethod(
q,
[this]() {
slotResult(VerificationResult{Error::fromCode(GPG_ERR_EEXIST)});
},
Qt::QueuedConnection);
return;
}
#endif
try {
std::unique_ptr<QGpgME::VerifyOpaqueJob> job{m_backend->verifyOpaqueJob()};
kleo_assert(job);
connect(job.get(), &QGpgME::VerifyOpaqueJob::result, q, [this](const GpgME::VerificationResult &result, const QByteArray &plainText) {
slotResult(result, plainText);
});
connect(job.get(), &QGpgME::Job::jobProgress, q, &VerifyOpaqueTask::setProgress);
#if QGPGME_SUPPORTS_PROCESS_ALL_SIGNATURES
job->setProcessAllSignatures(true);
#endif
#if QGPGME_FILE_JOBS_SUPPORT_DIRECT_FILE_IO
if (!m_inputFilePath.isEmpty() && !m_outputFilePath.isEmpty()) {
job->setInputFile(m_inputFilePath);
job->setOutputFile(m_outputFilePath);
const auto err = job->startIt();
} else {
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>());
}
#else
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>());
#endif
q->setJob(job.release());
} catch (const GpgME::Exception &e) {
q->emitResult(q->fromVerifyOpaqueResult(e.error(), QString::fromLocal8Bit(e.what()), AuditLogEntry()));
} catch (const std::exception &e) {
q->emitResult(
q->fromVerifyOpaqueResult(Error::fromCode(GPG_ERR_INTERNAL), i18n("Caught exception: %1", QString::fromLocal8Bit(e.what())), AuditLogEntry()));
} catch (...) {
q->emitResult(q->fromVerifyOpaqueResult(Error::fromCode(GPG_ERR_INTERNAL), i18n("Caught unknown exception"), AuditLogEntry()));
}
}
void VerifyOpaqueTask::Private::startDecryptVerifyArchiveJob()
{
std::unique_ptr<QGpgME::DecryptVerifyArchiveJob> job{m_backend->decryptVerifyArchiveJob()};
kleo_assert(job);
connect(job.get(), &QGpgME::DecryptVerifyArchiveJob::result, q, [this](const DecryptionResult &, const VerificationResult &verifyResult) {
slotResult(verifyResult);
});
connect(job.get(), &QGpgME::DecryptVerifyArchiveJob::dataProgress, q, &VerifyOpaqueTask::setProgress);
// make sure that we don't use an existing output directory
const auto outputDirectory = ensureUniqueDirectory(m_outputDirectory);
if (outputDirectory.isEmpty()) {
q->emitResult(q->fromDecryptVerifyResult(Error::fromCode(GPG_ERR_GENERAL), {}, {}));
return;
}
m_outputDirectory = outputDirectory;
#if QGPGME_SUPPORTS_PROCESS_ALL_SIGNATURES
job->setProcessAllSignatures(true);
#endif
job->setInputFile(m_inputFilePath);
job->setOutputDirectory(m_outputDirectory);
const auto err = job->startIt();
q->setJob(job.release());
if (err) {
q->emitResult(q->fromVerifyOpaqueResult(err, {}, {}));
}
}
class VerifyDetachedTask::Private
{
VerifyDetachedTask *const q;
public:
explicit Private(VerifyDetachedTask *qq)
: q{qq}
{
}
void slotResult(const VerificationResult &);
void registerJob(QGpgME::VerifyDetachedJob *job)
{
q->connect(job, SIGNAL(result(GpgME::VerificationResult)), q, SLOT(slotResult(GpgME::VerificationResult)));
q->connect(job, &QGpgME::Job::jobProgress, q, &VerifyDetachedTask::setProgress);
}
QString signatureLabel() const;
QString signedDataLabel() const;
std::shared_ptr<Input> m_input, m_signedData;
const QGpgME::Protocol *m_backend = nullptr;
Protocol m_protocol = UnknownProtocol;
QString m_signatureFilePath;
QString m_signedFilePath;
};
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));
}
}
QString VerifyDetachedTask::Private::signatureLabel() const
{
return m_input ? m_input->label() : m_signatureFilePath;
}
QString VerifyDetachedTask::Private::signedDataLabel() const
{
return m_signedData ? m_signedData->label() : m_signedFilePath;
}
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::setSignatureFile(const QString &path)
{
d->m_signatureFilePath = path;
}
void VerifyDetachedTask::setSignedFile(const QString &path)
{
d->m_signedFilePath = path;
}
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
{
const QString signedDataLabel = d->signedDataLabel();
if (!signedDataLabel.isEmpty()) {
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>...",
signedDataLabel,
d->signatureLabel());
}
return i18n("Verifying signature %1...", d->signatureLabel());
}
QString VerifyDetachedTask::inputLabel() const
{
const QString signatureLabel = d->signatureLabel();
const QString signedDataLabel = d->signedDataLabel();
if (!signedDataLabel.isEmpty() && !signatureLabel.isEmpty()) {
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>",
signedDataLabel,
signatureLabel);
}
return signatureLabel;
}
QString VerifyDetachedTask::outputLabel() const
{
return QString();
}
Protocol VerifyDetachedTask::protocol() const
{
return d->m_protocol;
}
void VerifyDetachedTask::doStart()
{
kleo_assert(d->m_backend);
try {
std::unique_ptr<QGpgME::VerifyDetachedJob> job{d->m_backend->verifyDetachedJob()};
kleo_assert(job);
d->registerJob(job.get());
#if QGPGME_SUPPORTS_PROCESS_ALL_SIGNATURES
job->setProcessAllSignatures(true);
#endif
#if QGPGME_FILE_JOBS_SUPPORT_DIRECT_FILE_IO
if (d->m_protocol == GpgME::OpenPGP && !d->m_signatureFilePath.isEmpty() && !d->m_signedFilePath.isEmpty()) {
job->setSignatureFile(d->m_signatureFilePath);
job->setSignedFile(d->m_signedFilePath);
job->startIt();
} else {
ensureIOOpen(d->m_input->ioDevice().get(), nullptr);
ensureIOOpen(d->m_signedData->ioDevice().get(), nullptr);
job->start(d->m_input->ioDevice(), d->m_signedData->ioDevice());
}
#else
ensureIOOpen(d->m_input->ioDevice().get(), nullptr);
ensureIOOpen(d->m_signedData->ioDevice().get(), nullptr);
job->start(d->m_input->ioDevice(), d->m_signedData->ioDevice());
#endif
setJob(job.release());
} catch (const GpgME::Exception &e) {
emitResult(fromVerifyDetachedResult(e.error(), QString::fromLocal8Bit(e.what()), AuditLogEntry()));
} catch (const std::exception &e) {
emitResult(
fromVerifyDetachedResult(Error::fromCode(GPG_ERR_INTERNAL), i18n("Caught exception: %1", QString::fromLocal8Bit(e.what())), AuditLogEntry()));
} catch (...) {
emitResult(fromVerifyDetachedResult(Error::fromCode(GPG_ERR_INTERNAL), i18n("Caught unknown exception"), AuditLogEntry()));
}
}
#include "moc_decryptverifytask.cpp"
diff --git a/src/crypto/decryptverifytask.h b/src/crypto/decryptverifytask.h
index 1bf79e60a..81bc7c657 100644
--- a/src/crypto/decryptverifytask.h
+++ b/src/crypto/decryptverifytask.h
@@ -1,276 +1,282 @@
/* -*- mode: c++; c-basic-offset:4 -*-
decryptverifytask.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 "task.h"
#include <utils/types.h>
#include <gpgme++/verificationresult.h>
#include <memory>
namespace KMime
{
namespace Types
{
class Mailbox;
}
}
namespace GpgME
{
class DecryptionResult;
class VerificationResult;
class Key;
class Signature;
}
namespace QGpgME
{
class Job;
}
namespace Kleo
{
class Input;
class Output;
class AuditLogEntry;
}
namespace Kleo
{
namespace Crypto
{
class DecryptVerifyResult;
+struct SignatureResult {
+ QString details;
+ Task::Result::VisualCode code;
+};
+
class AbstractDecryptVerifyTask : public Task
{
Q_OBJECT
public:
explicit AbstractDecryptVerifyTask(QObject *parent = nullptr);
~AbstractDecryptVerifyTask() override;
virtual void autodetectProtocolFromInput() = 0;
KMime::Types::Mailbox informativeSender() const;
void setInformativeSender(const KMime::Types::Mailbox &senders);
virtual QString inputLabel() const = 0;
virtual QString outputLabel() const = 0;
public Q_SLOTS:
void cancel() override;
protected:
std::shared_ptr<DecryptVerifyResult> fromDecryptResult(const GpgME::DecryptionResult &dr, const QByteArray &plaintext, const AuditLogEntry &auditLog);
std::shared_ptr<DecryptVerifyResult> fromDecryptResult(const GpgME::Error &err, const QString &details, const AuditLogEntry &auditLog);
std::shared_ptr<DecryptVerifyResult> fromDecryptVerifyResult(const GpgME::DecryptionResult &dr,
const GpgME::VerificationResult &vr,
const QByteArray &plaintext,
const QString &fileName,
const AuditLogEntry &auditLog);
std::shared_ptr<DecryptVerifyResult> fromDecryptVerifyResult(const GpgME::Error &err, const QString &what, const AuditLogEntry &auditLog);
std::shared_ptr<DecryptVerifyResult>
fromVerifyOpaqueResult(const GpgME::VerificationResult &vr, const QByteArray &plaintext, const AuditLogEntry &auditLog);
std::shared_ptr<DecryptVerifyResult> fromVerifyOpaqueResult(const GpgME::Error &err, const QString &details, const AuditLogEntry &auditLog);
std::shared_ptr<DecryptVerifyResult> fromVerifyDetachedResult(const GpgME::VerificationResult &vr, const AuditLogEntry &auditLog);
std::shared_ptr<DecryptVerifyResult> fromVerifyDetachedResult(const GpgME::Error &err, const QString &details, const AuditLogEntry &auditLog);
public:
// public to allow access from the Private classes of the concrete tasks
QGpgME::Job *job() const;
void setJob(QGpgME::Job *job);
private:
class Private;
const std::unique_ptr<Private> d;
};
class DecryptTask : public AbstractDecryptVerifyTask
{
Q_OBJECT
public:
explicit DecryptTask(QObject *parent = nullptr);
~DecryptTask() override;
void setInput(const std::shared_ptr<Input> &input);
void setOutput(const std::shared_ptr<Output> &output);
void setProtocol(GpgME::Protocol prot);
void autodetectProtocolFromInput() override;
QString label() const override;
GpgME::Protocol protocol() const override;
QString inputLabel() const override;
QString outputLabel() const override;
private:
void doStart() override;
unsigned long long inputSize() const override;
private:
class Private;
const std::unique_ptr<Private> d;
Q_PRIVATE_SLOT(d, void slotResult(GpgME::DecryptionResult, QByteArray))
};
class VerifyDetachedTask : public AbstractDecryptVerifyTask
{
Q_OBJECT
public:
explicit VerifyDetachedTask(QObject *parent = nullptr);
~VerifyDetachedTask() override;
void setInput(const std::shared_ptr<Input> &input);
void setSignedData(const std::shared_ptr<Input> &signedData);
void setSignatureFile(const QString &path);
void setSignedFile(const QString &path);
void setProtocol(GpgME::Protocol prot);
void autodetectProtocolFromInput() override;
QString label() const override;
GpgME::Protocol protocol() const override;
QString inputLabel() const override;
QString outputLabel() const override;
private:
void doStart() override;
unsigned long long inputSize() const override;
private:
class Private;
const std::unique_ptr<Private> d;
Q_PRIVATE_SLOT(d, void slotResult(GpgME::VerificationResult))
};
class VerifyOpaqueTask : public AbstractDecryptVerifyTask
{
Q_OBJECT
public:
explicit VerifyOpaqueTask(QObject *parent = nullptr);
~VerifyOpaqueTask() override;
void setInput(const std::shared_ptr<Input> &input);
void setOutput(const std::shared_ptr<Output> &output);
void setProtocol(GpgME::Protocol prot);
void autodetectProtocolFromInput() override;
QString label() const override;
GpgME::Protocol protocol() const override;
void setExtractArchive(bool extract);
void setInputFile(const QString &path);
// for files
void setOutputFile(const QString &path);
// for archives
void setOutputDirectory(const QString &directory);
QString inputLabel() const override;
QString outputLabel() const override;
private:
void doStart() override;
unsigned long long inputSize() const override;
private:
class Private;
const std::unique_ptr<Private> d;
Q_PRIVATE_SLOT(d, void slotResult(GpgME::VerificationResult, QByteArray))
};
class DecryptVerifyTask : public AbstractDecryptVerifyTask
{
Q_OBJECT
public:
explicit DecryptVerifyTask(QObject *parent = nullptr);
~DecryptVerifyTask() override;
void setInput(const std::shared_ptr<Input> &input);
void setOutput(const std::shared_ptr<Output> &output);
void setProtocol(GpgME::Protocol prot);
void autodetectProtocolFromInput() override;
QString label() const override;
GpgME::Protocol protocol() const override;
void setIgnoreMDCError(bool value);
void setExtractArchive(bool extract);
void setInputFile(const QString &path);
// for files
void setOutputFile(const QString &path);
// for archives
void setOutputDirectory(const QString &directory);
QString inputLabel() const override;
QString outputLabel() const override;
private:
void doStart() override;
unsigned long long inputSize() const override;
private:
class Private;
const std::unique_ptr<Private> d;
Q_PRIVATE_SLOT(d, void slotResult(GpgME::DecryptionResult, GpgME::VerificationResult, QByteArray))
};
class DecryptVerifyResult : public Task::Result
{
friend class ::Kleo::Crypto::AbstractDecryptVerifyTask;
public:
class SenderInfo;
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;
QPointer<Task> parentTask() const override;
Task::Result::ContentType viewableContentType() const override;
+ QList<Task::Result::ResultListItem> detailsList() const override;
GpgME::VerificationResult verificationResult() const;
GpgME::DecryptionResult decryptionResult() const;
QString fileName() const;
private:
DecryptVerifyResult();
DecryptVerifyResult(const DecryptVerifyResult &);
DecryptVerifyResult &operator=(const DecryptVerifyResult &other);
DecryptVerifyResult(DecryptVerifyOperation op,
const GpgME::VerificationResult &vr,
const GpgME::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 KMime::Types::Mailbox &informativeSender);
private:
class Private;
const std::unique_ptr<Private> d;
};
}
}
diff --git a/src/crypto/gui/decryptverifyfilesdialog.cpp b/src/crypto/gui/decryptverifyfilesdialog.cpp
index 4ad0f29c0..143041823 100644
--- a/src/crypto/gui/decryptverifyfilesdialog.cpp
+++ b/src/crypto/gui/decryptverifyfilesdialog.cpp
@@ -1,255 +1,263 @@
/* 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/decryptverifytask.h"
#include "crypto/gui/resultlistwidget.h"
#include "crypto/gui/resultpage.h"
#include "crypto/taskcollection.h"
#include "utils/path-helper.h"
#include <Libkleo/FileNameRequester>
#include <QLabel>
#include <QProgressBar>
#include <QPushButton>
#include <QVBoxLayout>
#include <QWindow>
#include <vector>
#include <KConfigGroup>
#include <KLocalizedContext>
#include <KLocalizedString>
#include <KMessageBox>
#include <KSharedConfig>
+#include <KTitleWidget>
#include <MimeTreeParserWidgets/MessageViewerDialog>
using namespace Kleo;
using namespace Kleo::Crypto;
using namespace Kleo::Crypto::Gui;
using namespace MimeTreeParser::Widgets;
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(i18nc("@label:textbox", "&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;
connect(m_resultList, &ResultListWidget::showButtonClicked, this, &DecryptVerifyFilesDialog::showContent);
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);
+ m_progressBar->setVisible(false);
for (const auto &i : m_progressLabelByTag.keys()) {
if (!i.isEmpty()) {
+ m_progressLabelByTag.value(i)->setVisible(false);
m_progressLabelByTag.value(i)->setText(i18n("%1: All operations completed.", i));
} else {
- m_progressLabelByTag.value(i)->setText(i18n("All operations completed."));
+ m_progressLabelByTag.value(i)->setVisible(false);
+ // TODO adjust decryption / verification / decryption and verification
+ m_progressLabelByTag.value(i)->setText(i18n("Verification finished"));
+ auto widget = new KTitleWidget;
+ widget->setText(i18n("Verification Finished"));
+ m_progressLabelLayout->addWidget(widget);
}
}
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(int progress, int total)
{
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."), i18nc("@title:window", "No Output Folder"));
return;
}
const QFileInfo fi(outLoc);
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,
xi18nc("@info",
"<para>Failed to create output folder <filename>%1</filename>.</para><para>Please select a different output folder.</para>",
outLoc),
i18nc("@title:window", "Unusable Output Folder"));
} else {
accept();
}
} else if (!fi.isDir()) {
KMessageBox::information(this, i18n("Please select a different output folder."), i18nc("@title:window", "Invalid Output Folder"));
} else if (!Kleo::isWritable(fi)) {
KMessageBox::information(
this,
xi18nc("@info",
"<para>Cannot write in the output folder <filename>%1</filename>.</para><para>Please select a different output folder.</para>",
outLoc),
i18nc("@title:window", "Unusable Output Folder"));
} else {
accept();
}
}
void DecryptVerifyFilesDialog::readConfig()
{
KConfigGroup dialog(KSharedConfig::openStateConfig(), QStringLiteral("DecryptVerifyFilesDialog"));
const QSize size = dialog.readEntry("Size", QSize(640, 480));
if (size.isValid()) {
resize(size);
}
}
void DecryptVerifyFilesDialog::writeConfig()
{
KConfigGroup dialog(KSharedConfig::openStateConfig(), QStringLiteral("DecryptVerifyFilesDialog"));
dialog.writeEntry("Size", size());
dialog.sync();
}
void DecryptVerifyFilesDialog::showContent(const std::shared_ptr<const Task::Result> &result)
{
if (auto decryptVerifyResult = std::dynamic_pointer_cast<const DecryptVerifyResult>(result)) {
MessageViewerDialog dialog(decryptVerifyResult->fileName());
dialog.exec();
}
}
#include "moc_decryptverifyfilesdialog.cpp"
diff --git a/src/crypto/gui/resultitemwidget.cpp b/src/crypto/gui/resultitemwidget.cpp
index a15d21509..2e0199e5d 100644
--- a/src/crypto/gui/resultitemwidget.cpp
+++ b/src/crypto/gui/resultitemwidget.cpp
@@ -1,375 +1,331 @@
/* -*- mode: c++; c-basic-offset:4 -*-
crypto/gui/resultitemwidget.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 "resultitemwidget.h"
#include "commands/command.h"
-#include "commands/importcertificatefromfilecommand.h"
#include "commands/lookupcertificatescommand.h"
#include "crypto/decryptverifytask.h"
#include "view/htmllabel.h"
#include "view/urllabel.h"
#include <Libkleo/AuditLogEntry>
#include <Libkleo/AuditLogViewer>
#include <Libkleo/Classify>
#include <Libkleo/SystemInfo>
#include <gpgme++/decryptionresult.h>
#include <gpgme++/key.h>
#include "kleopatra_debug.h"
#include <KColorScheme>
#include <KGuiItem>
#include <KLocalizedString>
#include <KStandardGuiItem>
#include <QHBoxLayout>
#include <QLabel>
#include <QPushButton>
#include <QUrl>
#include <QVBoxLayout>
using namespace Kleo;
using namespace Kleo::Crypto;
using namespace Kleo::Crypto::Gui;
namespace
{
// TODO move out of here
static QColor colorForVisualCode(Task::Result::VisualCode code)
{
switch (code) {
case Task::Result::AllGood:
return KColorScheme(QPalette::Active, KColorScheme::View).background(KColorScheme::PositiveBackground).color();
case Task::Result::NeutralError:
case Task::Result::Warning:
return KColorScheme(QPalette::Active, KColorScheme::View).background(KColorScheme::NormalBackground).color();
case Task::Result::Danger:
return KColorScheme(QPalette::Active, KColorScheme::View).background(KColorScheme::NegativeBackground).color();
case Task::Result::NeutralSuccess:
default:
return QColor(0x00, 0x80, 0xFF); // light blue
}
}
-static QColor txtColorForVisualCode(Task::Result::VisualCode code)
-{
- switch (code) {
- case Task::Result::AllGood:
- case Task::Result::NeutralError:
- case Task::Result::Warning:
- return KColorScheme(QPalette::Active, KColorScheme::View).foreground(KColorScheme::NormalText).color();
- case Task::Result::Danger:
- return KColorScheme(QPalette::Active, KColorScheme::View).foreground(KColorScheme::NegativeText).color();
- case Task::Result::NeutralSuccess:
- default:
- return QColor(0xFF, 0xFF, 0xFF); // white
- }
-}
}
class ResultItemWidget::Private
{
ResultItemWidget *const q;
public:
explicit Private(const std::shared_ptr<const Task::Result> &result, ResultItemWidget *qq)
: q(qq)
, m_result(result)
{
Q_ASSERT(m_result);
}
void slotLinkActivated(const QString &);
void updateShowDetailsLabel();
- void addKeyImportButton(QBoxLayout *lay, bool search);
void addIgnoreMDCButton(QBoxLayout *lay);
void oneImportFinished();
const std::shared_ptr<const Task::Result> m_result;
UrlLabel *m_auditLogLabel = nullptr;
QPushButton *m_closeButton = nullptr;
QPushButton *m_showButton = nullptr;
bool m_importCanceled = false;
};
void ResultItemWidget::Private::oneImportFinished()
{
if (m_importCanceled) {
return;
}
if (m_result->parentTask()) {
m_result->parentTask()->start();
}
q->setVisible(false);
}
void ResultItemWidget::Private::addIgnoreMDCButton(QBoxLayout *lay)
{
if (!m_result || !lay) {
return;
}
const auto dvResult = dynamic_cast<const DecryptVerifyResult *>(m_result.get());
if (!dvResult) {
return;
}
const auto decResult = dvResult->decryptionResult();
if (decResult.isNull() || !decResult.error() || !decResult.isLegacyCipherNoMDC()) {
return;
}
auto btn = new QPushButton(i18nc("@action:button", "Force decryption"));
btn->setFixedSize(btn->sizeHint());
connect(btn, &QPushButton::clicked, q, [this]() {
if (m_result->parentTask()) {
const auto dvTask = dynamic_cast<DecryptVerifyTask *>(m_result->parentTask().data());
dvTask->setIgnoreMDCError(true);
dvTask->start();
q->setVisible(false);
} else {
qCWarning(KLEOPATRA_LOG) << "Failed to get parent task";
}
});
lay->addWidget(btn);
}
-void ResultItemWidget::Private::addKeyImportButton(QBoxLayout *lay, bool search)
-{
- if (!m_result || !lay) {
- return;
- }
-
- const auto dvResult = dynamic_cast<const DecryptVerifyResult *>(m_result.get());
- if (!dvResult) {
- return;
- }
- const auto verifyResult = dvResult->verificationResult();
-
- if (verifyResult.isNull()) {
- return;
- }
-
- for (const auto &sig : verifyResult.signatures()) {
- if (!(sig.summary() & GpgME::Signature::KeyMissing)) {
- continue;
- }
-
- auto btn = new QPushButton;
- QString suffix;
- const auto keyid = QLatin1StringView(sig.fingerprint());
- if (verifyResult.numSignatures() > 1) {
- suffix = QLatin1Char(' ') + keyid;
- }
- btn = new QPushButton(search ? i18nc("1 is optional keyid. No space is intended as it can be empty.", "Search%1", suffix)
- : i18nc("1 is optional keyid. No space is intended as it can be empty.", "Import%1", suffix));
-
- if (search) {
- btn->setIcon(QIcon::fromTheme(QStringLiteral("edit-find")));
- connect(btn, &QPushButton::clicked, q, [this, btn, keyid]() {
- btn->setEnabled(false);
- m_importCanceled = false;
- auto cmd = new Kleo::Commands::LookupCertificatesCommand(keyid, nullptr);
- connect(cmd, &Kleo::Commands::LookupCertificatesCommand::canceled, q, [this]() {
- m_importCanceled = true;
- });
- connect(cmd, &Kleo::Commands::LookupCertificatesCommand::finished, q, [this, btn]() {
- btn->setEnabled(true);
- oneImportFinished();
- });
- cmd->setParentWidget(q);
- cmd->start();
- });
- } else {
- btn->setIcon(QIcon::fromTheme(QStringLiteral("view-certificate-import")));
- connect(btn, &QPushButton::clicked, q, [this, btn]() {
- btn->setEnabled(false);
- m_importCanceled = false;
- auto cmd = new Kleo::ImportCertificateFromFileCommand();
- connect(cmd, &Kleo::ImportCertificateFromFileCommand::canceled, q, [this]() {
- m_importCanceled = true;
- });
- connect(cmd, &Kleo::ImportCertificateFromFileCommand::finished, q, [this, btn]() {
- btn->setEnabled(true);
- oneImportFinished();
- });
- cmd->setParentWidget(q);
- cmd->start();
- });
- }
- btn->setFixedSize(btn->sizeHint());
- lay->addWidget(btn);
- }
-}
-
static QUrl auditlog_url_template()
{
QUrl url(QStringLiteral("kleoresultitem://showauditlog"));
return url;
}
void ResultItemWidget::Private::updateShowDetailsLabel()
{
const auto auditLogUrl = m_result->auditLog().asUrl(auditlog_url_template());
const auto auditLogLinkText = m_result->hasError() ? i18n("Diagnostics") //
: i18nc("The Audit Log is a detailed error log from the gnupg backend", "Show Audit Log");
m_auditLogLabel->setUrl(auditLogUrl, auditLogLinkText);
m_auditLogLabel->setVisible(!auditLogUrl.isEmpty());
}
ResultItemWidget::ResultItemWidget(const std::shared_ptr<const Task::Result> &result, QWidget *parent, Qt::WindowFlags flags)
: QWidget(parent, flags)
, d(new Private(result, this))
{
- const QColor color = colorForVisualCode(d->m_result->code());
- const QColor txtColor = txtColorForVisualCode(d->m_result->code());
- const QColor linkColor = SystemInfo::isHighContrastModeActive() ? QColor{} : txtColor;
+ const QColor color = colorForVisualCode(Task::Result::VisualCode::NeutralError);
const QString styleSheet = SystemInfo::isHighContrastModeActive()
? QStringLiteral(
- "QFrame,QLabel { margin: 0px; }"
- "QFrame#resultFrame{ border-style: solid; border-radius: 3px; border-width: 1px }"
- "QLabel { padding: 5px; border-radius: 3px }")
+ "QFrame,QLabel { margin: 0px; }"
+ "QFrame#resultFrame{ border-style: solid; border-radius: 3px; border-width: 1px }"
+ "QLabel { padding: 5px; border-radius: 3px }")
: QStringLiteral(
"QFrame,QLabel { background-color: %1; margin: 0px; }"
"QFrame#resultFrame{ border-color: %2; border-style: solid; border-radius: 3px; border-width: 1px }"
- "QLabel { color: %3; padding: 5px; border-radius: 3px }")
+ "QLabel { padding: 5px; border-radius: 3px }")
.arg(color.name())
- .arg(color.darker(150).name())
- .arg(txtColor.name());
+ .arg(color.darker(150).name());
auto topLayout = new QVBoxLayout(this);
auto frame = new QFrame;
frame->setObjectName(QLatin1StringView("resultFrame"));
frame->setStyleSheet(styleSheet);
topLayout->addWidget(frame);
- auto layout = new QHBoxLayout(frame);
- auto vlay = new QVBoxLayout();
+ auto vlay = new QVBoxLayout(frame);
+ auto layout = new QHBoxLayout();
auto overview = new HtmlLabel;
overview->setWordWrap(true);
overview->setHtml(d->m_result->overview());
overview->setStyleSheet(styleSheet);
- overview->setLinkColor(linkColor);
setFocusPolicy(overview->focusPolicy());
setFocusProxy(overview);
connect(overview, &QLabel::linkActivated, this, [this](const auto &link) {
d->slotLinkActivated(link);
});
- vlay->addWidget(overview);
- layout->addLayout(vlay);
+ layout->addWidget(overview);
+ vlay->addLayout(layout);
auto actionLayout = new QVBoxLayout;
layout->addLayout(actionLayout);
- d->addKeyImportButton(actionLayout, false);
- // TODO: Only show if auto-key-retrieve is not set.
- d->addKeyImportButton(actionLayout, true);
-
d->addIgnoreMDCButton(actionLayout);
d->m_auditLogLabel = new UrlLabel;
connect(d->m_auditLogLabel, &QLabel::linkActivated, this, [this](const auto &link) {
d->slotLinkActivated(link);
});
actionLayout->addWidget(d->m_auditLogLabel);
d->m_auditLogLabel->setStyleSheet(styleSheet);
- d->m_auditLogLabel->setLinkColor(linkColor);
-
- auto detailsLabel = new HtmlLabel;
- detailsLabel->setWordWrap(true);
- detailsLabel->setHtml(d->m_result->details());
- detailsLabel->setStyleSheet(styleSheet);
- detailsLabel->setLinkColor(linkColor);
- connect(detailsLabel, &QLabel::linkActivated, this, [this](const auto &link) {
- d->slotLinkActivated(link);
- });
- vlay->addWidget(detailsLabel);
+
+ for (const auto &detail : d->m_result.get()->detailsList()) {
+ auto frame = new QFrame;
+ auto row = new QHBoxLayout(frame);
+
+ auto iconLabel = new QLabel;
+ QString icon;
+ if (detail.code == Task::Result::AllGood) {
+ icon = QStringLiteral("data-success");
+ } else if (detail.code == Task::Result::NeutralSuccess) {
+ icon = QStringLiteral("data-information"); // TODO: different icon?
+ } else if (detail.code == Task::Result::NeutralError) {
+ icon = QStringLiteral("data-information"); // TODO different icon?
+ } else if (detail.code == Task::Result::Warning) {
+ icon = QStringLiteral("data-warning");
+ } else {
+ icon = QStringLiteral("data-error");
+ }
+ iconLabel->setPixmap(QIcon::fromTheme(icon).pixmap(32, 32));
+ row->addWidget(iconLabel, 0);
+
+ auto detailsLabel = new HtmlLabel;
+ detailsLabel->setWordWrap(true);
+ detailsLabel->setHtml(detail.details);
+ iconLabel->setStyleSheet(QStringLiteral("QLabel {border-width: 0; }"));
+ auto color = colorForVisualCode(detail.code);
+
+ const QString styleSheet = SystemInfo::isHighContrastModeActive()
+ ? QStringLiteral(
+ "QFrame { margin: 0px; }"
+ "QFrame { padding: 5px; border-radius: 3px }")
+ : QStringLiteral(
+ "QFrame { background-color: %1; margin: 0px; }"
+ "QFrame { padding: 5px; border-radius: 3px; border-style: solid; border-width: 1px; border-color: %2; }")
+ .arg(color.name())
+ .arg(color.darker(150).name());
+ frame->setStyleSheet(styleSheet);
+ detailsLabel->setStyleSheet(QStringLiteral("QLabel {border-width: 0; }"));
+ connect(detailsLabel, &QLabel::linkActivated, this, [this](const auto &link) {
+ d->slotLinkActivated(link);
+ });
+
+ row->addWidget(detailsLabel, 1);
+ vlay->addWidget(frame);
+ }
d->m_showButton = new QPushButton;
d->m_showButton->setVisible(false);
connect(d->m_showButton, &QAbstractButton::clicked, this, &ResultItemWidget::showButtonClicked);
actionLayout->addWidget(d->m_showButton);
d->m_closeButton = new QPushButton;
KGuiItem::assign(d->m_closeButton, KStandardGuiItem::close());
d->m_closeButton->setFixedSize(d->m_closeButton->sizeHint());
connect(d->m_closeButton, &QAbstractButton::clicked, this, &ResultItemWidget::closeButtonClicked);
actionLayout->addWidget(d->m_closeButton);
d->m_closeButton->setVisible(false);
layout->setStretch(0, 1);
actionLayout->addStretch(-1);
vlay->addStretch(-1);
d->updateShowDetailsLabel();
setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Maximum);
}
ResultItemWidget::~ResultItemWidget()
{
}
void ResultItemWidget::showCloseButton(bool show)
{
d->m_closeButton->setVisible(show);
}
void ResultItemWidget::setShowButton(const QString &text, bool show)
{
d->m_showButton->setText(text);
d->m_showButton->setVisible(show);
}
bool ResultItemWidget::hasErrorResult() const
{
return d->m_result->hasError();
}
void ResultItemWidget::Private::slotLinkActivated(const QString &link)
{
Q_ASSERT(m_result);
qCDebug(KLEOPATRA_LOG) << "Link activated: " << link;
if (link.startsWith(QLatin1StringView("key:"))) {
auto split = link.split(QLatin1Char(':'));
auto fpr = split.value(1);
if (split.size() == 2 && isFingerprint(fpr)) {
/* There might be a security consideration here if somehow
* a short keyid is used in a link and it collides with another.
* So we additionally check that it really is a fingerprint. */
auto cmd = Command::commandForQuery(fpr);
cmd->setParentWId(q->effectiveWinId());
cmd->start();
} else {
qCWarning(KLEOPATRA_LOG) << "key link invalid " << link;
}
return;
}
const QUrl url(link);
if (url.host() == QLatin1StringView("showauditlog")) {
q->showAuditLog();
return;
}
+
+ if (url.host() == QStringLiteral("lookupcertificate")) {
+ auto cmd = new Kleo::Commands::LookupCertificatesCommand(url.path().mid(1), nullptr);
+ connect(cmd, &Kleo::Commands::LookupCertificatesCommand::canceled, q, [this]() {
+ m_importCanceled = true;
+ });
+ connect(cmd, &Kleo::Commands::LookupCertificatesCommand::finished, q, [this]() {
+ oneImportFinished();
+ // TODO redo verification when key was imported?
+ });
+ cmd->setParentWidget(q);
+ cmd->start();
+ return;
+ }
qCWarning(KLEOPATRA_LOG) << "Unexpected link scheme: " << link;
}
void ResultItemWidget::showAuditLog()
{
AuditLogViewer::showAuditLog(parentWidget(), d->m_result->auditLog());
}
#include "moc_resultitemwidget.cpp"
diff --git a/src/crypto/signencrypttask.cpp b/src/crypto/signencrypttask.cpp
index d017e5dd8..adecc680a 100644
--- a/src/crypto/signencrypttask.cpp
+++ b/src/crypto/signencrypttask.cpp
@@ -1,969 +1,982 @@
/* -*- 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/gpgme-compat.h>
#include <utils/input.h>
#include <utils/kleo_assert.h>
#include <utils/output.h>
#include <utils/path-helper.h>
#include <Libkleo/AuditLogEntry>
#include <Libkleo/Formatting>
#include <Libkleo/KleoException>
#include <Libkleo/Stl_Util>
#include <QGpgME/EncryptArchiveJob>
#include <QGpgME/EncryptJob>
#include <QGpgME/Protocol>
#include <QGpgME/SignArchiveJob>
#include <QGpgME/SignEncryptArchiveJob>
#include <QGpgME/SignEncryptJob>
#include <QGpgME/SignJob>
#include <gpgme++/encryptionresult.h>
#include <gpgme++/key.h>
#include <gpgme++/signingresult.h>
#include <KLocalizedString>
#include "kleopatra_debug.h"
#include <QFileInfo>
#include <QPointer>
using namespace Kleo;
using namespace Kleo::Crypto;
using namespace GpgME;
namespace
{
-QString formatInputOutputLabel(const QString &input, const QString &output, bool outputDeleted)
+QString formatInputOutputLabel(const QString &input, const QString &output, bool outputDeleted, bool sign, bool encrypt)
{
- return i18nc("Input file --> Output file (rarr is arrow",
- "%1 &rarr; %2",
+ Q_ASSERT(sign || encrypt);
+ if (sign && encrypt) {
+ return i18nc("Signed and encrypted <file> as <file>",
+ "Signed and encrypted %1 as %2",
+ input.toHtmlEscaped(),
+ outputDeleted ? QStringLiteral("<s>%1</s>").arg(output.toHtmlEscaped()) : output.toHtmlEscaped());
+ } else if (sign) {
+ return i18nc("Signed <file> as <file>",
+ "Signed %1 as %2",
+ input.toHtmlEscaped(),
+ outputDeleted ? QStringLiteral("<s>%1</s>").arg(output.toHtmlEscaped()) : output.toHtmlEscaped());
+ }
+ return i18nc("Encrypted <file> as <file>",
+ "Encrypted %1 as %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 Formatting::errorAsString(err).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 Formatting::errorAsString(err).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 QString label = formatInputOutputLabel(m_inputLabel, m_outputLabel, true, m_sign, m_encrypt);
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:
QString inputLabel() const;
QString outputLabel() const;
bool removeExistingOutputFile();
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);
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);
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;
QString labelText;
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{OverwritePolicy::Ask}}
{
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, inputLabel(), outputLabel(), 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;
}
QString SignEncryptTask::outputFileName() const
{
return d->outputFileName;
}
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;
}
bool SignEncryptTask::detachedSignatureEnabled() const
{
return d->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->labelText.isEmpty()) {
return d->labelText;
}
return d->inputLabel();
}
QString SignEncryptTask::tag() const
{
return Formatting::displayName(protocol());
}
unsigned long long SignEncryptTask::inputSize() const
{
return d->input ? d->input->size() : 0U;
}
static bool archiveJobsCanBeUsed(GpgME::Protocol protocol)
{
return (protocol == GpgME::OpenPGP) && QGpgME::SignEncryptArchiveJob::isSupported();
}
void SignEncryptTask::doStart()
{
kleo_assert(!d->job);
if (d->sign) {
kleo_assert(!d->signers.empty());
if (d->archive) {
kleo_assert(!d->detached && !d->clearsign);
}
}
const auto proto = protocol();
if (d->archive && archiveJobsCanBeUsed(proto)) {
d->startSignEncryptArchiveJob(proto);
} else {
d->startSignEncryptJob(proto);
}
}
QString SignEncryptTask::Private::inputLabel() const
{
if (input) {
return input->label();
}
if (!inputFileNames.empty()) {
const auto firstFile = QFileInfo{inputFileNames.front()}.fileName();
return inputFileNames.size() == 1 ? firstFile : i18nc("<name of first file>, ...", "%1, ...", firstFile);
}
return {};
}
QString SignEncryptTask::Private::outputLabel() const
{
return output ? output->label() : QFileInfo{outputFileName}.fileName();
}
bool SignEncryptTask::Private::removeExistingOutputFile()
{
if (QFile::exists(outputFileName)) {
bool fileRemoved = false;
// we should already have asked the user for overwrite permission
if (m_overwritePolicy && (m_overwritePolicy->policy() == OverwritePolicy::Overwrite)) {
qCDebug(KLEOPATRA_LOG) << __func__ << "going to remove file for overwriting" << outputFileName;
fileRemoved = QFile::remove(outputFileName);
if (!fileRemoved) {
qCDebug(KLEOPATRA_LOG) << __func__ << "removing file to overwrite failed";
}
} else {
qCDebug(KLEOPATRA_LOG) << __func__ << "we have no permission to overwrite" << outputFileName;
}
if (!fileRemoved) {
QMetaObject::invokeMethod(
q,
[this]() {
slotResult(nullptr, SigningResult{}, EncryptionResult{Error::fromCode(GPG_ERR_EEXIST)});
},
Qt::QueuedConnection);
return false;
}
}
return true;
}
void SignEncryptTask::Private::startSignEncryptJob(GpgME::Protocol proto)
{
#if QGPGME_FILE_JOBS_SUPPORT_DIRECT_FILE_IO
if (proto == GpgME::OpenPGP) {
// either input and output are both set (e.g. when encrypting the notepad),
// or they are both unset (when encrypting files)
kleo_assert((!input && !output) || (input && output));
} else {
kleo_assert(input);
if (!output) {
output = Output::createFromFile(outputFileName, m_overwritePolicy);
}
}
#else
kleo_assert(input);
if (!output) {
output = Output::createFromFile(outputFileName, m_overwritePolicy);
}
#endif
if (encrypt || symmetric) {
Context::EncryptionFlags flags{Context::None};
if (proto == GpgME::OpenPGP) {
flags = static_cast<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_FILE_JOBS_SUPPORT_DIRECT_FILE_IO
if (proto == GpgME::OpenPGP && !input && !output) {
kleo_assert(inputFileNames.size() == 1);
job->setSigners(signers);
job->setRecipients(recipients);
job->setInputFile(inputFileNames.front());
job->setOutputFile(outputFileName);
job->setEncryptionFlags(flags);
if (!removeExistingOutputFile()) {
return;
}
job->startIt();
} else {
if (inputFileNames.size() == 1) {
job->setFileName(inputFileNames.front());
}
job->start(signers, recipients, input->ioDevice(), output->ioDevice(), flags);
}
#else
if (inputFileNames.size() == 1) {
job->setFileName(inputFileNames.front());
}
job->start(signers, recipients, input->ioDevice(), output->ioDevice(), flags);
#endif
this->job = job.release();
} else {
std::unique_ptr<QGpgME::EncryptJob> job = createEncryptJob(proto);
kleo_assert(job.get());
#if QGPGME_FILE_JOBS_SUPPORT_DIRECT_FILE_IO
if (proto == GpgME::OpenPGP && !input && !output) {
kleo_assert(inputFileNames.size() == 1);
job->setRecipients(recipients);
job->setInputFile(inputFileNames.front());
job->setOutputFile(outputFileName);
job->setEncryptionFlags(flags);
if (!removeExistingOutputFile()) {
return;
}
job->startIt();
} else {
if (inputFileNames.size() == 1) {
job->setFileName(inputFileNames.front());
}
job->start(recipients, input->ioDevice(), output->ioDevice(), flags);
}
#else
if (inputFileNames.size() == 1) {
job->setFileName(inputFileNames.front());
}
job->start(recipients, input->ioDevice(), output->ioDevice(), flags);
#endif
this->job = job.release();
}
} else if (sign) {
std::unique_ptr<QGpgME::SignJob> job = createSignJob(proto);
kleo_assert(job.get());
kleo_assert(!(detached && clearsign));
const GpgME::SignatureMode sigMode = detached ? GpgME::Detached : clearsign ? GpgME::Clearsigned : GpgME::NormalSignatureMode;
#if QGPGME_FILE_JOBS_SUPPORT_DIRECT_FILE_IO
if (proto == GpgME::OpenPGP && !input && !output) {
kleo_assert(inputFileNames.size() == 1);
job->setSigners(signers);
job->setInputFile(inputFileNames.front());
job->setOutputFile(outputFileName);
job->setSigningFlags(sigMode);
if (QFile::exists(outputFileName) && m_overwritePolicy && (m_overwritePolicy->policy() == OverwritePolicy::Append)) {
job->setAppendSignature(true);
} else if (!removeExistingOutputFile()) {
return;
}
job->startIt();
} else {
job->start(signers, input->ioDevice(), output->ioDevice(), sigMode);
}
#else
job->start(signers, input->ioDevice(), output->ioDevice(), sigMode);
#endif
this->job = job.release();
} else {
kleo_assert(!"Either 'sign' or 'encrypt' or 'symmetric' must be set!");
}
}
void SignEncryptTask::cancel()
{
qCDebug(KLEOPATRA_LOG) << this << __func__;
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(), &QGpgME::Job::jobProgress, q, &SignEncryptTask::setProgress);
connect(signJob.get(), &QGpgME::SignJob::result, q, [this](const GpgME::SigningResult &signingResult, const QByteArray &) {
slotResult(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(), &QGpgME::Job::jobProgress, q, &SignEncryptTask::setProgress);
connect(signEncryptJob.get(),
&QGpgME::SignEncryptJob::result,
q,
[this](const GpgME::SigningResult &signingResult, const GpgME::EncryptionResult &encryptionResult) {
slotResult(signingResult, 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(), &QGpgME::Job::jobProgress, q, &SignEncryptTask::setProgress);
connect(encryptJob.get(), &QGpgME::EncryptJob::result, q, [this](const GpgME::EncryptionResult &encryptionResult) {
slotResult(encryptionResult);
});
return encryptJob;
}
void SignEncryptTask::Private::startSignEncryptArchiveJob(GpgME::Protocol proto)
{
kleo_assert(!input);
kleo_assert(!output);
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::None};
if (proto == GpgME::OpenPGP) {
flags = static_cast<Context::EncryptionFlags>(flags | Context::AlwaysTrust);
}
if (symmetric) {
flags = static_cast<Context::EncryptionFlags>(flags | Context::Symmetric);
qCDebug(KLEOPATRA_LOG) << "Adding symmetric flag";
}
if (sign) {
labelText = i18nc("@info", "Creating signed and encrypted archive ...");
std::unique_ptr<QGpgME::SignEncryptArchiveJob> job = createSignEncryptArchiveJob(proto);
kleo_assert(job.get());
job->setBaseDirectory(baseDirectory);
job->setSigners(signers);
job->setRecipients(recipients);
job->setInputPaths(relativePaths);
job->setOutputFile(outputFileName);
job->setEncryptionFlags(flags);
if (!removeExistingOutputFile()) {
return;
}
job->startIt();
this->job = job.release();
} else {
labelText = i18nc("@info", "Creating encrypted archive ...");
std::unique_ptr<QGpgME::EncryptArchiveJob> job = createEncryptArchiveJob(proto);
kleo_assert(job.get());
job->setBaseDirectory(baseDirectory);
job->setRecipients(recipients);
job->setInputPaths(relativePaths);
job->setOutputFile(outputFileName);
job->setEncryptionFlags(flags);
if (!removeExistingOutputFile()) {
return;
}
job->startIt();
this->job = job.release();
}
} else if (sign) {
labelText = i18nc("@info", "Creating signed archive ...");
std::unique_ptr<QGpgME::SignArchiveJob> job = createSignArchiveJob(proto);
kleo_assert(job.get());
job->setBaseDirectory(baseDirectory);
job->setSigners(signers);
job->setInputPaths(relativePaths);
job->setOutputFile(outputFileName);
if (!removeExistingOutputFile()) {
return;
}
job->startIt();
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::dataProgress, q, &SignEncryptTask::setProgress);
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::dataProgress, q, &SignEncryptTask::setProgress);
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::dataProgress, q, &SignEncryptTask::setProgress);
connect(job, &QGpgME::EncryptArchiveJob::result, q, [this, job](const GpgME::EncryptionResult &encryptResult) {
slotResult(job, SigningResult{}, encryptResult);
});
return encryptJob;
}
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)
{
qCDebug(KLEOPATRA_LOG) << q << __func__ << "job:" << job << "signing result:" << QGpgME::toLogString(sresult)
<< "encryption result:" << QGpgME::toLogString(eresult);
const AuditLogEntry auditLog = AuditLogEntry::fromJob(job);
bool outputCreated = false;
if (input && input->failed()) {
if (output) {
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()) {
if (output) {
output->cancel();
}
if (!outputFileName.isEmpty() && eresult.error().code() != GPG_ERR_EEXIST) {
// ensure that the output file is removed if the task was canceled or an error occurred;
// unless a "file exists" error occurred because this means that the file with the name
// of outputFileName wasn't created as result of this task
if (QFile::exists(outputFileName)) {
qCDebug(KLEOPATRA_LOG) << __func__ << "Removing output file" << outputFileName << "after error or cancel";
if (!QFile::remove(outputFileName)) {
qCDebug(KLEOPATRA_LOG) << __func__ << "Removing output file" << outputFileName << "failed";
}
}
}
} else {
try {
kleo_assert(!sresult.isNull() || !eresult.isNull());
if (output) {
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{inputLabel(), input ? input->errorString() : QString{}};
const LabelAndError outputInfo{outputLabel(), output ? output->errorString() : QString{}};
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);
+ const QString files =
+ formatInputOutputLabel(m_input.label, m_output.label, !m_outputCreated, !m_sresult.isNull() /* TODO ?? */, !m_eresult.isNull() /* TODO ?? */);
return files + QLatin1StringView(": ") + 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) //
: 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.h b/src/crypto/task.h
index 5a7c2ea14..19b44821b 100644
--- a/src/crypto/task.h
+++ b/src/crypto/task.h
@@ -1,137 +1,146 @@
/* -*- 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 <gpgme++/global.h>
#include <QPointer>
#include <memory>
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;
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(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(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;
const std::unique_ptr<Private> d;
};
class Task::Result
{
const QString m_nonce;
public:
class Content;
Result();
virtual ~Result();
const QString &nonce() const
{
return m_nonce;
}
bool hasError() const;
enum VisualCode {
AllGood,
Warning,
Danger,
NeutralSuccess,
NeutralError,
};
enum class ContentType {
None,
Mime,
Mbox,
};
+ struct ResultListItem {
+ QString details;
+ Task::Result::VisualCode code;
+ };
+
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>();
}
virtual ContentType viewableContentType() const;
+ virtual QList<Task::Result::ResultListItem> detailsList() const
+ {
+ return {};
+ };
protected:
static QString makeOverview(const QString &msg);
private:
class Private;
const std::unique_ptr<Private> d;
};
}
}

File Metadata

Mime Type
text/x-diff
Expires
Mon, Dec 23, 4:13 PM (19 h, 47 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
9b/30/8e895cd54181c3053bcb959eb2b0

Event Timeline