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