File indexing completed on 2024-06-23 05:13: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 → %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"