1 /* -*- mode: c++; c-basic-offset:4 -*-
2     crypto/signencryptfilescontroller.cpp
3 
4     This file is part of Kleopatra, the KDE keymanager
5     SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB
6 
7     SPDX-FileCopyrightText: 2017 Bundesamt für Sicherheit in der Informationstechnik
8     SPDX-FileContributor: Intevation GmbH
9 
10     SPDX-License-Identifier: GPL-2.0-or-later
11 */
12 
13 #include <config-kleopatra.h>
14 
15 #include "signencryptfilescontroller.h"
16 
17 #include "signencrypttask.h"
18 #include "certificateresolver.h"
19 
20 #include "crypto/gui/signencryptfileswizard.h"
21 #include "crypto/taskcollection.h"
22 
23 #include "fileoperationspreferences.h"
24 
25 #include "utils/input.h"
26 #include "utils/output.h"
27 #include "utils/kleo_assert.h"
28 #include "utils/archivedefinition.h"
29 #include "utils/path-helper.h"
30 
31 #include <Libkleo/KleoException>
32 #include <Libkleo/Classify>
33 
34 
35 #include <KLocalizedString>
36 #include "kleopatra_debug.h"
37 
38 #include <QPointer>
39 #include <QTimer>
40 #include <QFileInfo>
41 #include <QDir>
42 
43 using namespace Kleo;
44 using namespace Kleo::Crypto;
45 using namespace GpgME;
46 using namespace KMime::Types;
47 
48 class SignEncryptFilesController::Private
49 {
50     friend class ::Kleo::Crypto::SignEncryptFilesController;
51     SignEncryptFilesController *const q;
52 public:
53     explicit Private(SignEncryptFilesController *qq);
54     ~Private();
55 
56 private:
57     void slotWizardOperationPrepared();
58     void slotWizardCanceled();
59 
60 private:
61     void ensureWizardCreated();
62     void ensureWizardVisible();
63     void updateWizardMode();
64     void cancelAllTasks();
reportError(int err,const QString & details)65     void reportError(int err, const QString &details)
66     {
67         q->setLastError(err, details);
68         q->emitDoneOrError();
69     }
70 
71     void schedule();
72     std::shared_ptr<SignEncryptTask> takeRunnable(GpgME::Protocol proto);
73 
74     static void assertValidOperation(unsigned int);
75     static QString titleForOperation(unsigned int op);
76 private:
77     std::vector< std::shared_ptr<SignEncryptTask> > runnable, completed;
78     std::shared_ptr<SignEncryptTask> cms, openpgp;
79     QPointer<SignEncryptFilesWizard> wizard;
80     QStringList files;
81     unsigned int operation;
82     Protocol protocol;
83 };
84 
Private(SignEncryptFilesController * qq)85 SignEncryptFilesController::Private::Private(SignEncryptFilesController *qq)
86     : q(qq),
87       runnable(),
88       cms(),
89       openpgp(),
90       wizard(),
91       files(),
92       operation(SignAllowed | EncryptAllowed | ArchiveAllowed),
93       protocol(UnknownProtocol)
94 {
95 
96 }
97 
~Private()98 SignEncryptFilesController::Private::~Private()
99 {
100     qCDebug(KLEOPATRA_LOG);
101 }
102 
titleForOperation(unsigned int op)103 QString SignEncryptFilesController::Private::titleForOperation(unsigned int op)
104 {
105     const bool signDisallowed = (op & SignMask) == SignDisallowed;
106     const bool encryptDisallowed = (op & EncryptMask) == EncryptDisallowed;
107     const bool archiveSelected = (op & ArchiveMask) == ArchiveForced;
108 
109     kleo_assert(!signDisallowed || !encryptDisallowed);
110 
111     if (!signDisallowed && encryptDisallowed) {
112         if (archiveSelected) {
113             return i18n("Archive and Sign Files");
114         } else {
115             return i18n("Sign Files");
116         }
117     }
118 
119     if (signDisallowed && !encryptDisallowed) {
120         if (archiveSelected) {
121             return i18n("Archive and Encrypt Files");
122         } else {
123             return i18n("Encrypt Files");
124         }
125     }
126 
127     if (archiveSelected) {
128         return i18n("Archive and Sign/Encrypt Files");
129     } else {
130         return i18n("Sign/Encrypt Files");
131     }
132 }
133 
SignEncryptFilesController(QObject * p)134 SignEncryptFilesController::SignEncryptFilesController(QObject *p)
135     : Controller(p), d(new Private(this))
136 {
137 
138 }
139 
SignEncryptFilesController(const std::shared_ptr<const ExecutionContext> & ctx,QObject * p)140 SignEncryptFilesController::SignEncryptFilesController(const std::shared_ptr<const ExecutionContext> &ctx, QObject *p)
141     : Controller(ctx, p), d(new Private(this))
142 {
143 
144 }
145 
~SignEncryptFilesController()146 SignEncryptFilesController::~SignEncryptFilesController()
147 {
148     qCDebug(KLEOPATRA_LOG);
149     if (d->wizard && !d->wizard->isVisible()) {
150         delete d->wizard;
151     }
152     //d->wizard->close(); ### ?
153 }
154 
setProtocol(Protocol proto)155 void SignEncryptFilesController::setProtocol(Protocol proto)
156 {
157     kleo_assert(d->protocol == UnknownProtocol ||
158                 d->protocol == proto);
159     d->protocol = proto;
160     d->ensureWizardCreated();
161 }
162 
protocol() const163 Protocol SignEncryptFilesController::protocol() const
164 {
165     return d->protocol;
166 }
167 
168 // static
assertValidOperation(unsigned int op)169 void SignEncryptFilesController::Private::assertValidOperation(unsigned int op)
170 {
171     kleo_assert((op & SignMask)    == SignDisallowed    ||
172                 (op & SignMask)    == SignAllowed       ||
173                 (op & SignMask)    == SignSelected);
174     kleo_assert((op & EncryptMask) == EncryptDisallowed ||
175                 (op & EncryptMask) == EncryptAllowed    ||
176                 (op & EncryptMask) == EncryptSelected);
177     kleo_assert((op & ArchiveMask) == ArchiveDisallowed ||
178                 (op & ArchiveMask) == ArchiveAllowed    ||
179                 (op & ArchiveMask) == ArchiveForced);
180     kleo_assert((op & ~(SignMask | EncryptMask | ArchiveMask)) == 0);
181 }
182 
setOperationMode(unsigned int mode)183 void SignEncryptFilesController::setOperationMode(unsigned int mode)
184 {
185     Private::assertValidOperation(mode);
186     d->operation = mode;
187     d->updateWizardMode();
188 }
189 
updateWizardMode()190 void SignEncryptFilesController::Private::updateWizardMode()
191 {
192     if (!wizard) {
193         return;
194     }
195     wizard->setWindowTitle(titleForOperation(operation));
196     const unsigned int signOp = (operation & SignMask);
197     const unsigned int encrOp = (operation & EncryptMask);
198     const unsigned int archOp = (operation & ArchiveMask);
199 
200     if (signOp == SignDisallowed) {
201         wizard->setSigningUserMutable(false);
202         wizard->setSigningPreset(false);
203     } else {
204         wizard->setSigningUserMutable(true);
205         wizard->setSigningPreset(signOp == SignSelected);
206     }
207 
208     if (encrOp == EncryptDisallowed) {
209         wizard->setEncryptionPreset(false);
210         wizard->setEncryptionUserMutable(false);
211     } else {
212         wizard->setEncryptionUserMutable(true);
213         wizard->setEncryptionPreset(encrOp == EncryptSelected);
214     }
215 
216     wizard->setArchiveForced(archOp == ArchiveForced);
217     wizard->setArchiveMutable(archOp == ArchiveAllowed);
218 }
219 
operationMode() const220 unsigned int SignEncryptFilesController::operationMode() const
221 {
222     return d->operation;
223 }
224 
extension(bool pgp,bool sign,bool encrypt,bool ascii,bool detached)225 static const char *extension(bool pgp, bool sign, bool encrypt, bool ascii, bool detached)
226 {
227     unsigned int cls = pgp ? Class::OpenPGP : Class::CMS;
228     if (encrypt) {
229         cls |= Class::CipherText;
230     } else if (sign) {
231         cls |= detached ? Class::DetachedSignature : Class::OpaqueSignature;
232     }
233     cls |= ascii ? Class::Ascii : Class::Binary;
234     const bool usePGPFileExt = FileOperationsPreferences().usePGPFileExt();
235     if (const char *const ext = outputFileExtension(cls, usePGPFileExt)) {
236         return ext;
237     } else {
238         return "out";
239     }
240 }
241 
getDefaultAd()242 static std::shared_ptr<ArchiveDefinition> getDefaultAd()
243 {
244     std::vector<std::shared_ptr<ArchiveDefinition> > ads = ArchiveDefinition::getArchiveDefinitions();
245     Q_ASSERT(!ads.empty());
246     std::shared_ptr<ArchiveDefinition> ad = ads.front();
247     const FileOperationsPreferences prefs;
248     Q_FOREACH (const std::shared_ptr<ArchiveDefinition> toCheck, ads) {
249         if (toCheck->id() == prefs.archiveCommand()) {
250             ad = toCheck;
251             break;
252         }
253     }
254     return ad;
255 }
256 
buildOutputNames(const QStringList & files,const bool archive)257 static QMap <int, QString> buildOutputNames(const QStringList &files, const bool archive)
258 {
259     QMap <int, QString> nameMap;
260 
261     // Build the default names for the wizard.
262     QString baseNameCms;
263     QString baseNamePgp;
264     const QFileInfo firstFile(files.first());
265     if (archive) {
266         QString baseName;
267         baseName = QDir(heuristicBaseDirectory(files)).absoluteFilePath(files.size() > 1 ?
268                 i18nc("base name of an archive file, e.g. archive.zip or archive.tar.gz", "archive") :
269                 firstFile.baseName());
270 
271         const auto ad = getDefaultAd();
272         baseNamePgp = baseName + QLatin1Char('.') + ad->extensions(GpgME::OpenPGP).first() + QLatin1Char('.');
273         baseNameCms = baseName + QLatin1Char('.') + ad->extensions(GpgME::CMS).first() + QLatin1Char('.');
274     } else {
275         baseNameCms = baseNamePgp = files.first() + QLatin1Char('.');
276     }
277     const FileOperationsPreferences prefs;
278     const bool ascii = prefs.addASCIIArmor();
279 
280     nameMap.insert(SignEncryptFilesWizard::SignatureCMS, baseNameCms + QString::fromLatin1(extension(false, true, false, ascii, true)));
281     nameMap.insert(SignEncryptFilesWizard::EncryptedCMS, baseNameCms + QString::fromLatin1(extension(false, false, true, ascii, false)));
282     nameMap.insert(SignEncryptFilesWizard::CombinedPGP,  baseNamePgp + QString::fromLatin1(extension(true, true, true, ascii, false)));
283     nameMap.insert(SignEncryptFilesWizard::EncryptedPGP, baseNamePgp + QString::fromLatin1(extension(true, false, true, ascii, false)));
284     nameMap.insert(SignEncryptFilesWizard::SignaturePGP, baseNamePgp + QString::fromLatin1(extension(true, true, false, ascii, true)));
285     nameMap.insert(SignEncryptFilesWizard::Directory, heuristicBaseDirectory(files));
286     return nameMap;
287 }
288 
buildOutputNamesForDir(const QString & file,const QMap<int,QString> & orig)289 static QMap <int, QString> buildOutputNamesForDir(const QString &file, const QMap <int, QString> &orig)
290 {
291     QMap <int, QString> ret;
292 
293     const QString dir = orig.value(SignEncryptFilesWizard::Directory);
294     if (dir.isEmpty()) {
295         return orig;
296     }
297 
298     // Build the default names for the wizard.
299     const QFileInfo fi(file);
300     const QString baseName = dir + QLatin1Char('/') + fi.fileName() + QLatin1Char('.');
301 
302     const FileOperationsPreferences prefs;
303     const bool ascii = prefs.addASCIIArmor();
304 
305     ret.insert(SignEncryptFilesWizard::SignatureCMS, baseName + QString::fromLatin1(extension(false, true, false, ascii, true)));
306     ret.insert(SignEncryptFilesWizard::EncryptedCMS, baseName + QString::fromLatin1(extension(false, false, true, ascii, false)));
307     ret.insert(SignEncryptFilesWizard::CombinedPGP,  baseName + QString::fromLatin1(extension(true, true, true, ascii, false)));
308     ret.insert(SignEncryptFilesWizard::EncryptedPGP, baseName + QString::fromLatin1(extension(true, false, true, ascii, false)));
309     ret.insert(SignEncryptFilesWizard::SignaturePGP, baseName + QString::fromLatin1(extension(true, true, false, ascii, true)));
310     return ret;
311 }
312 
setFiles(const QStringList & files)313 void SignEncryptFilesController::setFiles(const QStringList &files)
314 {
315     kleo_assert(!files.empty());
316     d->files = files;
317     bool archive = false;
318 
319     if (files.size() > 1) {
320         setOperationMode((operationMode() & ~ArchiveMask) | ArchiveAllowed);
321         archive = true;
322     }
323     for (const auto &file: files) {
324         if (QFileInfo(file).isDir()) {
325             setOperationMode((operationMode() & ~ArchiveMask) | ArchiveForced);
326             archive = true;
327             break;
328         }
329     }
330     d->ensureWizardCreated();
331     d->wizard->setSingleFile(!archive);
332     d->wizard->setOutputNames(buildOutputNames(files, archive));
333 }
334 
slotWizardCanceled()335 void SignEncryptFilesController::Private::slotWizardCanceled()
336 {
337     qCDebug(KLEOPATRA_LOG);
338     reportError(gpg_error(GPG_ERR_CANCELED), i18n("User cancel"));
339 }
340 
start()341 void SignEncryptFilesController::start()
342 {
343     d->ensureWizardVisible();
344 }
345 
346 static std::shared_ptr<SignEncryptTask>
createSignEncryptTaskForFileInfo(const QFileInfo & fi,bool ascii,const std::vector<Key> & recipients,const std::vector<Key> & signers,const QString & outputName,bool symmetric)347 createSignEncryptTaskForFileInfo(const QFileInfo &fi, bool ascii,
348                                  const std::vector<Key> &recipients, const std::vector<Key> &signers,
349                                  const QString &outputName, bool symmetric)
350 {
351     const std::shared_ptr<SignEncryptTask> task(new SignEncryptTask);
352     Q_ASSERT(!signers.empty() || !recipients.empty() || symmetric);
353     task->setAsciiArmor(ascii);
354     if (!signers.empty()) {
355         task->setSign(true);
356         task->setSigners(signers);
357         task->setDetachedSignature(true);
358     } else {
359         task->setSign(false);
360     }
361     if (!recipients.empty()) {
362         task->setEncrypt(true);
363         task->setRecipients(recipients);
364         task->setDetachedSignature(false);
365     } else {
366         task->setEncrypt(false);
367     }
368     task->setEncryptSymmetric(symmetric);
369     const QString input = fi.absoluteFilePath();
370     task->setInputFileName(input);
371     task->setInput(Input::createFromFile(input));
372 
373     task->setOutputFileName(outputName);
374 
375     return task;
376 }
377 
378 static std::shared_ptr<SignEncryptTask>
createArchiveSignEncryptTaskForFiles(const QStringList & files,const std::shared_ptr<ArchiveDefinition> & ad,bool pgp,bool ascii,const std::vector<Key> & recipients,const std::vector<Key> & signers,const QString & outputName,bool symmetric)379 createArchiveSignEncryptTaskForFiles(const QStringList &files,
380                                      const std::shared_ptr<ArchiveDefinition> &ad, bool pgp, bool ascii,
381                                      const std::vector<Key> &recipients, const std::vector<Key> &signers,
382                                      const QString& outputName, bool symmetric)
383 {
384     const std::shared_ptr<SignEncryptTask> task(new SignEncryptTask);
385     task->setEncryptSymmetric(symmetric);
386     Q_ASSERT(!signers.empty() || !recipients.empty() || symmetric);
387     task->setAsciiArmor(ascii);
388     if (!signers.empty()) {
389         task->setSign(true);
390         task->setSigners(signers);
391         task->setDetachedSignature(false);
392     } else {
393         task->setSign(false);
394     }
395     if (!recipients.empty()) {
396         task->setEncrypt(true);
397         task->setRecipients(recipients);
398     } else {
399         task->setEncrypt(false);
400     }
401 
402     kleo_assert(ad);
403 
404     const Protocol proto = pgp ? OpenPGP : CMS;
405 
406     task->setInputFileNames(files);
407     task->setInput(ad->createInputFromPackCommand(proto, files));
408 
409     task->setOutputFileName(outputName);
410 
411     return task;
412 }
413 
414 static std::vector< std::shared_ptr<SignEncryptTask> >
createSignEncryptTasksForFileInfo(const QFileInfo & fi,bool ascii,const std::vector<Key> & pgpRecipients,const std::vector<Key> & pgpSigners,const std::vector<Key> & cmsRecipients,const std::vector<Key> & cmsSigners,const QMap<int,QString> & outputNames,bool symmetric)415 createSignEncryptTasksForFileInfo(const QFileInfo &fi, bool ascii, const std::vector<Key> &pgpRecipients, const std::vector<Key> &pgpSigners,
416                                   const std::vector<Key> &cmsRecipients, const std::vector<Key> &cmsSigners, const QMap<int, QString> &outputNames,
417                                   bool symmetric)
418 {
419     std::vector< std::shared_ptr<SignEncryptTask> > result;
420 
421     const bool pgp = !pgpSigners.empty() || !pgpRecipients.empty();
422 
423     const bool cms = !cmsSigners.empty() || !cmsRecipients.empty();
424 
425     result.reserve(pgp + cms);
426 
427 
428     if (pgp || symmetric) {
429         // Symmetric encryption is only supported for PGP
430         int outKind = 0;
431         if ((!pgpRecipients.empty() || symmetric)&& !pgpSigners.empty()) {
432             outKind = SignEncryptFilesWizard::CombinedPGP;
433         } else if (!pgpRecipients.empty() || symmetric) {
434             outKind = SignEncryptFilesWizard::EncryptedPGP;
435         } else {
436             outKind = SignEncryptFilesWizard::SignaturePGP;
437         }
438         result.push_back(createSignEncryptTaskForFileInfo(fi, ascii, pgpRecipients, pgpSigners, outputNames[outKind], symmetric));
439     }
440     if (cms) {
441         // There is no combined sign / encrypt in gpgsm so we create one sign task
442         // and one encrypt task. Which leaves us with the age old dilemma, encrypt
443         // then sign, or sign then encrypt. Ugly.
444         if (!cmsSigners.empty()) {
445             result.push_back(createSignEncryptTaskForFileInfo(fi, ascii, std::vector<Key>(),
446                                                               cmsSigners, outputNames[SignEncryptFilesWizard::SignatureCMS],
447                                                               false));
448         }
449         if (!cmsRecipients.empty()) {
450             result.push_back(createSignEncryptTaskForFileInfo(fi, ascii, cmsRecipients,
451                                                               std::vector<Key>(), outputNames[SignEncryptFilesWizard::EncryptedCMS],
452                                                               false));
453         }
454     }
455 
456     return result;
457 }
458 
459 static std::vector< std::shared_ptr<SignEncryptTask> >
createArchiveSignEncryptTasksForFiles(const QStringList & files,const std::shared_ptr<ArchiveDefinition> & ad,bool ascii,const std::vector<Key> & pgpRecipients,const std::vector<Key> & pgpSigners,const std::vector<Key> & cmsRecipients,const std::vector<Key> & cmsSigners,const QMap<int,QString> & outputNames,bool symmetric)460 createArchiveSignEncryptTasksForFiles(const QStringList &files, const std::shared_ptr<ArchiveDefinition> &ad,
461                                       bool ascii, const std::vector<Key> &pgpRecipients,
462                                       const std::vector<Key> &pgpSigners, const std::vector<Key> &cmsRecipients, const std::vector<Key> &cmsSigners,
463                                       const QMap<int, QString> &outputNames, bool symmetric)
464 {
465     std::vector< std::shared_ptr<SignEncryptTask> > result;
466 
467     const bool pgp = !pgpSigners.empty() || !pgpRecipients.empty();
468 
469     const bool cms = !cmsSigners.empty() || !cmsRecipients.empty();
470 
471     result.reserve(pgp + cms);
472 
473     if (pgp || symmetric) {
474         int outKind = 0;
475         if ((!pgpRecipients.empty() || symmetric) && !pgpSigners.empty()) {
476             outKind = SignEncryptFilesWizard::CombinedPGP;
477         } else if (!pgpRecipients.empty() || symmetric) {
478             outKind = SignEncryptFilesWizard::EncryptedPGP;
479         } else {
480             outKind = SignEncryptFilesWizard::SignaturePGP;
481         }
482         result.push_back(createArchiveSignEncryptTaskForFiles(files, ad, true,  ascii, pgpRecipients, pgpSigners, outputNames[outKind], symmetric));
483     }
484     if (cms) {
485         if (!cmsSigners.empty()) {
486             result.push_back(createArchiveSignEncryptTaskForFiles(files, ad, false, ascii,
487                                                                   std::vector<Key>(), cmsSigners, outputNames[SignEncryptFilesWizard::SignatureCMS],
488                                                                   false));
489         }
490         if (!cmsRecipients.empty()) {
491             result.push_back(createArchiveSignEncryptTaskForFiles(files, ad, false, ascii,
492                                                                   cmsRecipients, std::vector<Key>(), outputNames[SignEncryptFilesWizard::EncryptedCMS],
493                                                                   false));
494         }
495     }
496 
497     return result;
498 }
499 
slotWizardOperationPrepared()500 void SignEncryptFilesController::Private::slotWizardOperationPrepared()
501 {
502 
503     try {
504         kleo_assert(wizard);
505         kleo_assert(!files.empty());
506 
507         const bool archive = (wizard->outputNames().value(SignEncryptFilesWizard::Directory).isNull() && files.size() > 1) ||
508                              ((operation & ArchiveMask) == ArchiveForced);
509 
510         const std::vector<Key> recipients = wizard->resolvedRecipients();
511         const std::vector<Key> signers = wizard->resolvedSigners();
512 
513         const FileOperationsPreferences prefs;
514         const bool ascii = prefs.addASCIIArmor();
515 
516         std::vector<Key> pgpRecipients, cmsRecipients, pgpSigners, cmsSigners;
517         Q_FOREACH (const Key &k, recipients) {
518             if (k.protocol() == GpgME::OpenPGP) {
519                 pgpRecipients.push_back(k);
520             } else {
521                 cmsRecipients.push_back(k);
522             }
523         }
524 
525         Q_FOREACH (const Key &k, signers) {
526             if (k.protocol() == GpgME::OpenPGP) {
527                 pgpSigners.push_back(k);
528             } else {
529                 cmsSigners.push_back(k);
530             }
531         }
532 
533         std::vector< std::shared_ptr<SignEncryptTask> > tasks;
534         if (!archive) {
535             tasks.reserve(files.size());
536         }
537 
538         if (archive) {
539             tasks = createArchiveSignEncryptTasksForFiles(files,
540                     getDefaultAd(),
541                     ascii,
542                     pgpRecipients,
543                     pgpSigners,
544                     cmsRecipients,
545                     cmsSigners,
546                     wizard->outputNames(),
547                     wizard->encryptSymmetric());
548 
549         } else {
550             Q_FOREACH (const QString &file, files) {
551                 const std::vector< std::shared_ptr<SignEncryptTask> > created =
552                     createSignEncryptTasksForFileInfo(QFileInfo(file), ascii,
553                             pgpRecipients,
554                             pgpSigners,
555                             cmsRecipients,
556                             cmsSigners,
557                             buildOutputNamesForDir(file, wizard->outputNames()),
558                             wizard->encryptSymmetric());
559                 tasks.insert(tasks.end(), created.begin(), created.end());
560             }
561         }
562 
563         const std::shared_ptr<OverwritePolicy> overwritePolicy(new OverwritePolicy(wizard));
564         Q_FOREACH (const std::shared_ptr<SignEncryptTask> &i, tasks) {
565             i->setOverwritePolicy(overwritePolicy);
566         }
567 
568         kleo_assert(runnable.empty());
569 
570         runnable.swap(tasks);
571 
572         for (const auto &task : std::as_const(runnable)) {
573             q->connectTask(task);
574         }
575 
576         std::shared_ptr<TaskCollection> coll(new TaskCollection);
577 
578         std::vector<std::shared_ptr<Task> > tmp;
579         std::copy(runnable.begin(), runnable.end(), std::back_inserter(tmp));
580         coll->setTasks(tmp);
581         wizard->setTaskCollection(coll);
582 
583         QTimer::singleShot(0, q, SLOT(schedule()));
584 
585     } catch (const Kleo::Exception &e) {
586         reportError(e.error().encodedError(), e.message());
587     } catch (const std::exception &e) {
588         reportError(gpg_error(GPG_ERR_UNEXPECTED),
589                     i18n("Caught unexpected exception in SignEncryptFilesController::Private::slotWizardOperationPrepared: %1",
590                          QString::fromLocal8Bit(e.what())));
591     } catch (...) {
592         reportError(gpg_error(GPG_ERR_UNEXPECTED),
593                     i18n("Caught unknown exception in SignEncryptFilesController::Private::slotWizardOperationPrepared"));
594     }
595 }
596 
schedule()597 void SignEncryptFilesController::Private::schedule()
598 {
599 
600     if (!cms)
601         if (const std::shared_ptr<SignEncryptTask> t = takeRunnable(CMS)) {
602             t->start();
603             cms = t;
604         }
605 
606     if (!openpgp)
607         if (const std::shared_ptr<SignEncryptTask> t = takeRunnable(OpenPGP)) {
608             t->start();
609             openpgp = t;
610         }
611 
612     if (!cms && !openpgp) {
613         kleo_assert(runnable.empty());
614         q->emitDoneOrError();
615     }
616 }
617 
takeRunnable(GpgME::Protocol proto)618 std::shared_ptr<SignEncryptTask> SignEncryptFilesController::Private::takeRunnable(GpgME::Protocol proto)
619 {
620     const auto it = std::find_if(runnable.begin(), runnable.end(),
621                                  [proto](const std::shared_ptr<Task> &task) { return task->protocol() == proto; });
622     if (it == runnable.end()) {
623         return std::shared_ptr<SignEncryptTask>();
624     }
625 
626     const std::shared_ptr<SignEncryptTask> result = *it;
627     runnable.erase(it);
628     return result;
629 }
630 
doTaskDone(const Task * task,const std::shared_ptr<const Task::Result> & result)631 void SignEncryptFilesController::doTaskDone(const Task *task, const std::shared_ptr<const Task::Result> &result)
632 {
633     Q_UNUSED(result)
634     Q_ASSERT(task);
635 
636     // We could just delete the tasks here, but we can't use
637     // Qt::QueuedConnection here (we need sender()) and other slots
638     // might not yet have executed. Therefore, we push completed tasks
639     // into a burial container
640 
641     if (task == d->cms.get()) {
642         d->completed.push_back(d->cms);
643         d->cms.reset();
644     } else if (task == d->openpgp.get()) {
645         d->completed.push_back(d->openpgp);
646         d->openpgp.reset();
647     }
648 
649     QTimer::singleShot(0, this, SLOT(schedule()));
650 }
651 
cancel()652 void SignEncryptFilesController::cancel()
653 {
654     qCDebug(KLEOPATRA_LOG);
655     try {
656         if (d->wizard) {
657             d->wizard->close();
658         }
659         d->cancelAllTasks();
660     } catch (const std::exception &e) {
661         qCDebug(KLEOPATRA_LOG) << "Caught exception: " << e.what();
662     }
663 }
664 
cancelAllTasks()665 void SignEncryptFilesController::Private::cancelAllTasks()
666 {
667 
668     // we just kill all runnable tasks - this will not result in
669     // signal emissions.
670     runnable.clear();
671 
672     // a cancel() will result in a call to
673     if (cms) {
674         cms->cancel();
675     }
676     if (openpgp) {
677         openpgp->cancel();
678     }
679 }
680 
ensureWizardCreated()681 void SignEncryptFilesController::Private::ensureWizardCreated()
682 {
683     if (wizard) {
684         return;
685     }
686 
687     std::unique_ptr<SignEncryptFilesWizard> w(new SignEncryptFilesWizard);
688     w->setAttribute(Qt::WA_DeleteOnClose);
689 
690     connect(w.get(), SIGNAL(operationPrepared()), q, SLOT(slotWizardOperationPrepared()), Qt::QueuedConnection);
691     connect(w.get(), SIGNAL(rejected()), q, SLOT(slotWizardCanceled()), Qt::QueuedConnection);
692     wizard = w.release();
693 
694     updateWizardMode();
695 }
696 
ensureWizardVisible()697 void SignEncryptFilesController::Private::ensureWizardVisible()
698 {
699     ensureWizardCreated();
700     q->bringToForeground(wizard);
701 }
702 
703 #include "moc_signencryptfilescontroller.cpp"
704