File indexing completed on 2024-06-16 04:55:58

0001 /* -*- mode: c++; c-basic-offset:4 -*-
0002     crypto/signemailcontroller.cpp
0003 
0004     This file is part of Kleopatra, the KDE keymanager
0005     SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 
0010 #include <config-kleopatra.h>
0011 
0012 #include "signemailcontroller.h"
0013 
0014 #include "certificateresolver.h"
0015 #include "kleopatra_debug.h"
0016 #include "signemailtask.h"
0017 #include "taskcollection.h"
0018 
0019 #include <crypto/gui/signemailwizard.h>
0020 
0021 #include <utils/input.h>
0022 #include <utils/kleo_assert.h>
0023 #include <utils/output.h>
0024 
0025 #include <Libkleo/Stl_Util>
0026 
0027 #include <KLocalizedString>
0028 
0029 #include <QPointer>
0030 #include <QTimer>
0031 
0032 using namespace Kleo;
0033 using namespace Kleo::Crypto;
0034 using namespace Kleo::Crypto::Gui;
0035 using namespace GpgME;
0036 using namespace KMime::Types;
0037 
0038 class SignEMailController::Private
0039 {
0040     friend class ::Kleo::Crypto::SignEMailController;
0041     SignEMailController *const q;
0042 
0043 public:
0044     explicit Private(Mode m, SignEMailController *qq);
0045     ~Private();
0046 
0047 private:
0048     void slotWizardSignersResolved();
0049     void slotWizardCanceled(); // ### extract to base
0050 
0051 private:
0052     void ensureWizardCreated(); // ### extract to base
0053     void ensureWizardVisible(); // ### extract to base
0054     void cancelAllJobs(); // ### extract to base
0055 
0056     void schedule(); // ### extract to base
0057     std::shared_ptr<SignEMailTask> takeRunnable(GpgME::Protocol proto); // ### extract to base
0058 
0059 private:
0060     const Mode mode;
0061     std::vector<std::shared_ptr<SignEMailTask>> runnable, completed; // ### extract to base
0062     std::shared_ptr<SignEMailTask> cms, openpgp; // ### extract to base
0063     QPointer<SignEMailWizard> wizard; // ### extract to base
0064     Protocol protocol; // ### extract to base
0065     bool detached : 1;
0066 };
0067 
0068 SignEMailController::Private::Private(Mode m, SignEMailController *qq)
0069     : q(qq)
0070     , mode(m)
0071     , runnable()
0072     , cms()
0073     , openpgp()
0074     , wizard()
0075     , protocol(UnknownProtocol)
0076     , detached(false)
0077 {
0078 }
0079 
0080 SignEMailController::Private::~Private()
0081 {
0082 }
0083 
0084 SignEMailController::SignEMailController(Mode mode, QObject *p)
0085     : Controller(p)
0086     , d(new Private(mode, this))
0087 {
0088 }
0089 
0090 SignEMailController::SignEMailController(const std::shared_ptr<ExecutionContext> &xc, Mode mode, QObject *p)
0091     : Controller(xc, p)
0092     , d(new Private(mode, this))
0093 {
0094 }
0095 
0096 SignEMailController::~SignEMailController()
0097 {
0098     /// ### extract to base
0099     if (d->wizard && !d->wizard->isVisible()) {
0100         delete d->wizard;
0101     }
0102 }
0103 
0104 SignEMailController::Mode SignEMailController::mode() const
0105 {
0106     return d->mode;
0107 }
0108 
0109 // ### extract to base
0110 void SignEMailController::setProtocol(Protocol proto)
0111 {
0112     kleo_assert(d->protocol == UnknownProtocol || d->protocol == proto);
0113     d->protocol = proto;
0114     d->ensureWizardCreated();
0115     d->wizard->setPresetProtocol(proto);
0116 }
0117 
0118 Protocol SignEMailController::protocol() const
0119 {
0120     return d->protocol;
0121 }
0122 
0123 void SignEMailController::startResolveSigners()
0124 {
0125     d->ensureWizardCreated();
0126 
0127     d->wizard->setSignersAndCandidates({}, {});
0128 
0129     d->ensureWizardVisible();
0130 }
0131 
0132 void SignEMailController::setDetachedSignature(bool detached)
0133 {
0134     kleo_assert(!d->openpgp);
0135     kleo_assert(!d->cms);
0136     kleo_assert(d->completed.empty());
0137     kleo_assert(d->runnable.empty());
0138 
0139     d->detached = detached;
0140 }
0141 
0142 void SignEMailController::Private::slotWizardSignersResolved()
0143 {
0144     Q_EMIT q->signersResolved();
0145 }
0146 
0147 // ### extract to base
0148 void SignEMailController::Private::slotWizardCanceled()
0149 {
0150     q->setLastError(gpg_error(GPG_ERR_CANCELED), i18n("User cancel"));
0151     q->emitDoneOrError();
0152 }
0153 
0154 void SignEMailController::setInputAndOutput(const std::shared_ptr<Input> &input, const std::shared_ptr<Output> &output)
0155 {
0156     setInputsAndOutputs(std::vector<std::shared_ptr<Input>>(1, input), std::vector<std::shared_ptr<Output>>(1, output));
0157 }
0158 
0159 // ### extract to base
0160 void SignEMailController::setInputsAndOutputs(const std::vector<std::shared_ptr<Input>> &inputs, const std::vector<std::shared_ptr<Output>> &outputs)
0161 {
0162     kleo_assert(!inputs.empty());
0163     kleo_assert(!outputs.empty());
0164 
0165     std::vector<std::shared_ptr<SignEMailTask>> tasks;
0166     tasks.reserve(inputs.size());
0167 
0168     d->ensureWizardCreated();
0169 
0170     const std::vector<Key> keys = d->wizard->resolvedSigners();
0171     kleo_assert(!keys.empty());
0172 
0173     for (unsigned int i = 0, end = inputs.size(); i < end; ++i) {
0174         const std::shared_ptr<SignEMailTask> task(new SignEMailTask);
0175         task->setInput(inputs[i]);
0176         task->setOutput(outputs[i]);
0177         task->setSigners(keys);
0178         task->setDetachedSignature(d->detached);
0179         if (d->mode == ClipboardMode) {
0180             if (d->protocol == OpenPGP) {
0181                 task->setClearsign(true);
0182             } else {
0183                 task->setAsciiArmor(true);
0184             }
0185         }
0186 
0187         tasks.push_back(task);
0188     }
0189 
0190     d->runnable.swap(tasks);
0191 }
0192 
0193 // ### extract to base
0194 void SignEMailController::start()
0195 {
0196     std::shared_ptr<TaskCollection> coll(new TaskCollection);
0197     std::vector<std::shared_ptr<Task>> tmp;
0198     std::copy(d->runnable.begin(), d->runnable.end(), std::back_inserter(tmp));
0199     coll->setTasks(tmp);
0200     d->ensureWizardCreated();
0201     d->wizard->setTaskCollection(coll);
0202     for (const std::shared_ptr<Task> &t : std::as_const(tmp)) {
0203         connectTask(t);
0204     }
0205 
0206     d->schedule();
0207 }
0208 
0209 // ### extract to base
0210 void SignEMailController::Private::schedule()
0211 {
0212     if (!cms)
0213         if (const std::shared_ptr<SignEMailTask> t = takeRunnable(CMS)) {
0214             t->start();
0215             cms = t;
0216         }
0217 
0218     if (!openpgp)
0219         if (const std::shared_ptr<SignEMailTask> t = takeRunnable(OpenPGP)) {
0220             t->start();
0221             openpgp = t;
0222         }
0223 
0224     if (!cms && !openpgp) {
0225         kleo_assert(runnable.empty());
0226         QPointer<QObject> Q = q;
0227         for (const std::shared_ptr<SignEMailTask> &t : completed) {
0228             Q_EMIT q->reportMicAlg(t->micAlg());
0229             if (!Q) {
0230                 return;
0231             }
0232         }
0233         q->emitDoneOrError();
0234     }
0235 }
0236 
0237 // ### extract to base
0238 std::shared_ptr<SignEMailTask> SignEMailController::Private::takeRunnable(GpgME::Protocol proto)
0239 {
0240     const auto it = std::find_if(runnable.begin(), runnable.end(), [proto](const std::shared_ptr<Task> &task) {
0241         return task->protocol() == proto;
0242     });
0243     if (it == runnable.end()) {
0244         return std::shared_ptr<SignEMailTask>();
0245     }
0246 
0247     const std::shared_ptr<SignEMailTask> result = *it;
0248     runnable.erase(it);
0249     return result;
0250 }
0251 
0252 // ### extract to base
0253 void SignEMailController::doTaskDone(const Task *task, const std::shared_ptr<const Task::Result> &result)
0254 {
0255     Q_UNUSED(result)
0256     Q_ASSERT(task);
0257 
0258     // We could just delete the tasks here, but we can't use
0259     // Qt::QueuedConnection here (we need sender()) and other slots
0260     // might not yet have executed. Therefore, we push completed tasks
0261     // into a burial container
0262 
0263     if (task == d->cms.get()) {
0264         d->completed.push_back(d->cms);
0265         d->cms.reset();
0266     } else if (task == d->openpgp.get()) {
0267         d->completed.push_back(d->openpgp);
0268         d->openpgp.reset();
0269     }
0270 
0271     QTimer::singleShot(0, this, SLOT(schedule()));
0272 }
0273 
0274 // ### extract to base
0275 void SignEMailController::cancel()
0276 {
0277     try {
0278         if (d->wizard) {
0279             d->wizard->close();
0280         }
0281         d->cancelAllJobs();
0282     } catch (const std::exception &e) {
0283         qCDebug(KLEOPATRA_LOG) << "Caught exception: " << e.what();
0284     }
0285 }
0286 
0287 // ### extract to base
0288 void SignEMailController::Private::cancelAllJobs()
0289 {
0290     // we just kill all runnable tasks - this will not result in
0291     // signal emissions.
0292     runnable.clear();
0293 
0294     // a cancel() will result in a call to
0295     if (cms) {
0296         cms->cancel();
0297     }
0298     if (openpgp) {
0299         openpgp->cancel();
0300     }
0301 }
0302 
0303 // ### extract to base
0304 void SignEMailController::Private::ensureWizardCreated()
0305 {
0306     if (wizard) {
0307         return;
0308     }
0309 
0310     std::unique_ptr<SignEMailWizard> w(new SignEMailWizard);
0311     w->setAttribute(Qt::WA_DeleteOnClose);
0312     connect(w.get(), SIGNAL(signersResolved()), q, SLOT(slotWizardSignersResolved()), Qt::QueuedConnection);
0313     connect(w.get(), SIGNAL(canceled()), q, SLOT(slotWizardCanceled()), Qt::QueuedConnection);
0314     w->setPresetProtocol(protocol);
0315     wizard = w.release();
0316 }
0317 
0318 // ### extract to base
0319 void SignEMailController::Private::ensureWizardVisible()
0320 {
0321     ensureWizardCreated();
0322     q->bringToForeground(wizard);
0323 }
0324 
0325 #include "moc_signemailcontroller.cpp"