File indexing completed on 2024-06-16 04:55:57

0001 /* -*- mode: c++; c-basic-offset:4 -*-
0002     decryptverifytask.cpp
0003 
0004     This file is part of Kleopatra, the KDE keymanager
0005     SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 
0010 #include <config-kleopatra.h>
0011 
0012 #include "decryptverifytask.h"
0013 
0014 #include <QGpgME/DecryptJob>
0015 #include <QGpgME/DecryptVerifyArchiveJob>
0016 #include <QGpgME/DecryptVerifyJob>
0017 #include <QGpgME/Protocol>
0018 #include <QGpgME/VerifyDetachedJob>
0019 #include <QGpgME/VerifyOpaqueJob>
0020 
0021 #include <Libkleo/AuditLogEntry>
0022 #include <Libkleo/Classify>
0023 #include <Libkleo/Compliance>
0024 #include <Libkleo/Dn>
0025 #include <Libkleo/Formatting>
0026 #include <Libkleo/KeyCache>
0027 #include <Libkleo/KleoException>
0028 #include <Libkleo/Predicates>
0029 #include <Libkleo/Stl_Util>
0030 
0031 #include <Libkleo/GnuPG>
0032 #include <utils/detail_p.h>
0033 #include <utils/input.h>
0034 #include <utils/kleo_assert.h>
0035 #include <utils/output.h>
0036 
0037 #include <KMime/Types>
0038 
0039 #include <gpgme++/context.h>
0040 #include <gpgme++/decryptionresult.h>
0041 #include <gpgme++/error.h>
0042 #include <gpgme++/key.h>
0043 #include <gpgme++/verificationresult.h>
0044 
0045 #include <gpg-error.h>
0046 
0047 #include "kleopatra_debug.h"
0048 
0049 #include <KFileUtils>
0050 #include <KLocalizedString>
0051 
0052 #include <QByteArray>
0053 #include <QDateTime>
0054 #include <QDir>
0055 #include <QFile>
0056 #include <QFileInfo>
0057 #include <QIODevice>
0058 #include <QLocale>
0059 #include <QMimeDatabase>
0060 #include <QStringList>
0061 #include <QTextDocument> // Qt::escape
0062 
0063 #include <algorithm>
0064 #include <sstream>
0065 
0066 using namespace Kleo::Crypto;
0067 using namespace Kleo;
0068 using namespace GpgME;
0069 using namespace KMime::Types;
0070 
0071 namespace
0072 {
0073 
0074 static AuditLogEntry auditLogFromSender(QObject *sender)
0075 {
0076     return AuditLogEntry::fromJob(qobject_cast<const QGpgME::Job *>(sender));
0077 }
0078 
0079 static bool addrspec_equal(const AddrSpec &lhs, const AddrSpec &rhs, Qt::CaseSensitivity cs)
0080 {
0081     return lhs.localPart.compare(rhs.localPart, cs) == 0 && lhs.domain.compare(rhs.domain, Qt::CaseInsensitive) == 0;
0082 }
0083 
0084 static bool mailbox_equal(const Mailbox &lhs, const Mailbox &rhs, Qt::CaseSensitivity cs)
0085 {
0086     return addrspec_equal(lhs.addrSpec(), rhs.addrSpec(), cs);
0087 }
0088 
0089 static std::string stripAngleBrackets(const std::string &str)
0090 {
0091     if (str.empty()) {
0092         return str;
0093     }
0094     if (str[0] == '<' && str[str.size() - 1] == '>') {
0095         return str.substr(1, str.size() - 2);
0096     }
0097     return str;
0098 }
0099 
0100 static std::string email(const UserID &uid)
0101 {
0102     if (uid.parent().protocol() == OpenPGP) {
0103         if (const char *const email = uid.email()) {
0104             return stripAngleBrackets(email);
0105         } else {
0106             return std::string();
0107         }
0108     }
0109 
0110     Q_ASSERT(uid.parent().protocol() == CMS);
0111 
0112     if (const char *const id = uid.id())
0113         if (*id == '<') {
0114             return stripAngleBrackets(id);
0115         } else {
0116             return DN(id)[QStringLiteral("EMAIL")].trimmed().toUtf8().constData();
0117         }
0118     else {
0119         return std::string();
0120     }
0121 }
0122 
0123 static Mailbox mailbox(const UserID &uid)
0124 {
0125     const std::string e = email(uid);
0126     Mailbox mbox;
0127     if (!e.empty()) {
0128         mbox.setAddress(e.c_str());
0129     }
0130     return mbox;
0131 }
0132 
0133 static std::vector<Mailbox> extractMailboxes(const Key &key)
0134 {
0135     std::vector<Mailbox> res;
0136     const auto userIDs{key.userIDs()};
0137     for (const UserID &id : userIDs) {
0138         const Mailbox mbox = mailbox(id);
0139         if (!mbox.addrSpec().isEmpty()) {
0140             res.push_back(mbox);
0141         }
0142     }
0143     return res;
0144 }
0145 
0146 static std::vector<Mailbox> extractMailboxes(const std::vector<Key> &signers)
0147 {
0148     std::vector<Mailbox> res;
0149     for (const Key &i : signers) {
0150         const std::vector<Mailbox> bxs = extractMailboxes(i);
0151         res.insert(res.end(), bxs.begin(), bxs.end());
0152     }
0153     return res;
0154 }
0155 
0156 static bool keyContainsMailbox(const Key &key, const Mailbox &mbox)
0157 {
0158     const std::vector<Mailbox> mbxs = extractMailboxes(key);
0159     return std::find_if(mbxs.cbegin(),
0160                         mbxs.cend(),
0161                         [mbox](const Mailbox &m) {
0162                             return mailbox_equal(mbox, m, Qt::CaseInsensitive);
0163                         })
0164         != mbxs.cend();
0165 }
0166 
0167 static bool keysContainMailbox(const std::vector<Key> &keys, const Mailbox &mbox)
0168 {
0169     return std::find_if(keys.cbegin(),
0170                         keys.cend(),
0171                         [mbox](const Key &key) {
0172                             return keyContainsMailbox(key, mbox);
0173                         })
0174         != keys.cend();
0175 }
0176 
0177 static bool relevantInDecryptVerifyContext(const VerificationResult &r)
0178 {
0179     // for D/V operations, we ignore verification results which are not errors and contain
0180     // no signatures (which means that the data was just not signed)
0181 
0182     return (r.error() && r.error().code() != GPG_ERR_DECRYPT_FAILED) || r.numSignatures() > 0;
0183 }
0184 
0185 static QString signatureSummaryToString(int summary)
0186 {
0187     if (summary & Signature::None) {
0188         return i18n("Error: Signature not verified");
0189     } else if (summary & Signature::Valid || summary & Signature::Green) {
0190         return i18n("Good signature");
0191     } else if (summary & Signature::KeyRevoked) {
0192         return i18n("Signing certificate was revoked");
0193     } else if (summary & Signature::KeyExpired) {
0194         return i18n("Signing certificate is expired");
0195     } else if (summary & Signature::KeyMissing) {
0196         return i18n("Certificate is not available");
0197     } else if (summary & Signature::SigExpired) {
0198         return i18n("Signature expired");
0199     } else if (summary & Signature::CrlMissing) {
0200         return i18n("CRL missing");
0201     } else if (summary & Signature::CrlTooOld) {
0202         return i18n("CRL too old");
0203     } else if (summary & Signature::BadPolicy) {
0204         return i18n("Bad policy");
0205     } else if (summary & Signature::SysError) {
0206         return i18n("System error"); // ### retrieve system error details?
0207     } else if (summary & Signature::Red) {
0208         return i18n("Bad signature");
0209     }
0210     return QString();
0211 }
0212 
0213 static QString formatValidSignatureWithTrustLevel(const UserID &id)
0214 {
0215     if (id.isNull()) {
0216         return QString();
0217     }
0218     switch (id.validity()) {
0219     case UserID::Marginal:
0220         return i18n("The signature is valid but the trust in the certificate's validity is only marginal.");
0221     case UserID::Full:
0222         return i18n("The signature is valid and the certificate's validity is fully trusted.");
0223     case UserID::Ultimate:
0224         return i18n("The signature is valid and the certificate's validity is ultimately trusted.");
0225     case UserID::Never:
0226         return i18n("The signature is valid but the certificate's validity is <em>not trusted</em>.");
0227     case UserID::Unknown:
0228         return i18n("The signature is valid but the certificate's validity is unknown.");
0229     case UserID::Undefined:
0230     default:
0231         return i18n("The signature is valid but the certificate's validity is undefined.");
0232     }
0233 }
0234 
0235 static QString renderKeyLink(const QString &fpr, const QString &text)
0236 {
0237     return QStringLiteral("<a href=\"key:%1\">%2</a>").arg(fpr, text);
0238 }
0239 
0240 static QString renderKey(const Key &key)
0241 {
0242     if (key.isNull()) {
0243         return i18n("Unknown certificate");
0244     }
0245 
0246     if (key.primaryFingerprint() && strlen(key.primaryFingerprint()) > 16 && key.numUserIDs()) {
0247         const QString text = QStringLiteral("%1 (%2)")
0248                                  .arg(Formatting::prettyNameAndEMail(key).toHtmlEscaped())
0249                                  .arg(Formatting::prettyID(QString::fromLocal8Bit(key.primaryFingerprint()).right(16).toLatin1().constData()));
0250         return renderKeyLink(QLatin1StringView(key.primaryFingerprint()), text);
0251     }
0252 
0253     return renderKeyLink(QLatin1StringView(key.primaryFingerprint()), Formatting::prettyID(key.primaryFingerprint()));
0254 }
0255 
0256 static QString renderKeyEMailOnlyNameAsFallback(const Key &key)
0257 {
0258     if (key.isNull()) {
0259         return i18n("Unknown certificate");
0260     }
0261     const QString email = Formatting::prettyEMail(key);
0262     const QString user = !email.isEmpty() ? email : Formatting::prettyName(key);
0263     return renderKeyLink(QLatin1StringView(key.primaryFingerprint()), user);
0264 }
0265 
0266 static QString formatDate(const QDateTime &dt)
0267 {
0268     return QLocale().toString(dt);
0269 }
0270 static QString formatSigningInformation(const Signature &sig)
0271 {
0272     if (sig.isNull()) {
0273         return QString();
0274     }
0275     const QDateTime dt = sig.creationTime() != 0 ? QDateTime::fromSecsSinceEpoch(quint32(sig.creationTime())) : QDateTime();
0276     QString text;
0277     Key key = sig.key();
0278     if (dt.isValid()) {
0279         text = i18nc("1 is a date", "Signature created on %1", formatDate(dt)) + QStringLiteral("<br>");
0280     }
0281     if (key.isNull()) {
0282         return text += i18n("With unavailable certificate:") + QStringLiteral("<br>ID: 0x%1").arg(QString::fromLatin1(sig.fingerprint()).toUpper());
0283     }
0284     text += i18n("With certificate:") + QStringLiteral("<br>") + renderKey(key);
0285 
0286     if (DeVSCompliance::isCompliant()) {
0287         text += (QStringLiteral("<br/>")
0288                  + (sig.isDeVs() ? i18nc("%1 is a placeholder for the name of a compliance mode. E.g. NATO RESTRICTED compliant or VS-NfD compliant",
0289                                          "The signature is %1",
0290                                          DeVSCompliance::name(true))
0291                                  : i18nc("%1 is a placeholder for the name of a compliance mode. E.g. NATO RESTRICTED compliant or VS-NfD compliant",
0292                                          "The signature <b>is not</b> %1.",
0293                                          DeVSCompliance::name(true))));
0294     }
0295 
0296     return text;
0297 }
0298 
0299 static QString strikeOut(const QString &str, bool strike)
0300 {
0301     return QString(strike ? QStringLiteral("<s>%1</s>") : QStringLiteral("%1")).arg(str.toHtmlEscaped());
0302 }
0303 
0304 static QString formatInputOutputLabel(const QString &input, const QString &output, bool inputDeleted, bool outputDeleted)
0305 {
0306     if (output.isEmpty()) {
0307         return strikeOut(input, inputDeleted);
0308     }
0309     return i18nc("Input file --> Output file (rarr is arrow", "%1 &rarr; %2", strikeOut(input, inputDeleted), strikeOut(output, outputDeleted));
0310 }
0311 
0312 static bool IsErrorOrCanceled(const GpgME::Error &err)
0313 {
0314     return err || err.isCanceled();
0315 }
0316 
0317 static bool IsErrorOrCanceled(const Result &res)
0318 {
0319     return IsErrorOrCanceled(res.error());
0320 }
0321 
0322 static bool IsBad(const Signature &sig)
0323 {
0324     return sig.summary() & Signature::Red;
0325 }
0326 
0327 static bool IsGoodOrValid(const Signature &sig)
0328 {
0329     return (sig.summary() & Signature::Valid) || (sig.summary() & Signature::Green);
0330 }
0331 
0332 static UserID findUserIDByMailbox(const Key &key, const Mailbox &mbox)
0333 {
0334     const auto userIDs{key.userIDs()};
0335     for (const UserID &id : userIDs)
0336         if (mailbox_equal(mailbox(id), mbox, Qt::CaseInsensitive)) {
0337             return id;
0338         }
0339     return UserID();
0340 }
0341 
0342 static void updateKeys(const VerificationResult &result)
0343 {
0344     // This little hack works around the problem that GnuPG / GpgME does not
0345     // provide Key information in a verification result. The Key object is
0346     // a dummy just holding the KeyID. This hack ensures that all available
0347     // keys are fetched from the backend and are populated
0348     for (const auto &sig : result.signatures()) {
0349         // Update key information
0350         sig.key(true, true);
0351     }
0352 }
0353 
0354 static QString ensureUniqueDirectory(const QString &path)
0355 {
0356     // make sure that we don't use an existing directory
0357     QString uniquePath = path;
0358     const QFileInfo outputInfo{path};
0359     if (outputInfo.exists()) {
0360         const auto uniqueName = KFileUtils::suggestName(QUrl::fromLocalFile(outputInfo.absolutePath()), outputInfo.fileName());
0361         uniquePath = outputInfo.dir().filePath(uniqueName);
0362     }
0363     if (!QDir{}.mkpath(uniquePath)) {
0364         return {};
0365     }
0366     return uniquePath;
0367 }
0368 
0369 static bool mimeTypeInherits(const QMimeType &mimeType, const QString &mimeTypeName)
0370 {
0371     // inherits is expensive on an invalid mimeType
0372     return mimeType.isValid() && mimeType.inherits(mimeTypeName);
0373 }
0374 }
0375 
0376 class DecryptVerifyResult::SenderInfo
0377 {
0378 public:
0379     explicit SenderInfo(const Mailbox &infSender, const std::vector<Key> &signers_)
0380         : informativeSender(infSender)
0381         , signers(signers_)
0382     {
0383     }
0384     const Mailbox informativeSender;
0385     const std::vector<Key> signers;
0386     bool hasInformativeSender() const
0387     {
0388         return !informativeSender.addrSpec().isEmpty();
0389     }
0390     bool conflicts() const
0391     {
0392         return hasInformativeSender() && hasKeys() && !keysContainMailbox(signers, informativeSender);
0393     }
0394     bool hasKeys() const
0395     {
0396         return std::any_of(signers.cbegin(), signers.cend(), [](const Key &key) {
0397             return !key.isNull();
0398         });
0399     }
0400     std::vector<Mailbox> signerMailboxes() const
0401     {
0402         return extractMailboxes(signers);
0403     }
0404 };
0405 
0406 namespace
0407 {
0408 
0409 static Task::Result::VisualCode codeForVerificationResult(const VerificationResult &res)
0410 {
0411     if (res.isNull()) {
0412         return Task::Result::NeutralSuccess;
0413     }
0414 
0415     const std::vector<Signature> sigs = res.signatures();
0416     if (sigs.empty()) {
0417         return Task::Result::Warning;
0418     }
0419 
0420     if (std::find_if(sigs.begin(), sigs.end(), IsBad) != sigs.end()) {
0421         return Task::Result::Danger;
0422     }
0423 
0424     if ((size_t)std::count_if(sigs.begin(), sigs.end(), IsGoodOrValid) == sigs.size()) {
0425         return Task::Result::AllGood;
0426     }
0427 
0428     return Task::Result::Warning;
0429 }
0430 
0431 static QString formatVerificationResultOverview(const VerificationResult &res, const DecryptVerifyResult::SenderInfo &info)
0432 {
0433     if (res.isNull()) {
0434         return QString();
0435     }
0436 
0437     const Error err = res.error();
0438 
0439     if (err.isCanceled()) {
0440         return i18n("<b>Verification canceled.</b>");
0441     } else if (err) {
0442         return i18n("<b>Verification failed: %1.</b>", Formatting::errorAsString(err).toHtmlEscaped());
0443     }
0444 
0445     const std::vector<Signature> sigs = res.signatures();
0446 
0447     if (sigs.empty()) {
0448         return i18n("<b>No signatures found.</b>");
0449     }
0450 
0451     const uint bad = std::count_if(sigs.cbegin(), sigs.cend(), IsBad);
0452     if (bad > 0) {
0453         return i18np("<b>Invalid signature.</b>", "<b>%1 invalid signatures.</b>", bad);
0454     }
0455     const uint warn = std::count_if(sigs.cbegin(), sigs.cend(), [](const Signature &sig) {
0456         return !IsGoodOrValid(sig);
0457     });
0458     if (warn == sigs.size()) {
0459         return i18np("<b>The data could not be verified.</b>", "<b>%1 signatures could not be verified.</b>", warn);
0460     }
0461 
0462     // Good signature:
0463     QString text;
0464     if (sigs.size() == 1) {
0465         text = i18n("<b>Valid signature by %1</b>", renderKeyEMailOnlyNameAsFallback(sigs[0].key()));
0466         if (info.conflicts())
0467             text += i18n("<br/><b>Warning:</b> The sender's mail address is not stored in the %1 used for signing.",
0468                          renderKeyLink(QLatin1StringView(sigs[0].key().primaryFingerprint()), i18n("certificate")));
0469     } else {
0470         text = i18np("<b>Valid signature.</b>", "<b>%1 valid signatures.</b>", sigs.size());
0471         if (info.conflicts()) {
0472             text += i18n("<br/><b>Warning:</b> The sender's mail address is not stored in the certificates used for signing.");
0473         }
0474     }
0475 
0476     return text;
0477 }
0478 
0479 static QString formatDecryptionResultOverview(const DecryptionResult &result, const QString &errorString = QString())
0480 {
0481     const Error err = result.error();
0482 
0483     if (err.isCanceled()) {
0484         return i18n("<b>Decryption canceled.</b>");
0485     } else if (result.isLegacyCipherNoMDC()) {
0486         return i18n("<b>Decryption failed: %1.</b>", i18n("No integrity protection (MDC)."));
0487     } else if (!errorString.isEmpty()) {
0488         return i18n("<b>Decryption failed: %1.</b>", errorString.toHtmlEscaped());
0489     } else if (err) {
0490         return i18n("<b>Decryption failed: %1.</b>", Formatting::errorAsString(err).toHtmlEscaped());
0491     }
0492     return i18n("<b>Decryption succeeded.</b>");
0493 }
0494 
0495 static QString formatSignature(const Signature &sig, const DecryptVerifyResult::SenderInfo &info)
0496 {
0497     if (sig.isNull()) {
0498         return QString();
0499     }
0500 
0501     const QString text = formatSigningInformation(sig) + QLatin1StringView("<br/>");
0502     const Key key = sig.key();
0503 
0504     // Green
0505     if (sig.summary() & Signature::Valid) {
0506         const UserID id = findUserIDByMailbox(key, info.informativeSender);
0507         return text + formatValidSignatureWithTrustLevel(!id.isNull() ? id : key.userID(0));
0508     }
0509 
0510     // Red
0511     if ((sig.summary() & Signature::Red)) {
0512         const QString ret = text + i18n("The signature is invalid: %1", signatureSummaryToString(sig.summary()));
0513         if (sig.summary() & Signature::SysError) {
0514             return ret + QStringLiteral(" (%1)").arg(Formatting::errorAsString(sig.status()));
0515         }
0516         return ret;
0517     }
0518 
0519     // Key missing
0520     if ((sig.summary() & Signature::KeyMissing)) {
0521         return text + i18n("You can search the certificate on a keyserver or import it from a file.");
0522     }
0523 
0524     // Yellow
0525     if ((sig.validity() & Signature::Validity::Undefined) //
0526         || (sig.validity() & Signature::Validity::Unknown) //
0527         || (sig.summary() == Signature::Summary::None)) {
0528         return text
0529             + (key.protocol() == OpenPGP
0530                    ? i18n("The used key is not certified by you or any trusted person.")
0531                    : i18n("The used certificate is not certified by a trustworthy Certificate Authority or the Certificate Authority is unknown."));
0532     }
0533 
0534     // Catch all fall through
0535     const QString ret = text + i18n("The signature is invalid: %1", signatureSummaryToString(sig.summary()));
0536     if (sig.summary() & Signature::SysError) {
0537         return ret + QStringLiteral(" (%1)").arg(Formatting::errorAsString(sig.status()));
0538     }
0539     return ret;
0540 }
0541 
0542 static QStringList format(const std::vector<Mailbox> &mbxs)
0543 {
0544     QStringList res;
0545     std::transform(mbxs.cbegin(), mbxs.cend(), std::back_inserter(res), [](const Mailbox &mbox) {
0546         return mbox.prettyAddress();
0547     });
0548     return res;
0549 }
0550 
0551 static QString formatVerificationResultDetails(const VerificationResult &res, const DecryptVerifyResult::SenderInfo &info, const QString &errorString)
0552 {
0553     if ((res.error().code() == GPG_ERR_EIO || res.error().code() == GPG_ERR_NO_DATA) && !errorString.isEmpty()) {
0554         return i18n("Input error: %1", errorString);
0555     }
0556 
0557     const std::vector<Signature> sigs = res.signatures();
0558     QString details;
0559     for (const Signature &sig : sigs) {
0560         details += formatSignature(sig, info) + QLatin1Char('\n');
0561     }
0562     details = details.trimmed();
0563     details.replace(QLatin1Char('\n'), QStringLiteral("<br/><br/>"));
0564     if (info.conflicts()) {
0565         details += i18n("<p>The sender's address %1 is not stored in the certificate. Stored: %2</p>",
0566                         info.informativeSender.prettyAddress(),
0567                         format(info.signerMailboxes()).join(i18nc("separator for a list of e-mail addresses", ", ")));
0568     }
0569     return details;
0570 }
0571 
0572 static QString formatRecipientsDetails(const std::vector<Key> &knownRecipients, unsigned int numRecipients)
0573 {
0574     if (numRecipients == 0) {
0575         return {};
0576     }
0577 
0578     if (knownRecipients.empty()) {
0579         return QLatin1StringView("<i>") + i18np("One unknown recipient.", "%1 unknown recipients.", numRecipients) + QLatin1String("</i>");
0580     }
0581 
0582     QString details = i18np("Recipient:", "Recipients:", numRecipients);
0583 
0584     if (numRecipients == 1) {
0585         details += QLatin1Char(' ') + renderKey(knownRecipients.front());
0586     } else {
0587         details += QLatin1StringView("<ul>");
0588         for (const Key &key : knownRecipients) {
0589             details += QLatin1StringView("<li>") + renderKey(key) + QLatin1String("</li>");
0590         }
0591         if (knownRecipients.size() < numRecipients) {
0592             details += QLatin1StringView("<li><i>") + i18np("One unknown recipient", "%1 unknown recipients", numRecipients - knownRecipients.size())
0593                 + QLatin1StringView("</i></li>");
0594         }
0595         details += QLatin1StringView("</ul>");
0596     }
0597 
0598     return details;
0599 }
0600 
0601 static QString formatDecryptionResultDetails(const DecryptionResult &res,
0602                                              const std::vector<Key> &recipients,
0603                                              const QString &errorString,
0604                                              bool isSigned,
0605                                              const QPointer<Task> &task)
0606 {
0607     if ((res.error().code() == GPG_ERR_EIO || res.error().code() == GPG_ERR_NO_DATA) && !errorString.isEmpty()) {
0608         return i18n("Input error: %1", errorString);
0609     }
0610 
0611     if (res.isNull() || res.error() || res.error().isCanceled()) {
0612         return formatRecipientsDetails(recipients, res.numRecipients());
0613     }
0614 
0615     QString details;
0616 
0617     if (DeVSCompliance::isCompliant()) {
0618         details += ((res.isDeVs() ? i18nc("%1 is a placeholder for the name of a compliance mode. E.g. NATO RESTRICTED compliant or VS-NfD compliant",
0619                                           "The decryption is %1.",
0620                                           DeVSCompliance::name(true))
0621                                   : i18nc("%1 is a placeholder for the name of a compliance mode. E.g. NATO RESTRICTED compliant or VS-NfD compliant",
0622                                           "The decryption <b>is not</b> %1.",
0623                                           DeVSCompliance::name(true)))
0624                     + QStringLiteral("<br/>"));
0625     }
0626 
0627     if (res.fileName()) {
0628         const auto decVerifyTask = qobject_cast<AbstractDecryptVerifyTask *>(task.data());
0629         if (decVerifyTask) {
0630             const auto embedFileName = QString::fromUtf8(res.fileName()).toHtmlEscaped();
0631 
0632             if (embedFileName != decVerifyTask->outputLabel()) {
0633                 details += i18n("Embedded file name: '%1'", embedFileName);
0634                 details += QStringLiteral("<br/>");
0635             }
0636         }
0637     }
0638 
0639     if (!isSigned) {
0640         details += i18n("<b>Note:</b> You cannot be sure who encrypted this message as it is not signed.") + QLatin1StringView("<br/>");
0641     }
0642 
0643     if (res.isLegacyCipherNoMDC()) {
0644         details += i18nc("Integrity protection was missing because an old cipher was used.",
0645                          "<b>Hint:</b> If this file was encrypted before the year 2003 it is "
0646                          "likely that the file is legitimate.  This is because back "
0647                          "then integrity protection was not widely used.")
0648             + QStringLiteral("<br/><br/>")
0649             + i18nc("The user is offered to force decrypt a non integrity protected message. With the strong advice to re-encrypt it.",
0650                     "If you are confident that the file was not manipulated you should re-encrypt it after you have forced the decryption.")
0651             + QStringLiteral("<br/><br/>");
0652     }
0653 
0654     details += formatRecipientsDetails(recipients, res.numRecipients());
0655 
0656     return details;
0657 }
0658 
0659 static QString formatDecryptVerifyResultOverview(const DecryptionResult &dr, const VerificationResult &vr, const DecryptVerifyResult::SenderInfo &info)
0660 {
0661     if (IsErrorOrCanceled(dr) || !relevantInDecryptVerifyContext(vr)) {
0662         return formatDecryptionResultOverview(dr);
0663     }
0664     return formatVerificationResultOverview(vr, info);
0665 }
0666 
0667 static QString formatDecryptVerifyResultDetails(const DecryptionResult &dr,
0668                                                 const VerificationResult &vr,
0669                                                 const std::vector<Key> &recipients,
0670                                                 const DecryptVerifyResult::SenderInfo &info,
0671                                                 const QString &errorString,
0672                                                 const QPointer<Task> &task)
0673 {
0674     const QString drDetails = formatDecryptionResultDetails(dr, recipients, errorString, relevantInDecryptVerifyContext(vr), task);
0675     if (IsErrorOrCanceled(dr) || !relevantInDecryptVerifyContext(vr)) {
0676         return drDetails;
0677     }
0678     return drDetails + (drDetails.isEmpty() ? QString() : QStringLiteral("<br/>")) + formatVerificationResultDetails(vr, info, errorString);
0679 }
0680 
0681 } // anon namespace
0682 
0683 class DecryptVerifyResult::Private
0684 {
0685     DecryptVerifyResult *const q;
0686 
0687 public:
0688     Private(DecryptVerifyOperation type,
0689             const VerificationResult &vr,
0690             const DecryptionResult &dr,
0691             const QByteArray &stuff,
0692             const QString &fileName,
0693             const GpgME::Error &error,
0694             const QString &errString,
0695             const QString &input,
0696             const QString &output,
0697             const AuditLogEntry &auditLog,
0698             Task *parentTask,
0699             const Mailbox &informativeSender,
0700             DecryptVerifyResult *qq)
0701         : q(qq)
0702         , m_type(type)
0703         , m_verificationResult(vr)
0704         , m_decryptionResult(dr)
0705         , m_stuff(stuff)
0706         , m_fileName(fileName)
0707         , m_error(error)
0708         , m_errorString(errString)
0709         , m_inputLabel(input)
0710         , m_outputLabel(output)
0711         , m_auditLog(auditLog)
0712         , m_parentTask(QPointer<Task>(parentTask))
0713         , m_informativeSender(informativeSender)
0714     {
0715     }
0716 
0717     QString label() const
0718     {
0719         return formatInputOutputLabel(m_inputLabel, m_outputLabel, false, q->hasError());
0720     }
0721 
0722     DecryptVerifyResult::SenderInfo makeSenderInfo() const;
0723 
0724     bool isDecryptOnly() const
0725     {
0726         return m_type == Decrypt;
0727     }
0728     bool isVerifyOnly() const
0729     {
0730         return m_type == Verify;
0731     }
0732     bool isDecryptVerify() const
0733     {
0734         return m_type == DecryptVerify;
0735     }
0736     DecryptVerifyOperation m_type;
0737     VerificationResult m_verificationResult;
0738     DecryptionResult m_decryptionResult;
0739     QByteArray m_stuff;
0740     QString m_fileName;
0741     GpgME::Error m_error;
0742     QString m_errorString;
0743     QString m_inputLabel;
0744     QString m_outputLabel;
0745     const AuditLogEntry m_auditLog;
0746     QPointer<Task> m_parentTask;
0747     const Mailbox m_informativeSender;
0748 };
0749 
0750 DecryptVerifyResult::SenderInfo DecryptVerifyResult::Private::makeSenderInfo() const
0751 {
0752     return SenderInfo(m_informativeSender, KeyCache::instance()->findSigners(m_verificationResult));
0753 }
0754 
0755 std::shared_ptr<DecryptVerifyResult>
0756 AbstractDecryptVerifyTask::fromDecryptResult(const DecryptionResult &dr, const QByteArray &plaintext, const AuditLogEntry &auditLog)
0757 {
0758     return std::shared_ptr<DecryptVerifyResult>(new DecryptVerifyResult(Decrypt, //
0759                                                                         VerificationResult(),
0760                                                                         dr,
0761                                                                         plaintext,
0762                                                                         {},
0763                                                                         {},
0764                                                                         QString(),
0765                                                                         inputLabel(),
0766                                                                         outputLabel(),
0767                                                                         auditLog,
0768                                                                         this,
0769                                                                         informativeSender()));
0770 }
0771 
0772 std::shared_ptr<DecryptVerifyResult> AbstractDecryptVerifyTask::fromDecryptResult(const GpgME::Error &err, const QString &what, const AuditLogEntry &auditLog)
0773 {
0774     return std::shared_ptr<DecryptVerifyResult>(new DecryptVerifyResult(Decrypt, //
0775                                                                         VerificationResult(),
0776                                                                         DecryptionResult(err),
0777                                                                         QByteArray(),
0778                                                                         {},
0779                                                                         err,
0780                                                                         what,
0781                                                                         inputLabel(),
0782                                                                         outputLabel(),
0783                                                                         auditLog,
0784                                                                         this,
0785                                                                         informativeSender()));
0786 }
0787 
0788 std::shared_ptr<DecryptVerifyResult> AbstractDecryptVerifyTask::fromDecryptVerifyResult(const DecryptionResult &dr,
0789                                                                                         const VerificationResult &vr,
0790                                                                                         const QByteArray &plaintext,
0791                                                                                         const QString &fileName,
0792                                                                                         const AuditLogEntry &auditLog)
0793 {
0794     const auto err = dr.error().code() ? dr.error() : vr.error();
0795     return std::shared_ptr<DecryptVerifyResult>(new DecryptVerifyResult(DecryptVerify, //
0796                                                                         vr,
0797                                                                         dr,
0798                                                                         plaintext,
0799                                                                         fileName,
0800                                                                         err,
0801                                                                         QString(),
0802                                                                         inputLabel(),
0803                                                                         outputLabel(),
0804                                                                         auditLog,
0805                                                                         this,
0806                                                                         informativeSender()));
0807 }
0808 
0809 std::shared_ptr<DecryptVerifyResult>
0810 AbstractDecryptVerifyTask::fromDecryptVerifyResult(const GpgME::Error &err, const QString &details, const AuditLogEntry &auditLog)
0811 {
0812     return std::shared_ptr<DecryptVerifyResult>(new DecryptVerifyResult(DecryptVerify, //
0813                                                                         VerificationResult(),
0814                                                                         DecryptionResult(err),
0815                                                                         QByteArray(),
0816                                                                         {},
0817                                                                         err,
0818                                                                         details,
0819                                                                         inputLabel(),
0820                                                                         outputLabel(),
0821                                                                         auditLog,
0822                                                                         this,
0823                                                                         informativeSender()));
0824 }
0825 
0826 std::shared_ptr<DecryptVerifyResult>
0827 AbstractDecryptVerifyTask::fromVerifyOpaqueResult(const VerificationResult &vr, const QByteArray &plaintext, const AuditLogEntry &auditLog)
0828 {
0829     return std::shared_ptr<DecryptVerifyResult>(new DecryptVerifyResult(Verify, //
0830                                                                         vr,
0831                                                                         DecryptionResult(),
0832                                                                         plaintext,
0833                                                                         {},
0834                                                                         {},
0835                                                                         QString(),
0836                                                                         inputLabel(),
0837                                                                         outputLabel(),
0838                                                                         auditLog,
0839                                                                         this,
0840                                                                         informativeSender()));
0841 }
0842 std::shared_ptr<DecryptVerifyResult>
0843 AbstractDecryptVerifyTask::fromVerifyOpaqueResult(const GpgME::Error &err, const QString &details, const AuditLogEntry &auditLog)
0844 {
0845     return std::shared_ptr<DecryptVerifyResult>(new DecryptVerifyResult(Verify, //
0846                                                                         VerificationResult(err),
0847                                                                         DecryptionResult(),
0848                                                                         QByteArray(),
0849                                                                         {},
0850                                                                         err,
0851                                                                         details,
0852                                                                         inputLabel(),
0853                                                                         outputLabel(),
0854                                                                         auditLog,
0855                                                                         this,
0856                                                                         informativeSender()));
0857 }
0858 
0859 std::shared_ptr<DecryptVerifyResult> AbstractDecryptVerifyTask::fromVerifyDetachedResult(const VerificationResult &vr, const AuditLogEntry &auditLog)
0860 {
0861     return std::shared_ptr<DecryptVerifyResult>(new DecryptVerifyResult(Verify, //
0862                                                                         vr,
0863                                                                         DecryptionResult(),
0864                                                                         QByteArray(),
0865                                                                         {},
0866                                                                         {},
0867                                                                         QString(),
0868                                                                         inputLabel(),
0869                                                                         outputLabel(),
0870                                                                         auditLog,
0871                                                                         this,
0872                                                                         informativeSender()));
0873 }
0874 std::shared_ptr<DecryptVerifyResult>
0875 AbstractDecryptVerifyTask::fromVerifyDetachedResult(const GpgME::Error &err, const QString &details, const AuditLogEntry &auditLog)
0876 {
0877     return std::shared_ptr<DecryptVerifyResult>(new DecryptVerifyResult(Verify, //
0878                                                                         VerificationResult(err),
0879                                                                         DecryptionResult(),
0880                                                                         QByteArray(),
0881                                                                         {},
0882                                                                         err,
0883                                                                         details,
0884                                                                         inputLabel(),
0885                                                                         outputLabel(),
0886                                                                         auditLog,
0887                                                                         this,
0888                                                                         informativeSender()));
0889 }
0890 
0891 DecryptVerifyResult::DecryptVerifyResult(DecryptVerifyOperation type,
0892                                          const VerificationResult &vr,
0893                                          const DecryptionResult &dr,
0894                                          const QByteArray &stuff,
0895                                          const QString &fileName,
0896                                          const GpgME::Error &error,
0897                                          const QString &errString,
0898                                          const QString &inputLabel,
0899                                          const QString &outputLabel,
0900                                          const AuditLogEntry &auditLog,
0901                                          Task *parentTask,
0902                                          const Mailbox &informativeSender)
0903     : Task::Result()
0904     , d(new Private(type, vr, dr, stuff, fileName, error, errString, inputLabel, outputLabel, auditLog, parentTask, informativeSender, this))
0905 {
0906 }
0907 
0908 Task::Result::ContentType DecryptVerifyResult::viewableContentType() const
0909 {
0910 #if QGPGME_SUPPORTS_IS_MIME
0911     if (decryptionResult().isMime()) {
0912         return Task::Result::ContentType::Mime;
0913     }
0914 #endif
0915 
0916     if (fileName().endsWith(QStringLiteral("openpgp-encrypted-message"))) {
0917         return Task::Result::ContentType::Mime;
0918     }
0919 
0920     QMimeDatabase mimeDatabase;
0921     const auto mimeType = mimeDatabase.mimeTypeForFile(fileName());
0922     if (mimeTypeInherits(mimeType, QStringLiteral("message/rfc822"))) {
0923         return Task::Result::ContentType::Mime;
0924     }
0925 
0926     if (mimeTypeInherits(mimeType, QStringLiteral("application/mbox"))) {
0927         return Task::Result::ContentType::Mbox;
0928     }
0929 
0930     return Task::Result::ContentType::None;
0931 }
0932 
0933 QString DecryptVerifyResult::overview() const
0934 {
0935     QString ov;
0936     if (d->isDecryptOnly()) {
0937         ov += formatDecryptionResultOverview(d->m_decryptionResult);
0938     } else if (d->isVerifyOnly()) {
0939         ov += formatVerificationResultOverview(d->m_verificationResult, d->makeSenderInfo());
0940     } else {
0941         ov += formatDecryptVerifyResultOverview(d->m_decryptionResult, d->m_verificationResult, d->makeSenderInfo());
0942     }
0943     if (ov.size() + d->label().size() > 120) {
0944         // Avoid ugly breaks
0945         ov = QStringLiteral("<br>") + ov;
0946     }
0947     return i18nc("label: result example: foo.sig: Verification failed. ", "%1: %2", d->label(), ov);
0948 }
0949 
0950 QString DecryptVerifyResult::details() const
0951 {
0952     if (d->isDecryptOnly()) {
0953         return formatDecryptionResultDetails(d->m_decryptionResult,
0954                                              KeyCache::instance()->findRecipients(d->m_decryptionResult),
0955                                              errorString(),
0956                                              false,
0957                                              d->m_parentTask);
0958     }
0959     if (d->isVerifyOnly()) {
0960         return formatVerificationResultDetails(d->m_verificationResult, d->makeSenderInfo(), errorString());
0961     }
0962     return formatDecryptVerifyResultDetails(d->m_decryptionResult,
0963                                             d->m_verificationResult,
0964                                             KeyCache::instance()->findRecipients(d->m_decryptionResult),
0965                                             d->makeSenderInfo(),
0966                                             errorString(),
0967                                             d->m_parentTask);
0968 }
0969 
0970 GpgME::Error DecryptVerifyResult::error() const
0971 {
0972     return d->m_error;
0973 }
0974 
0975 QString DecryptVerifyResult::errorString() const
0976 {
0977     return d->m_errorString;
0978 }
0979 
0980 AuditLogEntry DecryptVerifyResult::auditLog() const
0981 {
0982     return d->m_auditLog;
0983 }
0984 
0985 QPointer<Task> DecryptVerifyResult::parentTask() const
0986 {
0987     return d->m_parentTask;
0988 }
0989 
0990 Task::Result::VisualCode DecryptVerifyResult::code() const
0991 {
0992     if ((d->m_type == DecryptVerify || d->m_type == Verify) && relevantInDecryptVerifyContext(verificationResult())) {
0993         return codeForVerificationResult(verificationResult());
0994     }
0995     return hasError() ? NeutralError : NeutralSuccess;
0996 }
0997 
0998 GpgME::VerificationResult DecryptVerifyResult::verificationResult() const
0999 {
1000     return d->m_verificationResult;
1001 }
1002 
1003 GpgME::DecryptionResult DecryptVerifyResult::decryptionResult() const
1004 {
1005     return d->m_decryptionResult;
1006 }
1007 
1008 QString DecryptVerifyResult::fileName() const
1009 {
1010     return d->m_fileName;
1011 }
1012 
1013 class AbstractDecryptVerifyTask::Private
1014 {
1015 public:
1016     Mailbox informativeSender;
1017     QPointer<QGpgME::Job> job;
1018 };
1019 
1020 AbstractDecryptVerifyTask::AbstractDecryptVerifyTask(QObject *parent)
1021     : Task(parent)
1022     , d(new Private)
1023 {
1024 }
1025 
1026 AbstractDecryptVerifyTask::~AbstractDecryptVerifyTask()
1027 {
1028 }
1029 
1030 void AbstractDecryptVerifyTask::cancel()
1031 {
1032     qCDebug(KLEOPATRA_LOG) << this << __func__;
1033     if (d->job) {
1034         d->job->slotCancel();
1035     }
1036 }
1037 
1038 Mailbox AbstractDecryptVerifyTask::informativeSender() const
1039 {
1040     return d->informativeSender;
1041 }
1042 
1043 void AbstractDecryptVerifyTask::setInformativeSender(const Mailbox &sender)
1044 {
1045     d->informativeSender = sender;
1046 }
1047 
1048 QGpgME::Job *AbstractDecryptVerifyTask::job() const
1049 {
1050     return d->job;
1051 }
1052 
1053 void AbstractDecryptVerifyTask::setJob(QGpgME::Job *job)
1054 {
1055     d->job = job;
1056 }
1057 
1058 class DecryptVerifyTask::Private
1059 {
1060     DecryptVerifyTask *const q;
1061 
1062 public:
1063     explicit Private(DecryptVerifyTask *qq)
1064         : q{qq}
1065     {
1066     }
1067 
1068     void startDecryptVerifyJob();
1069     void startDecryptVerifyArchiveJob();
1070 
1071     void slotResult(const DecryptionResult &, const VerificationResult &, const QByteArray & = {});
1072 
1073     std::shared_ptr<Input> m_input;
1074     std::shared_ptr<Output> m_output;
1075     const QGpgME::Protocol *m_backend = nullptr;
1076     Protocol m_protocol = UnknownProtocol;
1077     bool m_ignoreMDCError = false;
1078     bool m_extractArchive = false;
1079     QString m_inputFilePath;
1080     QString m_outputFilePath;
1081     QString m_outputDirectory;
1082 };
1083 
1084 void DecryptVerifyTask::Private::slotResult(const DecryptionResult &dr, const VerificationResult &vr, const QByteArray &plainText)
1085 {
1086     updateKeys(vr);
1087     {
1088         std::stringstream ss;
1089         ss << dr << '\n' << vr;
1090         qCDebug(KLEOPATRA_LOG) << ss.str().c_str();
1091     }
1092     const AuditLogEntry auditLog = auditLogFromSender(q->sender());
1093     if (m_output) {
1094         if (dr.error().code() || vr.error().code()) {
1095             m_output->cancel();
1096         } else {
1097             try {
1098                 kleo_assert(!dr.isNull() || !vr.isNull());
1099                 m_output->finalize();
1100             } catch (const GpgME::Exception &e) {
1101                 q->emitResult(q->fromDecryptResult(e.error(), QString::fromLocal8Bit(e.what()), auditLog));
1102                 return;
1103             } catch (const std::exception &e) {
1104                 q->emitResult(
1105                     q->fromDecryptResult(Error::fromCode(GPG_ERR_INTERNAL), i18n("Caught exception: %1", QString::fromLocal8Bit(e.what())), auditLog));
1106                 return;
1107             } catch (...) {
1108                 q->emitResult(q->fromDecryptResult(Error::fromCode(GPG_ERR_INTERNAL), i18n("Caught unknown exception"), auditLog));
1109                 return;
1110             }
1111         }
1112     }
1113     const int drErr = dr.error().code();
1114     const QString errorString = m_output ? m_output->errorString() : QString{};
1115     if (((drErr == GPG_ERR_EIO || drErr == GPG_ERR_NO_DATA) && !errorString.isEmpty()) || (m_output && m_output->failed())) {
1116         q->emitResult(q->fromDecryptResult(drErr ? dr.error() : Error::fromCode(GPG_ERR_EIO), errorString, auditLog));
1117         return;
1118     }
1119 
1120     q->emitResult(q->fromDecryptVerifyResult(dr, vr, plainText, m_output ? m_output->fileName() : QString{}, auditLog));
1121 }
1122 
1123 DecryptVerifyTask::DecryptVerifyTask(QObject *parent)
1124     : AbstractDecryptVerifyTask(parent)
1125     , d(new Private(this))
1126 {
1127 }
1128 
1129 DecryptVerifyTask::~DecryptVerifyTask()
1130 {
1131 }
1132 
1133 void DecryptVerifyTask::setInput(const std::shared_ptr<Input> &input)
1134 {
1135     d->m_input = input;
1136     kleo_assert(d->m_input && d->m_input->ioDevice());
1137 }
1138 
1139 void DecryptVerifyTask::setOutput(const std::shared_ptr<Output> &output)
1140 {
1141     d->m_output = output;
1142     kleo_assert(d->m_output && d->m_output->ioDevice());
1143 }
1144 
1145 void DecryptVerifyTask::setProtocol(Protocol prot)
1146 {
1147     kleo_assert(prot != UnknownProtocol);
1148     d->m_protocol = prot;
1149     d->m_backend = prot == GpgME::OpenPGP ? QGpgME::openpgp() : QGpgME::smime();
1150     kleo_assert(d->m_backend);
1151 }
1152 
1153 void DecryptVerifyTask::autodetectProtocolFromInput()
1154 {
1155     if (!d->m_input) {
1156         return;
1157     }
1158     const Protocol p = findProtocol(d->m_input->classification());
1159     if (p == UnknownProtocol) {
1160         throw Exception(
1161             gpg_error(GPG_ERR_NOTHING_FOUND),
1162             i18n("Could not determine whether this is an S/MIME or an OpenPGP signature/ciphertext - maybe it is neither ciphertext nor a signature?"),
1163             Exception::MessageOnly);
1164     }
1165     setProtocol(p);
1166 }
1167 
1168 QString DecryptVerifyTask::label() const
1169 {
1170     return i18n("Decrypting %1...", inputLabel());
1171 }
1172 
1173 unsigned long long DecryptVerifyTask::inputSize() const
1174 {
1175     return d->m_input ? d->m_input->size() : 0;
1176 }
1177 
1178 QString DecryptVerifyTask::inputLabel() const
1179 {
1180     return d->m_input ? d->m_input->label() : QFileInfo{d->m_inputFilePath}.fileName();
1181 }
1182 
1183 QString DecryptVerifyTask::outputLabel() const
1184 {
1185     if (d->m_output) {
1186         return d->m_output->label();
1187     } else if (!d->m_outputFilePath.isEmpty()) {
1188         return QFileInfo{d->m_outputFilePath}.fileName();
1189     } else {
1190         return d->m_outputDirectory;
1191     }
1192 }
1193 
1194 Protocol DecryptVerifyTask::protocol() const
1195 {
1196     return d->m_protocol;
1197 }
1198 
1199 static void ensureIOOpen(QIODevice *input, QIODevice *output)
1200 {
1201     if (input && !input->isOpen()) {
1202         input->open(QIODevice::ReadOnly);
1203     }
1204     if (output && !output->isOpen()) {
1205         output->open(QIODevice::WriteOnly);
1206     }
1207 }
1208 
1209 void DecryptVerifyTask::setIgnoreMDCError(bool value)
1210 {
1211     d->m_ignoreMDCError = value;
1212 }
1213 
1214 void DecryptVerifyTask::setExtractArchive(bool extract)
1215 {
1216     d->m_extractArchive = extract;
1217 }
1218 
1219 void DecryptVerifyTask::setInputFile(const QString &path)
1220 {
1221     d->m_inputFilePath = path;
1222 }
1223 
1224 void DecryptVerifyTask::setOutputFile(const QString &path)
1225 {
1226     d->m_outputFilePath = path;
1227 }
1228 
1229 void DecryptVerifyTask::setOutputDirectory(const QString &directory)
1230 {
1231     d->m_outputDirectory = directory;
1232 }
1233 
1234 static bool archiveJobsCanBeUsed(GpgME::Protocol protocol)
1235 {
1236     return (protocol == GpgME::OpenPGP) && QGpgME::DecryptVerifyArchiveJob::isSupported();
1237 }
1238 
1239 void DecryptVerifyTask::doStart()
1240 {
1241     kleo_assert(d->m_backend);
1242     if (d->m_extractArchive && archiveJobsCanBeUsed(d->m_protocol)) {
1243         d->startDecryptVerifyArchiveJob();
1244     } else {
1245         d->startDecryptVerifyJob();
1246     }
1247 }
1248 
1249 static void setIgnoreMDCErrorFlag(QGpgME::Job *job, bool ignoreMDCError)
1250 {
1251     if (ignoreMDCError) {
1252         qCDebug(KLEOPATRA_LOG) << "Modifying job to ignore MDC errors.";
1253         auto ctx = QGpgME::Job::context(job);
1254         if (!ctx) {
1255             qCWarning(KLEOPATRA_LOG) << "Failed to get context for job";
1256         } else {
1257             const auto err = ctx->setFlag("ignore-mdc-error", "1");
1258             if (err) {
1259                 qCWarning(KLEOPATRA_LOG) << "Failed to set ignore mdc errors" << Formatting::errorAsString(err);
1260             }
1261         }
1262     }
1263 }
1264 
1265 void DecryptVerifyTask::Private::startDecryptVerifyJob()
1266 {
1267 #if QGPGME_FILE_JOBS_SUPPORT_DIRECT_FILE_IO
1268     if (!m_outputFilePath.isEmpty() && QFile::exists(m_outputFilePath)) {
1269         // The output files are always written to a temporary location. Therefore, this can only occur
1270         // if two signed/encrypted files with the same name in different folders are verified/decrypted
1271         // because they would be written to the same temporary location.
1272         QMetaObject::invokeMethod(
1273             q,
1274             [this]() {
1275                 slotResult(DecryptionResult{Error::fromCode(GPG_ERR_EEXIST)}, VerificationResult{});
1276             },
1277             Qt::QueuedConnection);
1278         return;
1279     }
1280 #endif
1281     try {
1282         std::unique_ptr<QGpgME::DecryptVerifyJob> job{m_backend->decryptVerifyJob()};
1283         kleo_assert(job);
1284         setIgnoreMDCErrorFlag(job.get(), m_ignoreMDCError);
1285         QObject::connect(job.get(),
1286                          &QGpgME::DecryptVerifyJob::result,
1287                          q,
1288                          [this](const GpgME::DecryptionResult &decryptResult, const GpgME::VerificationResult &verifyResult, const QByteArray &plainText) {
1289                              slotResult(decryptResult, verifyResult, plainText);
1290                          });
1291         connect(job.get(), &QGpgME::Job::jobProgress, q, &DecryptVerifyTask::setProgress);
1292 #if QGPGME_FILE_JOBS_SUPPORT_DIRECT_FILE_IO
1293         if (!m_inputFilePath.isEmpty() && !m_outputFilePath.isEmpty()) {
1294             job->setInputFile(m_inputFilePath);
1295             job->setOutputFile(m_outputFilePath);
1296             const auto err = job->startIt();
1297         } else {
1298             ensureIOOpen(m_input->ioDevice().get(), m_output->ioDevice().get());
1299             job->start(m_input->ioDevice(), m_output->ioDevice());
1300         }
1301 #else
1302         ensureIOOpen(m_input->ioDevice().get(), m_output->ioDevice().get());
1303         job->start(m_input->ioDevice(), m_output->ioDevice());
1304 #endif
1305         q->setJob(job.release());
1306     } catch (const GpgME::Exception &e) {
1307         q->emitResult(q->fromDecryptVerifyResult(e.error(), QString::fromLocal8Bit(e.what()), AuditLogEntry()));
1308     } catch (const std::exception &e) {
1309         q->emitResult(
1310             q->fromDecryptVerifyResult(Error::fromCode(GPG_ERR_INTERNAL), i18n("Caught exception: %1", QString::fromLocal8Bit(e.what())), AuditLogEntry()));
1311     } catch (...) {
1312         q->emitResult(q->fromDecryptVerifyResult(Error::fromCode(GPG_ERR_INTERNAL), i18n("Caught unknown exception"), AuditLogEntry()));
1313     }
1314 }
1315 
1316 void DecryptVerifyTask::Private::startDecryptVerifyArchiveJob()
1317 {
1318     std::unique_ptr<QGpgME::DecryptVerifyArchiveJob> job{m_backend->decryptVerifyArchiveJob()};
1319     kleo_assert(job);
1320     setIgnoreMDCErrorFlag(job.get(), m_ignoreMDCError);
1321     connect(job.get(),
1322             &QGpgME::DecryptVerifyArchiveJob::result,
1323             q,
1324             [this](const GpgME::DecryptionResult &decryptResult, const GpgME::VerificationResult &verifyResult) {
1325                 slotResult(decryptResult, verifyResult);
1326             });
1327     connect(job.get(), &QGpgME::Job::jobProgress, q, &DecryptVerifyTask::setProgress);
1328 #if QGPGME_ARCHIVE_JOBS_SUPPORT_INPUT_FILENAME
1329     // make sure that we don't use an existing output directory
1330     const auto outputDirectory = ensureUniqueDirectory(m_outputDirectory);
1331     if (outputDirectory.isEmpty()) {
1332         q->emitResult(q->fromDecryptVerifyResult(Error::fromCode(GPG_ERR_GENERAL), {}, {}));
1333         return;
1334     }
1335     m_outputDirectory = outputDirectory;
1336     job->setInputFile(m_inputFilePath);
1337     job->setOutputDirectory(m_outputDirectory);
1338     const auto err = job->startIt();
1339 #else
1340     ensureIOOpen(m_input->ioDevice().get(), nullptr);
1341     job->setOutputDirectory(m_outputDirectory);
1342     const auto err = job->start(m_input->ioDevice());
1343 #endif
1344     q->setJob(job.release());
1345     if (err) {
1346         q->emitResult(q->fromDecryptVerifyResult(err, {}, {}));
1347     }
1348 }
1349 
1350 class DecryptTask::Private
1351 {
1352     DecryptTask *const q;
1353 
1354 public:
1355     explicit Private(DecryptTask *qq)
1356         : q{qq}
1357     {
1358     }
1359 
1360     void slotResult(const DecryptionResult &, const QByteArray &);
1361 
1362     void registerJob(QGpgME::DecryptJob *job)
1363     {
1364         q->connect(job, SIGNAL(result(GpgME::DecryptionResult, QByteArray)), q, SLOT(slotResult(GpgME::DecryptionResult, QByteArray)));
1365         q->connect(job, &QGpgME::Job::jobProgress, q, &DecryptTask::setProgress);
1366     }
1367 
1368     std::shared_ptr<Input> m_input;
1369     std::shared_ptr<Output> m_output;
1370     const QGpgME::Protocol *m_backend = nullptr;
1371     Protocol m_protocol = UnknownProtocol;
1372 };
1373 
1374 void DecryptTask::Private::slotResult(const DecryptionResult &result, const QByteArray &plainText)
1375 {
1376     {
1377         std::stringstream ss;
1378         ss << result;
1379         qCDebug(KLEOPATRA_LOG) << ss.str().c_str();
1380     }
1381     const AuditLogEntry auditLog = auditLogFromSender(q->sender());
1382     if (result.error().code()) {
1383         m_output->cancel();
1384     } else {
1385         try {
1386             kleo_assert(!result.isNull());
1387             m_output->finalize();
1388         } catch (const GpgME::Exception &e) {
1389             q->emitResult(q->fromDecryptResult(e.error(), QString::fromLocal8Bit(e.what()), auditLog));
1390             return;
1391         } catch (const std::exception &e) {
1392             q->emitResult(q->fromDecryptResult(Error::fromCode(GPG_ERR_INTERNAL), i18n("Caught exception: %1", QString::fromLocal8Bit(e.what())), auditLog));
1393             return;
1394         } catch (...) {
1395             q->emitResult(q->fromDecryptResult(Error::fromCode(GPG_ERR_INTERNAL), i18n("Caught unknown exception"), auditLog));
1396             return;
1397         }
1398     }
1399 
1400     const int drErr = result.error().code();
1401     const QString errorString = m_output->errorString();
1402     if (((drErr == GPG_ERR_EIO || drErr == GPG_ERR_NO_DATA) && !errorString.isEmpty()) || m_output->failed()) {
1403         q->emitResult(q->fromDecryptResult(result.error() ? result.error() : Error::fromCode(GPG_ERR_EIO), errorString, auditLog));
1404         return;
1405     }
1406 
1407     q->emitResult(q->fromDecryptResult(result, plainText, auditLog));
1408 }
1409 
1410 DecryptTask::DecryptTask(QObject *parent)
1411     : AbstractDecryptVerifyTask(parent)
1412     , d(new Private(this))
1413 {
1414 }
1415 
1416 DecryptTask::~DecryptTask()
1417 {
1418 }
1419 
1420 void DecryptTask::setInput(const std::shared_ptr<Input> &input)
1421 {
1422     d->m_input = input;
1423     kleo_assert(d->m_input && d->m_input->ioDevice());
1424 }
1425 
1426 void DecryptTask::setOutput(const std::shared_ptr<Output> &output)
1427 {
1428     d->m_output = output;
1429     kleo_assert(d->m_output && d->m_output->ioDevice());
1430 }
1431 
1432 void DecryptTask::setProtocol(Protocol prot)
1433 {
1434     kleo_assert(prot != UnknownProtocol);
1435     d->m_protocol = prot;
1436     d->m_backend = (prot == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime();
1437     kleo_assert(d->m_backend);
1438 }
1439 
1440 void DecryptTask::autodetectProtocolFromInput()
1441 {
1442     if (!d->m_input) {
1443         return;
1444     }
1445     const Protocol p = findProtocol(d->m_input->classification());
1446     if (p == UnknownProtocol) {
1447         throw Exception(gpg_error(GPG_ERR_NOTHING_FOUND),
1448                         i18n("Could not determine whether this was S/MIME- or OpenPGP-encrypted - maybe it is not ciphertext at all?"),
1449                         Exception::MessageOnly);
1450     }
1451     setProtocol(p);
1452 }
1453 
1454 QString DecryptTask::label() const
1455 {
1456     return i18n("Decrypting: %1...", d->m_input->label());
1457 }
1458 
1459 unsigned long long DecryptTask::inputSize() const
1460 {
1461     return d->m_input ? d->m_input->size() : 0;
1462 }
1463 
1464 QString DecryptTask::inputLabel() const
1465 {
1466     return d->m_input ? d->m_input->label() : QString();
1467 }
1468 
1469 QString DecryptTask::outputLabel() const
1470 {
1471     return d->m_output ? d->m_output->label() : QString();
1472 }
1473 
1474 Protocol DecryptTask::protocol() const
1475 {
1476     return d->m_protocol;
1477 }
1478 
1479 void DecryptTask::doStart()
1480 {
1481     kleo_assert(d->m_backend);
1482 
1483     try {
1484         std::unique_ptr<QGpgME::DecryptJob> job{d->m_backend->decryptJob()};
1485         kleo_assert(job);
1486         d->registerJob(job.get());
1487         ensureIOOpen(d->m_input->ioDevice().get(), d->m_output->ioDevice().get());
1488         job->start(d->m_input->ioDevice(), d->m_output->ioDevice());
1489         setJob(job.release());
1490     } catch (const GpgME::Exception &e) {
1491         emitResult(fromDecryptResult(e.error(), QString::fromLocal8Bit(e.what()), AuditLogEntry()));
1492     } catch (const std::exception &e) {
1493         emitResult(fromDecryptResult(Error::fromCode(GPG_ERR_INTERNAL), i18n("Caught exception: %1", QString::fromLocal8Bit(e.what())), AuditLogEntry()));
1494     } catch (...) {
1495         emitResult(fromDecryptResult(Error::fromCode(GPG_ERR_INTERNAL), i18n("Caught unknown exception"), AuditLogEntry()));
1496     }
1497 }
1498 
1499 class VerifyOpaqueTask::Private
1500 {
1501     VerifyOpaqueTask *const q;
1502 
1503 public:
1504     explicit Private(VerifyOpaqueTask *qq)
1505         : q{qq}
1506     {
1507     }
1508 
1509     void startVerifyOpaqueJob();
1510     void startDecryptVerifyArchiveJob();
1511 
1512     void slotResult(const VerificationResult &, const QByteArray & = {});
1513 
1514     std::shared_ptr<Input> m_input;
1515     std::shared_ptr<Output> m_output;
1516     const QGpgME::Protocol *m_backend = nullptr;
1517     Protocol m_protocol = UnknownProtocol;
1518     bool m_extractArchive = false;
1519     QString m_inputFilePath;
1520     QString m_outputFilePath;
1521     QString m_outputDirectory;
1522 };
1523 
1524 void VerifyOpaqueTask::Private::slotResult(const VerificationResult &result, const QByteArray &plainText)
1525 {
1526     updateKeys(result);
1527     {
1528         std::stringstream ss;
1529         ss << result;
1530         qCDebug(KLEOPATRA_LOG) << ss.str().c_str();
1531     }
1532     const AuditLogEntry auditLog = auditLogFromSender(q->sender());
1533     if (m_output) {
1534         if (result.error().code()) {
1535             m_output->cancel();
1536         } else {
1537             try {
1538                 kleo_assert(!result.isNull());
1539                 m_output->finalize();
1540             } catch (const GpgME::Exception &e) {
1541                 q->emitResult(q->fromVerifyOpaqueResult(e.error(), QString::fromLocal8Bit(e.what()), auditLog));
1542                 return;
1543             } catch (const std::exception &e) {
1544                 q->emitResult(
1545                     q->fromVerifyOpaqueResult(Error::fromCode(GPG_ERR_INTERNAL), i18n("Caught exception: %1", QString::fromLocal8Bit(e.what())), auditLog));
1546                 return;
1547             } catch (...) {
1548                 q->emitResult(q->fromVerifyOpaqueResult(Error::fromCode(GPG_ERR_INTERNAL), i18n("Caught unknown exception"), auditLog));
1549                 return;
1550             }
1551         }
1552     }
1553 
1554     const int drErr = result.error().code();
1555     const QString errorString = m_output ? m_output->errorString() : QString{};
1556     if (((drErr == GPG_ERR_EIO || drErr == GPG_ERR_NO_DATA) && !errorString.isEmpty()) || (m_output && m_output->failed())) {
1557         q->emitResult(q->fromVerifyOpaqueResult(result.error() ? result.error() : Error::fromCode(GPG_ERR_EIO), errorString, auditLog));
1558         return;
1559     }
1560 
1561     q->emitResult(q->fromVerifyOpaqueResult(result, plainText, auditLog));
1562 }
1563 
1564 VerifyOpaqueTask::VerifyOpaqueTask(QObject *parent)
1565     : AbstractDecryptVerifyTask(parent)
1566     , d(new Private(this))
1567 {
1568 }
1569 
1570 VerifyOpaqueTask::~VerifyOpaqueTask()
1571 {
1572 }
1573 
1574 void VerifyOpaqueTask::setInput(const std::shared_ptr<Input> &input)
1575 {
1576     d->m_input = input;
1577     kleo_assert(d->m_input && d->m_input->ioDevice());
1578 }
1579 
1580 void VerifyOpaqueTask::setOutput(const std::shared_ptr<Output> &output)
1581 {
1582     d->m_output = output;
1583     kleo_assert(d->m_output && d->m_output->ioDevice());
1584 }
1585 
1586 void VerifyOpaqueTask::setProtocol(Protocol prot)
1587 {
1588     kleo_assert(prot != UnknownProtocol);
1589     d->m_protocol = prot;
1590     d->m_backend = (prot == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime();
1591     kleo_assert(d->m_backend);
1592 }
1593 
1594 void VerifyOpaqueTask::autodetectProtocolFromInput()
1595 {
1596     if (!d->m_input) {
1597         return;
1598     }
1599     const Protocol p = findProtocol(d->m_input->classification());
1600     if (p == UnknownProtocol) {
1601         throw Exception(gpg_error(GPG_ERR_NOTHING_FOUND),
1602                         i18n("Could not determine whether this is an S/MIME or an OpenPGP signature - maybe it is not a signature at all?"),
1603                         Exception::MessageOnly);
1604     }
1605     setProtocol(p);
1606 }
1607 
1608 QString VerifyOpaqueTask::label() const
1609 {
1610     return i18n("Verifying %1...", inputLabel());
1611 }
1612 
1613 unsigned long long VerifyOpaqueTask::inputSize() const
1614 {
1615     return d->m_input ? d->m_input->size() : 0;
1616 }
1617 
1618 QString VerifyOpaqueTask::inputLabel() const
1619 {
1620     return d->m_input ? d->m_input->label() : QFileInfo{d->m_inputFilePath}.fileName();
1621 }
1622 
1623 QString VerifyOpaqueTask::outputLabel() const
1624 {
1625     if (d->m_output) {
1626         return d->m_output->label();
1627     } else if (!d->m_outputFilePath.isEmpty()) {
1628         return QFileInfo{d->m_outputFilePath}.fileName();
1629     } else {
1630         return d->m_outputDirectory;
1631     }
1632 }
1633 
1634 Protocol VerifyOpaqueTask::protocol() const
1635 {
1636     return d->m_protocol;
1637 }
1638 
1639 void VerifyOpaqueTask::setExtractArchive(bool extract)
1640 {
1641     d->m_extractArchive = extract;
1642 }
1643 
1644 void VerifyOpaqueTask::setInputFile(const QString &path)
1645 {
1646     d->m_inputFilePath = path;
1647 }
1648 
1649 void VerifyOpaqueTask::setOutputFile(const QString &path)
1650 {
1651     d->m_outputFilePath = path;
1652 }
1653 
1654 void VerifyOpaqueTask::setOutputDirectory(const QString &directory)
1655 {
1656     d->m_outputDirectory = directory;
1657 }
1658 
1659 void VerifyOpaqueTask::doStart()
1660 {
1661     kleo_assert(d->m_backend);
1662     if (d->m_extractArchive && archiveJobsCanBeUsed(d->m_protocol)) {
1663         d->startDecryptVerifyArchiveJob();
1664     } else {
1665         d->startVerifyOpaqueJob();
1666     }
1667 }
1668 
1669 void VerifyOpaqueTask::Private::startVerifyOpaqueJob()
1670 {
1671 #if QGPGME_FILE_JOBS_SUPPORT_DIRECT_FILE_IO
1672     if (!m_outputFilePath.isEmpty() && QFile::exists(m_outputFilePath)) {
1673         // The output files are always written to a temporary location. Therefore, this can only occur
1674         // if two signed/encrypted files with the same name in different folders are verified/decrypted
1675         // because they would be written to the same temporary location.
1676         QMetaObject::invokeMethod(
1677             q,
1678             [this]() {
1679                 slotResult(VerificationResult{Error::fromCode(GPG_ERR_EEXIST)});
1680             },
1681             Qt::QueuedConnection);
1682         return;
1683     }
1684 #endif
1685     try {
1686         std::unique_ptr<QGpgME::VerifyOpaqueJob> job{m_backend->verifyOpaqueJob()};
1687         kleo_assert(job);
1688         connect(job.get(), &QGpgME::VerifyOpaqueJob::result, q, [this](const GpgME::VerificationResult &result, const QByteArray &plainText) {
1689             slotResult(result, plainText);
1690         });
1691         connect(job.get(), &QGpgME::Job::jobProgress, q, &VerifyOpaqueTask::setProgress);
1692 #if QGPGME_FILE_JOBS_SUPPORT_DIRECT_FILE_IO
1693         if (!m_inputFilePath.isEmpty() && !m_outputFilePath.isEmpty()) {
1694             job->setInputFile(m_inputFilePath);
1695             job->setOutputFile(m_outputFilePath);
1696             const auto err = job->startIt();
1697         } else {
1698             ensureIOOpen(m_input->ioDevice().get(), m_output ? m_output->ioDevice().get() : nullptr);
1699             job->start(m_input->ioDevice(), m_output ? m_output->ioDevice() : std::shared_ptr<QIODevice>());
1700         }
1701 #else
1702         ensureIOOpen(m_input->ioDevice().get(), m_output ? m_output->ioDevice().get() : nullptr);
1703         job->start(m_input->ioDevice(), m_output ? m_output->ioDevice() : std::shared_ptr<QIODevice>());
1704 #endif
1705         q->setJob(job.release());
1706     } catch (const GpgME::Exception &e) {
1707         q->emitResult(q->fromVerifyOpaqueResult(e.error(), QString::fromLocal8Bit(e.what()), AuditLogEntry()));
1708     } catch (const std::exception &e) {
1709         q->emitResult(
1710             q->fromVerifyOpaqueResult(Error::fromCode(GPG_ERR_INTERNAL), i18n("Caught exception: %1", QString::fromLocal8Bit(e.what())), AuditLogEntry()));
1711     } catch (...) {
1712         q->emitResult(q->fromVerifyOpaqueResult(Error::fromCode(GPG_ERR_INTERNAL), i18n("Caught unknown exception"), AuditLogEntry()));
1713     }
1714 }
1715 
1716 void VerifyOpaqueTask::Private::startDecryptVerifyArchiveJob()
1717 {
1718     std::unique_ptr<QGpgME::DecryptVerifyArchiveJob> job{m_backend->decryptVerifyArchiveJob()};
1719     kleo_assert(job);
1720     connect(job.get(), &QGpgME::DecryptVerifyArchiveJob::result, q, [this](const DecryptionResult &, const VerificationResult &verifyResult) {
1721         slotResult(verifyResult);
1722     });
1723     connect(job.get(), &QGpgME::DecryptVerifyArchiveJob::dataProgress, q, &VerifyOpaqueTask::setProgress);
1724 #if QGPGME_ARCHIVE_JOBS_SUPPORT_INPUT_FILENAME
1725     // make sure that we don't use an existing output directory
1726     const auto outputDirectory = ensureUniqueDirectory(m_outputDirectory);
1727     if (outputDirectory.isEmpty()) {
1728         q->emitResult(q->fromDecryptVerifyResult(Error::fromCode(GPG_ERR_GENERAL), {}, {}));
1729         return;
1730     }
1731     m_outputDirectory = outputDirectory;
1732     job->setInputFile(m_inputFilePath);
1733     job->setOutputDirectory(m_outputDirectory);
1734     const auto err = job->startIt();
1735 #else
1736     ensureIOOpen(m_input->ioDevice().get(), nullptr);
1737     job->setOutputDirectory(m_outputDirectory);
1738     const auto err = job->start(m_input->ioDevice());
1739 #endif
1740     q->setJob(job.release());
1741     if (err) {
1742         q->emitResult(q->fromVerifyOpaqueResult(err, {}, {}));
1743     }
1744 }
1745 
1746 class VerifyDetachedTask::Private
1747 {
1748     VerifyDetachedTask *const q;
1749 
1750 public:
1751     explicit Private(VerifyDetachedTask *qq)
1752         : q{qq}
1753     {
1754     }
1755 
1756     void slotResult(const VerificationResult &);
1757 
1758     void registerJob(QGpgME::VerifyDetachedJob *job)
1759     {
1760         q->connect(job, SIGNAL(result(GpgME::VerificationResult)), q, SLOT(slotResult(GpgME::VerificationResult)));
1761         q->connect(job, &QGpgME::Job::jobProgress, q, &VerifyDetachedTask::setProgress);
1762     }
1763 
1764     QString signatureLabel() const;
1765     QString signedDataLabel() const;
1766 
1767     std::shared_ptr<Input> m_input, m_signedData;
1768     const QGpgME::Protocol *m_backend = nullptr;
1769     Protocol m_protocol = UnknownProtocol;
1770     QString m_signatureFilePath;
1771     QString m_signedFilePath;
1772 };
1773 
1774 void VerifyDetachedTask::Private::slotResult(const VerificationResult &result)
1775 {
1776     updateKeys(result);
1777     {
1778         std::stringstream ss;
1779         ss << result;
1780         qCDebug(KLEOPATRA_LOG) << ss.str().c_str();
1781     }
1782     const AuditLogEntry auditLog = auditLogFromSender(q->sender());
1783     try {
1784         kleo_assert(!result.isNull());
1785         q->emitResult(q->fromVerifyDetachedResult(result, auditLog));
1786     } catch (const GpgME::Exception &e) {
1787         q->emitResult(q->fromVerifyDetachedResult(e.error(), QString::fromLocal8Bit(e.what()), auditLog));
1788     } catch (const std::exception &e) {
1789         q->emitResult(q->fromVerifyDetachedResult(Error::fromCode(GPG_ERR_INTERNAL), i18n("Caught exception: %1", QString::fromLocal8Bit(e.what())), auditLog));
1790     } catch (...) {
1791         q->emitResult(q->fromVerifyDetachedResult(Error::fromCode(GPG_ERR_INTERNAL), i18n("Caught unknown exception"), auditLog));
1792     }
1793 }
1794 
1795 QString VerifyDetachedTask::Private::signatureLabel() const
1796 {
1797     return m_input ? m_input->label() : m_signatureFilePath;
1798 }
1799 
1800 QString VerifyDetachedTask::Private::signedDataLabel() const
1801 {
1802     return m_signedData ? m_signedData->label() : m_signedFilePath;
1803 }
1804 
1805 VerifyDetachedTask::VerifyDetachedTask(QObject *parent)
1806     : AbstractDecryptVerifyTask(parent)
1807     , d(new Private(this))
1808 {
1809 }
1810 
1811 VerifyDetachedTask::~VerifyDetachedTask()
1812 {
1813 }
1814 
1815 void VerifyDetachedTask::setInput(const std::shared_ptr<Input> &input)
1816 {
1817     d->m_input = input;
1818     kleo_assert(d->m_input && d->m_input->ioDevice());
1819 }
1820 
1821 void VerifyDetachedTask::setSignedData(const std::shared_ptr<Input> &signedData)
1822 {
1823     d->m_signedData = signedData;
1824     kleo_assert(d->m_signedData && d->m_signedData->ioDevice());
1825 }
1826 
1827 void VerifyDetachedTask::setSignatureFile(const QString &path)
1828 {
1829     d->m_signatureFilePath = path;
1830 }
1831 
1832 void VerifyDetachedTask::setSignedFile(const QString &path)
1833 {
1834     d->m_signedFilePath = path;
1835 }
1836 
1837 void VerifyDetachedTask::setProtocol(Protocol prot)
1838 {
1839     kleo_assert(prot != UnknownProtocol);
1840     d->m_protocol = prot;
1841     d->m_backend = (prot == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime();
1842     kleo_assert(d->m_backend);
1843 }
1844 
1845 void VerifyDetachedTask::autodetectProtocolFromInput()
1846 {
1847     if (!d->m_input) {
1848         return;
1849     }
1850     const Protocol p = findProtocol(d->m_input->classification());
1851     if (p == UnknownProtocol) {
1852         throw Exception(gpg_error(GPG_ERR_NOTHING_FOUND),
1853                         i18n("Could not determine whether this is an S/MIME or an OpenPGP signature - maybe it is not a signature at all?"),
1854                         Exception::MessageOnly);
1855     }
1856     setProtocol(p);
1857 }
1858 
1859 unsigned long long VerifyDetachedTask::inputSize() const
1860 {
1861     return d->m_signedData ? d->m_signedData->size() : 0;
1862 }
1863 
1864 QString VerifyDetachedTask::label() const
1865 {
1866     const QString signedDataLabel = d->signedDataLabel();
1867     if (!signedDataLabel.isEmpty()) {
1868         return xi18nc(
1869             "Verification of a detached signature in progress. The first file contains the data."
1870             "The second file is the signature file.",
1871             "Verifying <filename>%1</filename> with <filename>%2</filename>...",
1872             signedDataLabel,
1873             d->signatureLabel());
1874     }
1875     return i18n("Verifying signature %1...", d->signatureLabel());
1876 }
1877 
1878 QString VerifyDetachedTask::inputLabel() const
1879 {
1880     const QString signatureLabel = d->signatureLabel();
1881     const QString signedDataLabel = d->signedDataLabel();
1882     if (!signedDataLabel.isEmpty() && !signatureLabel.isEmpty()) {
1883         return xi18nc(
1884             "Verification of a detached signature summary. The first file contains the data."
1885             "The second file is signature.",
1886             "Verified <filename>%1</filename> with <filename>%2</filename>",
1887             signedDataLabel,
1888             signatureLabel);
1889     }
1890     return signatureLabel;
1891 }
1892 
1893 QString VerifyDetachedTask::outputLabel() const
1894 {
1895     return QString();
1896 }
1897 
1898 Protocol VerifyDetachedTask::protocol() const
1899 {
1900     return d->m_protocol;
1901 }
1902 
1903 void VerifyDetachedTask::doStart()
1904 {
1905     kleo_assert(d->m_backend);
1906     try {
1907         std::unique_ptr<QGpgME::VerifyDetachedJob> job{d->m_backend->verifyDetachedJob()};
1908         kleo_assert(job);
1909         d->registerJob(job.get());
1910 #if QGPGME_FILE_JOBS_SUPPORT_DIRECT_FILE_IO
1911         if (d->m_protocol == GpgME::OpenPGP && !d->m_signatureFilePath.isEmpty() && !d->m_signedFilePath.isEmpty()) {
1912             job->setSignatureFile(d->m_signatureFilePath);
1913             job->setSignedFile(d->m_signedFilePath);
1914             job->startIt();
1915         } else {
1916             ensureIOOpen(d->m_input->ioDevice().get(), nullptr);
1917             ensureIOOpen(d->m_signedData->ioDevice().get(), nullptr);
1918             job->start(d->m_input->ioDevice(), d->m_signedData->ioDevice());
1919         }
1920 #else
1921         ensureIOOpen(d->m_input->ioDevice().get(), nullptr);
1922         ensureIOOpen(d->m_signedData->ioDevice().get(), nullptr);
1923         job->start(d->m_input->ioDevice(), d->m_signedData->ioDevice());
1924 #endif
1925         setJob(job.release());
1926     } catch (const GpgME::Exception &e) {
1927         emitResult(fromVerifyDetachedResult(e.error(), QString::fromLocal8Bit(e.what()), AuditLogEntry()));
1928     } catch (const std::exception &e) {
1929         emitResult(
1930             fromVerifyDetachedResult(Error::fromCode(GPG_ERR_INTERNAL), i18n("Caught exception: %1", QString::fromLocal8Bit(e.what())), AuditLogEntry()));
1931     } catch (...) {
1932         emitResult(fromVerifyDetachedResult(Error::fromCode(GPG_ERR_INTERNAL), i18n("Caught unknown exception"), AuditLogEntry()));
1933     }
1934 }
1935 
1936 #include "moc_decryptverifytask.cpp"