File indexing completed on 2024-06-23 05:14:00
0001 /* -*- mode: c++; c-basic-offset:4 -*- 0002 crypto/signencrypttask.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 "signencrypttask.h" 0013 0014 #include <utils/gpgme-compat.h> 0015 #include <utils/input.h> 0016 #include <utils/kleo_assert.h> 0017 #include <utils/output.h> 0018 #include <utils/path-helper.h> 0019 0020 #include <Libkleo/AuditLogEntry> 0021 #include <Libkleo/Formatting> 0022 #include <Libkleo/KleoException> 0023 #include <Libkleo/Stl_Util> 0024 0025 #include <QGpgME/EncryptArchiveJob> 0026 #include <QGpgME/EncryptJob> 0027 #include <QGpgME/Protocol> 0028 #include <QGpgME/SignArchiveJob> 0029 #include <QGpgME/SignEncryptArchiveJob> 0030 #include <QGpgME/SignEncryptJob> 0031 #include <QGpgME/SignJob> 0032 0033 #include <gpgme++/encryptionresult.h> 0034 #include <gpgme++/key.h> 0035 #include <gpgme++/signingresult.h> 0036 0037 #include <KLocalizedString> 0038 0039 #include "kleopatra_debug.h" 0040 #include <QFileInfo> 0041 #include <QPointer> 0042 #include <QTextDocument> // for Qt::escape 0043 0044 using namespace Kleo; 0045 using namespace Kleo::Crypto; 0046 using namespace GpgME; 0047 0048 namespace 0049 { 0050 0051 QString formatInputOutputLabel(const QString &input, const QString &output, bool outputDeleted) 0052 { 0053 return i18nc("Input file --> Output file (rarr is arrow", 0054 "%1 → %2", 0055 input.toHtmlEscaped(), 0056 outputDeleted ? QStringLiteral("<s>%1</s>").arg(output.toHtmlEscaped()) : output.toHtmlEscaped()); 0057 } 0058 0059 class ErrorResult : public Task::Result 0060 { 0061 public: 0062 ErrorResult(bool sign, bool encrypt, const Error &err, const QString &errStr, const QString &input, const QString &output, const AuditLogEntry &auditLog) 0063 : Task::Result() 0064 , m_sign(sign) 0065 , m_encrypt(encrypt) 0066 , m_error(err) 0067 , m_errString(errStr) 0068 , m_inputLabel(input) 0069 , m_outputLabel(output) 0070 , m_auditLog(auditLog) 0071 { 0072 } 0073 0074 QString overview() const override; 0075 QString details() const override; 0076 GpgME::Error error() const override 0077 { 0078 return m_error; 0079 } 0080 QString errorString() const override 0081 { 0082 return m_errString; 0083 } 0084 VisualCode code() const override 0085 { 0086 return NeutralError; 0087 } 0088 AuditLogEntry auditLog() const override 0089 { 0090 return m_auditLog; 0091 } 0092 0093 private: 0094 const bool m_sign; 0095 const bool m_encrypt; 0096 const Error m_error; 0097 const QString m_errString; 0098 const QString m_inputLabel; 0099 const QString m_outputLabel; 0100 const AuditLogEntry m_auditLog; 0101 }; 0102 0103 namespace 0104 { 0105 struct LabelAndError { 0106 QString label; 0107 QString errorString; 0108 }; 0109 } 0110 0111 class SignEncryptFilesResult : public Task::Result 0112 { 0113 public: 0114 SignEncryptFilesResult(const SigningResult &sr, const LabelAndError &input, const LabelAndError &output, bool outputCreated, const AuditLogEntry &auditLog) 0115 : Task::Result() 0116 , m_sresult(sr) 0117 , m_input{input} 0118 , m_output{output} 0119 , m_outputCreated(outputCreated) 0120 , m_auditLog(auditLog) 0121 { 0122 qCDebug(KLEOPATRA_LOG) << "\ninputError :" << m_input.errorString << "\noutputError:" << m_output.errorString; 0123 Q_ASSERT(!m_sresult.isNull()); 0124 } 0125 SignEncryptFilesResult(const EncryptionResult &er, 0126 const LabelAndError &input, 0127 const LabelAndError &output, 0128 bool outputCreated, 0129 const AuditLogEntry &auditLog) 0130 : Task::Result() 0131 , m_eresult(er) 0132 , m_input{input} 0133 , m_output{output} 0134 , m_outputCreated(outputCreated) 0135 , m_auditLog(auditLog) 0136 { 0137 qCDebug(KLEOPATRA_LOG) << "\ninputError :" << m_input.errorString << "\noutputError:" << m_output.errorString; 0138 Q_ASSERT(!m_eresult.isNull()); 0139 } 0140 SignEncryptFilesResult(const SigningResult &sr, 0141 const EncryptionResult &er, 0142 const LabelAndError &input, 0143 const LabelAndError &output, 0144 bool outputCreated, 0145 const AuditLogEntry &auditLog) 0146 : Task::Result() 0147 , m_sresult(sr) 0148 , m_eresult(er) 0149 , m_input{input} 0150 , m_output{output} 0151 , m_outputCreated(outputCreated) 0152 , m_auditLog(auditLog) 0153 { 0154 qCDebug(KLEOPATRA_LOG) << "\ninputError :" << m_input.errorString << "\noutputError:" << m_output.errorString; 0155 Q_ASSERT(!m_sresult.isNull() || !m_eresult.isNull()); 0156 } 0157 0158 QString overview() const override; 0159 QString details() const override; 0160 GpgME::Error error() const override; 0161 QString errorString() const override; 0162 VisualCode code() const override; 0163 AuditLogEntry auditLog() const override; 0164 0165 private: 0166 const SigningResult m_sresult; 0167 const EncryptionResult m_eresult; 0168 const LabelAndError m_input; 0169 const LabelAndError m_output; 0170 const bool m_outputCreated; 0171 const AuditLogEntry m_auditLog; 0172 }; 0173 0174 static QString makeSigningOverview(const Error &err) 0175 { 0176 if (err.isCanceled()) { 0177 return i18n("Signing canceled."); 0178 } 0179 0180 if (err) { 0181 return i18n("Signing failed."); 0182 } 0183 return i18n("Signing succeeded."); 0184 } 0185 0186 static QString makeResultOverview(const SigningResult &result) 0187 { 0188 return makeSigningOverview(result.error()); 0189 } 0190 0191 static QString makeEncryptionOverview(const Error &err) 0192 { 0193 if (err.isCanceled()) { 0194 return i18n("Encryption canceled."); 0195 } 0196 0197 if (err) { 0198 return i18n("Encryption failed."); 0199 } 0200 0201 return i18n("Encryption succeeded."); 0202 } 0203 0204 static QString makeResultOverview(const EncryptionResult &result) 0205 { 0206 return makeEncryptionOverview(result.error()); 0207 } 0208 0209 static QString makeResultOverview(const SigningResult &sr, const EncryptionResult &er) 0210 { 0211 if (er.isNull() && sr.isNull()) { 0212 return QString(); 0213 } 0214 if (er.isNull()) { 0215 return makeResultOverview(sr); 0216 } 0217 if (sr.isNull()) { 0218 return makeResultOverview(er); 0219 } 0220 if (sr.error().isCanceled() || sr.error()) { 0221 return makeResultOverview(sr); 0222 } 0223 if (er.error().isCanceled() || er.error()) { 0224 return makeResultOverview(er); 0225 } 0226 return i18n("Signing and encryption succeeded."); 0227 } 0228 0229 static QString escape(QString s) 0230 { 0231 s = s.toHtmlEscaped(); 0232 s.replace(QLatin1Char('\n'), QStringLiteral("<br>")); 0233 return s; 0234 } 0235 0236 static QString makeResultDetails(const SigningResult &result, const QString &inputError, const QString &outputError) 0237 { 0238 const Error err = result.error(); 0239 if (err.code() == GPG_ERR_EIO) { 0240 if (!inputError.isEmpty()) { 0241 return i18n("Input error: %1", escape(inputError)); 0242 } else if (!outputError.isEmpty()) { 0243 return i18n("Output error: %1", escape(outputError)); 0244 } 0245 } 0246 0247 if (err || err.isCanceled()) { 0248 return Formatting::errorAsString(err).toHtmlEscaped(); 0249 } 0250 return QString(); 0251 } 0252 0253 static QString makeResultDetails(const EncryptionResult &result, const QString &inputError, const QString &outputError) 0254 { 0255 const Error err = result.error(); 0256 if (err.code() == GPG_ERR_EIO) { 0257 if (!inputError.isEmpty()) { 0258 return i18n("Input error: %1", escape(inputError)); 0259 } else if (!outputError.isEmpty()) { 0260 return i18n("Output error: %1", escape(outputError)); 0261 } 0262 } 0263 0264 if (err || err.isCanceled()) { 0265 return Formatting::errorAsString(err).toHtmlEscaped(); 0266 } 0267 return i18n(" Encryption succeeded."); 0268 } 0269 0270 } 0271 0272 QString ErrorResult::overview() const 0273 { 0274 Q_ASSERT(m_error || m_error.isCanceled()); 0275 Q_ASSERT(m_sign || m_encrypt); 0276 const QString label = formatInputOutputLabel(m_inputLabel, m_outputLabel, true); 0277 const bool canceled = m_error.isCanceled(); 0278 if (m_sign && m_encrypt) { 0279 return canceled ? i18n("%1: <b>Sign/encrypt canceled.</b>", label) : i18n(" %1: Sign/encrypt failed.", label); 0280 } 0281 return i18nc("label: result. Example: foo -> foo.gpg: Encryption failed.", 0282 "%1: <b>%2</b>", 0283 label, 0284 m_sign ? makeSigningOverview(m_error) : makeEncryptionOverview(m_error)); 0285 } 0286 0287 QString ErrorResult::details() const 0288 { 0289 return m_errString; 0290 } 0291 0292 class SignEncryptTask::Private 0293 { 0294 friend class ::Kleo::Crypto::SignEncryptTask; 0295 SignEncryptTask *const q; 0296 0297 public: 0298 explicit Private(SignEncryptTask *qq); 0299 0300 private: 0301 QString inputLabel() const; 0302 QString outputLabel() const; 0303 0304 bool removeExistingOutputFile(); 0305 0306 void startSignEncryptJob(GpgME::Protocol proto); 0307 std::unique_ptr<QGpgME::SignJob> createSignJob(GpgME::Protocol proto); 0308 std::unique_ptr<QGpgME::SignEncryptJob> createSignEncryptJob(GpgME::Protocol proto); 0309 std::unique_ptr<QGpgME::EncryptJob> createEncryptJob(GpgME::Protocol proto); 0310 0311 void startSignEncryptArchiveJob(GpgME::Protocol proto); 0312 std::unique_ptr<QGpgME::SignArchiveJob> createSignArchiveJob(GpgME::Protocol proto); 0313 std::unique_ptr<QGpgME::SignEncryptArchiveJob> createSignEncryptArchiveJob(GpgME::Protocol proto); 0314 std::unique_ptr<QGpgME::EncryptArchiveJob> createEncryptArchiveJob(GpgME::Protocol proto); 0315 0316 std::shared_ptr<const Task::Result> makeErrorResult(const Error &err, const QString &errStr, const AuditLogEntry &auditLog); 0317 0318 private: 0319 void slotResult(const SigningResult &); 0320 void slotResult(const SigningResult &, const EncryptionResult &); 0321 void slotResult(const EncryptionResult &); 0322 0323 void slotResult(const QGpgME::Job *, const SigningResult &, const EncryptionResult &); 0324 0325 private: 0326 std::shared_ptr<Input> input; 0327 std::shared_ptr<Output> output; 0328 QStringList inputFileNames; 0329 QString outputFileName; 0330 std::vector<Key> signers; 0331 std::vector<Key> recipients; 0332 0333 bool sign : 1; 0334 bool encrypt : 1; 0335 bool detached : 1; 0336 bool symmetric : 1; 0337 bool clearsign : 1; 0338 bool archive : 1; 0339 0340 QPointer<QGpgME::Job> job; 0341 QString labelText; 0342 std::shared_ptr<OverwritePolicy> m_overwritePolicy; 0343 }; 0344 0345 SignEncryptTask::Private::Private(SignEncryptTask *qq) 0346 : q{qq} 0347 , sign{true} 0348 , encrypt{true} 0349 , detached{false} 0350 , clearsign{false} 0351 , archive{false} 0352 , m_overwritePolicy{new OverwritePolicy{OverwritePolicy::Ask}} 0353 { 0354 q->setAsciiArmor(true); 0355 } 0356 0357 std::shared_ptr<const Task::Result> SignEncryptTask::Private::makeErrorResult(const Error &err, const QString &errStr, const AuditLogEntry &auditLog) 0358 { 0359 return std::shared_ptr<const ErrorResult>(new ErrorResult(sign, encrypt, err, errStr, inputLabel(), outputLabel(), auditLog)); 0360 } 0361 0362 SignEncryptTask::SignEncryptTask(QObject *p) 0363 : Task(p) 0364 , d(new Private(this)) 0365 { 0366 } 0367 0368 SignEncryptTask::~SignEncryptTask() 0369 { 0370 } 0371 0372 void SignEncryptTask::setInputFileName(const QString &fileName) 0373 { 0374 kleo_assert(!d->job); 0375 kleo_assert(!fileName.isEmpty()); 0376 d->inputFileNames = QStringList(fileName); 0377 } 0378 0379 void SignEncryptTask::setInputFileNames(const QStringList &fileNames) 0380 { 0381 kleo_assert(!d->job); 0382 kleo_assert(!fileNames.empty()); 0383 d->inputFileNames = fileNames; 0384 } 0385 0386 void SignEncryptTask::setInput(const std::shared_ptr<Input> &input) 0387 { 0388 kleo_assert(!d->job); 0389 kleo_assert(input); 0390 d->input = input; 0391 } 0392 0393 void SignEncryptTask::setOutput(const std::shared_ptr<Output> &output) 0394 { 0395 kleo_assert(!d->job); 0396 kleo_assert(output); 0397 d->output = output; 0398 } 0399 0400 void SignEncryptTask::setOutputFileName(const QString &fileName) 0401 { 0402 kleo_assert(!d->job); 0403 kleo_assert(!fileName.isEmpty()); 0404 d->outputFileName = fileName; 0405 } 0406 0407 QString SignEncryptTask::outputFileName() const 0408 { 0409 return d->outputFileName; 0410 } 0411 0412 void SignEncryptTask::setSigners(const std::vector<Key> &signers) 0413 { 0414 kleo_assert(!d->job); 0415 d->signers = signers; 0416 } 0417 0418 void SignEncryptTask::setRecipients(const std::vector<Key> &recipients) 0419 { 0420 kleo_assert(!d->job); 0421 d->recipients = recipients; 0422 } 0423 0424 void SignEncryptTask::setOverwritePolicy(const std::shared_ptr<OverwritePolicy> &policy) 0425 { 0426 kleo_assert(!d->job); 0427 d->m_overwritePolicy = policy; 0428 } 0429 0430 void SignEncryptTask::setSign(bool sign) 0431 { 0432 kleo_assert(!d->job); 0433 d->sign = sign; 0434 } 0435 0436 void SignEncryptTask::setEncrypt(bool encrypt) 0437 { 0438 kleo_assert(!d->job); 0439 d->encrypt = encrypt; 0440 } 0441 0442 void SignEncryptTask::setDetachedSignature(bool detached) 0443 { 0444 kleo_assert(!d->job); 0445 d->detached = detached; 0446 } 0447 0448 void SignEncryptTask::setEncryptSymmetric(bool symmetric) 0449 { 0450 kleo_assert(!d->job); 0451 d->symmetric = symmetric; 0452 } 0453 0454 void SignEncryptTask::setClearsign(bool clearsign) 0455 { 0456 kleo_assert(!d->job); 0457 d->clearsign = clearsign; 0458 } 0459 0460 void SignEncryptTask::setCreateArchive(bool archive) 0461 { 0462 kleo_assert(!d->job); 0463 d->archive = archive; 0464 } 0465 0466 Protocol SignEncryptTask::protocol() const 0467 { 0468 if (d->sign && !d->signers.empty()) { 0469 return d->signers.front().protocol(); 0470 } 0471 if (d->encrypt || d->symmetric) { 0472 if (!d->recipients.empty()) { 0473 return d->recipients.front().protocol(); 0474 } else { 0475 return GpgME::OpenPGP; // symmetric OpenPGP encryption 0476 } 0477 } 0478 throw Kleo::Exception(gpg_error(GPG_ERR_INTERNAL), i18n("Cannot determine protocol for task")); 0479 } 0480 0481 QString SignEncryptTask::label() const 0482 { 0483 if (!d->labelText.isEmpty()) { 0484 return d->labelText; 0485 } 0486 return d->inputLabel(); 0487 } 0488 0489 QString SignEncryptTask::tag() const 0490 { 0491 return Formatting::displayName(protocol()); 0492 } 0493 0494 unsigned long long SignEncryptTask::inputSize() const 0495 { 0496 return d->input ? d->input->size() : 0U; 0497 } 0498 0499 static bool archiveJobsCanBeUsed(GpgME::Protocol protocol) 0500 { 0501 return (protocol == GpgME::OpenPGP) && QGpgME::SignEncryptArchiveJob::isSupported(); 0502 } 0503 0504 void SignEncryptTask::doStart() 0505 { 0506 kleo_assert(!d->job); 0507 if (d->sign) { 0508 kleo_assert(!d->signers.empty()); 0509 if (d->archive) { 0510 kleo_assert(!d->detached && !d->clearsign); 0511 } 0512 } 0513 0514 const auto proto = protocol(); 0515 if (d->archive && archiveJobsCanBeUsed(proto)) { 0516 d->startSignEncryptArchiveJob(proto); 0517 } else { 0518 d->startSignEncryptJob(proto); 0519 } 0520 } 0521 0522 QString SignEncryptTask::Private::inputLabel() const 0523 { 0524 if (input) { 0525 return input->label(); 0526 } 0527 if (!inputFileNames.empty()) { 0528 const auto firstFile = QFileInfo{inputFileNames.front()}.fileName(); 0529 return inputFileNames.size() == 1 ? firstFile : i18nc("<name of first file>, ...", "%1, ...", firstFile); 0530 } 0531 return {}; 0532 } 0533 0534 QString SignEncryptTask::Private::outputLabel() const 0535 { 0536 return output ? output->label() : QFileInfo{outputFileName}.fileName(); 0537 } 0538 0539 bool SignEncryptTask::Private::removeExistingOutputFile() 0540 { 0541 if (QFile::exists(outputFileName)) { 0542 bool fileRemoved = false; 0543 // we should already have asked the user for overwrite permission 0544 if (m_overwritePolicy && (m_overwritePolicy->policy() == OverwritePolicy::Overwrite)) { 0545 qCDebug(KLEOPATRA_LOG) << __func__ << "going to remove file for overwriting" << outputFileName; 0546 fileRemoved = QFile::remove(outputFileName); 0547 if (!fileRemoved) { 0548 qCDebug(KLEOPATRA_LOG) << __func__ << "removing file to overwrite failed"; 0549 } 0550 } else { 0551 qCDebug(KLEOPATRA_LOG) << __func__ << "we have no permission to overwrite" << outputFileName; 0552 } 0553 if (!fileRemoved) { 0554 QMetaObject::invokeMethod( 0555 q, 0556 [this]() { 0557 slotResult(nullptr, SigningResult{}, EncryptionResult{Error::fromCode(GPG_ERR_EEXIST)}); 0558 }, 0559 Qt::QueuedConnection); 0560 return false; 0561 } 0562 } 0563 0564 return true; 0565 } 0566 0567 void SignEncryptTask::Private::startSignEncryptJob(GpgME::Protocol proto) 0568 { 0569 #if QGPGME_FILE_JOBS_SUPPORT_DIRECT_FILE_IO 0570 if (proto == GpgME::OpenPGP) { 0571 // either input and output are both set (e.g. when encrypting the notepad), 0572 // or they are both unset (when encrypting files) 0573 kleo_assert((!input && !output) || (input && output)); 0574 } else { 0575 kleo_assert(input); 0576 0577 if (!output) { 0578 output = Output::createFromFile(outputFileName, m_overwritePolicy); 0579 } 0580 } 0581 #else 0582 kleo_assert(input); 0583 0584 if (!output) { 0585 output = Output::createFromFile(outputFileName, m_overwritePolicy); 0586 } 0587 #endif 0588 0589 if (encrypt || symmetric) { 0590 Context::EncryptionFlags flags{Context::None}; 0591 if (proto == GpgME::OpenPGP) { 0592 flags = static_cast<Context::EncryptionFlags>(flags | Context::AlwaysTrust); 0593 } 0594 if (symmetric) { 0595 flags = static_cast<Context::EncryptionFlags>(flags | Context::Symmetric); 0596 qCDebug(KLEOPATRA_LOG) << "Adding symmetric flag"; 0597 } 0598 if (sign) { 0599 std::unique_ptr<QGpgME::SignEncryptJob> job = createSignEncryptJob(proto); 0600 kleo_assert(job.get()); 0601 #if QGPGME_FILE_JOBS_SUPPORT_DIRECT_FILE_IO 0602 if (proto == GpgME::OpenPGP && !input && !output) { 0603 kleo_assert(inputFileNames.size() == 1); 0604 job->setSigners(signers); 0605 job->setRecipients(recipients); 0606 job->setInputFile(inputFileNames.front()); 0607 job->setOutputFile(outputFileName); 0608 job->setEncryptionFlags(flags); 0609 if (!removeExistingOutputFile()) { 0610 return; 0611 } 0612 job->startIt(); 0613 } else { 0614 if (inputFileNames.size() == 1) { 0615 job->setFileName(inputFileNames.front()); 0616 } 0617 job->start(signers, recipients, input->ioDevice(), output->ioDevice(), flags); 0618 } 0619 #else 0620 if (inputFileNames.size() == 1) { 0621 job->setFileName(inputFileNames.front()); 0622 } 0623 job->start(signers, recipients, input->ioDevice(), output->ioDevice(), flags); 0624 #endif 0625 this->job = job.release(); 0626 } else { 0627 std::unique_ptr<QGpgME::EncryptJob> job = createEncryptJob(proto); 0628 kleo_assert(job.get()); 0629 #if QGPGME_FILE_JOBS_SUPPORT_DIRECT_FILE_IO 0630 if (proto == GpgME::OpenPGP && !input && !output) { 0631 kleo_assert(inputFileNames.size() == 1); 0632 job->setRecipients(recipients); 0633 job->setInputFile(inputFileNames.front()); 0634 job->setOutputFile(outputFileName); 0635 job->setEncryptionFlags(flags); 0636 if (!removeExistingOutputFile()) { 0637 return; 0638 } 0639 job->startIt(); 0640 } else { 0641 if (inputFileNames.size() == 1) { 0642 job->setFileName(inputFileNames.front()); 0643 } 0644 job->start(recipients, input->ioDevice(), output->ioDevice(), flags); 0645 } 0646 #else 0647 if (inputFileNames.size() == 1) { 0648 job->setFileName(inputFileNames.front()); 0649 } 0650 job->start(recipients, input->ioDevice(), output->ioDevice(), flags); 0651 #endif 0652 this->job = job.release(); 0653 } 0654 } else if (sign) { 0655 std::unique_ptr<QGpgME::SignJob> job = createSignJob(proto); 0656 kleo_assert(job.get()); 0657 kleo_assert(!(detached && clearsign)); 0658 const GpgME::SignatureMode sigMode = detached ? GpgME::Detached : clearsign ? GpgME::Clearsigned : GpgME::NormalSignatureMode; 0659 #if QGPGME_FILE_JOBS_SUPPORT_DIRECT_FILE_IO 0660 if (proto == GpgME::OpenPGP && !input && !output) { 0661 kleo_assert(inputFileNames.size() == 1); 0662 job->setSigners(signers); 0663 job->setInputFile(inputFileNames.front()); 0664 job->setOutputFile(outputFileName); 0665 job->setSigningFlags(sigMode); 0666 if (!removeExistingOutputFile()) { 0667 return; 0668 } 0669 job->startIt(); 0670 } else { 0671 job->start(signers, input->ioDevice(), output->ioDevice(), sigMode); 0672 } 0673 #else 0674 job->start(signers, input->ioDevice(), output->ioDevice(), sigMode); 0675 #endif 0676 this->job = job.release(); 0677 } else { 0678 kleo_assert(!"Either 'sign' or 'encrypt' or 'symmetric' must be set!"); 0679 } 0680 } 0681 0682 void SignEncryptTask::cancel() 0683 { 0684 qCDebug(KLEOPATRA_LOG) << this << __func__; 0685 if (d->job) { 0686 d->job->slotCancel(); 0687 } 0688 } 0689 0690 std::unique_ptr<QGpgME::SignJob> SignEncryptTask::Private::createSignJob(GpgME::Protocol proto) 0691 { 0692 const QGpgME::Protocol *const backend = (proto == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime(); 0693 kleo_assert(backend); 0694 std::unique_ptr<QGpgME::SignJob> signJob(backend->signJob(q->asciiArmor(), /*textmode=*/false)); 0695 kleo_assert(signJob.get()); 0696 connect(signJob.get(), &QGpgME::Job::jobProgress, q, &SignEncryptTask::setProgress); 0697 connect(signJob.get(), SIGNAL(result(GpgME::SigningResult, QByteArray)), q, SLOT(slotResult(GpgME::SigningResult))); 0698 return signJob; 0699 } 0700 0701 std::unique_ptr<QGpgME::SignEncryptJob> SignEncryptTask::Private::createSignEncryptJob(GpgME::Protocol proto) 0702 { 0703 const QGpgME::Protocol *const backend = (proto == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime(); 0704 kleo_assert(backend); 0705 std::unique_ptr<QGpgME::SignEncryptJob> signEncryptJob(backend->signEncryptJob(q->asciiArmor(), /*textmode=*/false)); 0706 kleo_assert(signEncryptJob.get()); 0707 connect(signEncryptJob.get(), &QGpgME::Job::jobProgress, q, &SignEncryptTask::setProgress); 0708 connect(signEncryptJob.get(), 0709 SIGNAL(result(GpgME::SigningResult, GpgME::EncryptionResult, QByteArray)), 0710 q, 0711 SLOT(slotResult(GpgME::SigningResult, GpgME::EncryptionResult))); 0712 return signEncryptJob; 0713 } 0714 0715 std::unique_ptr<QGpgME::EncryptJob> SignEncryptTask::Private::createEncryptJob(GpgME::Protocol proto) 0716 { 0717 const QGpgME::Protocol *const backend = (proto == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime(); 0718 kleo_assert(backend); 0719 std::unique_ptr<QGpgME::EncryptJob> encryptJob(backend->encryptJob(q->asciiArmor(), /*textmode=*/false)); 0720 kleo_assert(encryptJob.get()); 0721 connect(encryptJob.get(), &QGpgME::Job::jobProgress, q, &SignEncryptTask::setProgress); 0722 connect(encryptJob.get(), SIGNAL(result(GpgME::EncryptionResult, QByteArray)), q, SLOT(slotResult(GpgME::EncryptionResult))); 0723 return encryptJob; 0724 } 0725 0726 void SignEncryptTask::Private::startSignEncryptArchiveJob(GpgME::Protocol proto) 0727 { 0728 kleo_assert(!input); 0729 kleo_assert(!output); 0730 0731 #if !QGPGME_ARCHIVE_JOBS_SUPPORT_OUTPUT_FILENAME 0732 output = Output::createFromFile(outputFileName, m_overwritePolicy); 0733 #endif 0734 0735 const auto baseDirectory = heuristicBaseDirectory(inputFileNames); 0736 if (baseDirectory.isEmpty()) { 0737 throw Kleo::Exception(GPG_ERR_CONFLICT, i18n("Cannot find common base directory for these files:\n%1", inputFileNames.join(QLatin1Char('\n')))); 0738 } 0739 qCDebug(KLEOPATRA_LOG) << "heuristicBaseDirectory(" << inputFileNames << ") ->" << baseDirectory; 0740 const auto tempPaths = makeRelativeTo(baseDirectory, inputFileNames); 0741 const auto relativePaths = std::vector<QString>{tempPaths.begin(), tempPaths.end()}; 0742 qCDebug(KLEOPATRA_LOG) << "relative paths:" << relativePaths; 0743 0744 if (encrypt || symmetric) { 0745 Context::EncryptionFlags flags{Context::None}; 0746 if (proto == GpgME::OpenPGP) { 0747 flags = static_cast<Context::EncryptionFlags>(flags | Context::AlwaysTrust); 0748 } 0749 if (symmetric) { 0750 flags = static_cast<Context::EncryptionFlags>(flags | Context::Symmetric); 0751 qCDebug(KLEOPATRA_LOG) << "Adding symmetric flag"; 0752 } 0753 if (sign) { 0754 labelText = i18nc("@info", "Creating signed and encrypted archive ..."); 0755 std::unique_ptr<QGpgME::SignEncryptArchiveJob> job = createSignEncryptArchiveJob(proto); 0756 kleo_assert(job.get()); 0757 job->setBaseDirectory(baseDirectory); 0758 #if QGPGME_ARCHIVE_JOBS_SUPPORT_OUTPUT_FILENAME 0759 job->setSigners(signers); 0760 job->setRecipients(recipients); 0761 job->setInputPaths(relativePaths); 0762 job->setOutputFile(outputFileName); 0763 job->setEncryptionFlags(flags); 0764 if (!removeExistingOutputFile()) { 0765 return; 0766 } 0767 job->startIt(); 0768 #else 0769 job->start(signers, recipients, relativePaths, output->ioDevice(), flags); 0770 #endif 0771 0772 this->job = job.release(); 0773 } else { 0774 labelText = i18nc("@info", "Creating encrypted archive ..."); 0775 std::unique_ptr<QGpgME::EncryptArchiveJob> job = createEncryptArchiveJob(proto); 0776 kleo_assert(job.get()); 0777 job->setBaseDirectory(baseDirectory); 0778 #if QGPGME_ARCHIVE_JOBS_SUPPORT_OUTPUT_FILENAME 0779 job->setRecipients(recipients); 0780 job->setInputPaths(relativePaths); 0781 job->setOutputFile(outputFileName); 0782 job->setEncryptionFlags(flags); 0783 if (!removeExistingOutputFile()) { 0784 return; 0785 } 0786 job->startIt(); 0787 #else 0788 job->start(recipients, relativePaths, output->ioDevice(), flags); 0789 #endif 0790 0791 this->job = job.release(); 0792 } 0793 } else if (sign) { 0794 labelText = i18nc("@info", "Creating signed archive ..."); 0795 std::unique_ptr<QGpgME::SignArchiveJob> job = createSignArchiveJob(proto); 0796 kleo_assert(job.get()); 0797 job->setBaseDirectory(baseDirectory); 0798 #if QGPGME_ARCHIVE_JOBS_SUPPORT_OUTPUT_FILENAME 0799 job->setSigners(signers); 0800 job->setInputPaths(relativePaths); 0801 job->setOutputFile(outputFileName); 0802 if (!removeExistingOutputFile()) { 0803 return; 0804 } 0805 job->startIt(); 0806 #else 0807 job->start(signers, relativePaths, output->ioDevice()); 0808 #endif 0809 0810 this->job = job.release(); 0811 } else { 0812 kleo_assert(!"Either 'sign' or 'encrypt' or 'symmetric' must be set!"); 0813 } 0814 } 0815 0816 std::unique_ptr<QGpgME::SignArchiveJob> SignEncryptTask::Private::createSignArchiveJob(GpgME::Protocol proto) 0817 { 0818 const QGpgME::Protocol *const backend = (proto == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime(); 0819 kleo_assert(backend); 0820 std::unique_ptr<QGpgME::SignArchiveJob> signJob(backend->signArchiveJob(q->asciiArmor())); 0821 auto job = signJob.get(); 0822 kleo_assert(job); 0823 connect(job, &QGpgME::SignArchiveJob::dataProgress, q, &SignEncryptTask::setProgress); 0824 connect(job, &QGpgME::SignArchiveJob::result, q, [this, job](const GpgME::SigningResult &signResult) { 0825 slotResult(job, signResult, EncryptionResult{}); 0826 }); 0827 return signJob; 0828 } 0829 0830 std::unique_ptr<QGpgME::SignEncryptArchiveJob> SignEncryptTask::Private::createSignEncryptArchiveJob(GpgME::Protocol proto) 0831 { 0832 const QGpgME::Protocol *const backend = (proto == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime(); 0833 kleo_assert(backend); 0834 std::unique_ptr<QGpgME::SignEncryptArchiveJob> signEncryptJob(backend->signEncryptArchiveJob(q->asciiArmor())); 0835 auto job = signEncryptJob.get(); 0836 kleo_assert(job); 0837 connect(job, &QGpgME::SignEncryptArchiveJob::dataProgress, q, &SignEncryptTask::setProgress); 0838 connect(job, &QGpgME::SignEncryptArchiveJob::result, q, [this, job](const GpgME::SigningResult &signResult, const GpgME::EncryptionResult &encryptResult) { 0839 slotResult(job, signResult, encryptResult); 0840 }); 0841 return signEncryptJob; 0842 } 0843 0844 std::unique_ptr<QGpgME::EncryptArchiveJob> SignEncryptTask::Private::createEncryptArchiveJob(GpgME::Protocol proto) 0845 { 0846 const QGpgME::Protocol *const backend = (proto == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime(); 0847 kleo_assert(backend); 0848 std::unique_ptr<QGpgME::EncryptArchiveJob> encryptJob(backend->encryptArchiveJob(q->asciiArmor())); 0849 auto job = encryptJob.get(); 0850 kleo_assert(job); 0851 connect(job, &QGpgME::EncryptArchiveJob::dataProgress, q, &SignEncryptTask::setProgress); 0852 connect(job, &QGpgME::EncryptArchiveJob::result, q, [this, job](const GpgME::EncryptionResult &encryptResult) { 0853 slotResult(job, SigningResult{}, encryptResult); 0854 }); 0855 return encryptJob; 0856 } 0857 0858 void SignEncryptTask::Private::slotResult(const SigningResult &result) 0859 { 0860 slotResult(qobject_cast<const QGpgME::Job *>(q->sender()), result, EncryptionResult{}); 0861 } 0862 0863 void SignEncryptTask::Private::slotResult(const SigningResult &sresult, const EncryptionResult &eresult) 0864 { 0865 slotResult(qobject_cast<const QGpgME::Job *>(q->sender()), sresult, eresult); 0866 } 0867 0868 void SignEncryptTask::Private::slotResult(const EncryptionResult &result) 0869 { 0870 slotResult(qobject_cast<const QGpgME::Job *>(q->sender()), SigningResult{}, result); 0871 } 0872 0873 void SignEncryptTask::Private::slotResult(const QGpgME::Job *job, const SigningResult &sresult, const EncryptionResult &eresult) 0874 { 0875 qCDebug(KLEOPATRA_LOG) << q << __func__ << "job:" << job << "signing result:" << QGpgME::toLogString(sresult) 0876 << "encryption result:" << QGpgME::toLogString(eresult); 0877 const AuditLogEntry auditLog = AuditLogEntry::fromJob(job); 0878 bool outputCreated = false; 0879 if (input && input->failed()) { 0880 if (output) { 0881 output->cancel(); 0882 } 0883 q->emitResult(makeErrorResult(Error::fromCode(GPG_ERR_EIO), i18n("Input error: %1", escape(input->errorString())), auditLog)); 0884 return; 0885 } else if (sresult.error().code() || eresult.error().code()) { 0886 if (output) { 0887 output->cancel(); 0888 } 0889 if (!outputFileName.isEmpty() && eresult.error().code() != GPG_ERR_EEXIST) { 0890 // ensure that the output file is removed if the task was canceled or an error occurred; 0891 // unless a "file exists" error occurred because this means that the file with the name 0892 // of outputFileName wasn't created as result of this task 0893 if (QFile::exists(outputFileName)) { 0894 qCDebug(KLEOPATRA_LOG) << __func__ << "Removing output file" << outputFileName << "after error or cancel"; 0895 if (!QFile::remove(outputFileName)) { 0896 qCDebug(KLEOPATRA_LOG) << __func__ << "Removing output file" << outputFileName << "failed"; 0897 } 0898 } 0899 } 0900 } else { 0901 try { 0902 kleo_assert(!sresult.isNull() || !eresult.isNull()); 0903 if (output) { 0904 output->finalize(); 0905 } 0906 outputCreated = true; 0907 if (input) { 0908 input->finalize(); 0909 } 0910 } catch (const GpgME::Exception &e) { 0911 q->emitResult(makeErrorResult(e.error(), QString::fromLocal8Bit(e.what()), auditLog)); 0912 return; 0913 } 0914 } 0915 0916 const LabelAndError inputInfo{inputLabel(), input ? input->errorString() : QString{}}; 0917 const LabelAndError outputInfo{outputLabel(), output ? output->errorString() : QString{}}; 0918 q->emitResult(std::shared_ptr<Result>(new SignEncryptFilesResult(sresult, eresult, inputInfo, outputInfo, outputCreated, auditLog))); 0919 } 0920 0921 QString SignEncryptFilesResult::overview() const 0922 { 0923 const QString files = formatInputOutputLabel(m_input.label, m_output.label, !m_outputCreated); 0924 return files + QLatin1StringView(": ") + makeOverview(makeResultOverview(m_sresult, m_eresult)); 0925 } 0926 0927 QString SignEncryptFilesResult::details() const 0928 { 0929 return errorString(); 0930 } 0931 0932 GpgME::Error SignEncryptFilesResult::error() const 0933 { 0934 if (m_sresult.error().code()) { 0935 return m_sresult.error(); 0936 } 0937 if (m_eresult.error().code()) { 0938 return m_eresult.error(); 0939 } 0940 return {}; 0941 } 0942 0943 QString SignEncryptFilesResult::errorString() const 0944 { 0945 const bool sign = !m_sresult.isNull(); 0946 const bool encrypt = !m_eresult.isNull(); 0947 0948 kleo_assert(sign || encrypt); 0949 0950 if (sign && encrypt) { 0951 return m_sresult.error().code() ? makeResultDetails(m_sresult, m_input.errorString, m_output.errorString) 0952 : m_eresult.error().code() ? makeResultDetails(m_eresult, m_input.errorString, m_output.errorString) 0953 : QString(); 0954 } 0955 0956 return sign ? makeResultDetails(m_sresult, m_input.errorString, m_output.errorString) // 0957 : makeResultDetails(m_eresult, m_input.errorString, m_output.errorString); 0958 } 0959 0960 Task::Result::VisualCode SignEncryptFilesResult::code() const 0961 { 0962 if (m_sresult.error().isCanceled() || m_eresult.error().isCanceled()) { 0963 return Warning; 0964 } 0965 return (m_sresult.error().code() || m_eresult.error().code()) ? NeutralError : NeutralSuccess; 0966 } 0967 0968 AuditLogEntry SignEncryptFilesResult::auditLog() const 0969 { 0970 return m_auditLog; 0971 } 0972 0973 #include "moc_signencrypttask.cpp"