File indexing completed on 2024-06-23 05:13:59
0001 /* -*- mode: c++; c-basic-offset:4 -*- 0002 crypto/signencryptfilescontroller.cpp 0003 0004 This file is part of Kleopatra, the KDE keymanager 0005 SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB 0006 0007 SPDX-FileCopyrightText: 2017 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 "signencryptfilescontroller.h" 0016 0017 #include "signencrypttask.h" 0018 0019 #include "crypto/gui/signencryptfileswizard.h" 0020 #include "crypto/taskcollection.h" 0021 0022 #include "fileoperationspreferences.h" 0023 0024 #include "utils/archivedefinition.h" 0025 #include "utils/input.h" 0026 #include "utils/kleo_assert.h" 0027 #include "utils/output.h" 0028 #include "utils/path-helper.h" 0029 0030 #include <Libkleo/Classify> 0031 #include <Libkleo/KleoException> 0032 0033 #include "kleopatra_debug.h" 0034 #include <KLocalizedString> 0035 0036 #include <QGpgME/SignEncryptArchiveJob> 0037 0038 #include <QDir> 0039 #include <QFileInfo> 0040 #include <QPointer> 0041 #include <QTimer> 0042 0043 using namespace Kleo; 0044 using namespace Kleo::Crypto; 0045 using namespace GpgME; 0046 0047 class SignEncryptFilesController::Private 0048 { 0049 friend class ::Kleo::Crypto::SignEncryptFilesController; 0050 SignEncryptFilesController *const q; 0051 0052 public: 0053 explicit Private(SignEncryptFilesController *qq); 0054 ~Private(); 0055 0056 private: 0057 void slotWizardOperationPrepared(); 0058 void slotWizardCanceled(); 0059 0060 private: 0061 void ensureWizardCreated(); 0062 void ensureWizardVisible(); 0063 void updateWizardMode(); 0064 void cancelAllTasks(); 0065 void reportError(int err, const QString &details) 0066 { 0067 q->setLastError(err, details); 0068 q->emitDoneOrError(); 0069 } 0070 0071 void schedule(); 0072 std::shared_ptr<SignEncryptTask> takeRunnable(GpgME::Protocol proto); 0073 0074 static void assertValidOperation(unsigned int); 0075 static QString titleForOperation(unsigned int op); 0076 0077 private: 0078 std::vector<std::shared_ptr<SignEncryptTask>> runnable, completed; 0079 std::shared_ptr<SignEncryptTask> cms, openpgp; 0080 QPointer<SignEncryptFilesWizard> wizard; 0081 QStringList files; 0082 unsigned int operation; 0083 Protocol protocol; 0084 }; 0085 0086 SignEncryptFilesController::Private::Private(SignEncryptFilesController *qq) 0087 : q(qq) 0088 , runnable() 0089 , cms() 0090 , openpgp() 0091 , wizard() 0092 , files() 0093 , operation(SignAllowed | EncryptAllowed | ArchiveAllowed) 0094 , protocol(UnknownProtocol) 0095 { 0096 } 0097 0098 SignEncryptFilesController::Private::~Private() 0099 { 0100 qCDebug(KLEOPATRA_LOG) << q << __func__; 0101 } 0102 0103 QString SignEncryptFilesController::Private::titleForOperation(unsigned int op) 0104 { 0105 const bool signDisallowed = (op & SignMask) == SignDisallowed; 0106 const bool encryptDisallowed = (op & EncryptMask) == EncryptDisallowed; 0107 const bool archiveSelected = (op & ArchiveMask) == ArchiveForced; 0108 0109 kleo_assert(!signDisallowed || !encryptDisallowed); 0110 0111 if (!signDisallowed && encryptDisallowed) { 0112 if (archiveSelected) { 0113 return i18n("Archive and Sign Files"); 0114 } else { 0115 return i18n("Sign Files"); 0116 } 0117 } 0118 0119 if (signDisallowed && !encryptDisallowed) { 0120 if (archiveSelected) { 0121 return i18n("Archive and Encrypt Files"); 0122 } else { 0123 return i18n("Encrypt Files"); 0124 } 0125 } 0126 0127 if (archiveSelected) { 0128 return i18n("Archive and Sign/Encrypt Files"); 0129 } else { 0130 return i18n("Sign/Encrypt Files"); 0131 } 0132 } 0133 0134 SignEncryptFilesController::SignEncryptFilesController(QObject *p) 0135 : Controller(p) 0136 , d(new Private(this)) 0137 { 0138 } 0139 0140 SignEncryptFilesController::SignEncryptFilesController(const std::shared_ptr<const ExecutionContext> &ctx, QObject *p) 0141 : Controller(ctx, p) 0142 , d(new Private(this)) 0143 { 0144 } 0145 0146 SignEncryptFilesController::~SignEncryptFilesController() 0147 { 0148 qCDebug(KLEOPATRA_LOG) << this << __func__; 0149 if (d->wizard && !d->wizard->isVisible()) { 0150 delete d->wizard; 0151 } 0152 // d->wizard->close(); ### ? 0153 } 0154 0155 void SignEncryptFilesController::setProtocol(Protocol proto) 0156 { 0157 kleo_assert(d->protocol == UnknownProtocol || d->protocol == proto); 0158 d->protocol = proto; 0159 d->ensureWizardCreated(); 0160 } 0161 0162 Protocol SignEncryptFilesController::protocol() const 0163 { 0164 return d->protocol; 0165 } 0166 0167 // static 0168 void SignEncryptFilesController::Private::assertValidOperation(unsigned int op) 0169 { 0170 kleo_assert((op & SignMask) == SignDisallowed || // 0171 (op & SignMask) == SignAllowed || // 0172 (op & SignMask) == SignSelected); 0173 kleo_assert((op & EncryptMask) == EncryptDisallowed || // 0174 (op & EncryptMask) == EncryptAllowed || // 0175 (op & EncryptMask) == EncryptSelected); 0176 kleo_assert((op & ArchiveMask) == ArchiveDisallowed || // 0177 (op & ArchiveMask) == ArchiveAllowed || // 0178 (op & ArchiveMask) == ArchiveForced); 0179 kleo_assert((op & ~(SignMask | EncryptMask | ArchiveMask)) == 0); 0180 } 0181 0182 void SignEncryptFilesController::setOperationMode(unsigned int mode) 0183 { 0184 Private::assertValidOperation(mode); 0185 d->operation = mode; 0186 d->updateWizardMode(); 0187 } 0188 0189 void SignEncryptFilesController::Private::updateWizardMode() 0190 { 0191 if (!wizard) { 0192 return; 0193 } 0194 wizard->setWindowTitle(titleForOperation(operation)); 0195 const unsigned int signOp = (operation & SignMask); 0196 const unsigned int encrOp = (operation & EncryptMask); 0197 const unsigned int archOp = (operation & ArchiveMask); 0198 0199 if (signOp == SignDisallowed) { 0200 wizard->setSigningUserMutable(false); 0201 wizard->setSigningPreset(false); 0202 } else { 0203 wizard->setSigningUserMutable(true); 0204 wizard->setSigningPreset(signOp == SignSelected); 0205 } 0206 0207 if (encrOp == EncryptDisallowed) { 0208 wizard->setEncryptionPreset(false); 0209 wizard->setEncryptionUserMutable(false); 0210 } else { 0211 wizard->setEncryptionUserMutable(true); 0212 wizard->setEncryptionPreset(encrOp == EncryptSelected); 0213 } 0214 0215 wizard->setArchiveForced(archOp == ArchiveForced); 0216 wizard->setArchiveMutable(archOp == ArchiveAllowed); 0217 } 0218 0219 unsigned int SignEncryptFilesController::operationMode() const 0220 { 0221 return d->operation; 0222 } 0223 0224 static QString extension(bool pgp, bool sign, bool encrypt, bool ascii, bool detached) 0225 { 0226 unsigned int cls = pgp ? Class::OpenPGP : Class::CMS; 0227 if (encrypt) { 0228 cls |= Class::CipherText; 0229 } else if (sign) { 0230 cls |= detached ? Class::DetachedSignature : Class::OpaqueSignature; 0231 } 0232 cls |= ascii ? Class::Ascii : Class::Binary; 0233 const bool usePGPFileExt = FileOperationsPreferences().usePGPFileExt(); 0234 const auto ext = outputFileExtension(cls, usePGPFileExt); 0235 if (!ext.isEmpty()) { 0236 return ext; 0237 } else { 0238 return QStringLiteral("out"); 0239 } 0240 } 0241 0242 static std::shared_ptr<ArchiveDefinition> getDefaultAd() 0243 { 0244 const std::vector<std::shared_ptr<ArchiveDefinition>> ads = ArchiveDefinition::getArchiveDefinitions(); 0245 Q_ASSERT(!ads.empty()); 0246 std::shared_ptr<ArchiveDefinition> ad = ads.front(); 0247 const FileOperationsPreferences prefs; 0248 const QString archiveCmd = prefs.archiveCommand(); 0249 auto it = std::find_if(ads.cbegin(), ads.cend(), [&archiveCmd](const std::shared_ptr<ArchiveDefinition> &toCheck) { 0250 return toCheck->id() == archiveCmd; 0251 }); 0252 if (it != ads.cend()) { 0253 ad = *it; 0254 } 0255 return ad; 0256 } 0257 0258 static QMap<int, QString> buildOutputNames(const QStringList &files, const bool archive) 0259 { 0260 QMap<int, QString> nameMap; 0261 0262 // Build the default names for the wizard. 0263 QString baseNameCms; 0264 QString baseNamePgp; 0265 const QFileInfo firstFile(files.first()); 0266 if (archive) { 0267 QString baseName = files.size() > 1 ? i18nc("base name of an archive file, e.g. archive.zip or archive.tar.gz", "archive") : firstFile.baseName(); 0268 baseName = QDir(heuristicBaseDirectory(files)).absoluteFilePath(baseName); 0269 0270 const auto ad = getDefaultAd(); 0271 baseNamePgp = baseName + QLatin1Char('.') + ad->extensions(GpgME::OpenPGP).first() + QLatin1Char('.'); 0272 baseNameCms = baseName + QLatin1Char('.') + ad->extensions(GpgME::CMS).first() + QLatin1Char('.'); 0273 } else { 0274 baseNameCms = baseNamePgp = files.first() + QLatin1Char('.'); 0275 } 0276 const FileOperationsPreferences prefs; 0277 const bool ascii = prefs.addASCIIArmor(); 0278 0279 nameMap.insert(SignEncryptFilesWizard::SignatureCMS, baseNameCms + extension(false, true, false, ascii, true)); 0280 nameMap.insert(SignEncryptFilesWizard::EncryptedCMS, baseNameCms + extension(false, false, true, ascii, false)); 0281 nameMap.insert(SignEncryptFilesWizard::CombinedPGP, baseNamePgp + extension(true, true, true, ascii, false)); 0282 nameMap.insert(SignEncryptFilesWizard::EncryptedPGP, baseNamePgp + extension(true, false, true, ascii, false)); 0283 nameMap.insert(SignEncryptFilesWizard::SignaturePGP, baseNamePgp + extension(true, true, false, ascii, true)); 0284 nameMap.insert(SignEncryptFilesWizard::Directory, heuristicBaseDirectory(files)); 0285 return nameMap; 0286 } 0287 0288 static QMap<int, QString> buildOutputNamesForDir(const QString &file, const QMap<int, QString> &orig) 0289 { 0290 QMap<int, QString> ret; 0291 0292 const QString dir = orig.value(SignEncryptFilesWizard::Directory); 0293 if (dir.isEmpty()) { 0294 return orig; 0295 } 0296 0297 // Build the default names for the wizard. 0298 const QFileInfo fi(file); 0299 const QString baseName = dir + QLatin1Char('/') + fi.fileName() + QLatin1Char('.'); 0300 0301 const FileOperationsPreferences prefs; 0302 const bool ascii = prefs.addASCIIArmor(); 0303 0304 ret.insert(SignEncryptFilesWizard::SignatureCMS, baseName + extension(false, true, false, ascii, true)); 0305 ret.insert(SignEncryptFilesWizard::EncryptedCMS, baseName + extension(false, false, true, ascii, false)); 0306 ret.insert(SignEncryptFilesWizard::CombinedPGP, baseName + extension(true, true, true, ascii, false)); 0307 ret.insert(SignEncryptFilesWizard::EncryptedPGP, baseName + extension(true, false, true, ascii, false)); 0308 ret.insert(SignEncryptFilesWizard::SignaturePGP, baseName + extension(true, true, false, ascii, true)); 0309 return ret; 0310 } 0311 0312 // strips all trailing slashes from the filename, but keeps filename "/" 0313 static QString stripTrailingSlashes(const QString &fileName) 0314 { 0315 if (fileName.size() < 2 || !fileName.endsWith(QLatin1Char('/'))) { 0316 return fileName; 0317 } 0318 auto tmp = QStringView{fileName}.chopped(1); 0319 while (tmp.size() > 1 && tmp.endsWith(QLatin1Char('/'))) { 0320 tmp.chop(1); 0321 } 0322 return tmp.toString(); 0323 } 0324 0325 static QStringList stripTrailingSlashesForAll(const QStringList &fileNames) 0326 { 0327 QStringList result; 0328 result.reserve(fileNames.size()); 0329 std::transform(fileNames.begin(), fileNames.end(), std::back_inserter(result), &stripTrailingSlashes); 0330 return result; 0331 } 0332 0333 void SignEncryptFilesController::setFiles(const QStringList &files) 0334 { 0335 kleo_assert(!files.empty()); 0336 d->files = stripTrailingSlashesForAll(files); 0337 bool archive = false; 0338 0339 if (d->files.size() > 1) { 0340 setOperationMode((operationMode() & ~ArchiveMask) | ArchiveAllowed); 0341 archive = true; 0342 } 0343 for (const auto &file : d->files) { 0344 if (QFileInfo(file).isDir()) { 0345 setOperationMode((operationMode() & ~ArchiveMask) | ArchiveForced); 0346 archive = true; 0347 break; 0348 } 0349 } 0350 d->ensureWizardCreated(); 0351 d->wizard->setSingleFile(!archive); 0352 d->wizard->setOutputNames(buildOutputNames(d->files, archive)); 0353 } 0354 0355 void SignEncryptFilesController::Private::slotWizardCanceled() 0356 { 0357 qCDebug(KLEOPATRA_LOG) << q << __func__; 0358 q->cancel(); 0359 reportError(gpg_error(GPG_ERR_CANCELED), i18n("User cancel")); 0360 } 0361 0362 void SignEncryptFilesController::start() 0363 { 0364 d->ensureWizardVisible(); 0365 } 0366 0367 static std::shared_ptr<SignEncryptTask> createSignEncryptTaskForFileInfo(const QFileInfo &fi, 0368 bool ascii, 0369 const std::vector<Key> &recipients, 0370 const std::vector<Key> &signers, 0371 const QString &outputName, 0372 bool symmetric) 0373 { 0374 const std::shared_ptr<SignEncryptTask> task(new SignEncryptTask); 0375 Q_ASSERT(!signers.empty() || !recipients.empty() || symmetric); 0376 task->setAsciiArmor(ascii); 0377 if (!signers.empty()) { 0378 task->setSign(true); 0379 task->setSigners(signers); 0380 task->setDetachedSignature(true); 0381 } else { 0382 task->setSign(false); 0383 } 0384 if (!recipients.empty()) { 0385 task->setEncrypt(true); 0386 task->setRecipients(recipients); 0387 task->setDetachedSignature(false); 0388 } else { 0389 task->setEncrypt(false); 0390 } 0391 task->setEncryptSymmetric(symmetric); 0392 const QString input = fi.absoluteFilePath(); 0393 task->setInputFileName(input); 0394 #if QGPGME_FILE_JOBS_SUPPORT_DIRECT_FILE_IO 0395 if (task->protocol() != GpgME::OpenPGP) { 0396 task->setInput(Input::createFromFile(input)); 0397 } 0398 #else 0399 task->setInput(Input::createFromFile(input)); 0400 #endif 0401 0402 task->setOutputFileName(outputName); 0403 0404 return task; 0405 } 0406 0407 static bool archiveJobsCanBeUsed([[maybe_unused]] GpgME::Protocol protocol) 0408 { 0409 return (protocol == GpgME::OpenPGP) && QGpgME::SignEncryptArchiveJob::isSupported(); 0410 } 0411 0412 static std::shared_ptr<SignEncryptTask> createArchiveSignEncryptTaskForFiles(const QStringList &files, 0413 const std::shared_ptr<ArchiveDefinition> &ad, 0414 bool pgp, 0415 bool ascii, 0416 const std::vector<Key> &recipients, 0417 const std::vector<Key> &signers, 0418 const QString &outputName, 0419 bool symmetric) 0420 { 0421 const std::shared_ptr<SignEncryptTask> task(new SignEncryptTask); 0422 task->setCreateArchive(true); 0423 task->setEncryptSymmetric(symmetric); 0424 Q_ASSERT(!signers.empty() || !recipients.empty() || symmetric); 0425 task->setAsciiArmor(ascii); 0426 if (!signers.empty()) { 0427 task->setSign(true); 0428 task->setSigners(signers); 0429 task->setDetachedSignature(false); 0430 } else { 0431 task->setSign(false); 0432 } 0433 if (!recipients.empty()) { 0434 task->setEncrypt(true); 0435 task->setRecipients(recipients); 0436 } else { 0437 task->setEncrypt(false); 0438 } 0439 0440 const Protocol proto = pgp ? OpenPGP : CMS; 0441 0442 task->setInputFileNames(files); 0443 if (!archiveJobsCanBeUsed(proto)) { 0444 // use legacy archive creation 0445 kleo_assert(ad); 0446 task->setInput(ad->createInputFromPackCommand(proto, files)); 0447 } 0448 0449 task->setOutputFileName(outputName); 0450 0451 return task; 0452 } 0453 0454 static std::vector<std::shared_ptr<SignEncryptTask>> createSignEncryptTasksForFileInfo(const QFileInfo &fi, 0455 bool ascii, 0456 const std::vector<Key> &pgpRecipients, 0457 const std::vector<Key> &pgpSigners, 0458 const std::vector<Key> &cmsRecipients, 0459 const std::vector<Key> &cmsSigners, 0460 const QMap<int, QString> &outputNames, 0461 bool symmetric) 0462 { 0463 std::vector<std::shared_ptr<SignEncryptTask>> result; 0464 0465 const bool pgp = !pgpSigners.empty() || !pgpRecipients.empty(); 0466 0467 const bool cms = !cmsSigners.empty() || !cmsRecipients.empty(); 0468 0469 result.reserve(pgp + cms); 0470 0471 if (pgp || symmetric) { 0472 // Symmetric encryption is only supported for PGP 0473 int outKind = 0; 0474 if ((!pgpRecipients.empty() || symmetric) && !pgpSigners.empty()) { 0475 outKind = SignEncryptFilesWizard::CombinedPGP; 0476 } else if (!pgpRecipients.empty() || symmetric) { 0477 outKind = SignEncryptFilesWizard::EncryptedPGP; 0478 } else { 0479 outKind = SignEncryptFilesWizard::SignaturePGP; 0480 } 0481 result.push_back(createSignEncryptTaskForFileInfo(fi, ascii, pgpRecipients, pgpSigners, outputNames[outKind], symmetric)); 0482 } 0483 if (cms) { 0484 // There is no combined sign / encrypt in gpgsm so we create one sign task 0485 // and one encrypt task. Which leaves us with the age old dilemma, encrypt 0486 // then sign, or sign then encrypt. Ugly. 0487 if (!cmsSigners.empty()) { 0488 result.push_back( 0489 createSignEncryptTaskForFileInfo(fi, ascii, std::vector<Key>(), cmsSigners, outputNames[SignEncryptFilesWizard::SignatureCMS], false)); 0490 } 0491 if (!cmsRecipients.empty()) { 0492 result.push_back( 0493 createSignEncryptTaskForFileInfo(fi, ascii, cmsRecipients, std::vector<Key>(), outputNames[SignEncryptFilesWizard::EncryptedCMS], false)); 0494 } 0495 } 0496 0497 return result; 0498 } 0499 0500 static std::vector<std::shared_ptr<SignEncryptTask>> createArchiveSignEncryptTasksForFiles(const QStringList &files, 0501 const std::shared_ptr<ArchiveDefinition> &ad, 0502 bool ascii, 0503 const std::vector<Key> &pgpRecipients, 0504 const std::vector<Key> &pgpSigners, 0505 const std::vector<Key> &cmsRecipients, 0506 const std::vector<Key> &cmsSigners, 0507 const QMap<int, QString> &outputNames, 0508 bool symmetric) 0509 { 0510 std::vector<std::shared_ptr<SignEncryptTask>> result; 0511 0512 const bool pgp = !pgpSigners.empty() || !pgpRecipients.empty(); 0513 0514 const bool cms = !cmsSigners.empty() || !cmsRecipients.empty(); 0515 0516 result.reserve(pgp + cms); 0517 0518 if (pgp || symmetric) { 0519 int outKind = 0; 0520 if ((!pgpRecipients.empty() || symmetric) && !pgpSigners.empty()) { 0521 outKind = SignEncryptFilesWizard::CombinedPGP; 0522 } else if (!pgpRecipients.empty() || symmetric) { 0523 outKind = SignEncryptFilesWizard::EncryptedPGP; 0524 } else { 0525 outKind = SignEncryptFilesWizard::SignaturePGP; 0526 } 0527 result.push_back(createArchiveSignEncryptTaskForFiles(files, ad, true, ascii, pgpRecipients, pgpSigners, outputNames[outKind], symmetric)); 0528 } 0529 if (cms) { 0530 if (!cmsSigners.empty()) { 0531 result.push_back(createArchiveSignEncryptTaskForFiles(files, 0532 ad, 0533 false, 0534 ascii, 0535 std::vector<Key>(), 0536 cmsSigners, 0537 outputNames[SignEncryptFilesWizard::SignatureCMS], 0538 false)); 0539 } 0540 if (!cmsRecipients.empty()) { 0541 result.push_back(createArchiveSignEncryptTaskForFiles(files, 0542 ad, 0543 false, 0544 ascii, 0545 cmsRecipients, 0546 std::vector<Key>(), 0547 outputNames[SignEncryptFilesWizard::EncryptedCMS], 0548 false)); 0549 } 0550 } 0551 0552 return result; 0553 } 0554 0555 namespace 0556 { 0557 static auto resolveFileNameConflicts(const std::vector<std::shared_ptr<SignEncryptTask>> &tasks, QWidget *parent) 0558 { 0559 std::vector<std::shared_ptr<SignEncryptTask>> resolvedTasks; 0560 0561 OverwritePolicy overwritePolicy{parent, tasks.size() > 1 ? OverwritePolicy::MultipleFiles : OverwritePolicy::Options{}}; 0562 for (auto &task : tasks) { 0563 // by default, do not overwrite existing files 0564 task->setOverwritePolicy(std::make_shared<OverwritePolicy>(OverwritePolicy::Skip)); 0565 const auto outputFileName = task->outputFileName(); 0566 if (QFile::exists(outputFileName)) { 0567 const auto newFileName = overwritePolicy.obtainOverwritePermission(outputFileName); 0568 if (newFileName.isEmpty()) { 0569 if (overwritePolicy.policy() == OverwritePolicy::Cancel) { 0570 resolvedTasks.clear(); 0571 break; 0572 } 0573 // else Skip -> do not add task to the final task list 0574 continue; 0575 } else if (newFileName != outputFileName) { 0576 task->setOutputFileName(newFileName); 0577 } else { 0578 task->setOverwritePolicy(std::make_shared<OverwritePolicy>(OverwritePolicy::Overwrite)); 0579 } 0580 } 0581 resolvedTasks.push_back(task); 0582 } 0583 0584 return resolvedTasks; 0585 } 0586 } 0587 0588 void SignEncryptFilesController::Private::slotWizardOperationPrepared() 0589 { 0590 try { 0591 kleo_assert(wizard); 0592 kleo_assert(!files.empty()); 0593 0594 const bool archive = ((wizard->outputNames().value(SignEncryptFilesWizard::Directory).isNull() && files.size() > 1) // 0595 || ((operation & ArchiveMask) == ArchiveForced)); 0596 0597 const std::vector<Key> recipients = wizard->resolvedRecipients(); 0598 const std::vector<Key> signers = wizard->resolvedSigners(); 0599 0600 const FileOperationsPreferences prefs; 0601 const bool ascii = prefs.addASCIIArmor(); 0602 0603 std::vector<Key> pgpRecipients, cmsRecipients, pgpSigners, cmsSigners; 0604 for (const Key &k : recipients) { 0605 if (k.protocol() == GpgME::OpenPGP) { 0606 pgpRecipients.push_back(k); 0607 } else { 0608 cmsRecipients.push_back(k); 0609 } 0610 } 0611 0612 for (const Key &k : signers) { 0613 if (k.protocol() == GpgME::OpenPGP) { 0614 pgpSigners.push_back(k); 0615 } else { 0616 cmsSigners.push_back(k); 0617 } 0618 } 0619 0620 std::vector<std::shared_ptr<SignEncryptTask>> tasks; 0621 if (!archive) { 0622 tasks.reserve(files.size()); 0623 } 0624 0625 if (archive) { 0626 tasks = createArchiveSignEncryptTasksForFiles(files, 0627 getDefaultAd(), 0628 ascii, 0629 pgpRecipients, 0630 pgpSigners, 0631 cmsRecipients, 0632 cmsSigners, 0633 wizard->outputNames(), 0634 wizard->encryptSymmetric()); 0635 } else { 0636 for (const QString &file : std::as_const(files)) { 0637 const std::vector<std::shared_ptr<SignEncryptTask>> created = 0638 createSignEncryptTasksForFileInfo(QFileInfo(file), 0639 ascii, 0640 pgpRecipients, 0641 pgpSigners, 0642 cmsRecipients, 0643 cmsSigners, 0644 buildOutputNamesForDir(file, wizard->outputNames()), 0645 wizard->encryptSymmetric()); 0646 tasks.insert(tasks.end(), created.begin(), created.end()); 0647 } 0648 } 0649 0650 tasks = resolveFileNameConflicts(tasks, wizard); 0651 if (tasks.empty()) { 0652 q->cancel(); 0653 return; 0654 } 0655 0656 kleo_assert(runnable.empty()); 0657 0658 runnable.swap(tasks); 0659 0660 for (const auto &task : std::as_const(runnable)) { 0661 q->connectTask(task); 0662 } 0663 0664 std::shared_ptr<TaskCollection> coll(new TaskCollection); 0665 0666 std::vector<std::shared_ptr<Task>> tmp; 0667 std::copy(runnable.begin(), runnable.end(), std::back_inserter(tmp)); 0668 coll->setTasks(tmp); 0669 wizard->setTaskCollection(coll); 0670 0671 QTimer::singleShot(0, q, SLOT(schedule())); 0672 0673 } catch (const Kleo::Exception &e) { 0674 reportError(e.error().encodedError(), e.message()); 0675 } catch (const std::exception &e) { 0676 reportError( 0677 gpg_error(GPG_ERR_UNEXPECTED), 0678 i18n("Caught unexpected exception in SignEncryptFilesController::Private::slotWizardOperationPrepared: %1", QString::fromLocal8Bit(e.what()))); 0679 } catch (...) { 0680 reportError(gpg_error(GPG_ERR_UNEXPECTED), i18n("Caught unknown exception in SignEncryptFilesController::Private::slotWizardOperationPrepared")); 0681 } 0682 } 0683 0684 void SignEncryptFilesController::Private::schedule() 0685 { 0686 if (!cms) 0687 if (const std::shared_ptr<SignEncryptTask> t = takeRunnable(CMS)) { 0688 t->start(); 0689 cms = t; 0690 } 0691 0692 if (!openpgp) 0693 if (const std::shared_ptr<SignEncryptTask> t = takeRunnable(OpenPGP)) { 0694 t->start(); 0695 openpgp = t; 0696 } 0697 0698 if (!cms && !openpgp) { 0699 kleo_assert(runnable.empty()); 0700 q->emitDoneOrError(); 0701 } 0702 } 0703 0704 std::shared_ptr<SignEncryptTask> SignEncryptFilesController::Private::takeRunnable(GpgME::Protocol proto) 0705 { 0706 const auto it = std::find_if(runnable.begin(), runnable.end(), [proto](const std::shared_ptr<Task> &task) { 0707 return task->protocol() == proto; 0708 }); 0709 if (it == runnable.end()) { 0710 return std::shared_ptr<SignEncryptTask>(); 0711 } 0712 0713 const std::shared_ptr<SignEncryptTask> result = *it; 0714 runnable.erase(it); 0715 return result; 0716 } 0717 0718 void SignEncryptFilesController::doTaskDone(const Task *task, const std::shared_ptr<const Task::Result> &result) 0719 { 0720 Q_UNUSED(result) 0721 Q_ASSERT(task); 0722 0723 // We could just delete the tasks here, but we can't use 0724 // Qt::QueuedConnection here (we need sender()) and other slots 0725 // might not yet have executed. Therefore, we push completed tasks 0726 // into a burial container 0727 0728 if (task == d->cms.get()) { 0729 d->completed.push_back(d->cms); 0730 d->cms.reset(); 0731 } else if (task == d->openpgp.get()) { 0732 d->completed.push_back(d->openpgp); 0733 d->openpgp.reset(); 0734 } 0735 0736 QTimer::singleShot(0, this, SLOT(schedule())); 0737 } 0738 0739 void SignEncryptFilesController::cancel() 0740 { 0741 qCDebug(KLEOPATRA_LOG) << this << __func__; 0742 try { 0743 if (d->wizard) { 0744 d->wizard->close(); 0745 } 0746 d->cancelAllTasks(); 0747 } catch (const std::exception &e) { 0748 qCDebug(KLEOPATRA_LOG) << "Caught exception: " << e.what(); 0749 } 0750 } 0751 0752 void SignEncryptFilesController::Private::cancelAllTasks() 0753 { 0754 // we just kill all runnable tasks - this will not result in 0755 // signal emissions. 0756 runnable.clear(); 0757 0758 // a cancel() will result in a call to 0759 if (cms) { 0760 cms->cancel(); 0761 } 0762 if (openpgp) { 0763 openpgp->cancel(); 0764 } 0765 } 0766 0767 void SignEncryptFilesController::Private::ensureWizardCreated() 0768 { 0769 if (wizard) { 0770 return; 0771 } 0772 0773 std::unique_ptr<SignEncryptFilesWizard> w(new SignEncryptFilesWizard); 0774 w->setAttribute(Qt::WA_DeleteOnClose); 0775 0776 connect(w.get(), SIGNAL(operationPrepared()), q, SLOT(slotWizardOperationPrepared()), Qt::QueuedConnection); 0777 connect(w.get(), SIGNAL(rejected()), q, SLOT(slotWizardCanceled()), Qt::QueuedConnection); 0778 wizard = w.release(); 0779 0780 updateWizardMode(); 0781 } 0782 0783 void SignEncryptFilesController::Private::ensureWizardVisible() 0784 { 0785 ensureWizardCreated(); 0786 q->bringToForeground(wizard); 0787 } 0788 0789 #include "moc_signencryptfilescontroller.cpp"