1 /* -*- mode: c++; c-basic-offset:4 -*-
2     crypto/signemailcontroller.cpp
3 
4     This file is part of Kleopatra, the KDE keymanager
5     SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB
6 
7     SPDX-License-Identifier: GPL-2.0-or-later
8 */
9 
10 #include <config-kleopatra.h>
11 #include "signemailcontroller.h"
12 #include "kleopatra_debug.h"
13 
14 #include "signemailtask.h"
15 #include "certificateresolver.h"
16 #include "taskcollection.h"
17 
18 #include <crypto/gui/signemailwizard.h>
19 
20 #include <utils/input.h>
21 #include <utils/output.h>
22 #include <utils/kleo_assert.h>
23 
24 #include "emailoperationspreferences.h"
25 
26 #include <Libkleo/Stl_Util>
27 
28 #include <KMime/HeaderParsing>
29 
30 #include <KLocalizedString>
31 
32 #include <QPointer>
33 #include <QTimer>
34 
35 using namespace Kleo;
36 using namespace Kleo::Crypto;
37 using namespace Kleo::Crypto::Gui;
38 using namespace GpgME;
39 using namespace KMime::Types;
40 
41 class SignEMailController::Private
42 {
43     friend class ::Kleo::Crypto::SignEMailController;
44     SignEMailController *const q;
45 public:
46     explicit Private(Mode m, SignEMailController *qq);
47     ~Private();
48 
49 private:
50     void slotWizardSignersResolved();
51     void slotWizardCanceled(); // ### extract to base
52 
53 private:
54     void ensureWizardCreated(); // ### extract to base
55     void ensureWizardVisible(); // ### extract to base
56     void cancelAllJobs();       // ### extract to base
57 
58     void schedule();            // ### extract to base
59     std::shared_ptr<SignEMailTask> takeRunnable(GpgME::Protocol proto);   // ### extract to base
60 
61 private:
62     const Mode mode;
63     std::vector< std::shared_ptr<SignEMailTask> > runnable, completed; // ### extract to base
64     std::shared_ptr<SignEMailTask> cms, openpgp; // ### extract to base
65     QPointer<SignEMailWizard> wizard; // ### extract to base
66     Protocol protocol;                  // ### extract to base
67     bool detached : 1;
68 };
69 
Private(Mode m,SignEMailController * qq)70 SignEMailController::Private::Private(Mode m, SignEMailController *qq)
71     : q(qq),
72       mode(m),
73       runnable(),
74       cms(),
75       openpgp(),
76       wizard(),
77       protocol(UnknownProtocol),
78       detached(false)
79 {
80 
81 }
82 
~Private()83 SignEMailController::Private::~Private() {}
84 
SignEMailController(Mode mode,QObject * p)85 SignEMailController::SignEMailController(Mode mode, QObject *p)
86     : Controller(p), d(new Private(mode, this))
87 {
88 
89 }
90 
SignEMailController(const std::shared_ptr<ExecutionContext> & xc,Mode mode,QObject * p)91 SignEMailController::SignEMailController(const std::shared_ptr<ExecutionContext> &xc, Mode mode, QObject *p)
92     : Controller(xc, p), d(new Private(mode, this))
93 {
94 
95 }
96 
~SignEMailController()97 SignEMailController::~SignEMailController()
98 {
99     /// ### extract to base
100     if (d->wizard && !d->wizard->isVisible()) {
101         delete d->wizard;
102     }
103     //d->wizard->close(); ### ?
104 }
105 
mode() const106 SignEMailController::Mode SignEMailController::mode() const
107 {
108     return d->mode;
109 }
110 
111 // ### extract to base
setProtocol(Protocol proto)112 void SignEMailController::setProtocol(Protocol proto)
113 {
114     kleo_assert(d->protocol == UnknownProtocol ||
115                 d->protocol == proto);
116     d->protocol = proto;
117     d->ensureWizardCreated();
118     d->wizard->setPresetProtocol(proto);
119 }
120 
protocol() const121 Protocol SignEMailController::protocol() const
122 {
123     return d->protocol;
124 }
125 
startResolveSigners()126 void SignEMailController::startResolveSigners()
127 {
128     startResolveSigners(std::vector<Mailbox>());
129 }
130 
startResolveSigners(const std::vector<Mailbox> & signers)131 void SignEMailController::startResolveSigners(const std::vector<Mailbox> &signers)
132 {
133     const std::vector< std::vector<Key> > keys = CertificateResolver::resolveSigners(signers, d->protocol);
134 
135     if (!signers.empty()) {
136         kleo_assert(keys.size() == static_cast<size_t>(signers.size()));
137     }
138 
139     d->ensureWizardCreated();
140 
141     d->wizard->setSignersAndCandidates(signers, keys);
142 
143     d->ensureWizardVisible();
144 }
145 
setDetachedSignature(bool detached)146 void SignEMailController::setDetachedSignature(bool detached)
147 {
148     kleo_assert(!d->openpgp);
149     kleo_assert(!d->cms);
150     kleo_assert(d->completed.empty());
151     kleo_assert(d->runnable.empty());
152 
153     d->detached = detached;
154 }
155 
slotWizardSignersResolved()156 void SignEMailController::Private::slotWizardSignersResolved()
157 {
158     Q_EMIT q->signersResolved();
159 }
160 
161 // ### extract to base
slotWizardCanceled()162 void SignEMailController::Private::slotWizardCanceled()
163 {
164     q->setLastError(gpg_error(GPG_ERR_CANCELED), i18n("User cancel"));
165     q->emitDoneOrError();
166 }
167 
setInputAndOutput(const std::shared_ptr<Input> & input,const std::shared_ptr<Output> & output)168 void SignEMailController::setInputAndOutput(const std::shared_ptr<Input> &input, const std::shared_ptr<Output> &output)
169 {
170     setInputsAndOutputs(std::vector< std::shared_ptr<Input> >(1, input), std::vector< std::shared_ptr<Output> >(1, output));
171 }
172 
173 // ### extract to base
setInputsAndOutputs(const std::vector<std::shared_ptr<Input>> & inputs,const std::vector<std::shared_ptr<Output>> & outputs)174 void SignEMailController::setInputsAndOutputs(const std::vector< std::shared_ptr<Input> > &inputs, const std::vector< std::shared_ptr<Output> > &outputs)
175 {
176     kleo_assert(!inputs.empty());
177     kleo_assert(!outputs.empty());
178 
179     std::vector< std::shared_ptr<SignEMailTask> > tasks;
180     tasks.reserve(inputs.size());
181 
182     d->ensureWizardCreated();
183 
184     const std::vector<Key> keys = d->wizard->resolvedSigners();
185     kleo_assert(!keys.empty());
186 
187     for (unsigned int i = 0, end = inputs.size(); i < end; ++i) {
188 
189         const std::shared_ptr<SignEMailTask> task(new SignEMailTask);
190         task->setInput(inputs[i]);
191         task->setOutput(outputs[i]);
192         task->setSigners(keys);
193         task->setDetachedSignature(d->detached);
194         if (d->mode == ClipboardMode) {
195             if (d->protocol == OpenPGP) {
196                 task->setClearsign(true);
197             } else {
198                 task->setAsciiArmor(true);
199             }
200         }
201 
202         tasks.push_back(task);
203     }
204 
205     d->runnable.swap(tasks);
206 }
207 
208 // ### extract to base
start()209 void SignEMailController::start()
210 {
211     std::shared_ptr<TaskCollection> coll(new TaskCollection);
212     std::vector<std::shared_ptr<Task> > tmp;
213     std::copy(d->runnable.begin(), d->runnable.end(), std::back_inserter(tmp));
214     coll->setTasks(tmp);
215     d->ensureWizardCreated();
216     d->wizard->setTaskCollection(coll);
217     for (const std::shared_ptr<Task> &t : std::as_const(tmp)) {
218         connectTask(t);
219     }
220 
221     d->schedule();
222 }
223 
224 // ### extract to base
schedule()225 void SignEMailController::Private::schedule()
226 {
227 
228     if (!cms)
229         if (const std::shared_ptr<SignEMailTask> t = takeRunnable(CMS)) {
230             t->start();
231             cms = t;
232         }
233 
234     if (!openpgp)
235         if (const std::shared_ptr<SignEMailTask> t = takeRunnable(OpenPGP)) {
236             t->start();
237             openpgp = t;
238         }
239 
240     if (!cms && !openpgp) {
241         kleo_assert(runnable.empty());
242         QPointer<QObject> Q = q;
243         Q_FOREACH (const std::shared_ptr<SignEMailTask> t, completed) {
244             Q_EMIT q->reportMicAlg(t->micAlg());
245             if (!Q) {
246                 return;
247             }
248         }
249         q->emitDoneOrError();
250     }
251 }
252 
253 // ### extract to base
takeRunnable(GpgME::Protocol proto)254 std::shared_ptr<SignEMailTask> SignEMailController::Private::takeRunnable(GpgME::Protocol proto)
255 {
256     const auto it = std::find_if(runnable.begin(), runnable.end(),
257                                  [proto](const std::shared_ptr<Task> &task) { return task->protocol() == proto; });
258     if (it == runnable.end()) {
259         return std::shared_ptr<SignEMailTask>();
260     }
261 
262     const std::shared_ptr<SignEMailTask> result = *it;
263     runnable.erase(it);
264     return result;
265 }
266 
267 // ### extract to base
doTaskDone(const Task * task,const std::shared_ptr<const Task::Result> & result)268 void SignEMailController::doTaskDone(const Task *task, const std::shared_ptr<const Task::Result> &result)
269 {
270     Q_UNUSED(result)
271     Q_ASSERT(task);
272 
273     // We could just delete the tasks here, but we can't use
274     // Qt::QueuedConnection here (we need sender()) and other slots
275     // might not yet have executed. Therefore, we push completed tasks
276     // into a burial container
277 
278     if (task == d->cms.get()) {
279         d->completed.push_back(d->cms);
280         d->cms.reset();
281     } else if (task == d->openpgp.get()) {
282         d->completed.push_back(d->openpgp);
283         d->openpgp.reset();
284     }
285 
286     QTimer::singleShot(0, this, SLOT(schedule()));
287 }
288 
289 // ### extract to base
cancel()290 void SignEMailController::cancel()
291 {
292     try {
293         if (d->wizard) {
294             d->wizard->close();
295         }
296         d->cancelAllJobs();
297     } catch (const std::exception &e) {
298         qCDebug(KLEOPATRA_LOG) << "Caught exception: " << e.what();
299     }
300 }
301 
302 // ### extract to base
cancelAllJobs()303 void SignEMailController::Private::cancelAllJobs()
304 {
305 
306     // we just kill all runnable tasks - this will not result in
307     // signal emissions.
308     runnable.clear();
309 
310     // a cancel() will result in a call to
311     if (cms) {
312         cms->cancel();
313     }
314     if (openpgp) {
315         openpgp->cancel();
316     }
317 }
318 
319 // ### extract to base
ensureWizardCreated()320 void SignEMailController::Private::ensureWizardCreated()
321 {
322     if (wizard) {
323         return;
324     }
325 
326     std::unique_ptr<SignEMailWizard> w(new SignEMailWizard);
327     w->setAttribute(Qt::WA_DeleteOnClose);
328     connect(w.get(), SIGNAL(signersResolved()), q, SLOT(slotWizardSignersResolved()), Qt::QueuedConnection);
329     connect(w.get(), SIGNAL(canceled()), q, SLOT(slotWizardCanceled()), Qt::QueuedConnection);
330     w->setPresetProtocol(protocol);
331     EMailOperationsPreferences prefs;
332     w->setQuickMode(prefs.quickSignEMail());
333     wizard = w.release();
334 }
335 
336 // ### extract to base
ensureWizardVisible()337 void SignEMailController::Private::ensureWizardVisible()
338 {
339     ensureWizardCreated();
340     q->bringToForeground(wizard);
341 }
342 
343 #include "moc_signemailcontroller.cpp"
344