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

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 <config-messagelib.h>
0009 
0010 #include "job/encryptjob.h"
0011 
0012 #include "contentjobbase_p.h"
0013 #include "job/protectedheadersjob.h"
0014 #include "utils/util_p.h"
0015 
0016 #include <QGpgME/EncryptJob>
0017 #include <QGpgME/Protocol>
0018 
0019 #include "messagecomposer_debug.h"
0020 
0021 #include <gpgme++/encryptionresult.h>
0022 #include <gpgme++/global.h>
0023 #include <gpgme++/signingresult.h>
0024 #include <sstream>
0025 
0026 using namespace MessageComposer;
0027 
0028 class MessageComposer::EncryptJobPrivate : public ContentJobBasePrivate
0029 {
0030 public:
0031     EncryptJobPrivate(EncryptJob *qq)
0032         : ContentJobBasePrivate(qq)
0033     {
0034     }
0035 
0036     QString gnupgHome;
0037     QStringList recipients;
0038     std::vector<GpgME::Key> keys;
0039     Kleo::CryptoMessageFormat format;
0040     KMime::Content *content = nullptr;
0041     KMime::Message *skeletonMessage = nullptr;
0042 
0043     bool protectedHeaders = true;
0044     bool protectedHeadersObvoscate = false;
0045 
0046     // copied from messagecomposer.cpp
0047     bool binaryHint(Kleo::CryptoMessageFormat f)
0048     {
0049         switch (f) {
0050         case Kleo::SMIMEFormat:
0051         case Kleo::SMIMEOpaqueFormat:
0052             return true;
0053         default:
0054         case Kleo::OpenPGPMIMEFormat:
0055         case Kleo::InlineOpenPGPFormat:
0056             return false;
0057         }
0058     }
0059 
0060     GpgME::SignatureMode signingMode(Kleo::CryptoMessageFormat f)
0061     {
0062         switch (f) {
0063         case Kleo::SMIMEOpaqueFormat:
0064             return GpgME::NormalSignatureMode;
0065         case Kleo::InlineOpenPGPFormat:
0066             return GpgME::Clearsigned;
0067         default:
0068         case Kleo::SMIMEFormat:
0069         case Kleo::OpenPGPMIMEFormat:
0070             return GpgME::Detached;
0071         }
0072     }
0073 
0074     Q_DECLARE_PUBLIC(EncryptJob)
0075 };
0076 
0077 EncryptJob::EncryptJob(QObject *parent)
0078     : ContentJobBase(*new EncryptJobPrivate(this), parent)
0079 {
0080 }
0081 
0082 EncryptJob::~EncryptJob() = default;
0083 
0084 void EncryptJob::setContent(KMime::Content *content)
0085 {
0086     Q_D(EncryptJob);
0087 
0088     d->content = content;
0089     d->content->assemble();
0090 }
0091 
0092 void EncryptJob::setCryptoMessageFormat(Kleo::CryptoMessageFormat format)
0093 {
0094     Q_D(EncryptJob);
0095 
0096     d->format = format;
0097 }
0098 
0099 void EncryptJob::setEncryptionKeys(const std::vector<GpgME::Key> &keys)
0100 {
0101     Q_D(EncryptJob);
0102 
0103     d->keys = keys;
0104 }
0105 
0106 void EncryptJob::setRecipients(const QStringList &recipients)
0107 {
0108     Q_D(EncryptJob);
0109 
0110     d->recipients = recipients;
0111 }
0112 
0113 void EncryptJob::setSkeletonMessage(KMime::Message *skeletonMessage)
0114 {
0115     Q_D(EncryptJob);
0116 
0117     d->skeletonMessage = skeletonMessage;
0118 }
0119 
0120 void EncryptJob::setProtectedHeaders(bool protectedHeaders)
0121 {
0122     Q_D(EncryptJob);
0123 
0124     d->protectedHeaders = protectedHeaders;
0125 }
0126 
0127 void EncryptJob::setProtectedHeadersObvoscate(bool protectedHeadersObvoscate)
0128 {
0129     Q_D(EncryptJob);
0130 
0131     d->protectedHeadersObvoscate = protectedHeadersObvoscate;
0132 }
0133 
0134 void EncryptJob::setGnupgHome(const QString &path)
0135 {
0136     Q_D(EncryptJob);
0137 
0138     d->gnupgHome = path;
0139 }
0140 
0141 QStringList EncryptJob::recipients() const
0142 {
0143     Q_D(const EncryptJob);
0144 
0145     return d->recipients;
0146 }
0147 
0148 std::vector<GpgME::Key> EncryptJob::encryptionKeys() const
0149 {
0150     Q_D(const EncryptJob);
0151 
0152     return d->keys;
0153 }
0154 
0155 void EncryptJob::doStart()
0156 {
0157     Q_D(EncryptJob);
0158     Q_ASSERT(d->resultContent == nullptr); // Not processed before.
0159 
0160     if (d->keys.size() == 0) { // should not happen---resolver should have dealt with it earlier
0161         qCDebug(MESSAGECOMPOSER_LOG) << "HELP! Encrypt job but have no keys to encrypt with.";
0162         return;
0163     }
0164 
0165     // if setContent hasn't been called, we assume that a subjob was added
0166     // and we want to use that
0167     if (!d->content || !d->content->hasContent()) {
0168         if (d->subjobContents.size() == 1) {
0169             d->content = d->subjobContents.constFirst();
0170         }
0171     }
0172 
0173     if (d->protectedHeaders && d->skeletonMessage && d->format & Kleo::OpenPGPMIMEFormat) {
0174         auto pJob = new ProtectedHeadersJob;
0175         pJob->setContent(d->content);
0176         pJob->setSkeletonMessage(d->skeletonMessage);
0177         pJob->setObvoscate(d->protectedHeadersObvoscate);
0178         QObject::connect(pJob, &ProtectedHeadersJob::finished, this, [d, pJob](KJob *job) {
0179             if (job->error()) {
0180                 return;
0181             }
0182             d->content = pJob->content();
0183         });
0184         appendSubjob(pJob);
0185     }
0186 
0187     ContentJobBase::doStart();
0188 }
0189 
0190 void EncryptJob::slotResult(KJob *job)
0191 {
0192     // Q_D(EncryptJob);
0193     if (error() || job->error()) {
0194         ContentJobBase::slotResult(job);
0195         return;
0196     }
0197     if (subjobs().size() == 2) {
0198         auto pjob = static_cast<ProtectedHeadersJob *>(subjobs().last());
0199         if (pjob) {
0200             auto cjob = qobject_cast<ContentJobBase *>(job);
0201             Q_ASSERT(cjob);
0202             pjob->setContent(cjob->content());
0203         }
0204     }
0205 
0206     ContentJobBase::slotResult(job);
0207 }
0208 
0209 void EncryptJob::process()
0210 {
0211     Q_D(EncryptJob);
0212 
0213     // if setContent hasn't been called, we assume that a subjob was added
0214     // and we want to use that
0215     if (!d->content || !d->content->hasContent()) {
0216         Q_ASSERT(d->subjobContents.size() == 1);
0217         d->content = d->subjobContents.constFirst();
0218     }
0219 
0220     const QGpgME::Protocol *proto = nullptr;
0221     if (d->format & Kleo::AnyOpenPGP) {
0222         proto = QGpgME::openpgp();
0223     } else if (d->format & Kleo::AnySMIME) {
0224         proto = QGpgME::smime();
0225     } else {
0226         qCDebug(MESSAGECOMPOSER_LOG) << "HELP! Encrypt job but have protocol to encrypt with.";
0227         return;
0228     }
0229 
0230     Q_ASSERT(proto);
0231 
0232     // for now just do the main recipients
0233     QByteArray content;
0234     d->content->assemble();
0235     if (d->format & Kleo::InlineOpenPGPFormat) {
0236         content = d->content->body();
0237     } else {
0238         content = d->content->encodedContent();
0239     }
0240 
0241     qCDebug(MESSAGECOMPOSER_LOG) << "got backend, starting job";
0242     QGpgME::EncryptJob *eJob = proto->encryptJob(!d->binaryHint(d->format), d->format == Kleo::InlineOpenPGPFormat);
0243 #if QGPGME_SUPPORTS_ENCRYPTION_JOB_SET_INPUT_ENCODING
0244     if (!(d->format & Kleo::InlineOpenPGPFormat)) {
0245         eJob->setInputEncoding(GpgME::Data::MimeEncoding);
0246     }
0247 #endif
0248 
0249     if (!d->gnupgHome.isEmpty()) {
0250         QGpgME::Job::context(eJob)->setEngineHomeDirectory(d->gnupgHome.toUtf8().constData());
0251     }
0252 
0253     QObject::connect(
0254         eJob,
0255         &QGpgME::EncryptJob::result,
0256         this,
0257         [this, d](const GpgME::EncryptionResult &result, const QByteArray &cipherText, const QString &auditLogAsHtml, const GpgME::Error &auditLogError) {
0258             Q_UNUSED(auditLogAsHtml)
0259             Q_UNUSED(auditLogError)
0260             if (result.error()) {
0261                 setError(result.error().code());
0262                 setErrorText(QString::fromLocal8Bit(result.error().asString()));
0263                 emitResult();
0264                 return;
0265             }
0266             d->resultContent = MessageComposer::Util::composeHeadersAndBody(d->content, cipherText, d->format, false);
0267 
0268             emitResult();
0269         });
0270 
0271     const auto error = eJob->start(d->keys, content, true);
0272     if (error.code()) {
0273         eJob->deleteLater();
0274         setError(error.code());
0275         setErrorText(QString::fromLocal8Bit(error.asString()));
0276         emitResult();
0277     }
0278 }
0279 
0280 #include "moc_encryptjob.cpp"