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