File indexing completed on 2024-06-23 05:13:54
0001 /* -*- mode: c++; c-basic-offset:4 -*- 0002 autodecryptverifyfilescontroller.cpp 0003 0004 This file is part of Kleopatra, the KDE keymanager 0005 SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB 0006 0007 SPDX-FileCopyrightText: 2016 Bundesamt für Sicherheit in der Informationstechnik 0008 SPDX-FileContributor: Intevation GmbH 0009 0010 SPDX-License-Identifier: GPL-2.0-or-later 0011 */ 0012 0013 #include <config-kleopatra.h> 0014 0015 #include "autodecryptverifyfilescontroller.h" 0016 0017 #include "fileoperationspreferences.h" 0018 0019 #include <crypto/decryptverifytask.h> 0020 #include <crypto/gui/decryptverifyfilesdialog.h> 0021 #include <crypto/gui/decryptverifyoperationwidget.h> 0022 #include <crypto/taskcollection.h> 0023 0024 #include "commands/decryptverifyfilescommand.h" 0025 0026 #include <utils/archivedefinition.h> 0027 #include <utils/input.h> 0028 #include <utils/kleo_assert.h> 0029 #include <utils/output.h> 0030 #include <utils/overwritedialog.h> 0031 #include <utils/path-helper.h> 0032 0033 #include <Libkleo/Algorithm> 0034 #include <Libkleo/Classify> 0035 #include <Libkleo/GnuPG> 0036 0037 #ifndef Q_OS_WIN 0038 #include <KIO/CopyJob> 0039 #include <KIO/Global> 0040 #endif 0041 #include "kleopatra_debug.h" 0042 #include <KLocalizedString> 0043 #include <KMessageBox> 0044 0045 #include <QGpgME/DecryptVerifyArchiveJob> 0046 0047 #include <QDir> 0048 #include <QFile> 0049 #include <QFileDialog> 0050 #include <QFileInfo> 0051 #include <QTemporaryDir> 0052 #include <QTimer> 0053 0054 #include <gpgme++/decryptionresult.h> 0055 0056 using namespace GpgME; 0057 using namespace Kleo; 0058 using namespace Kleo::Crypto; 0059 using namespace Kleo::Crypto::Gui; 0060 0061 class AutoDecryptVerifyFilesController::Private 0062 { 0063 AutoDecryptVerifyFilesController *const q; 0064 0065 public: 0066 explicit Private(AutoDecryptVerifyFilesController *qq); 0067 0068 void schedule(); 0069 0070 QString getEmbeddedFileName(const QString &fileName) const; 0071 void exec(); 0072 std::vector<std::shared_ptr<Task>> buildTasks(const QStringList &, QStringList &); 0073 0074 struct CryptoFile { 0075 QString baseName; 0076 QString fileName; 0077 GpgME::Protocol protocol = GpgME::UnknownProtocol; 0078 int classification = 0; 0079 std::shared_ptr<Output> output; 0080 }; 0081 QList<CryptoFile> classifyAndSortFiles(const QStringList &files); 0082 0083 void reportError(int err, const QString &details) 0084 { 0085 q->setLastError(err, details); 0086 q->emitDoneOrError(); 0087 } 0088 void cancelAllTasks(); 0089 0090 QStringList m_passedFiles, m_filesAfterPreparation; 0091 std::vector<std::shared_ptr<const DecryptVerifyResult>> m_results; 0092 std::vector<std::shared_ptr<Task>> m_runnableTasks, m_completedTasks; 0093 std::shared_ptr<Task> m_runningTask; 0094 bool m_errorDetected = false; 0095 DecryptVerifyOperation m_operation = DecryptVerify; 0096 QPointer<DecryptVerifyFilesDialog> m_dialog; 0097 std::unique_ptr<QTemporaryDir> m_workDir; 0098 }; 0099 0100 AutoDecryptVerifyFilesController::Private::Private(AutoDecryptVerifyFilesController *qq) 0101 : q(qq) 0102 { 0103 qRegisterMetaType<VerificationResult>(); 0104 } 0105 0106 void AutoDecryptVerifyFilesController::Private::schedule() 0107 { 0108 if (!m_runningTask && !m_runnableTasks.empty()) { 0109 const std::shared_ptr<Task> t = m_runnableTasks.back(); 0110 m_runnableTasks.pop_back(); 0111 t->start(); 0112 m_runningTask = t; 0113 } 0114 if (!m_runningTask) { 0115 kleo_assert(m_runnableTasks.empty()); 0116 for (const std::shared_ptr<const DecryptVerifyResult> &i : std::as_const(m_results)) { 0117 Q_EMIT q->verificationResult(i->verificationResult()); 0118 } 0119 } 0120 } 0121 0122 QString AutoDecryptVerifyFilesController::Private::getEmbeddedFileName(const QString &fileName) const 0123 { 0124 auto it = std::find_if(m_results.cbegin(), m_results.cend(), [fileName](const auto &r) { 0125 return r->fileName() == fileName; 0126 }); 0127 if (it != m_results.cend()) { 0128 const auto embeddedFilePath = QString::fromUtf8((*it)->decryptionResult().fileName()); 0129 if (embeddedFilePath.contains(QLatin1Char{'\\'})) { 0130 // ignore embedded file names containing '\' 0131 return {}; 0132 } 0133 // strip the path from the embedded file name 0134 return QFileInfo{embeddedFilePath}.fileName(); 0135 } else { 0136 return {}; 0137 } 0138 } 0139 0140 void AutoDecryptVerifyFilesController::Private::exec() 0141 { 0142 Q_ASSERT(!m_dialog); 0143 0144 QStringList undetected; 0145 std::vector<std::shared_ptr<Task>> tasks = buildTasks(m_passedFiles, undetected); 0146 0147 if (!undetected.isEmpty()) { 0148 // Since GpgME 1.7.0 Classification is supposed to be reliable 0149 // so we really can't do anything with this data. 0150 reportError(makeGnuPGError(GPG_ERR_GENERAL), 0151 xi18n("Failed to find encrypted or signed data in one or more files.<nl/>" 0152 "You can manually select what to do with the files now.<nl/>" 0153 "If they contain signed or encrypted data please report a bug (see Help->Report Bug).")); 0154 auto cmd = new Commands::DecryptVerifyFilesCommand(undetected, nullptr, true); 0155 cmd->start(); 0156 } 0157 if (tasks.empty()) { 0158 q->emitDoneOrError(); 0159 return; 0160 } 0161 Q_ASSERT(m_runnableTasks.empty()); 0162 m_runnableTasks.swap(tasks); 0163 0164 std::shared_ptr<TaskCollection> coll(new TaskCollection); 0165 for (const std::shared_ptr<Task> &i : std::as_const(m_runnableTasks)) { 0166 q->connectTask(i); 0167 } 0168 coll->setTasks(m_runnableTasks); 0169 DecryptVerifyFilesDialog dialog{coll}; 0170 m_dialog = &dialog; 0171 m_dialog->setOutputLocation(heuristicBaseDirectory(m_passedFiles)); 0172 q->applyWindowID(m_dialog); 0173 0174 QTimer::singleShot(0, q, SLOT(schedule())); 0175 const auto result = m_dialog->exec(); 0176 if (result == QDialog::Rejected) { 0177 q->cancel(); 0178 } else if (result == QDialog::Accepted && m_workDir) { 0179 // Without workdir there is nothing to move. 0180 const QDir workdir(m_workDir->path()); 0181 const QDir outDir(m_dialog->outputLocation()); 0182 qCDebug(KLEOPATRA_LOG) << workdir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot); 0183 const auto filesAndFoldersToMove = workdir.entryInfoList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot); 0184 const auto fileCount = Kleo::count_if(filesAndFoldersToMove, [](const auto &fi) { 0185 return !fi.isDir(); 0186 }); 0187 OverwritePolicy overwritePolicy{m_dialog, fileCount > 1 ? OverwritePolicy::MultipleFiles : OverwritePolicy::Options{}}; 0188 for (const QFileInfo &fi : filesAndFoldersToMove) { 0189 const auto inpath = fi.absoluteFilePath(); 0190 0191 if (fi.isDir()) { 0192 // A directory. Assume that the input was an archive 0193 // and avoid directory merges by trying to find a non 0194 // existing directory. 0195 auto candidate = fi.fileName(); 0196 if (candidate.startsWith(QLatin1Char('-'))) { 0197 // Bug in GpgTar Extracts stdout passed archives to a dir named - 0198 candidate = QFileInfo(m_passedFiles.first()).baseName(); 0199 } 0200 0201 QString suffix; 0202 QFileInfo ofi; 0203 int i = 0; 0204 do { 0205 ofi = QFileInfo(outDir.absoluteFilePath(candidate + suffix)); 0206 if (!ofi.exists()) { 0207 break; 0208 } 0209 suffix = QStringLiteral("_%1").arg(++i); 0210 } while (i < 1000); 0211 0212 const auto destPath = ofi.absoluteFilePath(); 0213 #ifndef Q_OS_WIN 0214 auto job = KIO::moveAs(QUrl::fromLocalFile(inpath), QUrl::fromLocalFile(destPath)); 0215 qCDebug(KLEOPATRA_LOG) << "Moving" << job->srcUrls().front().toLocalFile() << "to" << job->destUrl().toLocalFile(); 0216 if (!job->exec()) { 0217 if (job->error() == KIO::ERR_USER_CANCELED) { 0218 break; 0219 } 0220 reportError(makeGnuPGError(GPG_ERR_GENERAL), 0221 xi18nc("@info", 0222 "<para>Failed to move <filename>%1</filename> to <filename>%2</filename>.</para>" 0223 "<para><message>%3</message></para>", 0224 inpath, 0225 destPath, 0226 job->errorString())); 0227 } 0228 #else 0229 // On Windows, KIO::move does not work for folders when crossing partition boundaries 0230 if (!moveDir(inpath, destPath)) { 0231 reportError(makeGnuPGError(GPG_ERR_GENERAL), 0232 xi18nc("@info", "<para>Failed to move <filename>%1</filename> to <filename>%2</filename>.</para>", inpath, destPath)); 0233 } 0234 #endif 0235 continue; 0236 } 0237 0238 const auto embeddedFileName = getEmbeddedFileName(inpath); 0239 QString outFileName = fi.fileName(); 0240 if (!embeddedFileName.isEmpty() && embeddedFileName != fi.fileName()) { 0241 // we switch "Yes" and "No" because Yes is default, but saving with embedded file name could be dangerous 0242 const auto answer = KMessageBox::questionTwoActionsCancel( 0243 m_dialog, 0244 xi18n("Shall the file be saved with the original file name <filename>%1</filename>?", embeddedFileName), 0245 i18nc("@title:window", "Use Original File Name?"), 0246 KGuiItem(xi18n("No, Save As <filename>%1</filename>", fi.fileName())), 0247 KGuiItem(xi18n("Yes, Save As <filename>%1</filename>", embeddedFileName))); 0248 if (answer == KMessageBox::Cancel) { 0249 qCDebug(KLEOPATRA_LOG) << "Saving canceled for:" << inpath; 0250 continue; 0251 } else if (answer == KMessageBox::ButtonCode::SecondaryAction) { 0252 outFileName = embeddedFileName; 0253 } 0254 } 0255 auto outpath = outDir.absoluteFilePath(outFileName); 0256 qCDebug(KLEOPATRA_LOG) << "Moving " << inpath << " to " << outpath; 0257 const QFileInfo ofi(outpath); 0258 if (ofi.exists()) { 0259 const auto newPath = overwritePolicy.obtainOverwritePermission(outpath); 0260 if (newPath.isEmpty()) { 0261 if (overwritePolicy.policy() == OverwritePolicy::Cancel) { 0262 qCDebug(KLEOPATRA_LOG) << "Overwriting canceled for: " << outpath; 0263 break; 0264 } 0265 // else Skip 0266 continue; 0267 } else if (newPath == outpath) { 0268 // overwrite existing file 0269 if (!QFile::remove(outpath)) { 0270 reportError(makeGnuPGError(GPG_ERR_GENERAL), xi18n("Failed to delete <filename>%1</filename>.", outpath)); 0271 continue; 0272 } 0273 } else { 0274 // use new name for file 0275 outpath = newPath; 0276 } 0277 } 0278 if (!QFile::rename(inpath, outpath)) { 0279 reportError(makeGnuPGError(GPG_ERR_GENERAL), xi18n("Failed to move <filename>%1</filename> to <filename>%2</filename>.", inpath, outpath)); 0280 } 0281 } 0282 } 0283 q->emitDoneOrError(); 0284 } 0285 0286 QList<AutoDecryptVerifyFilesController::Private::CryptoFile> AutoDecryptVerifyFilesController::Private::classifyAndSortFiles(const QStringList &files) 0287 { 0288 const auto isSignature = [](int classification) -> bool { 0289 return mayBeDetachedSignature(classification) // 0290 || mayBeOpaqueSignature(classification) // 0291 || (classification & Class::TypeMask) == Class::ClearsignedMessage; 0292 }; 0293 0294 QList<CryptoFile> out; 0295 for (const auto &file : files) { 0296 CryptoFile cFile; 0297 cFile.fileName = file; 0298 cFile.baseName = stripSuffix(file); 0299 cFile.classification = classify(file); 0300 cFile.protocol = findProtocol(cFile.classification); 0301 0302 auto it = std::find_if(out.begin(), out.end(), [&cFile](const CryptoFile &other) { 0303 return other.protocol == cFile.protocol && other.baseName == cFile.baseName; 0304 }); 0305 if (it != out.end()) { 0306 // If we found a file with the same basename, make sure that encrypted 0307 // file is before the signature file, so that we first decrypt and then 0308 // verify 0309 if (isSignature(cFile.classification) && isCipherText(it->classification)) { 0310 out.insert(it + 1, cFile); 0311 } else if (isCipherText(cFile.classification) && isSignature(it->classification)) { 0312 out.insert(it, cFile); 0313 } else { 0314 // both are signatures or both are encrypted files, in which 0315 // case order does not matter 0316 out.insert(it, cFile); 0317 } 0318 } else { 0319 out.push_back(cFile); 0320 } 0321 } 0322 0323 return out; 0324 } 0325 0326 static bool archiveJobsCanBeUsed([[maybe_unused]] GpgME::Protocol protocol) 0327 { 0328 return (protocol == GpgME::OpenPGP) && QGpgME::DecryptVerifyArchiveJob::isSupported(); 0329 } 0330 0331 std::vector<std::shared_ptr<Task>> AutoDecryptVerifyFilesController::Private::buildTasks(const QStringList &fileNames, QStringList &undetected) 0332 { 0333 // sort files so that we make sure we first decrypt and then verify 0334 QList<CryptoFile> cryptoFiles = classifyAndSortFiles(fileNames); 0335 0336 std::vector<std::shared_ptr<Task>> tasks; 0337 for (auto it = cryptoFiles.begin(), end = cryptoFiles.end(); it != end; ++it) { 0338 auto &cFile = (*it); 0339 QFileInfo fi(cFile.fileName); 0340 qCDebug(KLEOPATRA_LOG) << "classified" << cFile.fileName << "as" << printableClassification(cFile.classification); 0341 0342 if (!fi.isReadable()) { 0343 reportError(makeGnuPGError(GPG_ERR_ASS_NO_INPUT), xi18n("Cannot open <filename>%1</filename> for reading.", cFile.fileName)); 0344 continue; 0345 } 0346 0347 if (mayBeAnyCertStoreType(cFile.classification)) { 0348 // Trying to verify a certificate. Possible because extensions are often similar 0349 // for PGP Keys. 0350 reportError(makeGnuPGError(GPG_ERR_ASS_NO_INPUT), 0351 xi18n("The file <filename>%1</filename> contains certificates and can't be decrypted or verified.", cFile.fileName)); 0352 qCDebug(KLEOPATRA_LOG) << "reported error"; 0353 continue; 0354 } 0355 0356 // We can't reliably detect CMS detached signatures, so we will try to do 0357 // our best to use the current file as a detached signature and fallback to 0358 // opaque signature otherwise. 0359 if (cFile.protocol == GpgME::CMS && mayBeDetachedSignature(cFile.classification)) { 0360 // First, see if previous task was a decryption task for the same file 0361 // and "pipe" it's output into our input 0362 std::shared_ptr<Input> input; 0363 bool prepend = false; 0364 if (it != cryptoFiles.begin()) { 0365 const auto prev = it - 1; 0366 if (prev->protocol == cFile.protocol && prev->baseName == cFile.baseName) { 0367 input = Input::createFromOutput(prev->output); 0368 prepend = true; 0369 } 0370 } 0371 0372 if (!input) { 0373 if (QFile::exists(cFile.baseName)) { 0374 input = Input::createFromFile(cFile.baseName); 0375 } 0376 } 0377 0378 if (input) { 0379 qCDebug(KLEOPATRA_LOG) << "Detached CMS verify: " << cFile.fileName; 0380 std::shared_ptr<VerifyDetachedTask> t(new VerifyDetachedTask); 0381 t->setInput(Input::createFromFile(cFile.fileName)); 0382 t->setSignedData(input); 0383 t->setProtocol(cFile.protocol); 0384 if (prepend) { 0385 // Put the verify task BEFORE the decrypt task in the tasks queue, 0386 // because the tasks are executed in reverse order! 0387 tasks.insert(tasks.end() - 1, t); 0388 } else { 0389 tasks.push_back(t); 0390 } 0391 continue; 0392 } else { 0393 // No signed data, maybe not a detached signature 0394 } 0395 } 0396 0397 if (isDetachedSignature(cFile.classification)) { 0398 // Detached signature, try to find data or ask the user. 0399 QString signedDataFileName = cFile.baseName; 0400 if (!QFile::exists(signedDataFileName)) { 0401 signedDataFileName = QFileDialog::getOpenFileName(nullptr, 0402 xi18n("Select the file to verify with the signature <filename>%1</filename>", fi.fileName()), 0403 fi.path()); 0404 } 0405 if (signedDataFileName.isEmpty()) { 0406 qCDebug(KLEOPATRA_LOG) << "No signed data selected. Verify aborted."; 0407 } else { 0408 qCDebug(KLEOPATRA_LOG) << "Detached verify: " << cFile.fileName << " Data: " << signedDataFileName; 0409 std::shared_ptr<VerifyDetachedTask> t(new VerifyDetachedTask); 0410 #if QGPGME_FILE_JOBS_SUPPORT_DIRECT_FILE_IO 0411 if (cFile.protocol == GpgME::OpenPGP) { 0412 t->setSignatureFile(cFile.fileName); 0413 t->setSignedFile(signedDataFileName); 0414 } else { 0415 t->setInput(Input::createFromFile(cFile.fileName)); 0416 t->setSignedData(Input::createFromFile(signedDataFileName)); 0417 } 0418 #else 0419 t->setInput(Input::createFromFile(cFile.fileName)); 0420 t->setSignedData(Input::createFromFile(signedDataFileName)); 0421 #endif 0422 t->setProtocol(cFile.protocol); 0423 tasks.push_back(t); 0424 } 0425 continue; 0426 } 0427 0428 if (!mayBeAnyMessageType(cFile.classification)) { 0429 // Not a Message? Maybe there is a signature for this file? 0430 const auto signatures = findSignatures(cFile.fileName); 0431 bool foundSig = false; 0432 if (!signatures.empty()) { 0433 for (const QString &sig : signatures) { 0434 const auto classification = classify(sig); 0435 qCDebug(KLEOPATRA_LOG) << "Guessing: " << sig << " is a signature for: " << cFile.fileName << "Classification: " << classification; 0436 const auto proto = findProtocol(classification); 0437 if (proto == GpgME::UnknownProtocol) { 0438 qCDebug(KLEOPATRA_LOG) << "Could not determine protocol. Skipping guess."; 0439 continue; 0440 } 0441 foundSig = true; 0442 std::shared_ptr<VerifyDetachedTask> t(new VerifyDetachedTask); 0443 #if QGPGME_FILE_JOBS_SUPPORT_DIRECT_FILE_IO 0444 if (cFile.protocol == GpgME::OpenPGP) { 0445 t->setSignatureFile(sig); 0446 t->setSignedFile(cFile.fileName); 0447 } else { 0448 t->setInput(Input::createFromFile(sig)); 0449 t->setSignedData(Input::createFromFile(cFile.fileName)); 0450 } 0451 #else 0452 t->setInput(Input::createFromFile(sig)); 0453 t->setSignedData(Input::createFromFile(cFile.fileName)); 0454 #endif 0455 t->setProtocol(proto); 0456 tasks.push_back(t); 0457 } 0458 } 0459 if (!foundSig) { 0460 undetected << cFile.fileName; 0461 qCDebug(KLEOPATRA_LOG) << "Failed detection for: " << cFile.fileName << " adding to undetected."; 0462 } 0463 } else { 0464 const FileOperationsPreferences fileOpSettings; 0465 // Any Message type so we have input and output. 0466 std::shared_ptr<Input> input; 0467 #if QGPGME_FILE_JOBS_SUPPORT_DIRECT_FILE_IO 0468 if (cFile.protocol != GpgME::OpenPGP) { 0469 input = Input::createFromFile(cFile.fileName); 0470 } 0471 #else 0472 input = Input::createFromFile(cFile.fileName); 0473 #endif 0474 0475 std::shared_ptr<ArchiveDefinition> ad; 0476 if (fileOpSettings.autoExtractArchives()) { 0477 const auto archiveDefinitions = ArchiveDefinition::getArchiveDefinitions(); 0478 ad = q->pick_archive_definition(cFile.protocol, archiveDefinitions, cFile.fileName); 0479 } 0480 0481 if (fileOpSettings.dontUseTmpDir()) { 0482 if (!m_workDir) { 0483 m_workDir = std::make_unique<QTemporaryDir>(heuristicBaseDirectory(fileNames) + QStringLiteral("/kleopatra-XXXXXX")); 0484 } 0485 if (!m_workDir->isValid()) { 0486 qCDebug(KLEOPATRA_LOG) << heuristicBaseDirectory(fileNames) << "not a valid temporary directory."; 0487 m_workDir.reset(); 0488 } 0489 } 0490 if (!m_workDir) { 0491 m_workDir = std::make_unique<QTemporaryDir>(); 0492 } 0493 qCDebug(KLEOPATRA_LOG) << "Using:" << m_workDir->path() << "as temporary directory."; 0494 0495 const auto wd = QDir(m_workDir->path()); 0496 0497 std::shared_ptr<Output> output; 0498 QString outputFilePath; 0499 if (ad) { 0500 if ((ad->id() == QLatin1StringView{"tar"}) && archiveJobsCanBeUsed(cFile.protocol)) { 0501 // we don't need an output 0502 } else { 0503 output = ad->createOutputFromUnpackCommand(cFile.protocol, ad->stripExtension(cFile.protocol, cFile.baseName), wd); 0504 } 0505 } else { 0506 outputFilePath = wd.absoluteFilePath(outputFileName(fi.fileName())); 0507 #if QGPGME_FILE_JOBS_SUPPORT_DIRECT_FILE_IO 0508 if (cFile.protocol != GpgME::OpenPGP) { 0509 output = Output::createFromFile(outputFilePath, false); 0510 } 0511 #else 0512 output = Output::createFromFile(outputFilePath, false); 0513 #endif 0514 } 0515 0516 // If this might be opaque CMS signature, then try that. We already handled 0517 // detached CMS signature above 0518 const auto isCMSOpaqueSignature = cFile.protocol == GpgME::CMS && mayBeOpaqueSignature(cFile.classification); 0519 0520 if (isOpaqueSignature(cFile.classification) || isCMSOpaqueSignature) { 0521 qCDebug(KLEOPATRA_LOG) << "creating a VerifyOpaqueTask"; 0522 std::shared_ptr<VerifyOpaqueTask> t(new VerifyOpaqueTask); 0523 if (input) { 0524 t->setInput(input); 0525 } 0526 if (output) { 0527 t->setOutput(output); 0528 } 0529 t->setProtocol(cFile.protocol); 0530 if (ad) { 0531 t->setExtractArchive(true); 0532 t->setInputFile(cFile.fileName); 0533 if (output) { 0534 t->setOutputDirectory(m_workDir->path()); 0535 } else { 0536 // make gpgtar extract to a subfolder of the work directory based on the input file name 0537 const auto baseFileName = QFileInfo{ad->stripExtension(cFile.protocol, cFile.baseName)}.fileName(); 0538 t->setOutputDirectory(QDir{m_workDir->path()}.filePath(baseFileName)); 0539 } 0540 #if QGPGME_FILE_JOBS_SUPPORT_DIRECT_FILE_IO 0541 } else if (cFile.protocol == GpgME::OpenPGP) { 0542 t->setInputFile(cFile.fileName); 0543 t->setOutputFile(outputFilePath); 0544 #endif 0545 } 0546 tasks.push_back(t); 0547 } else { 0548 // Any message. That is not an opaque signature needs to be 0549 // decrypted. Verify we always do because we can't know if 0550 // an encrypted message is also signed. 0551 qCDebug(KLEOPATRA_LOG) << "creating a DecryptVerifyTask"; 0552 std::shared_ptr<DecryptVerifyTask> t(new DecryptVerifyTask); 0553 if (input) { 0554 t->setInput(input); 0555 } 0556 if (output) { 0557 t->setOutput(output); 0558 } 0559 t->setProtocol(cFile.protocol); 0560 if (ad) { 0561 t->setExtractArchive(true); 0562 t->setInputFile(cFile.fileName); 0563 if (output) { 0564 t->setOutputDirectory(m_workDir->path()); 0565 } else { 0566 // make gpgtar extract to a subfolder of the work directory based on the input file name 0567 const auto baseFileName = QFileInfo{ad->stripExtension(cFile.protocol, cFile.baseName)}.fileName(); 0568 t->setOutputDirectory(QDir{m_workDir->path()}.filePath(baseFileName)); 0569 } 0570 #if QGPGME_FILE_JOBS_SUPPORT_DIRECT_FILE_IO 0571 } else if (cFile.protocol == GpgME::OpenPGP) { 0572 t->setInputFile(cFile.fileName); 0573 t->setOutputFile(outputFilePath); 0574 #endif 0575 } 0576 cFile.output = output; 0577 tasks.push_back(t); 0578 } 0579 } 0580 } 0581 0582 return tasks; 0583 } 0584 0585 void AutoDecryptVerifyFilesController::setFiles(const QStringList &files) 0586 { 0587 d->m_passedFiles = files; 0588 } 0589 0590 AutoDecryptVerifyFilesController::AutoDecryptVerifyFilesController(QObject *parent) 0591 : DecryptVerifyFilesController(parent) 0592 , d(new Private(this)) 0593 { 0594 } 0595 0596 AutoDecryptVerifyFilesController::AutoDecryptVerifyFilesController(const std::shared_ptr<const ExecutionContext> &ctx, QObject *parent) 0597 : DecryptVerifyFilesController(ctx, parent) 0598 , d(new Private(this)) 0599 { 0600 } 0601 0602 AutoDecryptVerifyFilesController::~AutoDecryptVerifyFilesController() 0603 { 0604 qCDebug(KLEOPATRA_LOG); 0605 } 0606 0607 void AutoDecryptVerifyFilesController::start() 0608 { 0609 d->exec(); 0610 } 0611 0612 void AutoDecryptVerifyFilesController::setOperation(DecryptVerifyOperation op) 0613 { 0614 d->m_operation = op; 0615 } 0616 0617 DecryptVerifyOperation AutoDecryptVerifyFilesController::operation() const 0618 { 0619 return d->m_operation; 0620 } 0621 0622 void AutoDecryptVerifyFilesController::Private::cancelAllTasks() 0623 { 0624 // we just kill all runnable tasks - this will not result in 0625 // signal emissions. 0626 m_runnableTasks.clear(); 0627 0628 // a cancel() will result in a call to 0629 if (m_runningTask) { 0630 m_runningTask->cancel(); 0631 } 0632 } 0633 0634 void AutoDecryptVerifyFilesController::cancel() 0635 { 0636 qCDebug(KLEOPATRA_LOG) << this << __func__; 0637 try { 0638 d->m_errorDetected = true; 0639 if (d->m_dialog) { 0640 d->m_dialog->close(); 0641 } 0642 d->cancelAllTasks(); 0643 } catch (const std::exception &e) { 0644 qCDebug(KLEOPATRA_LOG) << "Caught exception: " << e.what(); 0645 } 0646 } 0647 0648 void AutoDecryptVerifyFilesController::doTaskDone(const Task *task, const std::shared_ptr<const Task::Result> &result) 0649 { 0650 Q_ASSERT(task); 0651 Q_UNUSED(task) 0652 0653 // We could just delete the tasks here, but we can't use 0654 // Qt::QueuedConnection here (we need sender()) and other slots 0655 // might not yet have executed. Therefore, we push completed tasks 0656 // into a burial container 0657 0658 d->m_completedTasks.push_back(d->m_runningTask); 0659 d->m_runningTask.reset(); 0660 0661 if (const std::shared_ptr<const DecryptVerifyResult> &dvr = std::dynamic_pointer_cast<const DecryptVerifyResult>(result)) { 0662 d->m_results.push_back(dvr); 0663 } 0664 0665 QTimer::singleShot(0, this, SLOT(schedule())); 0666 } 0667 #include "moc_autodecryptverifyfilescontroller.cpp"