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 {
74  Q_D(SignEncryptJob);
75 
76  Q_ASSERT(content);
77 
78  d->content = content;
79 }
80 
81 void SignEncryptJob::setCryptoMessageFormat(Kleo::CryptoMessageFormat format)
82 {
83  Q_D(SignEncryptJob);
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 {
92  Q_D(SignEncryptJob);
93 
94  d->signers = signers;
95 }
96 
97 KMime::Content *SignEncryptJob::origContent()
98 {
99  Q_D(SignEncryptJob);
100 
101  return d->content;
102 }
103 
104 void SignEncryptJob::setEncryptionKeys(const std::vector<GpgME::Key> &keys)
105 {
106  Q_D(SignEncryptJob);
107 
108  d->encKeys = keys;
109 }
110 
111 void SignEncryptJob::setRecipients(const QStringList &recipients)
112 {
113  Q_D(SignEncryptJob);
114 
115  d->recipients = recipients;
116 }
117 
118 void SignEncryptJob::setSkeletonMessage(KMime::Message *skeletonMessage)
119 {
120  Q_D(SignEncryptJob);
121 
122  d->skeletonMessage = skeletonMessage;
123 }
124 
125 void SignEncryptJob::setProtectedHeaders(bool protectedHeaders)
126 {
127  Q_D(SignEncryptJob);
128 
129  d->protectedHeaders = protectedHeaders;
130 }
131 
132 void SignEncryptJob::setProtectedHeadersObvoscate(bool protectedHeadersObvoscate)
133 {
134  Q_D(SignEncryptJob);
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 {
155  Q_D(SignEncryptJob);
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 {
196  Q_D(SignEncryptJob);
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
224  QByteArray content;
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
230  content = d->content->encodedContent();
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());
269  setErrorText(QString::fromLocal8Bit(error.asString()));
270  emitResult();
271  }
272 }
virtual void doStart()
Reimplement to do additional stuff before processing children, such as adding more subjobs...
QString fromLocal8Bit(const char *str, int size)
void finished(KJob *job)
Copies headers from skeleton message to content.
void error(QWidget *parent, const QString &text, const QString &caption=QString(), Options options=Notify)
void deleteLater()
Signs and encrypt the contents of a message.
Simple interface that both EncryptJob and SignEncryptJob implement so the composer can extract some e...
virtual Q_SCRIPTABLE void start()=0
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
T qobject_cast(QObject *object)
int error() const
The ContentJobBase class.
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Sun Dec 5 2021 23:04:55 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.