Messagelib

signencryptjob.cpp
1 /*
2  SPDX-FileCopyrightText: 2009 Klaralvdalens Datakonsult AB, a KDAB Group company, [email protected]
3  SPDX-FileCopyrightText: 2009 Leo Franchi <[email protected]>
4 
5  SPDX-License-Identifier: LGPL-2.0-or-later
6 */
7 
8 #include "job/signencryptjob.h"
9 
10 #include "contentjobbase_p.h"
11 #include "job/protectedheadersjob.h"
12 #include "utils/util_p.h"
13 
14 #include <QGpgME/Protocol>
15 #include <QGpgME/SignEncryptJob>
16 
17 #include "messagecomposer_debug.h"
18 #include <KMime/Content>
19 #include <KMime/Headers>
20 #include <KMime/KMimeMessage>
21 
22 #include <gpgme++/encryptionresult.h>
23 #include <gpgme++/global.h>
24 #include <gpgme++/signingresult.h>
25 #include <sstream>
26 
27 using namespace MessageComposer;
28 
29 class MessageComposer::SignEncryptJobPrivate : public ContentJobBasePrivate
30 {
31 public:
32  SignEncryptJobPrivate(SignEncryptJob *qq)
33  : ContentJobBasePrivate(qq)
34  {
35  }
36 
37  std::vector<GpgME::Key> signers;
38 
39  std::vector<GpgME::Key> encKeys;
40  QStringList recipients;
41  Kleo::CryptoMessageFormat format;
42  KMime::Content *content = nullptr;
43  KMime::Message *skeletonMessage = nullptr;
44 
45  bool protectedHeaders = true;
46  bool protectedHeadersObvoscate = false;
47 
48  // copied from messagecomposer.cpp
49  bool binaryHint(Kleo::CryptoMessageFormat f)
50  {
51  switch (f) {
52  case Kleo::SMIMEFormat:
53  case Kleo::SMIMEOpaqueFormat:
54  return true;
55  default:
56  case Kleo::OpenPGPMIMEFormat:
57  case Kleo::InlineOpenPGPFormat:
58  return false;
59  }
60  }
61 
62  Q_DECLARE_PUBLIC(SignEncryptJob)
63 };
64 
65 SignEncryptJob::SignEncryptJob(QObject *parent)
66  : ContentJobBase(*new SignEncryptJobPrivate(this), parent)
67 {
68 }
69 
70 SignEncryptJob::~SignEncryptJob() = default;
71 
72 void SignEncryptJob::setContent(KMime::Content *content)
73 {
75 
76  Q_ASSERT(content);
77 
78  d->content = content;
79 }
80 
81 void SignEncryptJob::setCryptoMessageFormat(Kleo::CryptoMessageFormat format)
82 {
84 
85  // There *must* be a concrete format set at this point.
86  Q_ASSERT(format == Kleo::OpenPGPMIMEFormat || format == Kleo::InlineOpenPGPFormat || format == Kleo::SMIMEFormat || format == Kleo::SMIMEOpaqueFormat);
87  d->format = format;
88 }
89 
90 void SignEncryptJob::setSigningKeys(const std::vector<GpgME::Key> &signers)
91 {
93 
94  d->signers = signers;
95 }
96 
97 KMime::Content *SignEncryptJob::origContent()
98 {
100 
101  return d->content;
102 }
103 
104 void SignEncryptJob::setEncryptionKeys(const std::vector<GpgME::Key> &keys)
105 {
107 
108  d->encKeys = keys;
109 }
110 
111 void SignEncryptJob::setRecipients(const QStringList &recipients)
112 {
114 
115  d->recipients = recipients;
116 }
117 
118 void SignEncryptJob::setSkeletonMessage(KMime::Message *skeletonMessage)
119 {
121 
122  d->skeletonMessage = skeletonMessage;
123 }
124 
125 void SignEncryptJob::setProtectedHeaders(bool protectedHeaders)
126 {
128 
129  d->protectedHeaders = protectedHeaders;
130 }
131 
132 void SignEncryptJob::setProtectedHeadersObvoscate(bool protectedHeadersObvoscate)
133 {
135 
136  d->protectedHeadersObvoscate = protectedHeadersObvoscate;
137 }
138 
139 QStringList SignEncryptJob::recipients() const
140 {
141  Q_D(const SignEncryptJob);
142 
143  return d->recipients;
144 }
145 
146 std::vector<GpgME::Key> SignEncryptJob::encryptionKeys() const
147 {
148  Q_D(const SignEncryptJob);
149 
150  return d->encKeys;
151 }
152 
153 void SignEncryptJob::doStart()
154 {
156  Q_ASSERT(d->resultContent == nullptr); // Not processed before.
157 
158  if (d->protectedHeaders && d->skeletonMessage && d->format & Kleo::OpenPGPMIMEFormat) {
159  auto pJob = new ProtectedHeadersJob;
160  pJob->setContent(d->content);
161  pJob->setSkeletonMessage(d->skeletonMessage);
162  pJob->setObvoscate(d->protectedHeadersObvoscate);
163  QObject::connect(pJob, &ProtectedHeadersJob::finished, this, [d, pJob](KJob *job) {
164  if (job->error()) {
165  return;
166  }
167  d->content = pJob->content();
168  });
169  appendSubjob(pJob);
170  }
171 
173 }
174 
175 void SignEncryptJob::slotResult(KJob *job)
176 {
177  // Q_D(SignEncryptJob);
178  if (error() || job->error()) {
179  ContentJobBase::slotResult(job);
180  return;
181  }
182  if (subjobs().size() == 2) {
183  auto pjob = static_cast<ProtectedHeadersJob *>(subjobs().last());
184  if (pjob) {
185  auto cjob = qobject_cast<ContentJobBase *>(job);
186  Q_ASSERT(cjob);
187  pjob->setContent(cjob->content());
188  }
189  }
190 
191  ContentJobBase::slotResult(job);
192 }
193 
194 void SignEncryptJob::process()
195 {
197  Q_ASSERT(d->resultContent == nullptr); // Not processed before.
198 
199  // if setContent hasn't been called, we assume that a subjob was added
200  // and we want to use that
201  if (!d->content || !d->content->hasContent()) {
202  Q_ASSERT(d->subjobContents.size() == 1);
203  d->content = d->subjobContents.constFirst();
204  }
205 
206  const QGpgME::Protocol *proto = nullptr;
207  if (d->format & Kleo::AnyOpenPGP) {
208  proto = QGpgME::openpgp();
209  } else if (d->format & Kleo::AnySMIME) {
210  proto = QGpgME::smime();
211  } else {
212  return;
213  }
214  Q_ASSERT(proto);
215  // d->resultContent = new KMime::Content;
216 
217  qCDebug(MESSAGECOMPOSER_LOG) << "creating signencrypt from:" << proto->name() << proto->displayName();
218 
219  QByteArray encBody;
220  d->content->assemble();
221 
222  // replace simple LFs by CRLFs for all MIME supporting CryptPlugs
223  // according to RfC 2633, 3.1.1 Canonicalization
225  if (d->format & Kleo::InlineOpenPGPFormat) {
226  content = d->content->body();
227  } else if (!(d->format & Kleo::SMIMEOpaqueFormat)) {
228  content = KMime::LFtoCRLF(d->content->encodedContent());
229  } else { // SMimeOpaque doesn't need LFtoCRLF, else it gets munged
231  }
232 
233  QGpgME::SignEncryptJob *job(proto->signEncryptJob(!d->binaryHint(d->format), d->format == Kleo::InlineOpenPGPFormat));
234  QObject::connect(job,
235  &QGpgME::SignEncryptJob::result,
236  this,
237  [this, d](const GpgME::SigningResult &signingResult,
238  const GpgME::EncryptionResult &encryptionResult,
239  const QByteArray &cipherText,
240  const QString &auditLogAsHtml,
241  const GpgME::Error &auditLogError) {
242  Q_UNUSED(auditLogAsHtml)
243  Q_UNUSED(auditLogError)
244  if (signingResult.error()) {
245  qCDebug(MESSAGECOMPOSER_LOG) << "signing failed:" << signingResult.error().asString();
246  setError(signingResult.error().code());
247  setErrorText(QString::fromLocal8Bit(signingResult.error().asString()));
248  emitResult();
249  return;
250  }
251  if (encryptionResult.error()) {
252  qCDebug(MESSAGECOMPOSER_LOG) << "encrypting failed:" << encryptionResult.error().asString();
253  setError(encryptionResult.error().code());
254  setErrorText(QString::fromLocal8Bit(encryptionResult.error().asString()));
255  emitResult();
256  return;
257  }
258 
259  QByteArray signatureHashAlgo = signingResult.createdSignature(0).hashAlgorithmAsString();
260  d->resultContent = MessageComposer::Util::composeHeadersAndBody(d->content, cipherText, d->format, false, signatureHashAlgo);
261 
262  emitResult();
263  });
264 
265  const auto error = job->start(d->signers, d->encKeys, content, false);
266  if (error.code()) {
267  job->deleteLater();
268  setError(error.code());
270  emitResult();
271  }
272 }
Content * content(const ContentIndex &index) const
QByteArray body() const
Simple interface that both EncryptJob and SignEncryptJob implement so the composer can extract some e...
void finished(KJob *job)
void setErrorText(const QString &errorText)
virtual Q_SCRIPTABLE void start()=0
bool appendSubjob(ContentJobBase *job)
This is meant to be used instead of KCompositeJob::addSubjob(), making it possible to add subjobs fro...
Signs and encrypt the contents of a message.
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
const QList< KJob * > & subjobs() const
void deleteLater()
QString fromLocal8Bit(const char *str, int size)
QByteArray encodedContent(bool useCrLf=false)
KMime::Content * content() const
Get the resulting KMime::Content that the ContentJobBase has generated.
virtual void doStart()
Reimplement to do additional stuff before processing children, such as adding more subjobs.
The ContentJobBase class.
void emitResult()
int error() const
void setError(int errorCode)
Copies headers from skeleton message to content.
Q_D(Todo)
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Sun Mar 26 2023 04:08:12 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.