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

0001 /* -*- mode: c++; c-basic-offset:4 -*-
0002     decryptverifyemailcontroller.cpp
0003 
0004     This file is part of Kleopatra, the KDE keymanager
0005     SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 
0010 #include <config-kleopatra.h>
0011 
0012 #include "decryptverifyemailcontroller.h"
0013 #include "kleopatra_debug.h"
0014 
0015 #include "emailoperationspreferences.h"
0016 
0017 #include <crypto/decryptverifytask.h>
0018 #include <crypto/gui/newresultpage.h>
0019 #include <crypto/taskcollection.h>
0020 
0021 #include <Libkleo/GnuPG>
0022 #include <utils/input.h>
0023 #include <utils/kleo_assert.h>
0024 #include <utils/output.h>
0025 
0026 #include <QGpgME/Protocol>
0027 
0028 #include <Libkleo/Classify>
0029 #include <Libkleo/Formatting>
0030 
0031 #include <KMime/Types>
0032 
0033 #include <KLocalizedString>
0034 
0035 #include <QPoint>
0036 #include <QPointer>
0037 #include <QTimer>
0038 
0039 using namespace GpgME;
0040 using namespace Kleo;
0041 using namespace Kleo::Crypto;
0042 using namespace Kleo::Crypto::Gui;
0043 using namespace KMime::Types;
0044 
0045 namespace
0046 {
0047 
0048 class DecryptVerifyEMailWizard : public QWizard
0049 {
0050     Q_OBJECT
0051 public:
0052     explicit DecryptVerifyEMailWizard(QWidget *parent = nullptr, Qt::WindowFlags f = {})
0053         : QWizard(parent, f)
0054         , m_resultPage(this)
0055     {
0056         KDAB_SET_OBJECT_NAME(m_resultPage);
0057 
0058         m_resultPage.setSubTitle(i18n("Status and progress of the crypto operations is shown here."));
0059 
0060         addPage(&m_resultPage);
0061     }
0062 
0063     void addTaskCollection(const std::shared_ptr<TaskCollection> &coll)
0064     {
0065         m_resultPage.addTaskCollection(coll);
0066     }
0067 
0068 public Q_SLOTS:
0069     void accept() override
0070     {
0071         EMailOperationsPreferences prefs;
0072         prefs.setDecryptVerifyPopupGeometry(geometry());
0073         prefs.save();
0074         QWizard::accept();
0075     }
0076 
0077 private:
0078     NewResultPage m_resultPage;
0079 };
0080 
0081 }
0082 
0083 class DecryptVerifyEMailController::Private
0084 {
0085     DecryptVerifyEMailController *const q;
0086 
0087 public:
0088     explicit Private(DecryptVerifyEMailController *qq);
0089 
0090     void slotWizardCanceled();
0091     void schedule();
0092 
0093     std::vector<std::shared_ptr<AbstractDecryptVerifyTask>> buildTasks();
0094 
0095     static DecryptVerifyEMailWizard *findOrCreateWizard(unsigned int id);
0096 
0097     void ensureWizardCreated();
0098     void ensureWizardVisible();
0099     void reportError(int err, const QString &details)
0100     {
0101         q->setLastError(err, details);
0102         q->emitDoneOrError();
0103     }
0104 
0105     void cancelAllTasks();
0106 
0107     std::vector<std::shared_ptr<Input>> m_inputs, m_signedDatas;
0108     std::vector<std::shared_ptr<Output>> m_outputs;
0109 
0110     unsigned int m_sessionId;
0111     QPointer<DecryptVerifyEMailWizard> m_wizard;
0112     std::vector<std::shared_ptr<const DecryptVerifyResult>> m_results;
0113     std::vector<std::shared_ptr<AbstractDecryptVerifyTask>> m_runnableTasks, m_completedTasks;
0114     std::shared_ptr<AbstractDecryptVerifyTask> m_runningTask;
0115     bool m_silent;
0116     bool m_operationCompleted;
0117     DecryptVerifyOperation m_operation;
0118     Protocol m_protocol;
0119     VerificationMode m_verificationMode;
0120     std::vector<KMime::Types::Mailbox> m_informativeSenders;
0121 };
0122 
0123 DecryptVerifyEMailController::Private::Private(DecryptVerifyEMailController *qq)
0124     : q(qq)
0125     , m_sessionId(0)
0126     , m_silent(false)
0127     , m_operationCompleted(false)
0128     , m_operation(DecryptVerify)
0129     , m_protocol(UnknownProtocol)
0130     , m_verificationMode(Detached)
0131 {
0132     qRegisterMetaType<VerificationResult>();
0133 }
0134 
0135 void DecryptVerifyEMailController::Private::slotWizardCanceled()
0136 {
0137     qCDebug(KLEOPATRA_LOG);
0138     if (!m_operationCompleted) {
0139         reportError(gpg_error(GPG_ERR_CANCELED), i18n("User canceled"));
0140     }
0141 }
0142 
0143 void DecryptVerifyEMailController::doTaskDone(const Task *task, const std::shared_ptr<const Task::Result> &result)
0144 {
0145     Q_ASSERT(task);
0146 
0147     // We could just delete the tasks here, but we can't use
0148     // Qt::QueuedConnection here (we need sender()) and other slots
0149     // might not yet have executed. Therefore, we push completed tasks
0150     // into a burial container
0151 
0152     if (task == d->m_runningTask.get()) {
0153         d->m_completedTasks.push_back(d->m_runningTask);
0154         const std::shared_ptr<const DecryptVerifyResult> &dvr = std::dynamic_pointer_cast<const DecryptVerifyResult>(result);
0155         Q_ASSERT(dvr);
0156         d->m_results.push_back(dvr);
0157         d->m_runningTask.reset();
0158     }
0159 
0160     QTimer::singleShot(0, this, SLOT(schedule()));
0161 }
0162 
0163 void DecryptVerifyEMailController::Private::schedule()
0164 {
0165     if (!m_runningTask && !m_runnableTasks.empty()) {
0166         const std::shared_ptr<AbstractDecryptVerifyTask> t = m_runnableTasks.back();
0167         m_runnableTasks.pop_back();
0168         t->start();
0169         m_runningTask = t;
0170     }
0171     if (!m_runningTask) {
0172         kleo_assert(m_runnableTasks.empty());
0173         for (const std::shared_ptr<const DecryptVerifyResult> &i : std::as_const(m_results)) {
0174             Q_EMIT q->verificationResult(i->verificationResult());
0175         }
0176         // if there is a popup, wait for either the client cancel or the user closing the popup.
0177         // Otherwise (silent case), finish immediately
0178         m_operationCompleted = true;
0179         q->emitDoneOrError();
0180     }
0181 }
0182 
0183 void DecryptVerifyEMailController::Private::ensureWizardCreated()
0184 {
0185     if (m_wizard) {
0186         return;
0187     }
0188 
0189     DecryptVerifyEMailWizard *w = findOrCreateWizard(m_sessionId);
0190     connect(w, SIGNAL(destroyed()), q, SLOT(slotWizardCanceled()), Qt::QueuedConnection);
0191     m_wizard = w;
0192 }
0193 
0194 namespace
0195 {
0196 template<typename C>
0197 void collectGarbage(C &c)
0198 {
0199     auto it = c.begin();
0200     while (it != c.end() /*sic!*/)
0201         if (it->second) {
0202             ++it;
0203         } else {
0204             c.erase(it++ /*sic!*/);
0205         }
0206 }
0207 }
0208 
0209 // static
0210 DecryptVerifyEMailWizard *DecryptVerifyEMailController::Private::findOrCreateWizard(unsigned int id)
0211 {
0212     static std::map<unsigned int, QPointer<DecryptVerifyEMailWizard>> s_wizards;
0213 
0214     collectGarbage(s_wizards);
0215 
0216     qCDebug(KLEOPATRA_LOG) << "id = " << id;
0217 
0218     if (id != 0) {
0219         const auto it = s_wizards.find(id);
0220 
0221         if (it != s_wizards.end()) {
0222             Q_ASSERT(it->second && "This should have been garbage-collected");
0223             return it->second;
0224         }
0225     }
0226 
0227     auto w = new DecryptVerifyEMailWizard;
0228     w->setWindowTitle(i18nc("@title:window", "Decrypt/Verify E-Mail"));
0229     w->setAttribute(Qt::WA_DeleteOnClose);
0230 
0231     const QRect preferredGeometry = EMailOperationsPreferences().decryptVerifyPopupGeometry();
0232     if (preferredGeometry.isValid()) {
0233         w->setGeometry(preferredGeometry);
0234     }
0235 
0236     s_wizards[id] = w;
0237 
0238     return w;
0239 }
0240 
0241 std::vector<std::shared_ptr<AbstractDecryptVerifyTask>> DecryptVerifyEMailController::Private::buildTasks()
0242 {
0243     const uint numInputs = m_inputs.size();
0244     const uint numMessages = m_signedDatas.size();
0245     const uint numOutputs = m_outputs.size();
0246     const uint numInformativeSenders = m_informativeSenders.size();
0247 
0248     // these are duplicated from DecryptVerifyCommandEMailBase::Private::checkForErrors with slightly modified error codes/messages
0249     if (!numInputs)
0250         throw Kleo::Exception(makeGnuPGError(GPG_ERR_CONFLICT), i18n("At least one input needs to be provided"));
0251 
0252     if (numInformativeSenders > 0 && numInformativeSenders != numInputs)
0253         throw Kleo::Exception(makeGnuPGError(GPG_ERR_CONFLICT), // TODO use better error code if possible
0254                               i18n("Informative sender/signed data count mismatch"));
0255 
0256     if (numMessages) {
0257         if (numMessages != numInputs)
0258             throw Kleo::Exception(makeGnuPGError(GPG_ERR_CONFLICT), // TODO use better error code if possible
0259                                   i18n("Signature/signed data count mismatch"));
0260         else if (m_operation != Verify || m_verificationMode != Detached)
0261             throw Kleo::Exception(makeGnuPGError(GPG_ERR_CONFLICT), i18n("Signed data can only be given for detached signature verification"));
0262     }
0263 
0264     if (numOutputs) {
0265         if (numOutputs != numInputs)
0266             throw Kleo::Exception(makeGnuPGError(GPG_ERR_CONFLICT), // TODO use better error code if possible
0267                                   i18n("Input/Output count mismatch"));
0268         else if (numMessages)
0269             throw Kleo::Exception(makeGnuPGError(GPG_ERR_CONFLICT), i18n("Cannot use output and signed data simultaneously"));
0270     }
0271 
0272     kleo_assert(m_protocol != UnknownProtocol);
0273 
0274     const QGpgME::Protocol *const backend = (m_protocol == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime();
0275     if (!backend) {
0276         throw Kleo::Exception(makeGnuPGError(GPG_ERR_UNSUPPORTED_PROTOCOL), i18n("No backend support for %1", Formatting::displayName(m_protocol)));
0277     }
0278 
0279     if (m_operation != Decrypt && !m_silent) {
0280         ensureWizardVisible();
0281     }
0282 
0283     std::vector<std::shared_ptr<AbstractDecryptVerifyTask>> tasks;
0284 
0285     for (unsigned int i = 0; i < numInputs; ++i) {
0286         std::shared_ptr<AbstractDecryptVerifyTask> task;
0287         switch (m_operation) {
0288         case Decrypt: {
0289             std::shared_ptr<DecryptTask> t(new DecryptTask);
0290             t->setInput(m_inputs.at(i));
0291             Q_ASSERT(numOutputs);
0292             t->setOutput(m_outputs.at(i));
0293             t->setProtocol(m_protocol);
0294             task = t;
0295         } break;
0296         case Verify: {
0297             if (m_verificationMode == Detached) {
0298                 std::shared_ptr<VerifyDetachedTask> t(new VerifyDetachedTask);
0299                 t->setInput(m_inputs.at(i));
0300                 t->setSignedData(m_signedDatas.at(i));
0301                 if (numInformativeSenders > 0) {
0302                     t->setInformativeSender(m_informativeSenders.at(i));
0303                 }
0304                 t->setProtocol(m_protocol);
0305                 task = t;
0306             } else {
0307                 std::shared_ptr<VerifyOpaqueTask> t(new VerifyOpaqueTask);
0308                 t->setInput(m_inputs.at(i));
0309                 if (numOutputs) {
0310                     t->setOutput(m_outputs.at(i));
0311                 }
0312                 if (numInformativeSenders > 0) {
0313                     t->setInformativeSender(m_informativeSenders.at(i));
0314                 }
0315                 t->setProtocol(m_protocol);
0316                 task = t;
0317             }
0318         } break;
0319         case DecryptVerify: {
0320             std::shared_ptr<DecryptVerifyTask> t(new DecryptVerifyTask);
0321             t->setInput(m_inputs.at(i));
0322             Q_ASSERT(numOutputs);
0323             t->setOutput(m_outputs.at(i));
0324             if (numInformativeSenders > 0) {
0325                 t->setInformativeSender(m_informativeSenders.at(i));
0326             }
0327             t->setProtocol(m_protocol);
0328             task = t;
0329         }
0330         }
0331 
0332         Q_ASSERT(task);
0333         tasks.push_back(task);
0334     }
0335 
0336     return tasks;
0337 }
0338 
0339 void DecryptVerifyEMailController::Private::ensureWizardVisible()
0340 {
0341     ensureWizardCreated();
0342     q->bringToForeground(m_wizard);
0343 }
0344 
0345 DecryptVerifyEMailController::DecryptVerifyEMailController(QObject *parent)
0346     : Controller(parent)
0347     , d(new Private(this))
0348 {
0349 }
0350 
0351 DecryptVerifyEMailController::DecryptVerifyEMailController(const std::shared_ptr<const ExecutionContext> &ctx, QObject *parent)
0352     : Controller(ctx, parent)
0353     , d(new Private(this))
0354 {
0355 }
0356 
0357 DecryptVerifyEMailController::~DecryptVerifyEMailController()
0358 {
0359     qCDebug(KLEOPATRA_LOG);
0360 }
0361 
0362 void DecryptVerifyEMailController::start()
0363 {
0364     d->m_runnableTasks = d->buildTasks();
0365 
0366     const std::shared_ptr<TaskCollection> coll(new TaskCollection);
0367     std::vector<std::shared_ptr<Task>> tsks;
0368     for (std::shared_ptr<Task> i : std::as_const(d->m_runnableTasks)) {
0369         connectTask(i);
0370         tsks.push_back(i);
0371     }
0372     coll->setTasks(tsks);
0373     d->ensureWizardCreated();
0374     d->m_wizard->addTaskCollection(coll);
0375 
0376     d->ensureWizardVisible();
0377     QTimer::singleShot(0, this, SLOT(schedule()));
0378 }
0379 
0380 void DecryptVerifyEMailController::setInput(const std::shared_ptr<Input> &input)
0381 {
0382     d->m_inputs.resize(1, input);
0383 }
0384 
0385 void DecryptVerifyEMailController::setInputs(const std::vector<std::shared_ptr<Input>> &inputs)
0386 {
0387     d->m_inputs = inputs;
0388 }
0389 
0390 void DecryptVerifyEMailController::setSignedData(const std::shared_ptr<Input> &data)
0391 {
0392     d->m_signedDatas.resize(1, data);
0393 }
0394 
0395 void DecryptVerifyEMailController::setSignedData(const std::vector<std::shared_ptr<Input>> &data)
0396 {
0397     d->m_signedDatas = data;
0398 }
0399 
0400 void DecryptVerifyEMailController::setOutput(const std::shared_ptr<Output> &output)
0401 {
0402     d->m_outputs.resize(1, output);
0403 }
0404 
0405 void DecryptVerifyEMailController::setOutputs(const std::vector<std::shared_ptr<Output>> &outputs)
0406 {
0407     d->m_outputs = outputs;
0408 }
0409 
0410 void DecryptVerifyEMailController::setInformativeSenders(const std::vector<KMime::Types::Mailbox> &senders)
0411 {
0412     d->m_informativeSenders = senders;
0413 }
0414 
0415 void DecryptVerifyEMailController::setWizardShown(bool shown)
0416 {
0417     d->m_silent = !shown;
0418     if (d->m_wizard) {
0419         d->m_wizard->setVisible(shown);
0420     }
0421 }
0422 
0423 void DecryptVerifyEMailController::setOperation(DecryptVerifyOperation operation)
0424 {
0425     d->m_operation = operation;
0426 }
0427 
0428 void DecryptVerifyEMailController::setVerificationMode(VerificationMode vm)
0429 {
0430     d->m_verificationMode = vm;
0431 }
0432 
0433 void DecryptVerifyEMailController::setProtocol(Protocol prot)
0434 {
0435     d->m_protocol = prot;
0436 }
0437 
0438 void DecryptVerifyEMailController::setSessionId(unsigned int id)
0439 {
0440     qCDebug(KLEOPATRA_LOG) << "id = " << id;
0441     d->m_sessionId = id;
0442 }
0443 
0444 void DecryptVerifyEMailController::cancel()
0445 {
0446     qCDebug(KLEOPATRA_LOG);
0447     try {
0448         if (d->m_wizard) {
0449             disconnect(d->m_wizard);
0450             d->m_wizard->close();
0451         }
0452         d->cancelAllTasks();
0453     } catch (const std::exception &e) {
0454         qCDebug(KLEOPATRA_LOG) << "Caught exception: " << e.what();
0455     }
0456 }
0457 
0458 void DecryptVerifyEMailController::Private::cancelAllTasks()
0459 {
0460     // we just kill all runnable tasks - this will not result in
0461     // signal emissions.
0462     m_runnableTasks.clear();
0463 
0464     // a cancel() will result in a call to
0465     if (m_runningTask) {
0466         m_runningTask->cancel();
0467     }
0468 }
0469 
0470 #include "decryptverifyemailcontroller.moc"
0471 #include "moc_decryptverifyemailcontroller.cpp"