File indexing completed on 2024-06-16 04:55: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"