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

0001 /* -*- mode: c++; c-basic-offset:4 -*-
0002     crypto/signemailtask.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 "signemailtask.h"
0013 
0014 #include <utils/input.h>
0015 #include <utils/kleo_assert.h>
0016 #include <utils/output.h>
0017 
0018 #include <Libkleo/AuditLogEntry>
0019 #include <Libkleo/Formatting>
0020 #include <Libkleo/Stl_Util>
0021 
0022 #include <QGpgME/Protocol>
0023 #include <QGpgME/SignJob>
0024 
0025 #include <gpgme++/key.h>
0026 #include <gpgme++/signingresult.h>
0027 
0028 #include <KLocalizedString>
0029 
0030 #include <QPointer>
0031 #include <QTextDocument> // for Qt::escape
0032 
0033 #include <algorithm>
0034 #include <functional>
0035 
0036 using namespace Kleo;
0037 using namespace Kleo::Crypto;
0038 using namespace GpgME;
0039 
0040 namespace
0041 {
0042 
0043 class SignEMailResult : public Task::Result
0044 {
0045     const SigningResult m_result;
0046     const AuditLogEntry m_auditLog;
0047 
0048 public:
0049     explicit SignEMailResult(const SigningResult &r, const AuditLogEntry &auditLog)
0050         : Task::Result()
0051         , m_result(r)
0052         , m_auditLog(auditLog)
0053     {
0054     }
0055 
0056     QString overview() const override;
0057     QString details() const override;
0058     GpgME::Error error() const override;
0059     QString errorString() const override;
0060     VisualCode code() const override;
0061     AuditLogEntry auditLog() const override;
0062 };
0063 
0064 QString makeResultString(const SigningResult &res)
0065 {
0066     const Error err = res.error();
0067 
0068     if (err.isCanceled()) {
0069         return i18n("Signing canceled.");
0070     }
0071 
0072     if (err) {
0073         return i18n("Signing failed: %1", Formatting::errorAsString(err).toHtmlEscaped());
0074     }
0075 
0076     return i18n("Signing succeeded.");
0077 }
0078 }
0079 
0080 class SignEMailTask::Private
0081 {
0082     friend class ::Kleo::Crypto::SignEMailTask;
0083     SignEMailTask *const q;
0084 
0085 public:
0086     explicit Private(SignEMailTask *qq);
0087 
0088 private:
0089     std::unique_ptr<QGpgME::SignJob> createJob(GpgME::Protocol proto);
0090 
0091 private:
0092     void slotResult(const SigningResult &);
0093 
0094 private:
0095     std::shared_ptr<Input> input;
0096     std::shared_ptr<Output> output;
0097     std::vector<Key> signers;
0098     bool detached;
0099     bool clearsign;
0100 
0101     QString micAlg;
0102 
0103     QPointer<QGpgME::SignJob> job;
0104 };
0105 
0106 SignEMailTask::Private::Private(SignEMailTask *qq)
0107     : q(qq)
0108     , input()
0109     , output()
0110     , signers()
0111     , detached(false)
0112     , clearsign(false)
0113     , micAlg()
0114     , job(nullptr)
0115 {
0116 }
0117 
0118 SignEMailTask::SignEMailTask(QObject *p)
0119     : Task(p)
0120     , d(new Private(this))
0121 {
0122 }
0123 
0124 SignEMailTask::~SignEMailTask()
0125 {
0126 }
0127 
0128 void SignEMailTask::setInput(const std::shared_ptr<Input> &input)
0129 {
0130     kleo_assert(!d->job);
0131     kleo_assert(input);
0132     d->input = input;
0133 }
0134 
0135 void SignEMailTask::setOutput(const std::shared_ptr<Output> &output)
0136 {
0137     kleo_assert(!d->job);
0138     kleo_assert(output);
0139     d->output = output;
0140 }
0141 
0142 void SignEMailTask::setSigners(const std::vector<Key> &signers)
0143 {
0144     kleo_assert(!d->job);
0145     kleo_assert(!signers.empty());
0146     kleo_assert(std::none_of(signers.cbegin(), signers.cend(), std::mem_fn(&Key::isNull)));
0147     d->signers = signers;
0148 }
0149 
0150 void SignEMailTask::setDetachedSignature(bool detached)
0151 {
0152     kleo_assert(!d->job);
0153     d->detached = detached;
0154     d->clearsign = false;
0155 }
0156 
0157 void SignEMailTask::setClearsign(bool clear)
0158 {
0159     kleo_assert(!d->job);
0160     d->clearsign = clear;
0161     d->detached = false;
0162 }
0163 
0164 Protocol SignEMailTask::protocol() const
0165 {
0166     kleo_assert(!d->signers.empty());
0167     return d->signers.front().protocol();
0168 }
0169 
0170 QString SignEMailTask::label() const
0171 {
0172     return d->input ? d->input->label() : QString();
0173 }
0174 
0175 unsigned long long SignEMailTask::inputSize() const
0176 {
0177     return d->input ? d->input->size() : 0;
0178 }
0179 
0180 void SignEMailTask::doStart()
0181 {
0182     kleo_assert(!d->job);
0183     kleo_assert(d->input);
0184     kleo_assert(d->output);
0185     kleo_assert(!d->signers.empty());
0186 
0187     d->micAlg.clear();
0188 
0189     std::unique_ptr<QGpgME::SignJob> job = d->createJob(protocol());
0190     kleo_assert(job.get());
0191 
0192     job->start(d->signers,
0193                d->input->ioDevice(),
0194                d->output->ioDevice(),
0195                d->clearsign      ? GpgME::Clearsigned
0196                    : d->detached ? GpgME::Detached
0197                                  : GpgME::NormalSignatureMode);
0198 
0199     d->job = job.release();
0200 }
0201 
0202 void SignEMailTask::cancel()
0203 {
0204     if (d->job) {
0205         d->job->slotCancel();
0206     }
0207 }
0208 
0209 std::unique_ptr<QGpgME::SignJob> SignEMailTask::Private::createJob(GpgME::Protocol proto)
0210 {
0211     const QGpgME::Protocol *const backend = (proto == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime();
0212     kleo_assert(backend);
0213     bool shouldArmor = (proto == OpenPGP || q->asciiArmor()) && !output->binaryOpt();
0214     std::unique_ptr<QGpgME::SignJob> signJob(backend->signJob(/*armor=*/shouldArmor, /*textmode=*/false));
0215     kleo_assert(signJob.get());
0216     if (proto == CMS && !q->asciiArmor() && !output->binaryOpt()) {
0217         signJob->setOutputIsBase64Encoded(true);
0218     }
0219     connect(signJob.get(), &QGpgME::Job::jobProgress, q, &SignEMailTask::setProgress);
0220     connect(signJob.get(), SIGNAL(result(GpgME::SigningResult, QByteArray)), q, SLOT(slotResult(GpgME::SigningResult)));
0221     return signJob;
0222 }
0223 
0224 static QString collect_micalgs(const GpgME::SigningResult &result, GpgME::Protocol proto)
0225 {
0226     const std::vector<GpgME::CreatedSignature> css = result.createdSignatures();
0227     QStringList micalgs;
0228     std::transform(css.begin(), css.end(), std::back_inserter(micalgs), [](const GpgME::CreatedSignature &sig) {
0229         return QString::fromLatin1(sig.hashAlgorithmAsString()).toLower();
0230     });
0231     if (proto == GpgME::OpenPGP)
0232         for (QStringList::iterator it = micalgs.begin(), end = micalgs.end(); it != end; ++it) {
0233             it->prepend(QLatin1StringView("pgp-"));
0234         }
0235     micalgs.sort();
0236     micalgs.erase(std::unique(micalgs.begin(), micalgs.end()), micalgs.end());
0237     return micalgs.join(QLatin1Char(','));
0238 }
0239 
0240 void SignEMailTask::Private::slotResult(const SigningResult &result)
0241 {
0242     const auto *const job = qobject_cast<const QGpgME::Job *>(q->sender());
0243     if (result.error().code()) {
0244         output->cancel();
0245     } else {
0246         output->finalize();
0247         micAlg = collect_micalgs(result, q->protocol());
0248     }
0249     q->emitResult(std::shared_ptr<Result>(new SignEMailResult(result, AuditLogEntry::fromJob(job))));
0250 }
0251 
0252 QString SignEMailTask::micAlg() const
0253 {
0254     return d->micAlg;
0255 }
0256 
0257 QString SignEMailResult::overview() const
0258 {
0259     return makeOverview(makeResultString(m_result));
0260 }
0261 
0262 QString SignEMailResult::details() const
0263 {
0264     return QString();
0265 }
0266 
0267 GpgME::Error SignEMailResult::error() const
0268 {
0269     return m_result.error();
0270 }
0271 
0272 QString SignEMailResult::errorString() const
0273 {
0274     return hasError() ? makeResultString(m_result) : QString();
0275 }
0276 
0277 Task::Result::VisualCode SignEMailResult::code() const
0278 {
0279     if (m_result.error().isCanceled()) {
0280         return Warning;
0281     }
0282     return m_result.error().code() ? NeutralError : NeutralSuccess;
0283 }
0284 
0285 AuditLogEntry SignEMailResult::auditLog() const
0286 {
0287     return m_auditLog;
0288 }
0289 
0290 #include "moc_signemailtask.cpp"