Page MenuHome GnuPG

No OneTemporary

diff --git a/src/crypto/decryptverifytask.cpp b/src/crypto/decryptverifytask.cpp
index 03b3abe39..374631d02 100644
--- a/src/crypto/decryptverifytask.cpp
+++ b/src/crypto/decryptverifytask.cpp
@@ -1,1938 +1,1754 @@
/* -*- mode: c++; c-basic-offset:4 -*-
decryptverifytask.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-kleopatra.h>
#include "decryptverifytask.h"
#include <QGpgME/DecryptJob>
#include <QGpgME/DecryptVerifyArchiveJob>
#include <QGpgME/DecryptVerifyJob>
#include <QGpgME/Protocol>
#include <QGpgME/VerifyDetachedJob>
#include <QGpgME/VerifyOpaqueJob>
#include <Libkleo/AuditLogEntry>
#include <Libkleo/Classify>
#include <Libkleo/Compliance>
#include <Libkleo/Dn>
#include <Libkleo/Formatting>
#include <Libkleo/KeyCache>
#include <Libkleo/KleoException>
#include <Libkleo/Predicates>
#include <Libkleo/Stl_Util>
#include <Libkleo/GnuPG>
#include <utils/detail_p.h>
#include <utils/input.h>
#include <utils/kleo_assert.h>
#include <utils/output.h>
#include <KMime/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 bool addrspec_equal(const AddrSpec &lhs, const AddrSpec &rhs, Qt::CaseSensitivity cs)
{
return lhs.localPart.compare(rhs.localPart, cs) == 0 && lhs.domain.compare(rhs.domain, Qt::CaseInsensitive) == 0;
}
static bool mailbox_equal(const Mailbox &lhs, const Mailbox &rhs, Qt::CaseSensitivity cs)
{
return addrspec_equal(lhs.addrSpec(), rhs.addrSpec(), cs);
}
-static std::string stripAngleBrackets(const std::string &str)
-{
- if (str.empty()) {
- return str;
- }
- if (str[0] == '<' && str[str.size() - 1] == '>') {
- return str.substr(1, str.size() - 2);
- }
- return str;
-}
-
-static std::string email(const UserID &uid)
-{
- if (uid.parent().protocol() == OpenPGP) {
- if (const char *const email = uid.email()) {
- return stripAngleBrackets(email);
- } else {
- return std::string();
- }
- }
-
- Q_ASSERT(uid.parent().protocol() == CMS);
-
- if (const char *const id = uid.id())
- if (*id == '<') {
- return stripAngleBrackets(id);
- } else {
- return DN(id)[QStringLiteral("EMAIL")].trimmed().toUtf8().constData();
- }
- else {
- return std::string();
- }
-}
-
-static Mailbox mailbox(const UserID &uid)
-{
- const std::string e = email(uid);
- Mailbox mbox;
- if (!e.empty()) {
- mbox.setAddress(e.c_str());
- }
- return mbox;
-}
-
static std::vector<Mailbox> extractMailboxes(const Key &key)
{
std::vector<Mailbox> res;
const auto userIDs{key.userIDs()};
for (const UserID &id : userIDs) {
- const Mailbox mbox = mailbox(id);
+ const Mailbox mbox = Kleo::Formatting::mailbox(id);
if (!mbox.addrSpec().isEmpty()) {
res.push_back(mbox);
}
}
return res;
}
static std::vector<Mailbox> extractMailboxes(const std::vector<Key> &signers)
{
std::vector<Mailbox> res;
for (const Key &i : signers) {
const std::vector<Mailbox> bxs = extractMailboxes(i);
res.insert(res.end(), bxs.begin(), bxs.end());
}
return res;
}
static bool keyContainsMailbox(const Key &key, const Mailbox &mbox)
{
const std::vector<Mailbox> mbxs = extractMailboxes(key);
return std::find_if(mbxs.cbegin(),
mbxs.cend(),
[mbox](const Mailbox &m) {
return mailbox_equal(mbox, m, Qt::CaseInsensitive);
})
!= mbxs.cend();
}
static bool keysContainMailbox(const std::vector<Key> &keys, const Mailbox &mbox)
{
return std::find_if(keys.cbegin(),
keys.cend(),
[mbox](const Key &key) {
return keyContainsMailbox(key, mbox);
})
!= keys.cend();
}
static bool relevantInDecryptVerifyContext(const VerificationResult &r)
{
// for D/V operations, we ignore verification results which are not errors and contain
// no signatures (which means that the data was just not signed)
return (r.error() && r.error().code() != GPG_ERR_DECRYPT_FAILED) || r.numSignatures() > 0;
}
-static QString signatureSummaryToString(int summary)
-{
- if (summary & Signature::None) {
- return i18n("Error: Signature not verified");
- } else if (summary & Signature::Valid || summary & Signature::Green) {
- return i18n("Good signature");
- } else if (summary & Signature::KeyRevoked) {
- return i18n("Signing certificate was revoked");
- } else if (summary & Signature::KeyExpired) {
- return i18n("Signing certificate is expired");
- } else if (summary & Signature::KeyMissing) {
- return i18n("Certificate is not available");
- } else if (summary & Signature::SigExpired) {
- return i18n("Signature expired");
- } else if (summary & Signature::CrlMissing) {
- return i18n("CRL missing");
- } else if (summary & Signature::CrlTooOld) {
- return i18n("CRL too old");
- } else if (summary & Signature::BadPolicy) {
- return i18n("Bad policy");
- } else if (summary & Signature::SysError) {
- return i18n("System error"); // ### retrieve system error details?
- } else if (summary & Signature::Red) {
- return i18n("Bad signature");
- }
- return QString();
-}
-
-static QString formatValidSignatureWithTrustLevel(const UserID &id)
-{
- if (id.isNull()) {
- return QString();
- }
- switch (id.validity()) {
- case UserID::Marginal:
- return i18n("The signature is valid but the trust in the certificate's validity is only marginal.");
- case UserID::Full:
- return i18n("The signature is valid and the certificate's validity is fully trusted.");
- case UserID::Ultimate:
- return i18n("The signature is valid and the certificate's validity is ultimately trusted.");
- case UserID::Never:
- return i18n("The signature is valid but the certificate's validity is <em>not trusted</em>.");
- case UserID::Unknown:
- return i18n("The signature is valid but the certificate's validity is unknown.");
- case UserID::Undefined:
- default:
- return i18n("The signature is valid but the certificate's validity is undefined.");
- }
-}
-
static QString renderKeyLink(const QString &fpr, const QString &text)
{
return QStringLiteral("<a href=\"key:%1\">%2</a>").arg(fpr, text);
}
static QString renderKey(const Key &key)
{
if (key.isNull()) {
return i18n("Unknown certificate");
}
if (key.primaryFingerprint() && strlen(key.primaryFingerprint()) > 16 && key.numUserIDs()) {
const QString text = QStringLiteral("%1 (%2)")
.arg(Formatting::prettyNameAndEMail(key).toHtmlEscaped())
.arg(Formatting::prettyID(QString::fromLocal8Bit(key.primaryFingerprint()).right(16).toLatin1().constData()));
return renderKeyLink(QLatin1StringView(key.primaryFingerprint()), text);
}
return renderKeyLink(QLatin1StringView(key.primaryFingerprint()), Formatting::prettyID(key.primaryFingerprint()));
}
static QString renderKeyEMailOnlyNameAsFallback(const Key &key)
{
if (key.isNull()) {
return i18n("Unknown certificate");
}
const QString email = Formatting::prettyEMail(key);
const QString user = !email.isEmpty() ? email : Formatting::prettyName(key);
return renderKeyLink(QLatin1StringView(key.primaryFingerprint()), user);
}
-static QString formatDate(const QDateTime &dt)
-{
- return QLocale().toString(dt);
-}
-static QString formatSigningInformation(const Signature &sig)
-{
- if (sig.isNull()) {
- return QString();
- }
- const QDateTime dt = sig.creationTime() != 0 ? QDateTime::fromSecsSinceEpoch(quint32(sig.creationTime())) : QDateTime();
- QString text;
- Key key = sig.key();
- if (dt.isValid()) {
- text = i18nc("1 is a date", "Signature created on %1", formatDate(dt)) + QStringLiteral("<br>");
- }
- if (key.isNull()) {
- return text += i18n("With unavailable certificate:") + QStringLiteral("<br>ID: 0x%1").arg(QString::fromLatin1(sig.fingerprint()).toUpper());
- }
- text += i18n("With certificate:") + QStringLiteral("<br>") + renderKey(key);
-
- if (DeVSCompliance::isCompliant()) {
- text += (QStringLiteral("<br/>")
- + (sig.isDeVs() ? i18nc("%1 is a placeholder for the name of a compliance mode. E.g. NATO RESTRICTED compliant or VS-NfD compliant",
- "The signature is %1",
- DeVSCompliance::name(true))
- : i18nc("%1 is a placeholder for the name of a compliance mode. E.g. NATO RESTRICTED compliant or VS-NfD compliant",
- "The signature <b>is not</b> %1.",
- DeVSCompliance::name(true))));
- }
-
- return text;
-}
-
static QString strikeOut(const QString &str, bool strike)
{
return QString(strike ? QStringLiteral("<s>%1</s>") : QStringLiteral("%1")).arg(str.toHtmlEscaped());
}
static QString formatInputOutputLabel(const QString &input, const QString &output, bool inputDeleted, bool outputDeleted)
{
if (output.isEmpty()) {
return strikeOut(input, inputDeleted);
}
return i18nc("Input file --> Output file (rarr is arrow", "%1 &rarr; %2", strikeOut(input, inputDeleted), strikeOut(output, outputDeleted));
}
static bool IsErrorOrCanceled(const GpgME::Error &err)
{
return err || err.isCanceled();
}
static bool IsErrorOrCanceled(const Result &res)
{
return IsErrorOrCanceled(res.error());
}
static bool IsBad(const Signature &sig)
{
return sig.summary() & Signature::Red;
}
static bool IsGoodOrValid(const Signature &sig)
{
return (sig.summary() & Signature::Valid) || (sig.summary() & Signature::Green);
}
-static UserID findUserIDByMailbox(const Key &key, const Mailbox &mbox)
-{
- const auto userIDs{key.userIDs()};
- for (const UserID &id : userIDs)
- if (mailbox_equal(mailbox(id), mbox, Qt::CaseInsensitive)) {
- return id;
- }
- return UserID();
-}
-
static void updateKeys(const VerificationResult &result)
{
// This little hack works around the problem that GnuPG / GpgME does not
// provide Key information in a verification result. The Key object is
// a dummy just holding the KeyID. This hack ensures that all available
// keys are fetched from the backend and are populated
for (const auto &sig : result.signatures()) {
// Update key information
sig.key(true, true);
}
}
static QString ensureUniqueDirectory(const QString &path)
{
// make sure that we don't use an existing directory
QString uniquePath = path;
const QFileInfo outputInfo{path};
if (outputInfo.exists()) {
const auto uniqueName = KFileUtils::suggestName(QUrl::fromLocalFile(outputInfo.absolutePath()), outputInfo.fileName());
uniquePath = outputInfo.dir().filePath(uniqueName);
}
if (!QDir{}.mkpath(uniquePath)) {
return {};
}
return uniquePath;
}
static bool mimeTypeInherits(const QMimeType &mimeType, const QString &mimeTypeName)
{
// inherits is expensive on an invalid mimeType
return mimeType.isValid() && mimeType.inherits(mimeTypeName);
}
}
class DecryptVerifyResult::SenderInfo
{
public:
explicit SenderInfo(const Mailbox &infSender, const std::vector<Key> &signers_)
: informativeSender(infSender)
, signers(signers_)
{
}
const Mailbox informativeSender;
const std::vector<Key> signers;
bool hasInformativeSender() const
{
return !informativeSender.addrSpec().isEmpty();
}
bool conflicts() const
{
return hasInformativeSender() && hasKeys() && !keysContainMailbox(signers, informativeSender);
}
bool hasKeys() const
{
return std::any_of(signers.cbegin(), signers.cend(), [](const Key &key) {
return !key.isNull();
});
}
std::vector<Mailbox> signerMailboxes() const
{
return extractMailboxes(signers);
}
};
namespace
{
static Task::Result::VisualCode codeForVerificationResult(const VerificationResult &res)
{
if (res.isNull()) {
return Task::Result::NeutralSuccess;
}
const std::vector<Signature> sigs = res.signatures();
if (sigs.empty()) {
return Task::Result::Warning;
}
if (std::find_if(sigs.begin(), sigs.end(), IsBad) != sigs.end()) {
return Task::Result::Danger;
}
if ((size_t)std::count_if(sigs.begin(), sigs.end(), IsGoodOrValid) == sigs.size()) {
return Task::Result::AllGood;
}
return Task::Result::Warning;
}
static QString formatVerificationResultOverview(const VerificationResult &res, const DecryptVerifyResult::SenderInfo &info)
{
if (res.isNull()) {
return QString();
}
const Error err = res.error();
if (err.isCanceled()) {
return i18n("<b>Verification canceled.</b>");
} else if (err) {
return i18n("<b>Verification failed: %1.</b>", Formatting::errorAsString(err).toHtmlEscaped());
}
const std::vector<Signature> sigs = res.signatures();
if (sigs.empty()) {
return i18n("<b>No signatures found.</b>");
}
const uint bad = std::count_if(sigs.cbegin(), sigs.cend(), IsBad);
if (bad > 0) {
return i18np("<b>Invalid signature.</b>", "<b>%1 invalid signatures.</b>", bad);
}
const uint warn = std::count_if(sigs.cbegin(), sigs.cend(), [](const Signature &sig) {
return !IsGoodOrValid(sig);
});
if (warn == sigs.size()) {
return i18np("<b>The data could not be verified.</b>", "<b>%1 signatures could not be verified.</b>", warn);
}
// Good signature:
QString text;
if (sigs.size() == 1) {
text = i18n("<b>Valid signature by %1</b>", renderKeyEMailOnlyNameAsFallback(sigs[0].key()));
if (info.conflicts())
text += i18n("<br/><b>Warning:</b> The sender's mail address is not stored in the %1 used for signing.",
renderKeyLink(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.isLegacyCipherNoMDC()) {
return i18n("<b>Decryption failed: %1.</b>", i18n("No integrity protection (MDC)."));
} else if (!errorString.isEmpty()) {
return i18n("<b>Decryption failed: %1.</b>", errorString.toHtmlEscaped());
} else if (err) {
return i18n("<b>Decryption failed: %1.</b>", Formatting::errorAsString(err).toHtmlEscaped());
}
return i18n("<b>Decryption succeeded.</b>");
}
-static QString formatSignature(const Signature &sig, const DecryptVerifyResult::SenderInfo &info)
-{
- if (sig.isNull()) {
- return QString();
- }
-
- const QString text = formatSigningInformation(sig) + QLatin1StringView("<br/>");
- const Key key = sig.key();
-
- // Green
- if (sig.summary() & Signature::Valid) {
- const UserID id = findUserIDByMailbox(key, info.informativeSender);
- return text + formatValidSignatureWithTrustLevel(!id.isNull() ? id : key.userID(0));
- }
-
- // Red
- if ((sig.summary() & Signature::Red)) {
- const QString ret = text + i18n("The signature is invalid: %1", signatureSummaryToString(sig.summary()));
- if (sig.summary() & Signature::SysError) {
- return ret + QStringLiteral(" (%1)").arg(Formatting::errorAsString(sig.status()));
- }
- return ret;
- }
-
- // Key missing
- if ((sig.summary() & Signature::KeyMissing)) {
- return text + i18n("You can search the certificate on a keyserver or import it from a file.");
- }
-
- // Yellow
- if ((sig.validity() & Signature::Validity::Undefined) //
- || (sig.validity() & Signature::Validity::Unknown) //
- || (sig.summary() == Signature::Summary::None)) {
- return text
- + (key.protocol() == OpenPGP
- ? i18n("The used key is not certified by you or any trusted person.")
- : i18n("The used certificate is not certified by a trustworthy Certificate Authority or the Certificate Authority is unknown."));
- }
-
- // Catch all fall through
- const QString ret = text + i18n("The signature is invalid: %1", signatureSummaryToString(sig.summary()));
- if (sig.summary() & Signature::SysError) {
- return ret + QStringLiteral(" (%1)").arg(Formatting::errorAsString(sig.status()));
- }
- return ret;
-}
-
static QStringList format(const std::vector<Mailbox> &mbxs)
{
QStringList res;
std::transform(mbxs.cbegin(), mbxs.cend(), std::back_inserter(res), [](const Mailbox &mbox) {
return mbox.prettyAddress();
});
return res;
}
static QString formatVerificationResultDetails(const VerificationResult &res, const DecryptVerifyResult::SenderInfo &info, const QString &errorString)
{
if ((res.error().code() == GPG_ERR_EIO || res.error().code() == GPG_ERR_NO_DATA) && !errorString.isEmpty()) {
return i18n("Input error: %1", errorString);
}
const std::vector<Signature> sigs = res.signatures();
QString details;
for (const Signature &sig : sigs) {
- details += formatSignature(sig, info) + QLatin1Char('\n');
+ details += 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>",
info.informativeSender.prettyAddress(),
format(info.signerMailboxes()).join(i18nc("separator for a list of e-mail addresses", ", ")));
}
return details;
}
static QString formatRecipientsDetails(const std::vector<Key> &knownRecipients, unsigned int numRecipients)
{
if (numRecipients == 0) {
return {};
}
if (knownRecipients.empty()) {
return QLatin1StringView("<i>") + i18np("One unknown recipient.", "%1 unknown recipients.", numRecipients) + QLatin1String("</i>");
}
QString details = i18np("Recipient:", "Recipients:", numRecipients);
if (numRecipients == 1) {
details += QLatin1Char(' ') + renderKey(knownRecipients.front());
} else {
details += QLatin1StringView("<ul>");
for (const Key &key : knownRecipients) {
details += QLatin1StringView("<li>") + renderKey(key) + QLatin1String("</li>");
}
if (knownRecipients.size() < numRecipients) {
details += QLatin1StringView("<li><i>") + i18np("One unknown recipient", "%1 unknown recipients", numRecipients - knownRecipients.size())
+ QLatin1StringView("</i></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()) {
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(m_informativeSender, KeyCache::instance()->findSigners(m_verificationResult));
}
std::shared_ptr<DecryptVerifyResult>
AbstractDecryptVerifyTask::fromDecryptResult(const DecryptionResult &dr, const QByteArray &plaintext, const AuditLogEntry &auditLog)
{
return std::shared_ptr<DecryptVerifyResult>(new DecryptVerifyResult(Decrypt, //
VerificationResult(),
dr,
plaintext,
{},
{},
QString(),
inputLabel(),
outputLabel(),
auditLog,
this,
informativeSender()));
}
std::shared_ptr<DecryptVerifyResult> AbstractDecryptVerifyTask::fromDecryptResult(const GpgME::Error &err, const QString &what, const AuditLogEntry &auditLog)
{
return std::shared_ptr<DecryptVerifyResult>(new DecryptVerifyResult(Decrypt, //
VerificationResult(),
DecryptionResult(err),
QByteArray(),
{},
err,
what,
inputLabel(),
outputLabel(),
auditLog,
this,
informativeSender()));
}
std::shared_ptr<DecryptVerifyResult> AbstractDecryptVerifyTask::fromDecryptVerifyResult(const DecryptionResult &dr,
const VerificationResult &vr,
const QByteArray &plaintext,
const QString &fileName,
const AuditLogEntry &auditLog)
{
const auto err = dr.error().code() ? dr.error() : vr.error();
return std::shared_ptr<DecryptVerifyResult>(new DecryptVerifyResult(DecryptVerify, //
vr,
dr,
plaintext,
fileName,
err,
QString(),
inputLabel(),
outputLabel(),
auditLog,
this,
informativeSender()));
}
std::shared_ptr<DecryptVerifyResult>
AbstractDecryptVerifyTask::fromDecryptVerifyResult(const GpgME::Error &err, const QString &details, const AuditLogEntry &auditLog)
{
return std::shared_ptr<DecryptVerifyResult>(new DecryptVerifyResult(DecryptVerify, //
VerificationResult(),
DecryptionResult(err),
QByteArray(),
{},
err,
details,
inputLabel(),
outputLabel(),
auditLog,
this,
informativeSender()));
}
std::shared_ptr<DecryptVerifyResult>
AbstractDecryptVerifyTask::fromVerifyOpaqueResult(const VerificationResult &vr, const QByteArray &plaintext, const AuditLogEntry &auditLog)
{
return std::shared_ptr<DecryptVerifyResult>(new DecryptVerifyResult(Verify, //
vr,
DecryptionResult(),
plaintext,
{},
{},
QString(),
inputLabel(),
outputLabel(),
auditLog,
this,
informativeSender()));
}
std::shared_ptr<DecryptVerifyResult>
AbstractDecryptVerifyTask::fromVerifyOpaqueResult(const GpgME::Error &err, const QString &details, const AuditLogEntry &auditLog)
{
return std::shared_ptr<DecryptVerifyResult>(new DecryptVerifyResult(Verify, //
VerificationResult(err),
DecryptionResult(),
QByteArray(),
{},
err,
details,
inputLabel(),
outputLabel(),
auditLog,
this,
informativeSender()));
}
std::shared_ptr<DecryptVerifyResult> AbstractDecryptVerifyTask::fromVerifyDetachedResult(const VerificationResult &vr, const AuditLogEntry &auditLog)
{
return std::shared_ptr<DecryptVerifyResult>(new DecryptVerifyResult(Verify, //
vr,
DecryptionResult(),
QByteArray(),
{},
{},
QString(),
inputLabel(),
outputLabel(),
auditLog,
this,
informativeSender()));
}
std::shared_ptr<DecryptVerifyResult>
AbstractDecryptVerifyTask::fromVerifyDetachedResult(const GpgME::Error &err, const QString &details, const AuditLogEntry &auditLog)
{
return std::shared_ptr<DecryptVerifyResult>(new DecryptVerifyResult(Verify, //
VerificationResult(err),
DecryptionResult(),
QByteArray(),
{},
err,
details,
inputLabel(),
outputLabel(),
auditLog,
this,
informativeSender()));
}
DecryptVerifyResult::DecryptVerifyResult(DecryptVerifyOperation type,
const VerificationResult &vr,
const DecryptionResult &dr,
const QByteArray &stuff,
const QString &fileName,
const GpgME::Error &error,
const QString &errString,
const QString &inputLabel,
const QString &outputLabel,
const AuditLogEntry &auditLog,
Task *parentTask,
const Mailbox &informativeSender)
: Task::Result()
, d(new Private(type, vr, dr, stuff, fileName, error, errString, inputLabel, outputLabel, auditLog, parentTask, informativeSender, this))
{
}
Task::Result::ContentType DecryptVerifyResult::viewableContentType() const
{
#if QGPGME_SUPPORTS_IS_MIME
if (decryptionResult().isMime()) {
return Task::Result::ContentType::Mime;
}
#endif
if (fileName().endsWith(QStringLiteral("openpgp-encrypted-message"))) {
return Task::Result::ContentType::Mime;
}
QMimeDatabase mimeDatabase;
const auto mimeType = mimeDatabase.mimeTypeForFile(fileName());
if (mimeTypeInherits(mimeType, QStringLiteral("message/rfc822"))) {
return Task::Result::ContentType::Mime;
}
if (mimeTypeInherits(mimeType, QStringLiteral("application/mbox"))) {
return Task::Result::ContentType::Mbox;
}
return Task::Result::ContentType::None;
}
QString DecryptVerifyResult::overview() const
{
QString ov;
if (d->isDecryptOnly()) {
ov += formatDecryptionResultOverview(d->m_decryptionResult);
} else if (d->isVerifyOnly()) {
ov += formatVerificationResultOverview(d->m_verificationResult, d->makeSenderInfo());
} else {
ov += formatDecryptVerifyResultOverview(d->m_decryptionResult, d->m_verificationResult, d->makeSenderInfo());
}
if (ov.size() + d->label().size() > 120) {
// Avoid ugly breaks
ov = QStringLiteral("<br>") + ov;
}
return i18nc("label: result example: foo.sig: Verification failed. ", "%1: %2", d->label(), ov);
}
QString DecryptVerifyResult::details() const
{
if (d->isDecryptOnly()) {
return formatDecryptionResultDetails(d->m_decryptionResult,
KeyCache::instance()->findRecipients(d->m_decryptionResult),
errorString(),
false,
d->m_parentTask);
}
if (d->isVerifyOnly()) {
return formatVerificationResultDetails(d->m_verificationResult, d->makeSenderInfo(), errorString());
}
return formatDecryptVerifyResultDetails(d->m_decryptionResult,
d->m_verificationResult,
KeyCache::instance()->findRecipients(d->m_decryptionResult),
d->makeSenderInfo(),
errorString(),
d->m_parentTask);
}
GpgME::Error DecryptVerifyResult::error() const
{
return d->m_error;
}
QString DecryptVerifyResult::errorString() const
{
return d->m_errorString;
}
AuditLogEntry DecryptVerifyResult::auditLog() const
{
return d->m_auditLog;
}
QPointer<Task> DecryptVerifyResult::parentTask() const
{
return d->m_parentTask;
}
Task::Result::VisualCode DecryptVerifyResult::code() const
{
if ((d->m_type == DecryptVerify || d->m_type == Verify) && relevantInDecryptVerifyContext(verificationResult())) {
return codeForVerificationResult(verificationResult());
}
return hasError() ? NeutralError : NeutralSuccess;
}
GpgME::VerificationResult DecryptVerifyResult::verificationResult() const
{
return d->m_verificationResult;
}
GpgME::DecryptionResult DecryptVerifyResult::decryptionResult() const
{
return d->m_decryptionResult;
}
QString DecryptVerifyResult::fileName() const
{
return d->m_fileName;
}
class AbstractDecryptVerifyTask::Private
{
public:
Mailbox informativeSender;
QPointer<QGpgME::Job> job;
};
AbstractDecryptVerifyTask::AbstractDecryptVerifyTask(QObject *parent)
: Task(parent)
, d(new Private)
{
}
AbstractDecryptVerifyTask::~AbstractDecryptVerifyTask()
{
}
void AbstractDecryptVerifyTask::cancel()
{
qCDebug(KLEOPATRA_LOG) << this << __func__;
if (d->job) {
d->job->slotCancel();
}
}
Mailbox AbstractDecryptVerifyTask::informativeSender() const
{
return d->informativeSender;
}
void AbstractDecryptVerifyTask::setInformativeSender(const Mailbox &sender)
{
d->informativeSender = sender;
}
QGpgME::Job *AbstractDecryptVerifyTask::job() const
{
return d->job;
}
void AbstractDecryptVerifyTask::setJob(QGpgME::Job *job)
{
d->job = job;
}
class DecryptVerifyTask::Private
{
DecryptVerifyTask *const q;
public:
explicit Private(DecryptVerifyTask *qq)
: q{qq}
{
}
void startDecryptVerifyJob();
void startDecryptVerifyArchiveJob();
void slotResult(const DecryptionResult &, const VerificationResult &, const QByteArray & = {});
std::shared_ptr<Input> m_input;
std::shared_ptr<Output> m_output;
const QGpgME::Protocol *m_backend = nullptr;
Protocol m_protocol = UnknownProtocol;
bool m_ignoreMDCError = false;
bool m_extractArchive = false;
QString m_inputFilePath;
QString m_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"

File Metadata

Mime Type
text/x-diff
Expires
Mon, Dec 23, 2:06 PM (16 h, 41 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
8b/61/0f28d6006f8231e11854121ffa63

Event Timeline