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