File indexing completed on 2024-06-23 05:13: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"