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"