1 /* -*- mode: c++; c-basic-offset:4 -*-
2     crypto/newsignencryptemailcontroller.cpp
3 
4     This file is part of Kleopatra, the KDE keymanager
5     SPDX-FileCopyrightText: 2009, 2010 Klarälvdalens Datakonsult AB
6 
7     SPDX-License-Identifier: GPL-2.0-or-later
8 */
9 
10 #include <config-kleopatra.h>
11 
12 #include "newsignencryptemailcontroller.h"
13 #include "kleopatra_debug.h"
14 #include "encryptemailtask.h"
15 #include "signemailtask.h"
16 #include "taskcollection.h"
17 #include "sender.h"
18 #include "recipient.h"
19 
20 #include "emailoperationspreferences.h"
21 
22 #include <crypto/gui/signencryptemailconflictdialog.h>
23 
24 #include "utils/input.h"
25 #include "utils/output.h"
26 #include <Libkleo/GnuPG>
27 #include "utils/kleo_assert.h"
28 
29 #include <Libkleo/Stl_Util>
30 #include <Libkleo/KleoException>
31 
32 #include <gpgme++/key.h>
33 
34 #include <KMime/HeaderParsing>
35 
36 #include <KLocalizedString>
37 
38 #include <KMessageBox>
39 
40 #include <QPointer>
41 #include <QTimer>
42 
43 using namespace Kleo;
44 using namespace Kleo::Crypto;
45 using namespace Kleo::Crypto::Gui;
46 using namespace GpgME;
47 using namespace KMime::Types;
48 
49 //
50 // BEGIN Conflict Detection
51 //
52 
53 /*
54   This code implements the following conflict detection algorithm:
55 
56   1. There is no conflict if and only if we have a Perfect Match.
57   2. A Perfect Match is defined as:
58     a. either a Perfect OpenPGP-Match and not even a Partial S/MIME Match
59     b. or a Perfect S/MIME-Match and not even a Partial OpenPGP-Match
60     c. or a Perfect OpenPGP-Match and preselected protocol=OpenPGP
61     d. or a Perfect S/MIME-Match and preselected protocol=S/MIME
62   3. For Protocol \in {OpenPGP,S/MIME}, a Perfect Protocol-Match is defined as:
63     a. If signing, \foreach Sender, there is exactly one
64          Matching Protocol-Certificate with
65      i. can-sign=true
66      ii. has-secret=true
67     b. and, if encrypting, \foreach Recipient, there is exactly one
68          Matching Protocol-Certificate with
69      i. can-encrypt=true
70      ii. (validity is not considered, cf. msg 24059)
71   4. For Protocol \in {OpenPGP,S/MIME}, a Partial Protocol-Match is defined as:
72     a. If signing, \foreach Sender, there is at least one
73          Matching Protocol-Certificate with
74      i. can-sign=true
75      ii. has-secret=true
76     b. and, if encrypting, \foreach Recipient, there is at least
77      one Matching Protocol-Certificate with
78      i. can-encrypt=true
79      ii. (validity is not considered, cf. msg 24059)
80   5. For Protocol \in {OpenPGP,S/MIME}, a Matching Protocol-Certificate is
81      defined as matching by email-address. A revoked, disabled, or expired
82      certificate is not considered a match.
83   6. Sender is defined as those mailboxes that have been set with the SENDER
84      command.
85   7. Recipient is defined as those mailboxes that have been set with either the
86      SENDER or the RECIPIENT commands.
87 */
88 
89 namespace
90 {
91 
count_signing_certificates(Protocol proto,const Sender & sender)92 static size_t count_signing_certificates(Protocol proto, const Sender &sender)
93 {
94     const size_t result = sender.signingCertificateCandidates(proto).size();
95     qDebug("count_signing_certificates( %9s %20s ) == %2lu",
96             proto == OpenPGP ? "OpenPGP," : proto == CMS ? "CMS," : "<unknown>,",
97             qPrintable(sender.mailbox().prettyAddress()), result);
98     return result;
99 }
100 
count_encrypt_certificates(Protocol proto,const Sender & sender)101 static size_t count_encrypt_certificates(Protocol proto, const Sender &sender)
102 {
103     const size_t result = sender.encryptToSelfCertificateCandidates(proto).size();
104     qDebug("count_encrypt_certificates( %9s %20s ) == %2lu",
105             proto == OpenPGP ? "OpenPGP," : proto == CMS ? "CMS," : "<unknown>,",
106             qPrintable(sender.mailbox().prettyAddress()), result);
107     return result;
108 }
109 
count_encrypt_certificates(Protocol proto,const Recipient & recipient)110 static size_t count_encrypt_certificates(Protocol proto, const Recipient &recipient)
111 {        const size_t result = recipient.encryptionCertificateCandidates(proto).size();
112     qDebug("count_encrypt_certificates( %9s %20s ) == %2lu",
113             proto == OpenPGP ? "OpenPGP," : proto == CMS ? "CMS," : "<unknown>,",
114             qPrintable(recipient.mailbox().prettyAddress()), result);
115     return result;
116 }
117 
118 }
119 
has_perfect_match(bool sign,bool encrypt,Protocol proto,const std::vector<Sender> & senders,const std::vector<Recipient> & recipients)120 static bool has_perfect_match(bool sign, bool encrypt, Protocol proto, const std::vector<Sender> &senders, const std::vector<Recipient> &recipients)
121 {
122     if (sign)
123         if (!std::all_of(senders.cbegin(), senders.cend(),
124                          [proto](const Sender &sender) { return count_signing_certificates(proto, sender) == 1; })) {
125             return false;
126         }
127     if (encrypt)
128         if (!std::all_of(senders.cbegin(), senders.cend(),
129                          [proto](const Sender &sender) { return count_encrypt_certificates(proto, sender) == 1; })
130             || !std::all_of(recipients.cbegin(), recipients.cend(),
131                            [proto](const Recipient &rec) { return count_encrypt_certificates(proto, rec) == 1; })) {
132             return false;
133         }
134     return true;
135 }
136 
has_partial_match(bool sign,bool encrypt,Protocol proto,const std::vector<Sender> & senders,const std::vector<Recipient> & recipients)137 static bool has_partial_match(bool sign, bool encrypt, Protocol proto, const std::vector<Sender> &senders, const std::vector<Recipient> &recipients)
138 {
139     if (sign)
140         if (std::all_of(senders.cbegin(), senders.cend(),
141                         [proto](const Sender &sender) { return count_signing_certificates(proto, sender) >= 1; })) {
142             return false;
143         }
144     if (encrypt)
145         if (!std::all_of(senders.cbegin(), senders.cend(),
146                          [proto](const Sender &sender) { return count_encrypt_certificates(proto, sender) >= 1; })
147             || !std::all_of(recipients.cbegin(), recipients.cend(),
148                             [proto](const Recipient &rec) { return count_encrypt_certificates(proto, rec) >= 1; })) {
149             return false;
150         }
151     return true;
152 }
153 
has_perfect_overall_match(bool sign,bool encrypt,const std::vector<Sender> & senders,const std::vector<Recipient> & recipients,Protocol presetProtocol)154 static bool has_perfect_overall_match(bool sign, bool encrypt, const std::vector<Sender> &senders, const std::vector<Recipient> &recipients, Protocol presetProtocol)
155 {
156     return (presetProtocol == OpenPGP   &&   has_perfect_match(sign, encrypt, OpenPGP, senders, recipients))
157            || (presetProtocol == CMS    &&   has_perfect_match(sign, encrypt, CMS,     senders, recipients))
158            || (has_perfect_match(sign, encrypt, OpenPGP, senders, recipients)   &&   !has_partial_match(sign, encrypt, CMS,     senders, recipients))
159            || (has_perfect_match(sign, encrypt, CMS,     senders, recipients)   &&   !has_partial_match(sign, encrypt, OpenPGP, senders, recipients));
160 }
161 
has_conflict(bool sign,bool encrypt,const std::vector<Sender> & senders,const std::vector<Recipient> & recipients,Protocol presetProtocol)162 static bool has_conflict(bool sign, bool encrypt, const std::vector<Sender> &senders, const std::vector<Recipient> &recipients, Protocol presetProtocol)
163 {
164     return !has_perfect_overall_match(sign, encrypt, senders, recipients, presetProtocol);
165 }
166 
is_de_vs_compliant(bool sign,bool encrypt,const std::vector<Sender> & senders,const std::vector<Recipient> & recipients,Protocol presetProtocol)167 static bool is_de_vs_compliant(bool sign, bool encrypt, const std::vector<Sender> &senders, const std::vector<Recipient> &recipients, Protocol presetProtocol)
168 {
169     if (presetProtocol == Protocol::UnknownProtocol) {
170         return false;
171     }
172     if (sign) {
173         for (const auto &sender: senders) {
174             const auto &key = sender.resolvedSigningKey(presetProtocol);
175             if (!IS_DE_VS(key) || keyValidity(key) < GpgME::UserID::Validity::Full) {
176                 return false;
177             }
178         }
179     }
180 
181     if (encrypt) {
182         for (const auto &sender: senders) {
183             const auto &key = sender.resolvedSigningKey(presetProtocol);
184             if (!IS_DE_VS(key) || keyValidity(key) < GpgME::UserID::Validity::Full) {
185                 return false;
186             }
187         }
188 
189         for (const auto &recipient: recipients) {
190             const auto &key = recipient.resolvedEncryptionKey(presetProtocol);
191             if (!IS_DE_VS(key) || keyValidity(key) < GpgME::UserID::Validity::Full) {
192                 return false;
193             }
194         }
195     }
196 
197     return true;
198 }
199 
200 //
201 // END Conflict Detection
202 //
203 
mailbox2sender(const std::vector<Mailbox> & mbs)204 static std::vector<Sender> mailbox2sender(const std::vector<Mailbox> &mbs)
205 {
206     std::vector<Sender> senders;
207     senders.reserve(mbs.size());
208     for (const Mailbox &mb : mbs) {
209         senders.push_back(Sender(mb));
210     }
211     return senders;
212 }
213 
mailbox2recipient(const std::vector<Mailbox> & mbs)214 static std::vector<Recipient> mailbox2recipient(const std::vector<Mailbox> &mbs)
215 {
216     std::vector<Recipient> recipients;
217     recipients.reserve(mbs.size());
218     for (const Mailbox &mb : mbs) {
219         recipients.push_back(Recipient(mb));
220     }
221     return recipients;
222 }
223 
224 class NewSignEncryptEMailController::Private
225 {
226     friend class ::Kleo::Crypto::NewSignEncryptEMailController;
227     NewSignEncryptEMailController *const q;
228 public:
229     explicit Private(NewSignEncryptEMailController *qq);
230     ~Private();
231 
232 private:
233     void slotDialogAccepted();
234     void slotDialogRejected();
235 
236 private:
237     void ensureDialogVisible();
238     void cancelAllTasks();
239 
240     void startSigning();
241     void startEncryption();
242     void schedule();
243     std::shared_ptr<Task> takeRunnable(GpgME::Protocol proto);
244 
245 private:
246     bool sign : 1;
247     bool encrypt : 1;
248     bool resolvingInProgress : 1;
249     bool certificatesResolved : 1;
250     bool detached : 1;
251     Protocol presetProtocol;
252     std::vector<Key> signers, recipients;
253     std::vector< std::shared_ptr<Task> > runnable, completed;
254     std::shared_ptr<Task> cms, openpgp;
255     QPointer<SignEncryptEMailConflictDialog> dialog;
256 };
257 
Private(NewSignEncryptEMailController * qq)258 NewSignEncryptEMailController::Private::Private(NewSignEncryptEMailController *qq)
259     : q(qq),
260       sign(false),
261       encrypt(false),
262       resolvingInProgress(false),
263       certificatesResolved(false),
264       detached(false),
265       presetProtocol(UnknownProtocol),
266       signers(),
267       recipients(),
268       runnable(),
269       cms(),
270       openpgp(),
271       dialog(new SignEncryptEMailConflictDialog)
272 {
273     connect(dialog, SIGNAL(accepted()), q, SLOT(slotDialogAccepted()));
274     connect(dialog, SIGNAL(rejected()), q, SLOT(slotDialogRejected()));
275 }
276 
~Private()277 NewSignEncryptEMailController::Private::~Private()
278 {
279     delete dialog;
280 }
281 
NewSignEncryptEMailController(const std::shared_ptr<ExecutionContext> & xc,QObject * p)282 NewSignEncryptEMailController::NewSignEncryptEMailController(const std::shared_ptr<ExecutionContext> &xc, QObject *p)
283     : Controller(xc, p), d(new Private(this))
284 {
285 
286 }
287 
NewSignEncryptEMailController(QObject * p)288 NewSignEncryptEMailController::NewSignEncryptEMailController(QObject *p)
289     : Controller(p), d(new Private(this))
290 {
291 
292 }
293 
~NewSignEncryptEMailController()294 NewSignEncryptEMailController::~NewSignEncryptEMailController()
295 {
296     qCDebug(KLEOPATRA_LOG);
297 }
298 
setSubject(const QString & subject)299 void NewSignEncryptEMailController::setSubject(const QString &subject)
300 {
301     d->dialog->setSubject(subject);
302 }
303 
setProtocol(Protocol proto)304 void NewSignEncryptEMailController::setProtocol(Protocol proto)
305 {
306     d->presetProtocol = proto;
307     d->dialog->setPresetProtocol(proto);
308 }
309 
protocol() const310 Protocol NewSignEncryptEMailController::protocol() const
311 {
312     return d->dialog->selectedProtocol();
313 }
314 
protocolAsString() const315 const char *NewSignEncryptEMailController::protocolAsString() const
316 {
317     switch (protocol()) {
318     case OpenPGP: return "OpenPGP";
319     case CMS:     return "CMS";
320     default:
321         throw Kleo::Exception(gpg_error(GPG_ERR_INTERNAL),
322                               i18n("Call to NewSignEncryptEMailController::protocolAsString() is ambiguous."));
323     }
324 }
325 
setSigning(bool sign)326 void NewSignEncryptEMailController::setSigning(bool sign)
327 {
328     d->sign = sign;
329     d->dialog->setSign(sign);
330 }
331 
isSigning() const332 bool NewSignEncryptEMailController::isSigning() const
333 {
334     return d->sign;
335 }
336 
setEncrypting(bool encrypt)337 void NewSignEncryptEMailController::setEncrypting(bool encrypt)
338 {
339     d->encrypt = encrypt;
340     d->dialog->setEncrypt(encrypt);
341 }
342 
isEncrypting() const343 bool NewSignEncryptEMailController::isEncrypting() const
344 {
345     return d->encrypt;
346 }
347 
setDetachedSignature(bool detached)348 void NewSignEncryptEMailController::setDetachedSignature(bool detached)
349 {
350     d->detached = detached;
351 }
352 
isResolvingInProgress() const353 bool NewSignEncryptEMailController::isResolvingInProgress() const
354 {
355     return d->resolvingInProgress;
356 }
357 
areCertificatesResolved() const358 bool NewSignEncryptEMailController::areCertificatesResolved() const
359 {
360     return d->certificatesResolved;
361 }
362 
is_dialog_quick_mode(bool sign,bool encrypt)363 static bool is_dialog_quick_mode(bool sign, bool encrypt)
364 {
365     const EMailOperationsPreferences prefs;
366     return (!sign    || prefs.quickSignEMail())
367            && (!encrypt || prefs.quickEncryptEMail())
368            ;
369 }
370 
save_dialog_quick_mode(bool on)371 static void save_dialog_quick_mode(bool on)
372 {
373     EMailOperationsPreferences prefs;
374     prefs.setQuickSignEMail(on);
375     prefs.setQuickEncryptEMail(on);
376     prefs.save();
377 }
378 
startResolveCertificates(const std::vector<Mailbox> & r,const std::vector<Mailbox> & s)379 void NewSignEncryptEMailController::startResolveCertificates(const std::vector<Mailbox> &r, const std::vector<Mailbox> &s)
380 {
381     d->certificatesResolved = false;
382     d->resolvingInProgress = true;
383 
384     const std::vector<Sender> senders = mailbox2sender(s);
385     const std::vector<Recipient> recipients = mailbox2recipient(r);
386     const bool quickMode = is_dialog_quick_mode(d->sign, d->encrypt);
387 
388     const bool conflict = quickMode && has_conflict(d->sign, d->encrypt, senders, recipients, d->presetProtocol);
389 
390     d->dialog->setQuickMode(quickMode);
391     d->dialog->setSenders(senders);
392     d->dialog->setRecipients(recipients);
393     d->dialog->pickProtocol();
394     d->dialog->setConflict(conflict);
395 
396     const bool compliant = !Kleo::gnupgUsesDeVsCompliance() ||
397                            (Kleo::gnupgIsDeVsCompliant() && is_de_vs_compliant(d->sign,
398                                                                                d->encrypt,
399                                                                                senders,
400                                                                                recipients,
401                                                                                d->presetProtocol));
402 
403     if (quickMode && !conflict && compliant) {
404         QMetaObject::invokeMethod(this, "slotDialogAccepted", Qt::QueuedConnection);
405     } else {
406         d->ensureDialogVisible();
407     }
408 }
409 
slotDialogAccepted()410 void NewSignEncryptEMailController::Private::slotDialogAccepted()
411 {
412     if (dialog->isQuickMode() != is_dialog_quick_mode(sign, encrypt)) {
413         save_dialog_quick_mode(dialog->isQuickMode());
414     }
415     resolvingInProgress = false;
416     certificatesResolved = true;
417     signers = dialog->resolvedSigningKeys();
418     recipients = dialog->resolvedEncryptionKeys();
419     QMetaObject::invokeMethod(q, "certificatesResolved", Qt::QueuedConnection);
420 }
421 
slotDialogRejected()422 void NewSignEncryptEMailController::Private::slotDialogRejected()
423 {
424     resolvingInProgress = false;
425     certificatesResolved = false;
426     QMetaObject::invokeMethod(q, "error", Qt::QueuedConnection,
427                               Q_ARG(int, gpg_error(GPG_ERR_CANCELED)),
428                               Q_ARG(QString, i18n("User cancel")));
429 }
430 
startEncryption(const std::vector<std::shared_ptr<Input>> & inputs,const std::vector<std::shared_ptr<Output>> & outputs)431 void NewSignEncryptEMailController::startEncryption(const std::vector< std::shared_ptr<Input> > &inputs, const std::vector< std::shared_ptr<Output> > &outputs)
432 {
433 
434     kleo_assert(d->encrypt);
435     kleo_assert(!d->resolvingInProgress);
436 
437     kleo_assert(!inputs.empty());
438     kleo_assert(outputs.size() == inputs.size());
439 
440     std::vector< std::shared_ptr<Task> > tasks;
441     tasks.reserve(inputs.size());
442 
443     kleo_assert(!d->recipients.empty());
444 
445     for (unsigned int i = 0, end = inputs.size(); i < end; ++i) {
446 
447         const std::shared_ptr<EncryptEMailTask> task(new EncryptEMailTask);
448 
449         task->setInput(inputs[i]);
450         task->setOutput(outputs[i]);
451         task->setRecipients(d->recipients);
452 
453         tasks.push_back(task);
454     }
455 
456     // append to runnable stack
457     d->runnable.insert(d->runnable.end(), tasks.begin(), tasks.end());
458 
459     d->startEncryption();
460 }
461 
startEncryption()462 void NewSignEncryptEMailController::Private::startEncryption()
463 {
464     std::shared_ptr<TaskCollection> coll(new TaskCollection);
465     std::vector<std::shared_ptr<Task> > tmp;
466     tmp.reserve(runnable.size());
467     std::copy(runnable.cbegin(), runnable.cend(), std::back_inserter(tmp));
468     coll->setTasks(tmp);
469 #if 0
470 #warning use a new result dialog
471     // ### use a new result dialog
472     dialog->setTaskCollection(coll);
473 #endif
474     for (const std::shared_ptr<Task> &t : std::as_const(tmp)) {
475         q->connectTask(t);
476     }
477     schedule();
478 }
479 
startSigning(const std::vector<std::shared_ptr<Input>> & inputs,const std::vector<std::shared_ptr<Output>> & outputs)480 void NewSignEncryptEMailController::startSigning(const std::vector< std::shared_ptr<Input> > &inputs, const std::vector< std::shared_ptr<Output> > &outputs)
481 {
482 
483     kleo_assert(d->sign);
484     kleo_assert(!d->resolvingInProgress);
485 
486     kleo_assert(!inputs.empty());
487     kleo_assert(!outputs.empty());
488 
489     std::vector< std::shared_ptr<Task> > tasks;
490     tasks.reserve(inputs.size());
491 
492     kleo_assert(!d->signers.empty());
493     kleo_assert(std::none_of(d->signers.cbegin(), d->signers.cend(), std::mem_fn(&Key::isNull)));
494 
495     for (unsigned int i = 0, end = inputs.size(); i < end; ++i) {
496 
497         const std::shared_ptr<SignEMailTask> task(new SignEMailTask);
498 
499         task->setInput(inputs[i]);
500         task->setOutput(outputs[i]);
501         task->setSigners(d->signers);
502         task->setDetachedSignature(d->detached);
503 
504         tasks.push_back(task);
505     }
506 
507     // append to runnable stack
508     d->runnable.insert(d->runnable.end(), tasks.begin(), tasks.end());
509 
510     d->startSigning();
511 }
512 
startSigning()513 void NewSignEncryptEMailController::Private::startSigning()
514 {
515     std::shared_ptr<TaskCollection> coll(new TaskCollection);
516     std::vector<std::shared_ptr<Task> > tmp;
517     tmp.reserve(runnable.size());
518     std::copy(runnable.cbegin(), runnable.cend(), std::back_inserter(tmp));
519     coll->setTasks(tmp);
520 #if 0
521 #warning use a new result dialog
522     // ### use a new result dialog
523     dialog->setTaskCollection(coll);
524 #endif
525     for (const std::shared_ptr<Task> &t : std::as_const(tmp)) {
526         q->connectTask(t);
527     }
528     schedule();
529 }
530 
schedule()531 void NewSignEncryptEMailController::Private::schedule()
532 {
533 
534     if (!cms)
535         if (const std::shared_ptr<Task> t = takeRunnable(CMS)) {
536             t->start();
537             cms = t;
538         }
539 
540     if (!openpgp)
541         if (const std::shared_ptr<Task> t = takeRunnable(OpenPGP)) {
542             t->start();
543             openpgp = t;
544         }
545 
546     if (cms || openpgp) {
547         return;
548     }
549     kleo_assert(runnable.empty());
550     q->emitDoneOrError();
551 }
552 
takeRunnable(GpgME::Protocol proto)553 std::shared_ptr<Task> NewSignEncryptEMailController::Private::takeRunnable(GpgME::Protocol proto)
554 {
555     const auto it = std::find_if(runnable.begin(), runnable.end(),
556                                  [proto](const std::shared_ptr<Task> &task) { return task->protocol() == proto; });
557     if (it == runnable.end()) {
558         return std::shared_ptr<Task>();
559     }
560 
561     const std::shared_ptr<Task> result = *it;
562     runnable.erase(it);
563     return result;
564 }
565 
doTaskDone(const Task * task,const std::shared_ptr<const Task::Result> & result)566 void NewSignEncryptEMailController::doTaskDone(const Task *task, const std::shared_ptr<const Task::Result> &result)
567 {
568     Q_ASSERT(task);
569 
570     if (result && result->hasError()) {
571         QPointer<QObject> that = this;
572         if (result->details().isEmpty())
573             KMessageBox::        sorry(nullptr,
574                                        result->overview(),
575                                        i18nc("@title:window", "Error"));
576         else
577             KMessageBox::detailedSorry(nullptr,
578                                        result->overview(),
579                                        result->details(),
580                                        i18nc("@title:window", "Error"));
581         if (!that) {
582             return;
583         }
584     }
585 
586     // We could just delete the tasks here, but we can't use
587     // Qt::QueuedConnection here (we need sender()) and other slots
588     // might not yet have executed. Therefore, we push completed tasks
589     // into a burial container
590 
591     if (task == d->cms.get()) {
592         d->completed.push_back(d->cms);
593         d->cms.reset();
594     } else if (task == d->openpgp.get()) {
595         d->completed.push_back(d->openpgp);
596         d->openpgp.reset();
597     }
598 
599     QTimer::singleShot(0, this, SLOT(schedule()));
600 }
601 
cancel()602 void NewSignEncryptEMailController::cancel()
603 {
604     try {
605         d->dialog->close();
606         d->cancelAllTasks();
607     } catch (const std::exception &e) {
608         qCDebug(KLEOPATRA_LOG) << "Caught exception: " << e.what();
609     }
610 }
611 
cancelAllTasks()612 void NewSignEncryptEMailController::Private::cancelAllTasks()
613 {
614 
615     // we just kill all runnable tasks - this will not result in
616     // signal emissions.
617     runnable.clear();
618 
619     // a cancel() will result in a call to
620     if (cms) {
621         cms->cancel();
622     }
623     if (openpgp) {
624         openpgp->cancel();
625     }
626 }
627 
ensureDialogVisible()628 void NewSignEncryptEMailController::Private::ensureDialogVisible()
629 {
630     q->bringToForeground(dialog, true);
631 }
632 
633 #include "moc_newsignencryptemailcontroller.cpp"
634