Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F34606723
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
74 KB
Subscribers
None
View Options
diff --git a/src/crypto/decryptverifytask.cpp b/src/crypto/decryptverifytask.cpp
index 41d107412..c2f8f971e 100644
--- a/src/crypto/decryptverifytask.cpp
+++ b/src/crypto/decryptverifytask.cpp
@@ -1,1806 +1,1751 @@
/* -*- 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;
using namespace Qt::Literals::StringLiterals;
using namespace Qt::Literals::StringLiterals;
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 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("Verification canceled.");
- } else if (err) {
- return Formatting::errorAsString(err).toHtmlEscaped();
- }
-
- const std::vector<Signature> sigs = res.signatures();
-
- if (sigs.empty()) {
- return i18n("No signatures found.");
- }
-
- const uint bad = std::count_if(sigs.cbegin(), sigs.cend(), IsBad);
- if (bad > 0) {
- return i18np("Invalid signature.", "%1 invalid signatures.", bad);
- }
- const uint warn = std::count_if(sigs.cbegin(), sigs.cend(), [](const Signature &sig) {
- return !IsGoodOrValid(sig);
- });
- if (warn == sigs.size()) {
- return i18np("The data could not be verified.", "%1 signatures could not be verified.", warn);
- }
-
- // Good signature:
- QString text;
- if (sigs.size() == 1) {
- text = i18n("Valid signature by %1.", renderKeyEMailOnlyNameAsFallback(sigs[0].key()));
- if (info.conflicts()) {
- text += "<br />"_L1
- + i18nc("@info",
- "Warning: The sender's mail address is not stored in the <a href=\"key:%1\">certificate</a> used for signing.",
- QString::fromLatin1(sigs[0].key().primaryFingerprint()));
- }
- } else {
- text = i18np("Valid signature.", "%1 valid signatures.", sigs.size());
- if (info.conflicts()) {
- text += i18n("<br/>Warning: 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 auto err = result.error();
-
- if (err.isCanceled()) {
- return i18n("Decryption canceled.");
- } else if (err.code() == GPG_ERR_NO_SECKEY) {
- return Formatting::errorAsString(err).append(u'.');
- } else if (result.isLegacyCipherNoMDC()) {
- return i18n("No integrity protection (MDC).");
- } else if (!errorString.isEmpty()) {
- if (err.code()) {
- return Formatting::errorAsString(err) + ": "_L1 + errorString.toHtmlEscaped();
- }
- return errorString.toHtmlEscaped();
- } else if (err) {
- return Formatting::errorAsString(err).append(u'.');
- }
- return {};
-}
-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 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 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 formatResultLine(const QString &input,
- const QString &output,
- const Error &error,
- const QString &errorString,
- const VerificationResult &verificationResult,
- const DecryptionResult &decryptionResult,
- const DecryptVerifyResult::SenderInfo &senderInfo,
- Task *task)
-{
- const auto verify = verificationResult.numSignatures() > 0 || verificationResult.error().code();
- const auto decrypt = !decryptionResult.isNull();
- const auto recipients = KeyCache::instance()->findRecipients(decryptionResult);
-
- Q_ASSERT(verify || decrypt);
-
- QString label;
- if (verify && decrypt) {
- if (error.code() == GPG_ERR_NO_SECKEY) {
- label =
- xi18nc("@info Unable to decrypt and verify <file>: <reason><nl />The data was not encrypted for any secret key in your certificate list.",
- "Unable to decrypt and verify <filename>%1</filename>: <emphasis strong='true'>%2</emphasis><nl />The data was not encrypted for any "
- "secret key in your certificate list.",
- input,
- formatDecryptVerifyResultOverview(decryptionResult, verificationResult, senderInfo));
- } else if (error.code() != GPG_ERR_NO_ERROR) {
- label = xi18nc("@info Failed to decrypt and verify <file>: <reason>",
- "Failed to decrypt and verify <filename>%1</filename>: <emphasis strong='true'>%2</emphasis>",
- input,
- formatDecryptVerifyResultOverview(decryptionResult, verificationResult, senderInfo));
- } else {
- label = xi18nc("@info Successfully decrypted and verified <file> as <file>.",
- "Successfully decrypted and verified <filename>%1</filename> as <filename>%2</filename>.",
- input,
- output);
- }
- }
-
- if (verify) {
- if (error) {
- label = xi18nc("@info Failed to verify <file>: <reason>",
- "Failed to verify <filename>%1</filename>: <emphasis strong='true'>%2</emphasis>",
- input,
- formatVerificationResultOverview(verificationResult, senderInfo));
- } else {
- label = xi18nc("@info Verified <file> with signature in <file>.",
- "Verified <filename>%1</filename> with signature in <filename>%2</filename>.",
- output,
- input);
- }
- }
-
- if (error.code() == GPG_ERR_NO_SECKEY) {
- label = xi18nc("@info Unable to decrypt <file>: <reason><nl />The data was not encrypted for any secret key in your certificate list.",
- "Unable to decrypt <filename>%1</filename>: <emphasis strong='true'>%2</emphasis><nl />The data was not encrypted for any secret key in "
- "your certificate list.",
- input,
- formatDecryptionResultOverview(decryptionResult));
- } else if (error.code() != GPG_ERR_NO_ERROR) {
- label = xi18nc("@info Failed to decrypt <file>: <reason>",
- "Failed to decrypt <filename>%1</filename>: %2",
- input,
- formatDecryptionResultOverview(decryptionResult, errorString));
- } else {
- label = xi18nc("@info Successfully decrypted <file> as <file>.",
- "Successfully decrypted <filename>%1</filename> as <filename>%2</filename>.",
- input,
- output);
- }
-
- label += "<br />"_L1;
-
- if (!errorString.isEmpty()) {
- return errorString;
- }
-
- if (senderInfo.conflicts()) {
- return i18n("<p>The sender's address %1 is not stored in the certificate. Stored: %2</p>",
- formatEmail(senderInfo.informativeSender),
- formatEmails(senderInfo.signerEmails()).join(i18nc("separator for a list of e-mail addresses", ", ")));
- }
-
- if (decryptionResult.isNull() || decryptionResult.error() || decryptionResult.error().isCanceled()) {
- if (decryptionResult.error().code() == GPG_ERR_NO_SECKEY && recipients.empty()) {
- return {};
- }
- return formatRecipientsDetails(recipients, decryptionResult.numRecipients());
- }
-
- if (DeVSCompliance::isCompliant()) {
- label +=
- ((decryptionResult.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 (decryptionResult.fileName()) {
- const auto decVerifyTask = qobject_cast<AbstractDecryptVerifyTask *>(task);
- if (decVerifyTask) {
- const auto embedFileName = QString::fromUtf8(decryptionResult.fileName()).toHtmlEscaped();
-
- if (embedFileName != decVerifyTask->outputLabel()) {
- label += i18n("Embedded file name: '%1'", embedFileName);
- label += QStringLiteral("<br/>");
- }
- }
- }
-
- if (!verify) {
- label += i18n("<b>Note:</b> You cannot be sure who encrypted this message as it is not signed.") + QLatin1StringView("<br/>");
- }
-
- if (decryptionResult.isLegacyCipherNoMDC()) {
- label += 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/>");
- }
-
- label += formatRecipientsDetails(recipients, decryptionResult.numRecipients());
-
- return label;
-}
-
} // 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,
bool isNotepad,
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)
{
q->setIsNotepad(isNotepad);
}
QString label() const
{
- return formatResultLine(m_inputLabel,
- m_outputLabel,
- q->error(),
- q->errorString(),
- m_verificationResult,
- m_decryptionResult,
- makeSenderInfo(),
- m_parentTask);
+ const auto verify = m_verificationResult.numSignatures() > 0 || m_verificationResult.error().code();
+ const auto decrypt = !m_decryptionResult.isNull();
+ const auto recipients = KeyCache::instance()->findRecipients(m_decryptionResult);
+
+ Q_ASSERT(verify || decrypt);
+
+ Error error;
+ if (m_error.code()) {
+ error = m_error;
+ } else if (m_decryptionResult.error().code()) {
+ error = m_decryptionResult.error();
+ } else {
+ error = m_verificationResult.error();
+ }
+
+ QString label;
+ if (verify && decrypt) {
+ if (error.code() == GPG_ERR_NO_SECKEY) {
+ label = xi18nc("@info Unable to decrypt and verify <file>: <reason>", "Unable to decrypt and verify <filename>%1</filename>:", m_inputLabel);
+ } else if (error.code() != GPG_ERR_NO_ERROR) {
+ label = xi18nc("@info Failed to decrypt and verify <file>: <reason>", "Failed to decrypt and verify <filename>%1</filename>:", m_inputLabel);
+ } else {
+ label = xi18nc("@info Successfully decrypted and verified <file> as <file>.",
+ "Successfully decrypted and verified <filename>%1</filename> as <filename>%2</filename>.",
+ m_inputLabel,
+ m_outputLabel);
+ }
+ }
+
+ if (verify) {
+ if (error) {
+ label = xi18nc("@info Failed to verify <file>: <reason>", "Failed to verify <filename>%1</filename>:", m_inputLabel);
+ } else {
+ label = xi18nc("@info Verified <file> with signature in <file>.",
+ "Verified <filename>%1</filename> with signature in <filename>%2</filename>.",
+ m_outputLabel,
+ m_inputLabel);
+ }
+ }
+
+ if (error.code() == GPG_ERR_NO_SECKEY) {
+ label = xi18nc("@info Unable to decrypt <file>: <reason>", "Unable to decrypt <filename>%1</filename>:", m_inputLabel);
+ } else if (error.code() != GPG_ERR_NO_ERROR) {
+ label = xi18nc("@info Failed to decrypt <file>: <reason>", "Failed to decrypt <filename>%1</filename>:", m_inputLabel);
+ } else {
+ label = xi18nc("@info Successfully decrypted <file> as <file>.",
+ "Successfully decrypted <filename>%1</filename> as <filename>%2</filename>.",
+ m_inputLabel,
+ m_outputLabel);
+ }
+
+ if (error.isCanceled()) {
+ // TODO: fold into code at beginning of function
+ if (decrypt && verify) {
+ return i18nc("@info", "Decryption and verification canceled.");
+ } else if (verify) {
+ return i18nc("@info", "Verification canceled.");
+ } else {
+ return i18nc("@info", "Decryption canceled.");
+ }
+ } else if (error.code() == GPG_ERR_NO_SECKEY) {
+ label += u' ' + Formatting::errorAsString(error) + ".<br />"_L1
+ + i18nc("@info",
+ "The data was not encrypted for any "
+ "secret key in your certificate list.");
+ } else if (m_decryptionResult.isLegacyCipherNoMDC()) {
+ label += i18n("No integrity protection (MDC).");
+ } else if (!m_errorString.isEmpty()) {
+ if (error.code()) {
+ label += Formatting::errorAsString(error) + ": "_L1 + m_errorString.toHtmlEscaped();
+ }
+ label += m_errorString.toHtmlEscaped();
+ } else if (error) {
+ label += Formatting::errorAsString(error).append(u'.');
+ }
+
+ const std::vector<Signature> sigs = m_verificationResult.signatures();
+ if (verify && sigs.empty()) {
+ // TODO: test
+ return i18nc("@info", "No signatures found.");
+ }
+
+ // TODO: do we want this?
+ // const uint bad = std::count_if(sigs.cbegin(), sigs.cend(), IsBad);
+ // if (bad > 0) {
+ // return i18np("Invalid signature.", "%1 invalid signatures.", bad);
+ // }
+
+ // TODO: do the individual signatures already show this?
+ // const uint warn = std::count_if(sigs.cbegin(), sigs.cend(), [](const Signature &sig) {
+ // return !IsGoodOrValid(sig);
+ // });
+ // if (warn == sigs.size()) {
+ // return i18np("The data could not be verified.", "%1 signatures could not be verified.", warn);
+ // }
+
+ // TODO: this must be done for the individual signatures
+ // text = i18n("Valid signature by %1.", renderKeyEMailOnlyNameAsFallback(sigs[0].key()));
+ // if (info.conflicts()) {
+ // text += "<br />"_L1
+ // + i18nc("@info",
+ // "Warning: The sender's mail address is not stored in the <a href=\"key:%1\">certificate</a> used for signing.",
+ // QString::fromLatin1(sigs[0].key().primaryFingerprint()));
+ // }
+
+ label += "<br />"_L1;
+
+ if (makeSenderInfo().conflicts()) {
+ // TODO test
+ label += i18n("<p>The sender's address %1 is not stored in the certificate. Stored: %2</p>",
+ formatEmail(makeSenderInfo().informativeSender),
+ formatEmails(makeSenderInfo().signerEmails()).join(i18nc("separator for a list of e-mail addresses", ", ")));
+ }
+
+ // TODO
+ // if (m_decryptionResult.isNull() || m_decryptionResult.error() || m_decryptionResult.error().isCanceled()) {
+ // if (m_decryptionResult.error().code() == GPG_ERR_NO_SECKEY && recipients.empty()) {
+ // return {};
+ // }
+ // return formatRecipientsDetails(recipients, m_decryptionResult.numRecipients());
+ // }
+
+ if (DeVSCompliance::isCompliant()) {
+ label += ((m_decryptionResult.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 (m_decryptionResult.fileName()) {
+ const auto decVerifyTask = qobject_cast<AbstractDecryptVerifyTask *>(m_parentTask.data());
+ if (decVerifyTask) {
+ const auto embedFileName = QString::fromUtf8(m_decryptionResult.fileName()).toHtmlEscaped();
+
+ if (embedFileName != decVerifyTask->outputLabel()) {
+ label += i18n("Embedded file name: '%1'", embedFileName);
+ label += QStringLiteral("<br/>");
+ }
+ }
+ }
+
+ if (!verify) {
+ label += i18n("<b>Note:</b> You cannot be sure who encrypted this message as it is not signed.") + QLatin1StringView("<br/>");
+ }
+
+ if (m_decryptionResult.isLegacyCipherNoMDC()) {
+ label += 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/>");
+ }
+
+ label += formatRecipientsDetails(recipients, m_decryptionResult.numRecipients());
+
+ return label;
}
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(),
isNotepad()));
}
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(),
isNotepad()));
}
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(),
isNotepad()));
}
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(),
isNotepad()));
}
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(),
isNotepad()));
}
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(),
isNotepad()));
}
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(),
isNotepad()));
}
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(),
isNotepad()));
}
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,
bool isNotepad)
: Task::Result()
, d(new Private(type, vr, dr, stuff, fileName, error, errString, inputLabel, outputLabel, auditLog, parentTask, informativeSender, isNotepad, this))
{
}
Task::Result::ContentType DecryptVerifyResult::viewableContentType() const
{
if (decryptionResult().isMime()) {
return Task::Result::ContentType::Mime;
}
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::details() const
{
return {};
}
QString DecryptVerifyResult::overview() const
{
const auto decrypting = !d->m_decryptionResult.isNull();
const auto verifying = d->m_verificationResult.numSignatures() > 0;
if (isNotepad()) {
if (decrypting && d->m_decryptionResult.error()) {
return i18nc("@info", "Failed to decrypt the notepad: %1", Formatting::errorAsString(d->m_decryptionResult.error()));
}
if (verifying && d->m_verificationResult.error()) {
return i18nc("@info", "Failed to verify the notepad: %1", Formatting::errorAsString(d->m_verificationResult.error()));
}
if (decrypting && verifying) {
return i18nc("@info", "Successfully decrypted and verified the notepad");
}
if (decrypting) {
return i18nc("@info", "Successfully decrypted the notepad");
}
if (verifying) {
return i18nc("@info", "Successfully verified the notepad");
}
return {};
}
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;
}
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;
bool isNotepad = false;
};
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;
}
// TODO: remove q->emitResult(q->fromVerifyDetachedResult(Error::fromCode(GPG_ERR_INTERNAL), i18n("Caught unknown exception"), auditLog));
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;
m_outputFilePath = 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_outputFilePath = outputDirectory;
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
{
return d->signatureLabel();
}
QString VerifyDetachedTask::outputLabel() const
{
return d->signedDataLabel();
}
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()));
}
}
void AbstractDecryptVerifyTask::setIsNotepad(bool isNotepad)
{
d->isNotepad = isNotepad;
}
bool AbstractDecryptVerifyTask::isNotepad() const
{
return d->isNotepad;
}
#include "moc_decryptverifytask.cpp"
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sun, Jan 18, 11:06 PM (1 d, 18 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
e3/a3/373c8f17f566a993935cc9cf477e
Attached To
rKLEOPATRA Kleopatra
Event Timeline
Log In to Comment