diff options
author | Christian Mollekopf <chrigi_1@fastmail.fm> | 2017-08-03 10:00:35 -0600 |
---|---|---|
committer | Christian Mollekopf <chrigi_1@fastmail.fm> | 2017-08-03 10:00:35 -0600 |
commit | b7e18a461fd14ec34723d689f644880964314f1b (patch) | |
tree | f7ef6f625a987d8bd484ec283f4d1e5da38aa124 | |
parent | c71bc1b1fdd4055dfe2e2155961827b8652dd96c (diff) | |
download | kube-b7e18a461fd14ec34723d689f644880964314f1b.tar.gz kube-b7e18a461fd14ec34723d689f644880964314f1b.zip |
Commit missing files
-rw-r--r-- | framework/src/CMakeLists.txt | 2 | ||||
-rw-r--r-- | framework/src/domain/mime/mailcrypto.cpp | 444 | ||||
-rw-r--r-- | framework/src/domain/mime/mailcrypto.h | 30 | ||||
-rw-r--r-- | framework/src/domain/mime/mailtemplates.cpp | 5 | ||||
-rw-r--r-- | framework/src/domain/mime/tests/mailtemplatetest.cpp | 30 |
5 files changed, 507 insertions, 4 deletions
diff --git a/framework/src/CMakeLists.txt b/framework/src/CMakeLists.txt index 85ad8344..8436705c 100644 --- a/framework/src/CMakeLists.txt +++ b/framework/src/CMakeLists.txt | |||
@@ -35,6 +35,7 @@ set(SRCS | |||
35 | domain/mime/attachmentmodel.cpp | 35 | domain/mime/attachmentmodel.cpp |
36 | domain/mime/partmodel.cpp | 36 | domain/mime/partmodel.cpp |
37 | domain/mime/mailtemplates.cpp | 37 | domain/mime/mailtemplates.cpp |
38 | domain/mime/mailcrypto.cpp | ||
38 | accounts/accountfactory.cpp | 39 | accounts/accountfactory.cpp |
39 | accounts/accountsmodel.cpp | 40 | accounts/accountsmodel.cpp |
40 | fabric.cpp | 41 | fabric.cpp |
@@ -56,6 +57,7 @@ target_link_libraries(frameworkplugin | |||
56 | KF5::Contacts | 57 | KF5::Contacts |
57 | KF5::Package | 58 | KF5::Package |
58 | KAsync | 59 | KAsync |
60 | QGpgme | ||
59 | ) | 61 | ) |
60 | install(TARGETS frameworkplugin DESTINATION ${FRAMEWORK_INSTALL_DIR}) | 62 | install(TARGETS frameworkplugin DESTINATION ${FRAMEWORK_INSTALL_DIR}) |
61 | 63 | ||
diff --git a/framework/src/domain/mime/mailcrypto.cpp b/framework/src/domain/mime/mailcrypto.cpp new file mode 100644 index 00000000..a5565b7d --- /dev/null +++ b/framework/src/domain/mime/mailcrypto.cpp | |||
@@ -0,0 +1,444 @@ | |||
1 | /* | ||
2 | Copyright (c) 2016 Christian Mollekopf <mollekopf@kolabsys.com> | ||
3 | |||
4 | This library is free software; you can redistribute it and/or modify it | ||
5 | under the terms of the GNU Library General Public License as published by | ||
6 | the Free Software Foundation; either version 2 of the License, or (at your | ||
7 | option) any later version. | ||
8 | |||
9 | This library is distributed in the hope that it will be useful, but WITHOUT | ||
10 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
11 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public | ||
12 | License for more details. | ||
13 | |||
14 | You should have received a copy of the GNU Library General Public License | ||
15 | along with this library; see the file COPYING.LIB. If not, write to the | ||
16 | Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
17 | 02110-1301, USA. | ||
18 | */ | ||
19 | #include "mailcrypto.h" | ||
20 | #include <QGpgME/Protocol> | ||
21 | #include <QGpgME/SignJob> | ||
22 | #include <gpgme++/global.h> | ||
23 | #include <gpgme++/signingresult.h> | ||
24 | #include <QDebug> | ||
25 | |||
26 | /* | ||
27 | * Creating an ecrypted mail: | ||
28 | * * get keys (email -> fingreprint -> key) | ||
29 | * * Use Kleo::OpenPGPMIMEFormat, | ||
30 | * | ||
31 | */ | ||
32 | |||
33 | // bool chooseCTE() | ||
34 | // { | ||
35 | // Q_Q(SinglepartJob); | ||
36 | |||
37 | // auto allowed = KMime::encodingsForData(data); | ||
38 | |||
39 | // if (!q->globalPart()->is8BitAllowed()) { | ||
40 | // allowed.removeAll(KMime::Headers::CE8Bit); | ||
41 | // } | ||
42 | |||
43 | // #if 0 //TODO signing | ||
44 | // // In the following cases only QP and Base64 are allowed: | ||
45 | // // - the buffer will be OpenPGP/MIME signed and it contains trailing | ||
46 | // // whitespace (cf. RFC 3156) | ||
47 | // // - a line starts with "From " | ||
48 | // if ((willBeSigned && cf.hasTrailingWhitespace()) || | ||
49 | // cf.hasLeadingFrom()) { | ||
50 | // ret.removeAll(DwMime::kCte8bit); | ||
51 | // ret.removeAll(DwMime::kCte7bit); | ||
52 | // } | ||
53 | // #endif | ||
54 | |||
55 | // if (contentTransferEncoding) { | ||
56 | // // Specific CTE set. Check that our data fits in it. | ||
57 | // if (!allowed.contains(contentTransferEncoding->encoding())) { | ||
58 | // q->setError(JobBase::BugError); | ||
59 | // q->setErrorText(i18n("%1 Content-Transfer-Encoding cannot correctly encode this message.", | ||
60 | // KMime::nameForEncoding(contentTransferEncoding->encoding()))); | ||
61 | // return false; | ||
62 | // // TODO improve error message in case 8bit is requested but not allowed. | ||
63 | // } | ||
64 | // } else { | ||
65 | // // No specific CTE set. Choose the best one. | ||
66 | // Q_ASSERT(!allowed.isEmpty()); | ||
67 | // contentTransferEncoding = new KMime::Headers::ContentTransferEncoding; | ||
68 | // contentTransferEncoding->setEncoding(allowed.first()); | ||
69 | // } | ||
70 | // qCDebug(MESSAGECOMPOSER_LOG) << "Settled on encoding" << KMime::nameForEncoding(contentTransferEncoding->encoding()); | ||
71 | // return true; | ||
72 | // } | ||
73 | |||
74 | KMime::Content *createPart(const QByteArray &encodedBody, const QByteArray &mimeType, const QByteArray &charset) | ||
75 | { | ||
76 | auto resultContent = new KMime::Content; | ||
77 | |||
78 | auto contentType = new KMime::Headers::ContentType; | ||
79 | contentType->setMimeType(mimeType); | ||
80 | contentType->setMimeType(charset); | ||
81 | // if (!chooseCTE()) { | ||
82 | // Q_ASSERT(error()); | ||
83 | // emitResult(); | ||
84 | // return; | ||
85 | // } | ||
86 | |||
87 | // Set headers. | ||
88 | // if (contentDescription) { | ||
89 | // resultContent->setHeader(contentDescription); | ||
90 | // } | ||
91 | // if (contentDisposition) { | ||
92 | // resultContent->setHeader(contentDisposition); | ||
93 | // } | ||
94 | // if (contentID) { | ||
95 | // resultContent->setHeader(contentID); | ||
96 | // } | ||
97 | // Q_ASSERT(contentTransferEncoding); // chooseCTE() created it if it didn't exist. | ||
98 | auto contentTransferEncoding = new KMime::Headers::ContentTransferEncoding; | ||
99 | auto allowed = KMime::encodingsForData(encodedBody); | ||
100 | Q_ASSERT(!allowed.isEmpty()); | ||
101 | contentTransferEncoding->setEncoding(allowed.first()); | ||
102 | resultContent->setHeader(contentTransferEncoding); | ||
103 | |||
104 | if (contentType) { | ||
105 | resultContent->setHeader(contentType); | ||
106 | } | ||
107 | |||
108 | // Set data. | ||
109 | resultContent->setBody(encodedBody); | ||
110 | return resultContent; | ||
111 | } | ||
112 | |||
113 | KMime::Content *setBodyAndCTE(QByteArray &encodedBody, KMime::Headers::ContentType *contentType, KMime::Content *ret) | ||
114 | { | ||
115 | // MessageComposer::Composer composer; | ||
116 | // MessageComposer::SinglepartJob cteJob(&composer); | ||
117 | auto part = createPart(encodedBody, contentType->mimeType(), contentType->charset()); | ||
118 | part->assemble(); | ||
119 | |||
120 | // cteJob.contentType()->setMimeType(contentType->mimeType()); | ||
121 | // cteJob.contentType()->setCharset(contentType->charset()); | ||
122 | // cteJob.setData(encodedBody); | ||
123 | // cteJob.exec(); | ||
124 | // cteJob.content()->assemble(); | ||
125 | |||
126 | ret->contentTransferEncoding()->setEncoding(part->contentTransferEncoding()->encoding()); | ||
127 | ret->setBody(part->encodedBody()); | ||
128 | |||
129 | return ret; | ||
130 | } | ||
131 | |||
132 | void makeToplevelContentType(KMime::Content *content, bool sign, const QByteArray &hashAlgo) | ||
133 | { | ||
134 | //Kleo::CryptoMessageFormat format, | ||
135 | // switch (format) { | ||
136 | // default: | ||
137 | // case Kleo::InlineOpenPGPFormat: | ||
138 | // case Kleo::OpenPGPMIMEFormat: | ||
139 | if (sign) { | ||
140 | content->contentType()->setMimeType(QByteArrayLiteral("multipart/signed")); | ||
141 | content->contentType()->setParameter(QStringLiteral("protocol"), QStringLiteral("application/pgp-signature")); | ||
142 | content->contentType()->setParameter(QStringLiteral("micalg"), QString::fromLatin1(QByteArray(QByteArrayLiteral("pgp-") + hashAlgo)).toLower()); | ||
143 | |||
144 | } else { | ||
145 | content->contentType()->setMimeType(QByteArrayLiteral("multipart/encrypted")); | ||
146 | content->contentType()->setParameter(QStringLiteral("protocol"), QStringLiteral("application/pgp-encrypted")); | ||
147 | } | ||
148 | return; | ||
149 | // case Kleo::SMIMEFormat: | ||
150 | // if (sign) { | ||
151 | // qCDebug(MESSAGECOMPOSER_LOG) << "setting headers for SMIME"; | ||
152 | // content->contentType()->setMimeType(QByteArrayLiteral("multipart/signed")); | ||
153 | // content->contentType()->setParameter(QStringLiteral("protocol"), QString::fromAscii("application/pkcs7-signature")); | ||
154 | // content->contentType()->setParameter(QStringLiteral("micalg"), QString::fromAscii(hashAlgo).toLower()); | ||
155 | // return; | ||
156 | // } | ||
157 | // // fall through (for encryption, there's no difference between | ||
158 | // // SMIME and SMIMEOpaque, since there is no mp/encrypted for | ||
159 | // // S/MIME) | ||
160 | // case Kleo::SMIMEOpaqueFormat: | ||
161 | |||
162 | // qCDebug(MESSAGECOMPOSER_LOG) << "setting headers for SMIME/opaque"; | ||
163 | // content->contentType()->setMimeType(QByteArrayLiteral("application/pkcs7-mime")); | ||
164 | |||
165 | // if (sign) { | ||
166 | // content->contentType()->setParameter(QStringLiteral("smime-type"), QString::fromAscii("signed-data")); | ||
167 | // } else { | ||
168 | // content->contentType()->setParameter(QStringLiteral("smime-type"), QString::fromAscii("enveloped-data")); | ||
169 | // } | ||
170 | // content->contentType()->setParameter(QStringLiteral("name"), QString::fromAscii("smime.p7m")); | ||
171 | // } | ||
172 | } | ||
173 | |||
174 | void setNestedContentType(KMime::Content *content, bool sign) | ||
175 | { | ||
176 | // , Kleo::CryptoMessageFormat format | ||
177 | // switch (format) { | ||
178 | // case Kleo::OpenPGPMIMEFormat: | ||
179 | if (sign) { | ||
180 | content->contentType()->setMimeType(QByteArrayLiteral("application/pgp-signature")); | ||
181 | content->contentType()->setParameter(QStringLiteral("name"), QString::fromLatin1("signature.asc")); | ||
182 | content->contentDescription()->from7BitString("This is a digitally signed message part."); | ||
183 | } else { | ||
184 | content->contentType()->setMimeType(QByteArrayLiteral("application/octet-stream")); | ||
185 | } | ||
186 | return; | ||
187 | // case Kleo::SMIMEFormat: | ||
188 | // if (sign) { | ||
189 | // content->contentType()->setMimeType(QByteArrayLiteral("application/pkcs7-signature")); | ||
190 | // content->contentType()->setParameter(QStringLiteral("name"), QString::fromAscii("smime.p7s")); | ||
191 | // return; | ||
192 | // } | ||
193 | // // fall through: | ||
194 | // default: | ||
195 | // case Kleo::InlineOpenPGPFormat: | ||
196 | // case Kleo::SMIMEOpaqueFormat: | ||
197 | // ; | ||
198 | // } | ||
199 | } | ||
200 | |||
201 | void setNestedContentDisposition(KMime::Content *content, bool sign) | ||
202 | { | ||
203 | // Kleo::CryptoMessageFormat format, | ||
204 | // if (!sign && format & Kleo::OpenPGPMIMEFormat) { | ||
205 | if (!sign) { | ||
206 | content->contentDisposition()->setDisposition(KMime::Headers::CDinline); | ||
207 | content->contentDisposition()->setFilename(QStringLiteral("msg.asc")); | ||
208 | // } else if (sign && format & Kleo::SMIMEFormat) { | ||
209 | // content->contentDisposition()->setDisposition(KMime::Headers::CDattachment); | ||
210 | // content->contentDisposition()->setFilename(QStringLiteral("smime.p7s")); | ||
211 | } | ||
212 | } | ||
213 | |||
214 | // bool MessageComposer::Util::makeMultiMime(Kleo::CryptoMessageFormat format, bool sign) | ||
215 | // { | ||
216 | // switch (format) { | ||
217 | // default: | ||
218 | // case Kleo::InlineOpenPGPFormat: | ||
219 | // case Kleo::SMIMEOpaqueFormat: return false; | ||
220 | // case Kleo::OpenPGPMIMEFormat: return true; | ||
221 | // case Kleo::SMIMEFormat: return sign; // only on sign - there's no mp/encrypted for S/MIME | ||
222 | // } | ||
223 | // } | ||
224 | |||
225 | KMime::Content *composeHeadersAndBody(KMime::Content *orig, QByteArray encodedBody, bool sign, const QByteArray &hashAlgo) | ||
226 | { | ||
227 | // Kleo::CryptoMessageFormat format, | ||
228 | KMime::Content *result = new KMime::Content; | ||
229 | |||
230 | // called should have tested that the signing/encryption failed | ||
231 | Q_ASSERT(!encodedBody.isEmpty()); | ||
232 | |||
233 | // if (!(format & Kleo::InlineOpenPGPFormat)) { // make a MIME message | ||
234 | // qDebug() << "making MIME message, format:" << format; | ||
235 | makeToplevelContentType(result, sign, hashAlgo); | ||
236 | |||
237 | // if (makeMultiMime(sign)) { // sign/enc PGPMime, sign SMIME | ||
238 | if (true) { // sign/enc PGPMime, sign SMIME | ||
239 | |||
240 | const QByteArray boundary = KMime::multiPartBoundary(); | ||
241 | result->contentType()->setBoundary(boundary); | ||
242 | |||
243 | result->assemble(); | ||
244 | //qCDebug(MESSAGECOMPOSER_LOG) << "processed header:" << result->head(); | ||
245 | |||
246 | // Build the encapsulated MIME parts. | ||
247 | // Build a MIME part holding the code information | ||
248 | // taking the body contents returned in ciphertext. | ||
249 | KMime::Content *code = new KMime::Content; | ||
250 | setNestedContentType(code, sign); | ||
251 | setNestedContentDisposition(code, sign); | ||
252 | |||
253 | if (sign) { // sign PGPMime, sign SMIME | ||
254 | // if (format & Kleo::AnySMIME) { // sign SMIME | ||
255 | // code->contentTransferEncoding()->setEncoding(KMime::Headers::CEbase64); | ||
256 | // code->contentTransferEncoding()->needToEncode(); | ||
257 | // code->setBody(encodedBody); | ||
258 | // } else { // sign PGPMmime | ||
259 | setBodyAndCTE(encodedBody, orig->contentType(), code); | ||
260 | // } | ||
261 | result->addContent(orig); | ||
262 | result->addContent(code); | ||
263 | } else { // enc PGPMime | ||
264 | setBodyAndCTE(encodedBody, orig->contentType(), code); | ||
265 | |||
266 | // Build a MIME part holding the version information | ||
267 | // taking the body contents returned in | ||
268 | // structuring.data.bodyTextVersion. | ||
269 | KMime::Content *vers = new KMime::Content; | ||
270 | vers->contentType()->setMimeType("application/pgp-encrypted"); | ||
271 | vers->contentDisposition()->setDisposition(KMime::Headers::CDattachment); | ||
272 | vers->contentTransferEncoding()->setEncoding(KMime::Headers::CE7Bit); | ||
273 | vers->setBody("Version: 1"); | ||
274 | |||
275 | result->addContent(vers); | ||
276 | result->addContent(code); | ||
277 | } | ||
278 | } else { //enc SMIME, sign/enc SMIMEOpaque | ||
279 | result->contentTransferEncoding()->setEncoding(KMime::Headers::CEbase64); | ||
280 | result->contentDisposition()->setDisposition(KMime::Headers::CDattachment); | ||
281 | result->contentDisposition()->setFilename(QStringLiteral("smime.p7m")); | ||
282 | |||
283 | result->assemble(); | ||
284 | //qCDebug(MESSAGECOMPOSER_LOG) << "processed header:" << result->head(); | ||
285 | |||
286 | result->setBody(encodedBody); | ||
287 | } | ||
288 | // } else { // sign/enc PGPInline | ||
289 | // result->setHead(orig->head()); | ||
290 | // result->parse(); | ||
291 | |||
292 | // // fixing ContentTransferEncoding | ||
293 | // setBodyAndCTE(encodedBody, orig->contentType(), result); | ||
294 | // } | ||
295 | result->assemble(); | ||
296 | return result; | ||
297 | } | ||
298 | |||
299 | // bool binaryHint(Kleo::CryptoMessageFormat f) | ||
300 | // { | ||
301 | // switch (f) { | ||
302 | // case Kleo::SMIMEFormat: | ||
303 | // case Kleo::SMIMEOpaqueFormat: | ||
304 | // return true; | ||
305 | // default: | ||
306 | // case Kleo::OpenPGPMIMEFormat: | ||
307 | // case Kleo::InlineOpenPGPFormat: | ||
308 | // return false; | ||
309 | // } | ||
310 | // } | ||
311 | // | ||
312 | // GpgME::SignatureMode signingMode(Kleo::CryptoMessageFormat f) | ||
313 | // { | ||
314 | // switch (f) { | ||
315 | // case Kleo::SMIMEOpaqueFormat: | ||
316 | // return GpgME::NormalSignatureMode; | ||
317 | // case Kleo::InlineOpenPGPFormat: | ||
318 | // return GpgME::Clearsigned; | ||
319 | // default: | ||
320 | // case Kleo::SMIMEFormat: | ||
321 | // case Kleo::OpenPGPMIMEFormat: | ||
322 | // return GpgME::Detached; | ||
323 | // } | ||
324 | // } | ||
325 | |||
326 | //Hardcoded OpenPGPGMIMEFormat for now | ||
327 | KMime::Content *MailCrypto::sign(KMime::Content *content, const std::vector<GpgME::Key> &signers) | ||
328 | { | ||
329 | |||
330 | // if setContent hasn't been called, we assume that a subjob was added | ||
331 | // and we want to use that | ||
332 | // if (!d->content) { | ||
333 | // Q_ASSERT(d->subjobContents.size() == 1); | ||
334 | // d->content = d->subjobContents.first(); | ||
335 | // } | ||
336 | |||
337 | //d->resultContent = new KMime::Content; | ||
338 | |||
339 | // const QGpgME::Protocol *proto = nullptr; | ||
340 | // if (d->format & Kleo::AnyOpenPGP) { | ||
341 | // proto = QGpgME::openpgp(); | ||
342 | // } else if (d->format & Kleo::AnySMIME) { | ||
343 | // proto = QGpgME::smime(); | ||
344 | // } | ||
345 | |||
346 | const QGpgME::Protocol *proto = QGpgME::openpgp(); | ||
347 | Q_ASSERT(proto); | ||
348 | |||
349 | qDebug() << "creating signJob from:" << proto->name() << proto->displayName(); | ||
350 | // std::unique_ptr<QGpgME::SignJob> job(proto->signJob(!d->binaryHint(d->format), d->format == Kleo::InlineOpenPGPFormat)); | ||
351 | bool armor = true; | ||
352 | bool textMode = false; | ||
353 | std::unique_ptr<QGpgME::SignJob> job(proto->signJob(armor, textMode)); | ||
354 | // for now just do the main recipients | ||
355 | QByteArray signature; | ||
356 | |||
357 | content->assemble(); | ||
358 | |||
359 | // replace simple LFs by CRLFs for all MIME supporting CryptPlugs | ||
360 | // according to RfC 2633, 3.1.1 Canonicalization | ||
361 | QByteArray contentData; | ||
362 | // if (d->format & Kleo::InlineOpenPGPFormat) { | ||
363 | // content = d->content->body(); | ||
364 | // } else if (!(d->format & Kleo::SMIMEOpaqueFormat)) { | ||
365 | |||
366 | // replace "From " and "--" at the beginning of lines | ||
367 | // with encoded versions according to RfC 3156, 3 | ||
368 | // Note: If any line begins with the string "From ", it is strongly | ||
369 | // suggested that either the Quoted-Printable or Base64 MIME encoding | ||
370 | // be applied. | ||
371 | const auto encoding = content->contentTransferEncoding()->encoding(); | ||
372 | if ((encoding == KMime::Headers::CEquPr || encoding == KMime::Headers::CE7Bit) | ||
373 | && !content->contentType(false)) { | ||
374 | QByteArray body = content->encodedBody(); | ||
375 | bool changed = false; | ||
376 | QList<QByteArray> search; | ||
377 | QList<QByteArray> replacements; | ||
378 | |||
379 | search << "From " | ||
380 | << "from " | ||
381 | << "-"; | ||
382 | replacements << "From=20" | ||
383 | << "from=20" | ||
384 | << "=2D"; | ||
385 | |||
386 | if (content->contentTransferEncoding()->encoding() == KMime::Headers::CE7Bit) { | ||
387 | for (int i = 0; i < search.size(); ++i) { | ||
388 | const auto pos = body.indexOf(search[i]); | ||
389 | if (pos == 0 || (pos > 0 && body.at(pos - 1) == '\n')) { | ||
390 | changed = true; | ||
391 | break; | ||
392 | } | ||
393 | } | ||
394 | if (changed) { | ||
395 | content->contentTransferEncoding()->setEncoding(KMime::Headers::CEquPr); | ||
396 | content->assemble(); | ||
397 | body = content->encodedBody(); | ||
398 | } | ||
399 | } | ||
400 | |||
401 | for (int i = 0; i < search.size(); ++i) { | ||
402 | const auto pos = body.indexOf(search[i]); | ||
403 | if (pos == 0 || (pos > 0 && body.at(pos - 1) == '\n')) { | ||
404 | changed = true; | ||
405 | body.replace(pos, search[i].size(), replacements[i]); | ||
406 | } | ||
407 | } | ||
408 | |||
409 | if (changed) { | ||
410 | qDebug() << "Content changed"; | ||
411 | content->setBody(body); | ||
412 | content->contentTransferEncoding()->setDecoded(false); | ||
413 | } | ||
414 | } | ||
415 | |||
416 | contentData = KMime::LFtoCRLF(content->encodedContent()); | ||
417 | // } else { // SMimeOpaque doesn't need LFtoCRLF, else it gets munged | ||
418 | // contentData = content->encodedContent(); | ||
419 | // } | ||
420 | |||
421 | auto signingMode = GpgME::Detached; | ||
422 | |||
423 | // FIXME: Make this async | ||
424 | GpgME::SigningResult res = job->exec(signers, | ||
425 | contentData, | ||
426 | signingMode, | ||
427 | signature); | ||
428 | |||
429 | // exec'ed jobs don't delete themselves | ||
430 | job->deleteLater(); | ||
431 | |||
432 | if (res.error().code()) { | ||
433 | qWarning() << "signing failed:" << res.error().asString(); | ||
434 | // job->showErrorDialog( globalPart()->parentWidgetForGui() ); | ||
435 | // setError(res.error().code()); | ||
436 | // setErrorText(QString::fromLocal8Bit(res.error().asString())); | ||
437 | } else { | ||
438 | QByteArray signatureHashAlgo = res.createdSignature(0).hashAlgorithmAsString(); | ||
439 | bool sign = true; | ||
440 | return composeHeadersAndBody(content, signature, sign, signatureHashAlgo); | ||
441 | } | ||
442 | return nullptr; | ||
443 | } | ||
444 | |||
diff --git a/framework/src/domain/mime/mailcrypto.h b/framework/src/domain/mime/mailcrypto.h new file mode 100644 index 00000000..2261182d --- /dev/null +++ b/framework/src/domain/mime/mailcrypto.h | |||
@@ -0,0 +1,30 @@ | |||
1 | /* | ||
2 | Copyright (c) 2016 Christian Mollekopf <mollekopf@kolabsys.com> | ||
3 | |||
4 | This library is free software; you can redistribute it and/or modify it | ||
5 | under the terms of the GNU Library General Public License as published by | ||
6 | the Free Software Foundation; either version 2 of the License, or (at your | ||
7 | option) any later version. | ||
8 | |||
9 | This library is distributed in the hope that it will be useful, but WITHOUT | ||
10 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
11 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public | ||
12 | License for more details. | ||
13 | |||
14 | You should have received a copy of the GNU Library General Public License | ||
15 | along with this library; see the file COPYING.LIB. If not, write to the | ||
16 | Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
17 | 02110-1301, USA. | ||
18 | */ | ||
19 | |||
20 | #pragma once | ||
21 | |||
22 | #include <QByteArray> | ||
23 | #include <KMime/Message> | ||
24 | #include <gpgme++/key.h> | ||
25 | #include <functional> | ||
26 | |||
27 | namespace MailCrypto | ||
28 | { | ||
29 | KMime::Content *sign(KMime::Content *content, const std::vector<GpgME::Key> &signers); | ||
30 | }; | ||
diff --git a/framework/src/domain/mime/mailtemplates.cpp b/framework/src/domain/mime/mailtemplates.cpp index dc713ee4..0380fa5c 100644 --- a/framework/src/domain/mime/mailtemplates.cpp +++ b/framework/src/domain/mime/mailtemplates.cpp | |||
@@ -977,11 +977,8 @@ KMime::Message::Ptr MailTemplates::createMessage(KMime::Message::Ptr existingMes | |||
977 | for (const auto &attachment : attachments) { | 977 | for (const auto &attachment : attachments) { |
978 | mail->addContent(createAttachmentPart(attachment.data, attachment.filename, attachment.isInline, attachment.mimeType, attachment.name)); | 978 | mail->addContent(createAttachmentPart(attachment.data, attachment.filename, attachment.isInline, attachment.mimeType, attachment.name)); |
979 | } | 979 | } |
980 | bodyPart = createBodyPart(body.toUtf8()); | ||
981 | } else { | ||
982 | //FIXME same implementation as above for attachments | ||
983 | bodyPart = createBodyPart(body.toUtf8()); | ||
984 | } | 980 | } |
981 | bodyPart = createBodyPart(body.toUtf8()); | ||
985 | mail->assemble(); | 982 | mail->assemble(); |
986 | 983 | ||
987 | KMime::Content *signedResult = nullptr; | 984 | KMime::Content *signedResult = nullptr; |
diff --git a/framework/src/domain/mime/tests/mailtemplatetest.cpp b/framework/src/domain/mime/tests/mailtemplatetest.cpp index 62b17a6c..e814f75f 100644 --- a/framework/src/domain/mime/tests/mailtemplatetest.cpp +++ b/framework/src/domain/mime/tests/mailtemplatetest.cpp | |||
@@ -214,6 +214,36 @@ private slots: | |||
214 | qWarning() << "---------------------------------"; | 214 | qWarning() << "---------------------------------"; |
215 | QCOMPARE(result->subject()->asUnicodeString(), subject); | 215 | QCOMPARE(result->subject()->asUnicodeString(), subject); |
216 | QVERIFY(result->contentType()->isMimeType("multipart/signed")); | 216 | QVERIFY(result->contentType()->isMimeType("multipart/signed")); |
217 | |||
218 | const auto contents = result->contents(); | ||
219 | QCOMPARE(contents.size(), 2); | ||
220 | { | ||
221 | auto c = contents.at(0); | ||
222 | QVERIFY(c->contentType()->isMimeType("text/plain")); | ||
223 | } | ||
224 | { | ||
225 | auto c = contents.at(1); | ||
226 | QVERIFY(c->contentType()->isMimeType("application/pgp-signature")); | ||
227 | } | ||
228 | } | ||
229 | |||
230 | void testCreatePlainMailWithAttachmentsSigned() | ||
231 | { | ||
232 | QStringList to = {{"to@example.org"}}; | ||
233 | QStringList cc = {{"cc@example.org"}};; | ||
234 | QStringList bcc = {{"bcc@example.org"}};; | ||
235 | KMime::Types::Mailbox from; | ||
236 | from.fromUnicodeString("from@example.org"); | ||
237 | QString subject = "subject"; | ||
238 | QString body = "body"; | ||
239 | QList<Attachment> attachments = {{"name", "filename", "mimetype", true, "inlineAttachment"}, {"name", "filename", "mimetype", false, "nonInlineAttachment"}}; | ||
240 | |||
241 | std::vector<GpgME::Key> keys = getKeys(); | ||
242 | |||
243 | auto result = MailTemplates::createMessage({}, to, cc, bcc, from, subject, body, attachments, keys); | ||
244 | |||
245 | QVERIFY(result); | ||
246 | QVERIFY(result->contentType()->isMimeType("multipart/signed")); | ||
217 | } | 247 | } |
218 | }; | 248 | }; |
219 | 249 | ||