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

0001 /* -*- mode: c++; c-basic-offset:4 -*-
0002     crypto/encryptemailcontroller.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 "encryptemailcontroller.h"
0013 #include "encryptemailtask.h"
0014 #include "kleopatra_debug.h"
0015 #include "taskcollection.h"
0016 
0017 #include <crypto/gui/encryptemailwizard.h>
0018 
0019 #include <utils/input.h>
0020 #include <utils/kleo_assert.h>
0021 #include <utils/output.h>
0022 
0023 #include <Libkleo/KleoException>
0024 #include <Libkleo/Stl_Util>
0025 
0026 #include <gpgme++/key.h>
0027 
0028 #include <KLocalizedString>
0029 
0030 #include <QPointer>
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 EncryptEMailController::Private
0039 {
0040     friend class ::Kleo::Crypto::EncryptEMailController;
0041     EncryptEMailController *const q;
0042 
0043 public:
0044     explicit Private(Mode mode, EncryptEMailController *qq);
0045 
0046 private:
0047     void slotWizardCanceled();
0048 
0049 private:
0050     void ensureWizardCreated();
0051     void ensureWizardVisible();
0052     void cancelAllTasks();
0053 
0054     void schedule();
0055     std::shared_ptr<EncryptEMailTask> takeRunnable(GpgME::Protocol proto);
0056 
0057 private:
0058     const Mode mode;
0059     std::vector<std::shared_ptr<EncryptEMailTask>> runnable, completed;
0060     std::shared_ptr<EncryptEMailTask> cms, openpgp;
0061     QPointer<EncryptEMailWizard> wizard;
0062 };
0063 
0064 EncryptEMailController::Private::Private(Mode m, EncryptEMailController *qq)
0065     : q(qq)
0066     , mode(m)
0067     , runnable()
0068     , cms()
0069     , openpgp()
0070     , wizard()
0071 {
0072 }
0073 
0074 EncryptEMailController::EncryptEMailController(const std::shared_ptr<ExecutionContext> &xc, Mode mode, QObject *p)
0075     : Controller(xc, p)
0076     , d(new Private(mode, this))
0077 {
0078 }
0079 
0080 EncryptEMailController::EncryptEMailController(Mode mode, QObject *p)
0081     : Controller(p)
0082     , d(new Private(mode, this))
0083 {
0084 }
0085 
0086 EncryptEMailController::~EncryptEMailController()
0087 {
0088     if (d->wizard && !d->wizard->isVisible()) {
0089         delete d->wizard;
0090     }
0091 }
0092 
0093 EncryptEMailController::Mode EncryptEMailController::mode() const
0094 {
0095     return d->mode;
0096 }
0097 
0098 void EncryptEMailController::setProtocol(Protocol proto)
0099 {
0100     d->ensureWizardCreated();
0101     const Protocol protocol = d->wizard->presetProtocol();
0102     kleo_assert(protocol == UnknownProtocol || protocol == proto);
0103 
0104     d->wizard->setPresetProtocol(proto);
0105 }
0106 
0107 Protocol EncryptEMailController::protocol()
0108 {
0109     d->ensureWizardCreated();
0110     return d->wizard->selectedProtocol();
0111 }
0112 
0113 const char *EncryptEMailController::protocolAsString()
0114 {
0115     switch (protocol()) {
0116     case OpenPGP:
0117         return "OpenPGP";
0118     case CMS:
0119         return "CMS";
0120     default:
0121         throw Kleo::Exception(gpg_error(GPG_ERR_INTERNAL), i18n("Call to EncryptEMailController::protocolAsString() is ambiguous."));
0122     }
0123 }
0124 
0125 void EncryptEMailController::startResolveRecipients()
0126 {
0127     d->ensureWizardCreated();
0128     d->ensureWizardVisible();
0129 }
0130 
0131 void EncryptEMailController::Private::slotWizardCanceled()
0132 {
0133     q->setLastError(gpg_error(GPG_ERR_CANCELED), i18n("User cancel"));
0134     q->emitDoneOrError();
0135 }
0136 
0137 void EncryptEMailController::setInputAndOutput(const std::shared_ptr<Input> &input, const std::shared_ptr<Output> &output)
0138 {
0139     setInputsAndOutputs(std::vector<std::shared_ptr<Input>>(1, input), std::vector<std::shared_ptr<Output>>(1, output));
0140 }
0141 
0142 void EncryptEMailController::setInputsAndOutputs(const std::vector<std::shared_ptr<Input>> &inputs, const std::vector<std::shared_ptr<Output>> &outputs)
0143 {
0144     kleo_assert(!inputs.empty());
0145     kleo_assert(outputs.size() == inputs.size());
0146 
0147     std::vector<std::shared_ptr<EncryptEMailTask>> tasks;
0148     tasks.reserve(inputs.size());
0149 
0150     d->ensureWizardCreated();
0151 
0152     const std::vector<Key> keys = d->wizard->resolvedCertificates();
0153     kleo_assert(!keys.empty());
0154 
0155     for (unsigned int i = 0, end = inputs.size(); i < end; ++i) {
0156         const std::shared_ptr<EncryptEMailTask> task(new EncryptEMailTask);
0157         task->setInput(inputs[i]);
0158         task->setOutput(outputs[i]);
0159         if (d->mode == ClipboardMode) {
0160             task->setAsciiArmor(true);
0161         }
0162         task->setRecipients(keys);
0163 
0164         tasks.push_back(task);
0165     }
0166 
0167     d->runnable.swap(tasks);
0168 }
0169 
0170 void EncryptEMailController::start()
0171 {
0172     std::shared_ptr<TaskCollection> coll(new TaskCollection);
0173     std::vector<std::shared_ptr<Task>> tmp;
0174     std::copy(d->runnable.begin(), d->runnable.end(), std::back_inserter(tmp));
0175     coll->setTasks(tmp);
0176     d->ensureWizardCreated();
0177     d->wizard->setTaskCollection(coll);
0178     for (const std::shared_ptr<Task> &t : std::as_const(tmp)) {
0179         connectTask(t);
0180     }
0181     d->schedule();
0182 }
0183 
0184 void EncryptEMailController::Private::schedule()
0185 {
0186     if (!cms)
0187         if (const std::shared_ptr<EncryptEMailTask> t = takeRunnable(CMS)) {
0188             t->start();
0189             cms = t;
0190         }
0191 
0192     if (!openpgp)
0193         if (const std::shared_ptr<EncryptEMailTask> t = takeRunnable(OpenPGP)) {
0194             t->start();
0195             openpgp = t;
0196         }
0197 
0198     if (cms || openpgp) {
0199         return;
0200     }
0201     kleo_assert(runnable.empty());
0202     q->emitDoneOrError();
0203 }
0204 
0205 std::shared_ptr<EncryptEMailTask> EncryptEMailController::Private::takeRunnable(GpgME::Protocol proto)
0206 {
0207     const auto it = std::find_if(runnable.begin(), runnable.end(), [proto](const std::shared_ptr<Kleo::Crypto::EncryptEMailTask> &task) {
0208         return task->protocol() == proto;
0209     });
0210     if (it == runnable.end()) {
0211         return std::shared_ptr<EncryptEMailTask>();
0212     }
0213 
0214     const std::shared_ptr<EncryptEMailTask> result = *it;
0215     runnable.erase(it);
0216     return result;
0217 }
0218 
0219 void EncryptEMailController::doTaskDone(const Task *task, const std::shared_ptr<const Task::Result> &result)
0220 {
0221     Q_UNUSED(result)
0222     Q_ASSERT(task);
0223 
0224     // We could just delete the tasks here, but we can't use
0225     // Qt::QueuedConnection here (we need sender()) and other slots
0226     // might not yet have executed. Therefore, we push completed tasks
0227     // into a burial container
0228 
0229     if (task == d->cms.get()) {
0230         d->completed.push_back(d->cms);
0231         d->cms.reset();
0232     } else if (task == d->openpgp.get()) {
0233         d->completed.push_back(d->openpgp);
0234         d->openpgp.reset();
0235     }
0236 
0237     QMetaObject::invokeMethod(
0238         this,
0239         [this]() {
0240             d->schedule();
0241         },
0242         Qt::QueuedConnection);
0243 }
0244 
0245 void EncryptEMailController::cancel()
0246 {
0247     try {
0248         if (d->wizard) {
0249             d->wizard->close();
0250         }
0251         d->cancelAllTasks();
0252     } catch (const std::exception &e) {
0253         qCDebug(KLEOPATRA_LOG) << "Caught exception: " << e.what();
0254     }
0255 }
0256 
0257 void EncryptEMailController::Private::cancelAllTasks()
0258 {
0259     // we just kill all runnable tasks - this will not result in
0260     // signal emissions.
0261     runnable.clear();
0262 
0263     // a cancel() will result in a call to
0264     if (cms) {
0265         cms->cancel();
0266     }
0267     if (openpgp) {
0268         openpgp->cancel();
0269     }
0270 }
0271 
0272 void EncryptEMailController::Private::ensureWizardCreated()
0273 {
0274     if (wizard) {
0275         return;
0276     }
0277 
0278     std::unique_ptr<EncryptEMailWizard> w(new EncryptEMailWizard);
0279     w->setAttribute(Qt::WA_DeleteOnClose);
0280     connect(w.get(), &EncryptEMailWizard::recipientsResolved, q, &EncryptEMailController::recipientsResolved, Qt::QueuedConnection);
0281     connect(
0282         w.get(),
0283         &EncryptEMailWizard::canceled,
0284         q,
0285         [this]() {
0286             slotWizardCanceled();
0287         },
0288         Qt::QueuedConnection);
0289 
0290     wizard = w.release();
0291 }
0292 
0293 void EncryptEMailController::Private::ensureWizardVisible()
0294 {
0295     ensureWizardCreated();
0296     q->bringToForeground(wizard);
0297 }
0298 
0299 #include "moc_encryptemailcontroller.cpp"