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