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

0001 /* -*- mode: c++; c-basic-offset:4 -*-
0002     crypto/newsignencryptemailcontroller.cpp
0003 
0004     This file is part of Kleopatra, the KDE keymanager
0005     SPDX-FileCopyrightText: 2009, 2010 Klarälvdalens Datakonsult AB
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 
0010 #include <config-kleopatra.h>
0011 
0012 #include "encryptemailtask.h"
0013 #include "kleopatra_debug.h"
0014 #include "newsignencryptemailcontroller.h"
0015 #include "recipient.h"
0016 #include "sender.h"
0017 #include "signemailtask.h"
0018 #include "taskcollection.h"
0019 
0020 #include "emailoperationspreferences.h"
0021 
0022 #include <crypto/gui/signencryptemailconflictdialog.h>
0023 
0024 #include "utils/input.h"
0025 #include "utils/kleo_assert.h"
0026 #include "utils/output.h"
0027 #include <Libkleo/GnuPG>
0028 
0029 #include <Libkleo/Compliance>
0030 #include <Libkleo/KleoException>
0031 #include <Libkleo/Stl_Util>
0032 
0033 #include <gpgme++/key.h>
0034 
0035 #include <KMime/Types>
0036 
0037 #include <KLocalizedString>
0038 
0039 #include <KMessageBox>
0040 
0041 #include <QPointer>
0042 #include <QTimer>
0043 
0044 using namespace Kleo;
0045 using namespace Kleo::Crypto;
0046 using namespace Kleo::Crypto::Gui;
0047 using namespace GpgME;
0048 using namespace KMime::Types;
0049 
0050 static std::vector<Sender> mailbox2sender(const std::vector<Mailbox> &mbs)
0051 {
0052     std::vector<Sender> senders;
0053     senders.reserve(mbs.size());
0054     for (const Mailbox &mb : mbs) {
0055         senders.push_back(Sender(mb));
0056     }
0057     return senders;
0058 }
0059 
0060 static std::vector<Recipient> mailbox2recipient(const std::vector<Mailbox> &mbs)
0061 {
0062     std::vector<Recipient> recipients;
0063     recipients.reserve(mbs.size());
0064     for (const Mailbox &mb : mbs) {
0065         recipients.push_back(Recipient(mb));
0066     }
0067     return recipients;
0068 }
0069 
0070 class NewSignEncryptEMailController::Private
0071 {
0072     friend class ::Kleo::Crypto::NewSignEncryptEMailController;
0073     NewSignEncryptEMailController *const q;
0074 
0075 public:
0076     explicit Private(NewSignEncryptEMailController *qq);
0077     ~Private();
0078 
0079 private:
0080     void slotDialogAccepted();
0081     void slotDialogRejected();
0082 
0083 private:
0084     void ensureDialogVisible();
0085     void cancelAllTasks();
0086 
0087     void startSigning();
0088     void startEncryption();
0089     void schedule();
0090     std::shared_ptr<Task> takeRunnable(GpgME::Protocol proto);
0091 
0092 private:
0093     bool sign : 1;
0094     bool encrypt : 1;
0095     bool resolvingInProgress : 1;
0096     bool certificatesResolved : 1;
0097     bool detached : 1;
0098     Protocol presetProtocol;
0099     std::vector<Key> signers, recipients;
0100     std::vector<std::shared_ptr<Task>> runnable, completed;
0101     std::shared_ptr<Task> cms, openpgp;
0102     QPointer<SignEncryptEMailConflictDialog> dialog;
0103 };
0104 
0105 NewSignEncryptEMailController::Private::Private(NewSignEncryptEMailController *qq)
0106     : q(qq)
0107     , sign(false)
0108     , encrypt(false)
0109     , resolvingInProgress(false)
0110     , certificatesResolved(false)
0111     , detached(false)
0112     , presetProtocol(UnknownProtocol)
0113     , signers()
0114     , recipients()
0115     , runnable()
0116     , cms()
0117     , openpgp()
0118     , dialog(new SignEncryptEMailConflictDialog)
0119 {
0120     connect(dialog, SIGNAL(accepted()), q, SLOT(slotDialogAccepted()));
0121     connect(dialog, SIGNAL(rejected()), q, SLOT(slotDialogRejected()));
0122 }
0123 
0124 NewSignEncryptEMailController::Private::~Private()
0125 {
0126     delete dialog;
0127 }
0128 
0129 NewSignEncryptEMailController::NewSignEncryptEMailController(const std::shared_ptr<ExecutionContext> &xc, QObject *p)
0130     : Controller(xc, p)
0131     , d(new Private(this))
0132 {
0133 }
0134 
0135 NewSignEncryptEMailController::NewSignEncryptEMailController(QObject *p)
0136     : Controller(p)
0137     , d(new Private(this))
0138 {
0139 }
0140 
0141 NewSignEncryptEMailController::~NewSignEncryptEMailController()
0142 {
0143     qCDebug(KLEOPATRA_LOG);
0144 }
0145 
0146 void NewSignEncryptEMailController::setSubject(const QString &subject)
0147 {
0148     d->dialog->setSubject(subject);
0149 }
0150 
0151 void NewSignEncryptEMailController::setProtocol(Protocol proto)
0152 {
0153     d->presetProtocol = proto;
0154     d->dialog->setPresetProtocol(proto);
0155 }
0156 
0157 Protocol NewSignEncryptEMailController::protocol() const
0158 {
0159     return d->dialog->selectedProtocol();
0160 }
0161 
0162 const char *NewSignEncryptEMailController::protocolAsString() const
0163 {
0164     switch (protocol()) {
0165     case OpenPGP:
0166         return "OpenPGP";
0167     case CMS:
0168         return "CMS";
0169     default:
0170         throw Kleo::Exception(gpg_error(GPG_ERR_INTERNAL), i18n("Call to NewSignEncryptEMailController::protocolAsString() is ambiguous."));
0171     }
0172 }
0173 
0174 void NewSignEncryptEMailController::setSigning(bool sign)
0175 {
0176     d->sign = sign;
0177     d->dialog->setSign(sign);
0178 }
0179 
0180 bool NewSignEncryptEMailController::isSigning() const
0181 {
0182     return d->sign;
0183 }
0184 
0185 void NewSignEncryptEMailController::setEncrypting(bool encrypt)
0186 {
0187     d->encrypt = encrypt;
0188     d->dialog->setEncrypt(encrypt);
0189 }
0190 
0191 bool NewSignEncryptEMailController::isEncrypting() const
0192 {
0193     return d->encrypt;
0194 }
0195 
0196 void NewSignEncryptEMailController::setDetachedSignature(bool detached)
0197 {
0198     d->detached = detached;
0199 }
0200 
0201 bool NewSignEncryptEMailController::isResolvingInProgress() const
0202 {
0203     return d->resolvingInProgress;
0204 }
0205 
0206 bool NewSignEncryptEMailController::areCertificatesResolved() const
0207 {
0208     return d->certificatesResolved;
0209 }
0210 
0211 void NewSignEncryptEMailController::startResolveCertificates(const std::vector<Mailbox> &r, const std::vector<Mailbox> &s)
0212 {
0213     d->certificatesResolved = false;
0214     d->resolvingInProgress = true;
0215 
0216     const std::vector<Sender> senders = mailbox2sender(s);
0217     const std::vector<Recipient> recipients = mailbox2recipient(r);
0218 
0219     d->dialog->setQuickMode(false);
0220     d->dialog->setSenders(senders);
0221     d->dialog->setRecipients(recipients);
0222     d->dialog->pickProtocol();
0223     d->dialog->setConflict(false);
0224 
0225     d->ensureDialogVisible();
0226 }
0227 
0228 void NewSignEncryptEMailController::Private::slotDialogAccepted()
0229 {
0230     resolvingInProgress = false;
0231     certificatesResolved = true;
0232     signers = dialog->resolvedSigningKeys();
0233     recipients = dialog->resolvedEncryptionKeys();
0234     QMetaObject::invokeMethod(q, "certificatesResolved", Qt::QueuedConnection);
0235 }
0236 
0237 void NewSignEncryptEMailController::Private::slotDialogRejected()
0238 {
0239     resolvingInProgress = false;
0240     certificatesResolved = false;
0241     QMetaObject::invokeMethod(q, "error", Qt::QueuedConnection, Q_ARG(int, gpg_error(GPG_ERR_CANCELED)), Q_ARG(QString, i18n("User cancel")));
0242 }
0243 
0244 void NewSignEncryptEMailController::startEncryption(const std::vector<std::shared_ptr<Input>> &inputs, const std::vector<std::shared_ptr<Output>> &outputs)
0245 {
0246     kleo_assert(d->encrypt);
0247     kleo_assert(!d->resolvingInProgress);
0248 
0249     kleo_assert(!inputs.empty());
0250     kleo_assert(outputs.size() == inputs.size());
0251 
0252     std::vector<std::shared_ptr<Task>> tasks;
0253     tasks.reserve(inputs.size());
0254 
0255     kleo_assert(!d->recipients.empty());
0256 
0257     for (unsigned int i = 0, end = inputs.size(); i < end; ++i) {
0258         const std::shared_ptr<EncryptEMailTask> task(new EncryptEMailTask);
0259 
0260         task->setInput(inputs[i]);
0261         task->setOutput(outputs[i]);
0262         task->setRecipients(d->recipients);
0263 
0264         tasks.push_back(task);
0265     }
0266 
0267     // append to runnable stack
0268     d->runnable.insert(d->runnable.end(), tasks.begin(), tasks.end());
0269 
0270     d->startEncryption();
0271 }
0272 
0273 void NewSignEncryptEMailController::Private::startEncryption()
0274 {
0275     std::shared_ptr<TaskCollection> coll(new TaskCollection);
0276     std::vector<std::shared_ptr<Task>> tmp;
0277     tmp.reserve(runnable.size());
0278     std::copy(runnable.cbegin(), runnable.cend(), std::back_inserter(tmp));
0279     coll->setTasks(tmp);
0280 #if 0
0281 #warning use a new result dialog
0282     // ### use a new result dialog
0283     dialog->setTaskCollection(coll);
0284 #endif
0285     for (const std::shared_ptr<Task> &t : std::as_const(tmp)) {
0286         q->connectTask(t);
0287     }
0288     schedule();
0289 }
0290 
0291 void NewSignEncryptEMailController::startSigning(const std::vector<std::shared_ptr<Input>> &inputs, const std::vector<std::shared_ptr<Output>> &outputs)
0292 {
0293     kleo_assert(d->sign);
0294     kleo_assert(!d->resolvingInProgress);
0295 
0296     kleo_assert(!inputs.empty());
0297     kleo_assert(!outputs.empty());
0298 
0299     std::vector<std::shared_ptr<Task>> tasks;
0300     tasks.reserve(inputs.size());
0301 
0302     kleo_assert(!d->signers.empty());
0303     kleo_assert(std::none_of(d->signers.cbegin(), d->signers.cend(), std::mem_fn(&Key::isNull)));
0304 
0305     for (unsigned int i = 0, end = inputs.size(); i < end; ++i) {
0306         const std::shared_ptr<SignEMailTask> task(new SignEMailTask);
0307 
0308         task->setInput(inputs[i]);
0309         task->setOutput(outputs[i]);
0310         task->setSigners(d->signers);
0311         task->setDetachedSignature(d->detached);
0312 
0313         tasks.push_back(task);
0314     }
0315 
0316     // append to runnable stack
0317     d->runnable.insert(d->runnable.end(), tasks.begin(), tasks.end());
0318 
0319     d->startSigning();
0320 }
0321 
0322 void NewSignEncryptEMailController::Private::startSigning()
0323 {
0324     std::shared_ptr<TaskCollection> coll(new TaskCollection);
0325     std::vector<std::shared_ptr<Task>> tmp;
0326     tmp.reserve(runnable.size());
0327     std::copy(runnable.cbegin(), runnable.cend(), std::back_inserter(tmp));
0328     coll->setTasks(tmp);
0329 #if 0
0330 #warning use a new result dialog
0331     // ### use a new result dialog
0332     dialog->setTaskCollection(coll);
0333 #endif
0334     for (const std::shared_ptr<Task> &t : std::as_const(tmp)) {
0335         q->connectTask(t);
0336     }
0337     schedule();
0338 }
0339 
0340 void NewSignEncryptEMailController::Private::schedule()
0341 {
0342     if (!cms)
0343         if (const std::shared_ptr<Task> t = takeRunnable(CMS)) {
0344             t->start();
0345             cms = t;
0346         }
0347 
0348     if (!openpgp)
0349         if (const std::shared_ptr<Task> t = takeRunnable(OpenPGP)) {
0350             t->start();
0351             openpgp = t;
0352         }
0353 
0354     if (cms || openpgp) {
0355         return;
0356     }
0357     kleo_assert(runnable.empty());
0358     q->emitDoneOrError();
0359 }
0360 
0361 std::shared_ptr<Task> NewSignEncryptEMailController::Private::takeRunnable(GpgME::Protocol proto)
0362 {
0363     const auto it = std::find_if(runnable.begin(), runnable.end(), [proto](const std::shared_ptr<Task> &task) {
0364         return task->protocol() == proto;
0365     });
0366     if (it == runnable.end()) {
0367         return std::shared_ptr<Task>();
0368     }
0369 
0370     const std::shared_ptr<Task> result = *it;
0371     runnable.erase(it);
0372     return result;
0373 }
0374 
0375 void NewSignEncryptEMailController::doTaskDone(const Task *task, const std::shared_ptr<const Task::Result> &result)
0376 {
0377     Q_ASSERT(task);
0378 
0379     if (result && result->hasError()) {
0380         QPointer<QObject> that = this;
0381         if (result->details().isEmpty())
0382             KMessageBox::error(nullptr, result->overview(), i18nc("@title:window", "Error"));
0383         else
0384             KMessageBox::detailedError(nullptr, result->overview(), result->details(), i18nc("@title:window", "Error"));
0385         if (!that) {
0386             return;
0387         }
0388     }
0389 
0390     // We could just delete the tasks here, but we can't use
0391     // Qt::QueuedConnection here (we need sender()) and other slots
0392     // might not yet have executed. Therefore, we push completed tasks
0393     // into a burial container
0394 
0395     if (task == d->cms.get()) {
0396         d->completed.push_back(d->cms);
0397         d->cms.reset();
0398     } else if (task == d->openpgp.get()) {
0399         d->completed.push_back(d->openpgp);
0400         d->openpgp.reset();
0401     }
0402 
0403     QTimer::singleShot(0, this, SLOT(schedule()));
0404 }
0405 
0406 void NewSignEncryptEMailController::cancel()
0407 {
0408     try {
0409         d->dialog->close();
0410         d->cancelAllTasks();
0411     } catch (const std::exception &e) {
0412         qCDebug(KLEOPATRA_LOG) << "Caught exception: " << e.what();
0413     }
0414 }
0415 
0416 void NewSignEncryptEMailController::Private::cancelAllTasks()
0417 {
0418     // we just kill all runnable tasks - this will not result in
0419     // signal emissions.
0420     runnable.clear();
0421 
0422     // a cancel() will result in a call to
0423     if (cms) {
0424         cms->cancel();
0425     }
0426     if (openpgp) {
0427         openpgp->cancel();
0428     }
0429 }
0430 
0431 void NewSignEncryptEMailController::Private::ensureDialogVisible()
0432 {
0433     q->bringToForeground(dialog, true);
0434 }
0435 
0436 #include "moc_newsignencryptemailcontroller.cpp"