File indexing completed on 2024-06-23 05:14:13

0001 /* -*- mode: c++; c-basic-offset:4 -*-
0002     uiserver/prepsigncommand.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 "prepsigncommand.h"
0013 
0014 #include <crypto/newsignencryptemailcontroller.h>
0015 
0016 #include <utils/kleo_assert.h>
0017 
0018 #include <Libkleo/KleoException>
0019 
0020 #include <KLocalizedString>
0021 
0022 #include <QPointer>
0023 #include <QTimer>
0024 
0025 using namespace Kleo;
0026 using namespace Kleo::Crypto;
0027 
0028 class PrepSignCommand::Private : public QObject
0029 {
0030     Q_OBJECT
0031 private:
0032     friend class ::Kleo::PrepSignCommand;
0033     PrepSignCommand *const q;
0034 
0035 public:
0036     explicit Private(PrepSignCommand *qq)
0037         : q(qq)
0038         , controller()
0039     {
0040     }
0041 
0042 private:
0043     void checkForErrors() const;
0044     void connectController();
0045 
0046 public Q_SLOTS:
0047     void slotSignersResolved();
0048     void slotError(int, const QString &);
0049 
0050 private:
0051     std::shared_ptr<NewSignEncryptEMailController> controller;
0052 };
0053 
0054 PrepSignCommand::PrepSignCommand()
0055     : AssuanCommandMixin<PrepSignCommand>()
0056     , d(new Private(this))
0057 {
0058 }
0059 
0060 PrepSignCommand::~PrepSignCommand()
0061 {
0062 }
0063 
0064 void PrepSignCommand::Private::checkForErrors() const
0065 {
0066     if (!q->inputs().empty() || !q->outputs().empty() || !q->messages().empty())
0067         throw Exception(makeError(GPG_ERR_CONFLICT), i18n("INPUT/OUTPUT/MESSAGE may only be given after PREP_SIGN"));
0068 
0069     if (q->numFiles())
0070         throw Exception(makeError(GPG_ERR_CONFLICT), i18n("PREP_SIGN is an email mode command, connection seems to be in filemanager mode"));
0071 
0072     if (q->senders().empty())
0073         throw Exception(makeError(GPG_ERR_CONFLICT), i18n("No SENDER given"));
0074 
0075     const auto m = q->mementoContent<std::shared_ptr<NewSignEncryptEMailController>>(NewSignEncryptEMailController::mementoName());
0076 
0077     if (m && m->isSigning()) {
0078         if (q->hasOption("protocol"))
0079             if (m->protocol() != q->checkProtocol(EMail))
0080                 throw Exception(makeError(GPG_ERR_CONFLICT), i18n("Protocol given conflicts with protocol determined by PREP_ENCRYPT in this session"));
0081 
0082         // ### check that any SENDER here is the same as the one for PREP_ENCRYPT
0083 
0084         // ### ditto RECIPIENT
0085     }
0086 }
0087 
0088 void PrepSignCommand::Private::connectController()
0089 {
0090     auto ptr = controller.get();
0091     connect(ptr, &NewSignEncryptEMailController::certificatesResolved, this, &PrepSignCommand::Private::slotSignersResolved);
0092     connect(ptr, &Controller::error, this, &PrepSignCommand::Private::slotError);
0093 }
0094 
0095 int PrepSignCommand::doStart()
0096 {
0097     d->checkForErrors();
0098 
0099     const auto seec = mementoContent<std::shared_ptr<NewSignEncryptEMailController>>(NewSignEncryptEMailController::mementoName());
0100 
0101     if (seec && seec->isSigning()) {
0102         // reuse the controller from a previous PREP_ENCRYPT --expect-sign, if available:
0103         d->controller = seec;
0104         d->connectController();
0105         seec->setExecutionContext(shared_from_this());
0106         if (seec->areCertificatesResolved()) {
0107             QTimer::singleShot(0, d.get(), &Private::slotSignersResolved);
0108         } else {
0109             kleo_assert(seec->isResolvingInProgress());
0110         }
0111     } else {
0112         // use a new controller
0113         d->controller.reset(new NewSignEncryptEMailController(shared_from_this()));
0114 
0115         const QString session = sessionTitle();
0116         if (!session.isEmpty()) {
0117             d->controller->setSubject(session);
0118         }
0119 
0120         if (hasOption("protocol"))
0121         // --protocol is optional for PREP_SIGN
0122         {
0123             d->controller->setProtocol(checkProtocol(EMail));
0124         }
0125 
0126         d->controller->setEncrypting(false);
0127         d->controller->setSigning(true);
0128         d->connectController();
0129         d->controller->startResolveCertificates(recipients(), senders());
0130     }
0131 
0132     return 0;
0133 }
0134 
0135 void PrepSignCommand::Private::slotSignersResolved()
0136 {
0137     // hold local std::shared_ptr to member as q->done() deletes *this
0138     const std::shared_ptr<NewSignEncryptEMailController> cont = controller;
0139     QPointer<Private> that(this);
0140 
0141     try {
0142         q->sendStatus("PROTOCOL", QLatin1StringView(controller->protocolAsString()));
0143         q->registerMemento(NewSignEncryptEMailController::mementoName(), make_typed_memento(controller));
0144         q->done();
0145         return;
0146 
0147     } catch (const Exception &e) {
0148         q->done(e.error(), e.message());
0149     } catch (const std::exception &e) {
0150         q->done(makeError(GPG_ERR_UNEXPECTED),
0151                 i18n("Caught unexpected exception in PrepSignCommand::Private::slotRecipientsResolved: %1", QString::fromLocal8Bit(e.what())));
0152     } catch (...) {
0153         q->done(makeError(GPG_ERR_UNEXPECTED), i18n("Caught unknown exception in PrepSignCommand::Private::slotRecipientsResolved"));
0154     }
0155     if (that) { // isn't this always deleted here and thus unnecessary?
0156         q->removeMemento(NewSignEncryptEMailController::mementoName());
0157     }
0158     cont->cancel();
0159 }
0160 
0161 void PrepSignCommand::Private::slotError(int err, const QString &details)
0162 {
0163     q->done(err, details);
0164 }
0165 
0166 void PrepSignCommand::doCanceled()
0167 {
0168     if (d->controller) {
0169         d->controller->cancel();
0170     }
0171 }
0172 
0173 #include "prepsigncommand.moc"