File indexing completed on 2024-06-23 05:18:33

0001 /*
0002   SPDX-FileCopyrightText: 2009 Klaralvdalens Datakonsult AB, a KDAB Group company, info@kdab.net
0003   SPDX-FileCopyrightText: 2009 Leo Franchi <lfranchi@kde.org>
0004 
0005   SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "job/signencryptjob.h"
0009 
0010 #include "contentjobbase_p.h"
0011 #include "job/protectedheadersjob.h"
0012 #include "utils/util_p.h"
0013 
0014 #include <QGpgME/Protocol>
0015 #include <QGpgME/SignEncryptJob>
0016 
0017 #include "messagecomposer_debug.h"
0018 #include <KMime/Content>
0019 #include <KMime/Headers>
0020 #include <KMime/KMimeMessage>
0021 
0022 #include <gpgme++/encryptionresult.h>
0023 #include <gpgme++/global.h>
0024 #include <gpgme++/signingresult.h>
0025 #include <sstream>
0026 
0027 using namespace MessageComposer;
0028 
0029 class MessageComposer::SignEncryptJobPrivate : public ContentJobBasePrivate
0030 {
0031 public:
0032     SignEncryptJobPrivate(SignEncryptJob *qq)
0033         : ContentJobBasePrivate(qq)
0034     {
0035     }
0036 
0037     std::vector<GpgME::Key> signers;
0038 
0039     std::vector<GpgME::Key> encKeys;
0040     QStringList recipients;
0041     Kleo::CryptoMessageFormat format;
0042     KMime::Content *content = nullptr;
0043     KMime::Message *skeletonMessage = nullptr;
0044 
0045     bool protectedHeaders = true;
0046     bool protectedHeadersObvoscate = false;
0047 
0048     // copied from messagecomposer.cpp
0049     bool binaryHint(Kleo::CryptoMessageFormat f)
0050     {
0051         switch (f) {
0052         case Kleo::SMIMEFormat:
0053         case Kleo::SMIMEOpaqueFormat:
0054             return true;
0055         default:
0056         case Kleo::OpenPGPMIMEFormat:
0057         case Kleo::InlineOpenPGPFormat:
0058             return false;
0059         }
0060     }
0061 
0062     Q_DECLARE_PUBLIC(SignEncryptJob)
0063 };
0064 
0065 SignEncryptJob::SignEncryptJob(QObject *parent)
0066     : ContentJobBase(*new SignEncryptJobPrivate(this), parent)
0067 {
0068 }
0069 
0070 SignEncryptJob::~SignEncryptJob() = default;
0071 
0072 void SignEncryptJob::setContent(KMime::Content *content)
0073 {
0074     Q_D(SignEncryptJob);
0075 
0076     Q_ASSERT(content);
0077 
0078     d->content = content;
0079 }
0080 
0081 void SignEncryptJob::setCryptoMessageFormat(Kleo::CryptoMessageFormat format)
0082 {
0083     Q_D(SignEncryptJob);
0084 
0085     // There *must* be a concrete format set at this point.
0086     Q_ASSERT(format == Kleo::OpenPGPMIMEFormat || format == Kleo::InlineOpenPGPFormat || format == Kleo::SMIMEFormat || format == Kleo::SMIMEOpaqueFormat);
0087     d->format = format;
0088 }
0089 
0090 void SignEncryptJob::setSigningKeys(const std::vector<GpgME::Key> &signers)
0091 {
0092     Q_D(SignEncryptJob);
0093 
0094     d->signers = signers;
0095 }
0096 
0097 KMime::Content *SignEncryptJob::origContent()
0098 {
0099     Q_D(SignEncryptJob);
0100 
0101     return d->content;
0102 }
0103 
0104 void SignEncryptJob::setEncryptionKeys(const std::vector<GpgME::Key> &keys)
0105 {
0106     Q_D(SignEncryptJob);
0107 
0108     d->encKeys = keys;
0109 }
0110 
0111 void SignEncryptJob::setRecipients(const QStringList &recipients)
0112 {
0113     Q_D(SignEncryptJob);
0114 
0115     d->recipients = recipients;
0116 }
0117 
0118 void SignEncryptJob::setSkeletonMessage(KMime::Message *skeletonMessage)
0119 {
0120     Q_D(SignEncryptJob);
0121 
0122     d->skeletonMessage = skeletonMessage;
0123 }
0124 
0125 void SignEncryptJob::setProtectedHeaders(bool protectedHeaders)
0126 {
0127     Q_D(SignEncryptJob);
0128 
0129     d->protectedHeaders = protectedHeaders;
0130 }
0131 
0132 void SignEncryptJob::setProtectedHeadersObvoscate(bool protectedHeadersObvoscate)
0133 {
0134     Q_D(SignEncryptJob);
0135 
0136     d->protectedHeadersObvoscate = protectedHeadersObvoscate;
0137 }
0138 
0139 QStringList SignEncryptJob::recipients() const
0140 {
0141     Q_D(const SignEncryptJob);
0142 
0143     return d->recipients;
0144 }
0145 
0146 std::vector<GpgME::Key> SignEncryptJob::encryptionKeys() const
0147 {
0148     Q_D(const SignEncryptJob);
0149 
0150     return d->encKeys;
0151 }
0152 
0153 void SignEncryptJob::doStart()
0154 {
0155     Q_D(SignEncryptJob);
0156     Q_ASSERT(d->resultContent == nullptr); // Not processed before.
0157 
0158     if (d->protectedHeaders && d->skeletonMessage && d->format & Kleo::OpenPGPMIMEFormat) {
0159         auto pJob = new ProtectedHeadersJob;
0160         pJob->setContent(d->content);
0161         pJob->setSkeletonMessage(d->skeletonMessage);
0162         pJob->setObvoscate(d->protectedHeadersObvoscate);
0163         QObject::connect(pJob, &ProtectedHeadersJob::finished, this, [d, pJob](KJob *job) {
0164             if (job->error()) {
0165                 return;
0166             }
0167             d->content = pJob->content();
0168         });
0169         appendSubjob(pJob);
0170     }
0171 
0172     ContentJobBase::doStart();
0173 }
0174 
0175 void SignEncryptJob::slotResult(KJob *job)
0176 {
0177     // Q_D(SignEncryptJob);
0178     if (error() || job->error()) {
0179         ContentJobBase::slotResult(job);
0180         return;
0181     }
0182     if (subjobs().size() == 2) {
0183         auto pjob = static_cast<ProtectedHeadersJob *>(subjobs().last());
0184         if (pjob) {
0185             auto cjob = qobject_cast<ContentJobBase *>(job);
0186             Q_ASSERT(cjob);
0187             pjob->setContent(cjob->content());
0188         }
0189     }
0190 
0191     ContentJobBase::slotResult(job);
0192 }
0193 
0194 void SignEncryptJob::process()
0195 {
0196     Q_D(SignEncryptJob);
0197     Q_ASSERT(d->resultContent == nullptr); // Not processed before.
0198 
0199     // if setContent hasn't been called, we assume that a subjob was added
0200     // and we want to use that
0201     if (!d->content || !d->content->hasContent()) {
0202         Q_ASSERT(d->subjobContents.size() == 1);
0203         d->content = d->subjobContents.constFirst();
0204     }
0205 
0206     const QGpgME::Protocol *proto = nullptr;
0207     if (d->format & Kleo::AnyOpenPGP) {
0208         proto = QGpgME::openpgp();
0209     } else if (d->format & Kleo::AnySMIME) {
0210         proto = QGpgME::smime();
0211     } else {
0212         return;
0213     }
0214     Q_ASSERT(proto);
0215     // d->resultContent = new KMime::Content;
0216 
0217     qCDebug(MESSAGECOMPOSER_LOG) << "creating signencrypt from:" << proto->name() << proto->displayName();
0218 
0219     QByteArray encBody;
0220     d->content->assemble();
0221 
0222     // replace simple LFs by CRLFs for all MIME supporting CryptPlugs
0223     // according to RfC 2633, 3.1.1 Canonicalization
0224     QByteArray content;
0225     if (d->format & Kleo::InlineOpenPGPFormat) {
0226         content = d->content->body();
0227     } else if (!(d->format & Kleo::SMIMEOpaqueFormat)) {
0228         content = KMime::LFtoCRLF(d->content->encodedContent());
0229     } else { // SMimeOpaque doesn't need LFtoCRLF, else it gets munged
0230         content = d->content->encodedContent();
0231     }
0232 
0233     QGpgME::SignEncryptJob *job(proto->signEncryptJob(!d->binaryHint(d->format), d->format == Kleo::InlineOpenPGPFormat));
0234     QObject::connect(job,
0235                      &QGpgME::SignEncryptJob::result,
0236                      this,
0237                      [this, d](const GpgME::SigningResult &signingResult,
0238                                const GpgME::EncryptionResult &encryptionResult,
0239                                const QByteArray &cipherText,
0240                                const QString &auditLogAsHtml,
0241                                const GpgME::Error &auditLogError) {
0242                          Q_UNUSED(auditLogAsHtml)
0243                          Q_UNUSED(auditLogError)
0244                          if (signingResult.error()) {
0245                              qCDebug(MESSAGECOMPOSER_LOG) << "signing failed:" << signingResult.error().asString();
0246                              setError(signingResult.error().code());
0247                              setErrorText(QString::fromLocal8Bit(signingResult.error().asString()));
0248                              emitResult();
0249                              return;
0250                          }
0251                          if (encryptionResult.error()) {
0252                              qCDebug(MESSAGECOMPOSER_LOG) << "encrypting failed:" << encryptionResult.error().asString();
0253                              setError(encryptionResult.error().code());
0254                              setErrorText(QString::fromLocal8Bit(encryptionResult.error().asString()));
0255                              emitResult();
0256                              return;
0257                          }
0258 
0259                          QByteArray signatureHashAlgo = signingResult.createdSignature(0).hashAlgorithmAsString();
0260                          d->resultContent = MessageComposer::Util::composeHeadersAndBody(d->content, cipherText, d->format, false, signatureHashAlgo);
0261 
0262                          emitResult();
0263                      });
0264 
0265     const auto error = job->start(d->signers, d->encKeys, content, false);
0266     if (error.code()) {
0267         job->deleteLater();
0268         setError(error.code());
0269         setErrorText(QString::fromLocal8Bit(error.asString()));
0270         emitResult();
0271     }
0272 }
0273 
0274 #include "moc_signencryptjob.cpp"