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"