File indexing completed on 2024-06-16 04:55:59

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 &rarr; %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"