diff options
Diffstat (limited to 'framework/src/domain/mime/mimetreeparser/messagepart.cpp')
-rw-r--r-- | framework/src/domain/mime/mimetreeparser/messagepart.cpp | 1258 |
1 files changed, 1258 insertions, 0 deletions
diff --git a/framework/src/domain/mime/mimetreeparser/messagepart.cpp b/framework/src/domain/mime/mimetreeparser/messagepart.cpp new file mode 100644 index 00000000..f93d4ea5 --- /dev/null +++ b/framework/src/domain/mime/mimetreeparser/messagepart.cpp | |||
@@ -0,0 +1,1258 @@ | |||
1 | /* | ||
2 | Copyright (c) 2015 Sandro Knauß <sknauss@kde.org> | ||
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 | #include "messagepart.h" | ||
21 | #include "mimetreeparser_debug.h" | ||
22 | #include "cryptohelper.h" | ||
23 | #include "objecttreeparser.h" | ||
24 | #include "qgpgmejobexecutor.h" | ||
25 | |||
26 | #include "cryptobodypartmemento.h" | ||
27 | #include "decryptverifybodypartmemento.h" | ||
28 | #include "verifydetachedbodypartmemento.h" | ||
29 | #include "verifyopaquebodypartmemento.h" | ||
30 | |||
31 | #include "utils.h" | ||
32 | |||
33 | #include <KMime/Content> | ||
34 | |||
35 | #include <QGpgME/DN> | ||
36 | #include <QGpgME/Protocol> | ||
37 | #include <QGpgME/ImportJob> | ||
38 | #include <QGpgME/KeyListJob> | ||
39 | #include <QGpgME/VerifyDetachedJob> | ||
40 | #include <QGpgME/VerifyOpaqueJob> | ||
41 | |||
42 | #include <gpgme++/key.h> | ||
43 | #include <gpgme++/keylistresult.h> | ||
44 | #include <gpgme.h> | ||
45 | |||
46 | #include <KLocalizedString> | ||
47 | |||
48 | #include <QTextCodec> | ||
49 | |||
50 | using namespace MimeTreeParser; | ||
51 | |||
52 | //------MessagePart----------------------- | ||
53 | MessagePart::MessagePart(ObjectTreeParser *otp, const QString &text, KMime::Content *node) | ||
54 | : mText(text) | ||
55 | , mOtp(otp) | ||
56 | , mNode(node) //only null for messagepartlist | ||
57 | , mParentPart(nullptr) | ||
58 | , mRoot(false) | ||
59 | { | ||
60 | } | ||
61 | |||
62 | MessagePart::~MessagePart() | ||
63 | { | ||
64 | } | ||
65 | |||
66 | /* | ||
67 | QByteArray MailMime::cid() const | ||
68 | { | ||
69 | if (!d->mNode || !d->mNode->contentID()) { | ||
70 | return QByteArray(); | ||
71 | } | ||
72 | return d->mNode->contentID()->identifier(); | ||
73 | } | ||
74 | */ | ||
75 | |||
76 | /* | ||
77 | bool MailMime::isFirstTextPart() const | ||
78 | { | ||
79 | if (!d->mNode || !d->mNode->topLevel()) { | ||
80 | return false; | ||
81 | } | ||
82 | return (d->mNode->topLevel()->textContent() == d->mNode); | ||
83 | } | ||
84 | |||
85 | bool MailMime::isFirstPart() const | ||
86 | { | ||
87 | if (!d->mNode || !d->mNode->parent()) { | ||
88 | return false; | ||
89 | } | ||
90 | return (d->mNode->parent()->contents().first() == d->mNode); | ||
91 | } | ||
92 | |||
93 | bool MailMime::isTopLevelPart() const | ||
94 | { | ||
95 | if (!d->mNode) { | ||
96 | return false; | ||
97 | } | ||
98 | return (d->mNode->topLevel() == d->mNode); | ||
99 | } | ||
100 | */ | ||
101 | |||
102 | MessagePart::Disposition MessagePart::disposition() const | ||
103 | { | ||
104 | if (!mNode) { | ||
105 | return Invalid; | ||
106 | } | ||
107 | const auto cd = mNode->contentDisposition(false); | ||
108 | if (!cd) { | ||
109 | return Invalid; | ||
110 | } | ||
111 | switch (cd->disposition()){ | ||
112 | case KMime::Headers::CDinline: | ||
113 | return Inline; | ||
114 | case KMime::Headers::CDattachment: | ||
115 | return Attachment; | ||
116 | default: | ||
117 | return Invalid; | ||
118 | } | ||
119 | } | ||
120 | |||
121 | QString MessagePart::filename() const | ||
122 | { | ||
123 | if (!mNode) { | ||
124 | return QString(); | ||
125 | } | ||
126 | const auto cd = mNode->contentDisposition(false); | ||
127 | if (!cd) { | ||
128 | return QString(); | ||
129 | } | ||
130 | return cd->filename(); | ||
131 | } | ||
132 | |||
133 | static KMime::Headers::ContentType *contentType(KMime::Content *node) | ||
134 | { | ||
135 | if (node) { | ||
136 | return node->contentType(false); | ||
137 | } | ||
138 | return nullptr; | ||
139 | } | ||
140 | |||
141 | QByteArray MessagePart::charset() const | ||
142 | { | ||
143 | if (auto ct = contentType(mNode)) { | ||
144 | return ct->charset(); | ||
145 | } | ||
146 | return mNode->defaultCharset(); | ||
147 | } | ||
148 | |||
149 | QByteArray MessagePart::mimeType() const | ||
150 | { | ||
151 | if (auto ct = contentType(mNode)) { | ||
152 | return ct->mimeType(); | ||
153 | } | ||
154 | return {}; | ||
155 | } | ||
156 | |||
157 | bool MessagePart::isText() const | ||
158 | { | ||
159 | if (auto ct = contentType(mNode)) { | ||
160 | return ct->isText(); | ||
161 | } | ||
162 | return false; | ||
163 | } | ||
164 | |||
165 | int MessagePart::error() const | ||
166 | { | ||
167 | if (dynamic_cast<const EncryptedMessagePart*>(this)) { | ||
168 | //TODO Find a better way to detect errors | ||
169 | if (mMetaData.errorText != QStringLiteral("Success")) { | ||
170 | return 1; | ||
171 | } | ||
172 | } | ||
173 | return 0; | ||
174 | } | ||
175 | |||
176 | QString MessagePart::errorString() const | ||
177 | { | ||
178 | return mMetaData.errorText; | ||
179 | } | ||
180 | |||
181 | PartMetaData *MessagePart::partMetaData() | ||
182 | { | ||
183 | return &mMetaData; | ||
184 | } | ||
185 | |||
186 | bool MessagePart::isAttachment() const | ||
187 | { | ||
188 | return true; | ||
189 | } | ||
190 | |||
191 | KMime::Content *MessagePart::node() const | ||
192 | { | ||
193 | return mNode; | ||
194 | } | ||
195 | |||
196 | void MessagePart::setIsRoot(bool root) | ||
197 | { | ||
198 | mRoot = root; | ||
199 | } | ||
200 | |||
201 | bool MessagePart::isRoot() const | ||
202 | { | ||
203 | return mRoot; | ||
204 | } | ||
205 | |||
206 | QString MessagePart::text() const | ||
207 | { | ||
208 | return mText; | ||
209 | } | ||
210 | |||
211 | void MessagePart::setText(const QString &text) | ||
212 | { | ||
213 | mText = text; | ||
214 | } | ||
215 | |||
216 | bool MessagePart::isHtml() const | ||
217 | { | ||
218 | return false; | ||
219 | } | ||
220 | |||
221 | MessagePart *MessagePart::parentPart() const | ||
222 | { | ||
223 | return mParentPart; | ||
224 | } | ||
225 | |||
226 | void MessagePart::setParentPart(MessagePart *parentPart) | ||
227 | { | ||
228 | mParentPart = parentPart; | ||
229 | } | ||
230 | |||
231 | QString MessagePart::htmlContent() const | ||
232 | { | ||
233 | return text(); | ||
234 | } | ||
235 | |||
236 | QString MessagePart::plaintextContent() const | ||
237 | { | ||
238 | return text(); | ||
239 | } | ||
240 | |||
241 | |||
242 | |||
243 | void MessagePart::parseInternal(KMime::Content *node, bool onlyOneMimePart) | ||
244 | { | ||
245 | auto subMessagePart = mOtp->parseObjectTreeInternal(node, onlyOneMimePart); | ||
246 | mRoot = subMessagePart->isRoot(); | ||
247 | foreach (const auto &part, subMessagePart->subParts()) { | ||
248 | appendSubPart(part); | ||
249 | } | ||
250 | } | ||
251 | |||
252 | QString MessagePart::renderInternalText() const | ||
253 | { | ||
254 | QString text; | ||
255 | foreach (const auto &mp, subParts()) { | ||
256 | text += mp->text(); | ||
257 | } | ||
258 | return text; | ||
259 | } | ||
260 | |||
261 | void MessagePart::appendSubPart(const MessagePart::Ptr &messagePart) | ||
262 | { | ||
263 | messagePart->setParentPart(this); | ||
264 | mBlocks.append(messagePart); | ||
265 | } | ||
266 | |||
267 | const QVector<MessagePart::Ptr> &MessagePart::subParts() const | ||
268 | { | ||
269 | return mBlocks; | ||
270 | } | ||
271 | |||
272 | bool MessagePart::hasSubParts() const | ||
273 | { | ||
274 | return !mBlocks.isEmpty(); | ||
275 | } | ||
276 | |||
277 | QVector<SignedMessagePart*> MessagePart::signatures() const | ||
278 | { | ||
279 | QVector<SignedMessagePart*> list; | ||
280 | if (auto sig = dynamic_cast<SignedMessagePart*>(const_cast<MessagePart*>(this))) { | ||
281 | list << sig; | ||
282 | } | ||
283 | auto parent = parentPart(); | ||
284 | while (parent) { | ||
285 | if (auto sig = dynamic_cast<SignedMessagePart*>(parent)) { | ||
286 | list << sig; | ||
287 | } | ||
288 | parent = parent->parentPart(); | ||
289 | } | ||
290 | return list; | ||
291 | } | ||
292 | |||
293 | QVector<EncryptedMessagePart*> MessagePart::encryptions() const | ||
294 | { | ||
295 | QVector<EncryptedMessagePart*> list; | ||
296 | if (auto sig = dynamic_cast<EncryptedMessagePart*>(const_cast<MessagePart*>(this))) { | ||
297 | list << sig; | ||
298 | } | ||
299 | auto parent = parentPart(); | ||
300 | while (parent) { | ||
301 | if (auto sig = dynamic_cast<EncryptedMessagePart*>(parent)) { | ||
302 | list << sig; | ||
303 | } | ||
304 | parent = parent->parentPart(); | ||
305 | } | ||
306 | return list; | ||
307 | } | ||
308 | |||
309 | //-----MessagePartList---------------------- | ||
310 | MessagePartList::MessagePartList(ObjectTreeParser *otp, KMime::Content *node) | ||
311 | : MessagePart(otp, QString(), node) | ||
312 | { | ||
313 | } | ||
314 | |||
315 | MessagePartList::~MessagePartList() | ||
316 | { | ||
317 | |||
318 | } | ||
319 | |||
320 | QString MessagePartList::text() const | ||
321 | { | ||
322 | return renderInternalText(); | ||
323 | } | ||
324 | |||
325 | QString MessagePartList::plaintextContent() const | ||
326 | { | ||
327 | return QString(); | ||
328 | } | ||
329 | |||
330 | QString MessagePartList::htmlContent() const | ||
331 | { | ||
332 | return QString(); | ||
333 | } | ||
334 | |||
335 | //-----TextMessageBlock---------------------- | ||
336 | |||
337 | TextMessagePart::TextMessagePart(ObjectTreeParser *otp, KMime::Content *node) | ||
338 | : MessagePartList(otp, node) | ||
339 | { | ||
340 | if (!mNode) { | ||
341 | qCWarning(MIMETREEPARSER_LOG) << "not a valid node"; | ||
342 | return; | ||
343 | } | ||
344 | |||
345 | |||
346 | parseContent(); | ||
347 | } | ||
348 | |||
349 | TextMessagePart::~TextMessagePart() | ||
350 | { | ||
351 | |||
352 | } | ||
353 | |||
354 | void TextMessagePart::parseContent() | ||
355 | { | ||
356 | const auto aCodec = mOtp->codecFor(mNode); | ||
357 | const QString &fromAddress = mOtp->nodeHelper()->fromAsString(mNode); | ||
358 | mSignatureState = KMMsgNotSigned; | ||
359 | mEncryptionState = KMMsgNotEncrypted; | ||
360 | const auto blocks = prepareMessageForDecryption(mNode->decodedContent()); | ||
361 | |||
362 | const auto cryptProto = QGpgME::openpgp(); | ||
363 | |||
364 | if (!blocks.isEmpty()) { | ||
365 | |||
366 | /* The (overall) signature/encrypted status is broken | ||
367 | * if one unencrypted part is at the beginning or in the middle | ||
368 | * because mailmain adds an unencrypted part at the end this should not break the overall status | ||
369 | * | ||
370 | * That's why we first set the tmp status and if one crypted/signed block comes afterwards, than | ||
371 | * the status is set to unencryped | ||
372 | */ | ||
373 | bool fullySignedOrEncrypted = true; | ||
374 | bool fullySignedOrEncryptedTmp = true; | ||
375 | |||
376 | for (const auto &block : blocks) { | ||
377 | |||
378 | if (!fullySignedOrEncryptedTmp) { | ||
379 | fullySignedOrEncrypted = false; | ||
380 | } | ||
381 | |||
382 | if (block.type() == NoPgpBlock && !block.text().trimmed().isEmpty()) { | ||
383 | fullySignedOrEncryptedTmp = false; | ||
384 | appendSubPart(MessagePart::Ptr(new MessagePart(mOtp, aCodec->toUnicode(block.text())))); | ||
385 | } else if (block.type() == PgpMessageBlock) { | ||
386 | KMime::Content *content = new KMime::Content; | ||
387 | content->setBody(block.text()); | ||
388 | content->parse(); | ||
389 | EncryptedMessagePart::Ptr mp(new EncryptedMessagePart(mOtp, QString(), cryptProto, fromAddress, nullptr, content)); | ||
390 | mp->setIsEncrypted(true); | ||
391 | appendSubPart(mp); | ||
392 | continue; | ||
393 | } else if (block.type() == ClearsignedBlock) { | ||
394 | KMime::Content *content = new KMime::Content; | ||
395 | content->setBody(block.text()); | ||
396 | content->parse(); | ||
397 | SignedMessagePart::Ptr mp(new SignedMessagePart(mOtp, QString(), cryptProto, fromAddress, nullptr, content)); | ||
398 | mp->setIsSigned(true); | ||
399 | appendSubPart(mp); | ||
400 | continue; | ||
401 | } else { | ||
402 | continue; | ||
403 | } | ||
404 | |||
405 | const auto mp = subParts().last().staticCast<MessagePart>(); | ||
406 | const PartMetaData *messagePart(mp->partMetaData()); | ||
407 | |||
408 | if (!messagePart->isEncrypted && !messagePart->isSigned && !block.text().trimmed().isEmpty()) { | ||
409 | mp->setText(aCodec->toUnicode(block.text())); | ||
410 | } | ||
411 | |||
412 | if (messagePart->isEncrypted) { | ||
413 | mEncryptionState = KMMsgPartiallyEncrypted; | ||
414 | } | ||
415 | |||
416 | if (messagePart->isSigned) { | ||
417 | mSignatureState = KMMsgPartiallySigned; | ||
418 | } | ||
419 | } | ||
420 | |||
421 | //Do we have an fully Signed/Encrypted Message? | ||
422 | if (fullySignedOrEncrypted) { | ||
423 | if (mSignatureState == KMMsgPartiallySigned) { | ||
424 | mSignatureState = KMMsgFullySigned; | ||
425 | } | ||
426 | if (mEncryptionState == KMMsgPartiallyEncrypted) { | ||
427 | mEncryptionState = KMMsgFullyEncrypted; | ||
428 | } | ||
429 | } | ||
430 | } | ||
431 | } | ||
432 | |||
433 | KMMsgEncryptionState TextMessagePart::encryptionState() const | ||
434 | { | ||
435 | return mEncryptionState; | ||
436 | } | ||
437 | |||
438 | KMMsgSignatureState TextMessagePart::signatureState() const | ||
439 | { | ||
440 | return mSignatureState; | ||
441 | } | ||
442 | |||
443 | //-----AttachmentMessageBlock---------------------- | ||
444 | |||
445 | AttachmentMessagePart::AttachmentMessagePart(ObjectTreeParser *otp, KMime::Content *node) | ||
446 | : TextMessagePart(otp, node) | ||
447 | { | ||
448 | |||
449 | } | ||
450 | |||
451 | AttachmentMessagePart::~AttachmentMessagePart() | ||
452 | { | ||
453 | |||
454 | } | ||
455 | |||
456 | |||
457 | //-----HtmlMessageBlock---------------------- | ||
458 | |||
459 | HtmlMessagePart::HtmlMessagePart(ObjectTreeParser *otp, KMime::Content *node) | ||
460 | : MessagePart(otp, QString(), node) | ||
461 | { | ||
462 | if (!mNode) { | ||
463 | qCWarning(MIMETREEPARSER_LOG) << "not a valid node"; | ||
464 | return; | ||
465 | } | ||
466 | |||
467 | const QByteArray partBody(mNode->decodedContent()); | ||
468 | mBodyHTML = mOtp->codecFor(mNode)->toUnicode(partBody); | ||
469 | } | ||
470 | |||
471 | HtmlMessagePart::~HtmlMessagePart() | ||
472 | { | ||
473 | } | ||
474 | |||
475 | QString HtmlMessagePart::text() const | ||
476 | { | ||
477 | return mBodyHTML; | ||
478 | } | ||
479 | |||
480 | bool HtmlMessagePart::isHtml() const | ||
481 | { | ||
482 | return true; | ||
483 | } | ||
484 | |||
485 | //-----MimeMessageBlock---------------------- | ||
486 | |||
487 | MimeMessagePart::MimeMessagePart(ObjectTreeParser *otp, KMime::Content *node, bool onlyOneMimePart) | ||
488 | : MessagePart(otp, QString(), node) | ||
489 | { | ||
490 | if (!mNode) { | ||
491 | qCWarning(MIMETREEPARSER_LOG) << "not a valid node"; | ||
492 | return; | ||
493 | } | ||
494 | |||
495 | parseInternal(mNode, onlyOneMimePart); | ||
496 | } | ||
497 | |||
498 | MimeMessagePart::~MimeMessagePart() | ||
499 | { | ||
500 | |||
501 | } | ||
502 | |||
503 | QString MimeMessagePart::text() const | ||
504 | { | ||
505 | return renderInternalText(); | ||
506 | } | ||
507 | |||
508 | QString MimeMessagePart::plaintextContent() const | ||
509 | { | ||
510 | return QString(); | ||
511 | } | ||
512 | |||
513 | QString MimeMessagePart::htmlContent() const | ||
514 | { | ||
515 | return QString(); | ||
516 | } | ||
517 | |||
518 | //-----AlternativeMessagePart---------------------- | ||
519 | |||
520 | AlternativeMessagePart::AlternativeMessagePart(ObjectTreeParser *otp, KMime::Content *node, Util::HtmlMode preferredMode) | ||
521 | : MessagePart(otp, QString(), node) | ||
522 | , mPreferredMode(preferredMode) | ||
523 | { | ||
524 | KMime::Content *dataIcal = findTypeInDirectChilds(mNode, "text/calendar"); | ||
525 | KMime::Content *dataHtml = findTypeInDirectChilds(mNode, "text/html"); | ||
526 | KMime::Content *dataText = findTypeInDirectChilds(mNode, "text/plain"); | ||
527 | |||
528 | if (!dataHtml) { | ||
529 | // If we didn't find the HTML part as the first child of the multipart/alternative, it might | ||
530 | // be that this is a HTML message with images, and text/plain and multipart/related are the | ||
531 | // immediate children of this multipart/alternative node. | ||
532 | // In this case, the HTML node is a child of multipart/related. | ||
533 | dataHtml = findTypeInDirectChilds(mNode, "multipart/related"); | ||
534 | |||
535 | // Still not found? Stupid apple mail actually puts the attachments inside of the | ||
536 | // multipart/alternative, which is wrong. Therefore we also have to look for multipart/mixed | ||
537 | // here. | ||
538 | // Do this only when prefering HTML mail, though, since otherwise the attachments are hidden | ||
539 | // when displaying plain text. | ||
540 | if (!dataHtml) { | ||
541 | dataHtml = findTypeInDirectChilds(mNode, "multipart/mixed"); | ||
542 | } | ||
543 | } | ||
544 | |||
545 | if (dataIcal) { | ||
546 | mChildNodes[Util::MultipartIcal] = dataIcal; | ||
547 | } | ||
548 | |||
549 | if (dataText) { | ||
550 | mChildNodes[Util::MultipartPlain] = dataText; | ||
551 | } | ||
552 | |||
553 | if (dataHtml) { | ||
554 | mChildNodes[Util::MultipartHtml] = dataHtml; | ||
555 | } | ||
556 | |||
557 | if (mChildNodes.isEmpty()) { | ||
558 | qCWarning(MIMETREEPARSER_LOG) << "no valid nodes"; | ||
559 | return; | ||
560 | } | ||
561 | |||
562 | QMapIterator<Util::HtmlMode, KMime::Content *> i(mChildNodes); | ||
563 | while (i.hasNext()) { | ||
564 | i.next(); | ||
565 | mChildParts[i.key()] = MimeMessagePart::Ptr(new MimeMessagePart(mOtp, i.value(), true)); | ||
566 | } | ||
567 | } | ||
568 | |||
569 | AlternativeMessagePart::~AlternativeMessagePart() | ||
570 | { | ||
571 | |||
572 | } | ||
573 | |||
574 | Util::HtmlMode AlternativeMessagePart::preferredMode() const | ||
575 | { | ||
576 | return mPreferredMode; | ||
577 | } | ||
578 | |||
579 | QList<Util::HtmlMode> AlternativeMessagePart::availableModes() | ||
580 | { | ||
581 | return mChildParts.keys(); | ||
582 | } | ||
583 | |||
584 | QString AlternativeMessagePart::text() const | ||
585 | { | ||
586 | if (mChildParts.contains(Util::MultipartPlain)) { | ||
587 | return mChildParts[Util::MultipartPlain]->text(); | ||
588 | } | ||
589 | return QString(); | ||
590 | } | ||
591 | |||
592 | bool AlternativeMessagePart::isHtml() const | ||
593 | { | ||
594 | return mChildParts.contains(Util::MultipartHtml); | ||
595 | } | ||
596 | |||
597 | QString AlternativeMessagePart::plaintextContent() const | ||
598 | { | ||
599 | return text(); | ||
600 | } | ||
601 | |||
602 | QString AlternativeMessagePart::htmlContent() const | ||
603 | { | ||
604 | if (mChildParts.contains(Util::MultipartHtml)) { | ||
605 | return mChildParts[Util::MultipartHtml]->text(); | ||
606 | } else { | ||
607 | return plaintextContent(); | ||
608 | } | ||
609 | } | ||
610 | |||
611 | //-----CertMessageBlock---------------------- | ||
612 | |||
613 | CertMessagePart::CertMessagePart(ObjectTreeParser *otp, KMime::Content *node, const QGpgME::Protocol *cryptoProto) | ||
614 | : MessagePart(otp, QString(), node) | ||
615 | , mCryptoProto(cryptoProto) | ||
616 | { | ||
617 | if (!mNode) { | ||
618 | qCWarning(MIMETREEPARSER_LOG) << "not a valid node"; | ||
619 | return; | ||
620 | } | ||
621 | } | ||
622 | |||
623 | CertMessagePart::~CertMessagePart() | ||
624 | { | ||
625 | |||
626 | } | ||
627 | |||
628 | void CertMessagePart::import() | ||
629 | { | ||
630 | const QByteArray certData = mNode->decodedContent(); | ||
631 | QGpgME::ImportJob *import = mCryptoProto->importJob(); | ||
632 | QGpgMEJobExecutor executor; | ||
633 | auto result = executor.exec(import, certData); | ||
634 | } | ||
635 | |||
636 | QString CertMessagePart::text() const | ||
637 | { | ||
638 | return QString(); | ||
639 | } | ||
640 | |||
641 | //-----SignedMessageBlock--------------------- | ||
642 | SignedMessagePart::SignedMessagePart(ObjectTreeParser *otp, | ||
643 | const QString &text, | ||
644 | const QGpgME::Protocol *cryptoProto, | ||
645 | const QString &fromAddress, | ||
646 | KMime::Content *node, KMime::Content *signedData) | ||
647 | : MessagePart(otp, text, node) | ||
648 | , mCryptoProto(cryptoProto) | ||
649 | , mFromAddress(fromAddress) | ||
650 | , mSignedData(signedData) | ||
651 | { | ||
652 | mMetaData.technicalProblem = (mCryptoProto == nullptr); | ||
653 | mMetaData.isSigned = true; | ||
654 | mMetaData.isGoodSignature = false; | ||
655 | mMetaData.keyTrust = GpgME::Signature::Unknown; | ||
656 | mMetaData.status = i18n("Wrong Crypto Plug-In."); | ||
657 | mMetaData.status_code = GPGME_SIG_STAT_NONE; | ||
658 | } | ||
659 | |||
660 | SignedMessagePart::~SignedMessagePart() | ||
661 | { | ||
662 | |||
663 | } | ||
664 | |||
665 | void SignedMessagePart::setIsSigned(bool isSigned) | ||
666 | { | ||
667 | mMetaData.isSigned = isSigned; | ||
668 | } | ||
669 | |||
670 | bool SignedMessagePart::isSigned() const | ||
671 | { | ||
672 | return mMetaData.isSigned; | ||
673 | } | ||
674 | |||
675 | bool SignedMessagePart::okVerify(const QByteArray &data, const QByteArray &signature, KMime::Content *textNode) | ||
676 | { | ||
677 | NodeHelper *nodeHelper = mOtp->nodeHelper(); | ||
678 | |||
679 | mMetaData.isSigned = false; | ||
680 | mMetaData.technicalProblem = (mCryptoProto == nullptr); | ||
681 | mMetaData.keyTrust = GpgME::Signature::Unknown; | ||
682 | mMetaData.status = i18n("Wrong Crypto Plug-In."); | ||
683 | mMetaData.status_code = GPGME_SIG_STAT_NONE; | ||
684 | |||
685 | const QByteArray mementoName = "verification"; | ||
686 | |||
687 | //TODO for the async case remember the memento | ||
688 | CryptoBodyPartMemento *m = nullptr; | ||
689 | Q_ASSERT(!m || mCryptoProto); //No CryptoPlugin and having a bodyPartMemento -> there is something completely wrong | ||
690 | |||
691 | if (!m && mCryptoProto) { | ||
692 | if (!signature.isEmpty()) { | ||
693 | QGpgME::VerifyDetachedJob *job = mCryptoProto->verifyDetachedJob(); | ||
694 | if (job) { | ||
695 | m = new VerifyDetachedBodyPartMemento(job, mCryptoProto->keyListJob(), signature, data); | ||
696 | } | ||
697 | } else { | ||
698 | QGpgME::VerifyOpaqueJob *job = mCryptoProto->verifyOpaqueJob(); | ||
699 | if (job) { | ||
700 | m = new VerifyOpaqueBodyPartMemento(job, mCryptoProto->keyListJob(), data); | ||
701 | } | ||
702 | } | ||
703 | if (m) { | ||
704 | if (mOtp->allowAsync()) { | ||
705 | QObject::connect(m, &CryptoBodyPartMemento::update, | ||
706 | nodeHelper, &NodeHelper::update); | ||
707 | // QObject::connect(m, SIGNAL(update(MimeTreeParser::UpdateMode)), | ||
708 | // _source->sourceObject(), SLOT(update(MimeTreeParser::UpdateMode))); | ||
709 | |||
710 | if (m->start()) { | ||
711 | mMetaData.inProgress = true; | ||
712 | mOtp->mHasPendingAsyncJobs = true; | ||
713 | } | ||
714 | //FIXME delete memento once done | ||
715 | } else { | ||
716 | m->exec(); | ||
717 | } | ||
718 | } | ||
719 | //only relevant in async case | ||
720 | // } else if (m->isRunning()) { | ||
721 | // mMetaData.inProgress = true; | ||
722 | // mOtp->mHasPendingAsyncJobs = true; | ||
723 | // } else { | ||
724 | // mMetaData.inProgress = false; | ||
725 | // mOtp->mHasPendingAsyncJobs = false; | ||
726 | } | ||
727 | |||
728 | if (m && !mMetaData.inProgress) { | ||
729 | if (!signature.isEmpty()) { | ||
730 | mVerifiedText = data; | ||
731 | } | ||
732 | setVerificationResult(m, textNode); | ||
733 | } | ||
734 | |||
735 | if (!m && !mMetaData.inProgress) { | ||
736 | QString errorMsg; | ||
737 | QString cryptPlugLibName; | ||
738 | QString cryptPlugDisplayName; | ||
739 | if (mCryptoProto) { | ||
740 | cryptPlugLibName = mCryptoProto->name(); | ||
741 | cryptPlugDisplayName = mCryptoProto->displayName(); | ||
742 | } | ||
743 | |||
744 | if (!mCryptoProto) { | ||
745 | if (cryptPlugDisplayName.isEmpty()) { | ||
746 | errorMsg = i18n("No appropriate crypto plug-in was found."); | ||
747 | } else { | ||
748 | errorMsg = i18nc("%1 is either 'OpenPGP' or 'S/MIME'", | ||
749 | "No %1 plug-in was found.", | ||
750 | cryptPlugDisplayName); | ||
751 | } | ||
752 | } else { | ||
753 | errorMsg = i18n("Crypto plug-in \"%1\" cannot verify signatures.", | ||
754 | cryptPlugLibName); | ||
755 | } | ||
756 | mMetaData.errorText = i18n("The message is signed, but the " | ||
757 | "validity of the signature cannot be " | ||
758 | "verified.<br />" | ||
759 | "Reason: %1", | ||
760 | errorMsg); | ||
761 | } | ||
762 | //TODO don't delete in async case | ||
763 | if (m) { | ||
764 | delete m; | ||
765 | } | ||
766 | |||
767 | return mMetaData.isSigned; | ||
768 | } | ||
769 | |||
770 | static int signatureToStatus(const GpgME::Signature &sig) | ||
771 | { | ||
772 | switch (sig.status().code()) { | ||
773 | case GPG_ERR_NO_ERROR: | ||
774 | return GPGME_SIG_STAT_GOOD; | ||
775 | case GPG_ERR_BAD_SIGNATURE: | ||
776 | return GPGME_SIG_STAT_BAD; | ||
777 | case GPG_ERR_NO_PUBKEY: | ||
778 | return GPGME_SIG_STAT_NOKEY; | ||
779 | case GPG_ERR_NO_DATA: | ||
780 | return GPGME_SIG_STAT_NOSIG; | ||
781 | case GPG_ERR_SIG_EXPIRED: | ||
782 | return GPGME_SIG_STAT_GOOD_EXP; | ||
783 | case GPG_ERR_KEY_EXPIRED: | ||
784 | return GPGME_SIG_STAT_GOOD_EXPKEY; | ||
785 | default: | ||
786 | return GPGME_SIG_STAT_ERROR; | ||
787 | } | ||
788 | } | ||
789 | |||
790 | QString prettifyDN(const char *uid) | ||
791 | { | ||
792 | return QGpgME::DN(uid).prettyDN(); | ||
793 | } | ||
794 | |||
795 | void SignedMessagePart::sigStatusToMetaData() | ||
796 | { | ||
797 | GpgME::Key key; | ||
798 | if (mMetaData.isSigned) { | ||
799 | GpgME::Signature signature = mSignatures.front(); | ||
800 | mMetaData.status_code = signatureToStatus(signature); | ||
801 | mMetaData.isGoodSignature = mMetaData.status_code & GPGME_SIG_STAT_GOOD; | ||
802 | // save extended signature status flags | ||
803 | mMetaData.sigSummary = signature.summary(); | ||
804 | |||
805 | if (mMetaData.isGoodSignature && !key.keyID()) { | ||
806 | // Search for the key by its fingerprint so that we can check for | ||
807 | // trust etc. | ||
808 | QGpgME::KeyListJob *job = mCryptoProto->keyListJob(false); // local, no sigs | ||
809 | if (!job) { | ||
810 | qCDebug(MIMETREEPARSER_LOG) << "The Crypto backend does not support listing keys. "; | ||
811 | } else { | ||
812 | std::vector<GpgME::Key> found_keys; | ||
813 | // As we are local it is ok to make this synchronous | ||
814 | GpgME::KeyListResult res = job->exec(QStringList(QLatin1String(signature.fingerprint())), false, found_keys); | ||
815 | if (res.error()) { | ||
816 | qCDebug(MIMETREEPARSER_LOG) << "Error while searching key for Fingerprint: " << signature.fingerprint(); | ||
817 | } | ||
818 | if (found_keys.size() > 1) { | ||
819 | // Should not Happen | ||
820 | qCDebug(MIMETREEPARSER_LOG) << "Oops: Found more then one Key for Fingerprint: " << signature.fingerprint(); | ||
821 | } | ||
822 | if (found_keys.size() != 1) { | ||
823 | // Should not Happen at this point | ||
824 | qCDebug(MIMETREEPARSER_LOG) << "Oops: Found no Key for Fingerprint: " << signature.fingerprint(); | ||
825 | } else { | ||
826 | key = found_keys[0]; | ||
827 | } | ||
828 | delete job; | ||
829 | } | ||
830 | } | ||
831 | |||
832 | if (key.keyID()) { | ||
833 | mMetaData.keyId = key.keyID(); | ||
834 | } | ||
835 | if (mMetaData.keyId.isEmpty()) { | ||
836 | mMetaData.keyId = signature.fingerprint(); | ||
837 | } | ||
838 | mMetaData.keyTrust = signature.validity(); | ||
839 | if (key.numUserIDs() > 0 && key.userID(0).id()) { | ||
840 | mMetaData.signer = prettifyDN(key.userID(0).id()); | ||
841 | } | ||
842 | for (uint iMail = 0; iMail < key.numUserIDs(); ++iMail) { | ||
843 | // The following if /should/ always result in TRUE but we | ||
844 | // won't trust implicitely the plugin that gave us these data. | ||
845 | if (key.userID(iMail).email()) { | ||
846 | QString email = QString::fromUtf8(key.userID(iMail).email()); | ||
847 | // ### work around gpgme 0.3.QString text() const Q_DECL_OVERRIDE;x / cryptplug bug where the | ||
848 | // ### email addresses are specified as angle-addr, not addr-spec: | ||
849 | if (email.startsWith(QLatin1Char('<')) && email.endsWith(QLatin1Char('>'))) { | ||
850 | email = email.mid(1, email.length() - 2); | ||
851 | } | ||
852 | if (!email.isEmpty()) { | ||
853 | mMetaData.signerMailAddresses.append(email); | ||
854 | } | ||
855 | } | ||
856 | } | ||
857 | |||
858 | if (signature.creationTime()) { | ||
859 | mMetaData.creationTime.setTime_t(signature.creationTime()); | ||
860 | } else { | ||
861 | mMetaData.creationTime = QDateTime(); | ||
862 | } | ||
863 | if (mMetaData.signer.isEmpty()) { | ||
864 | if (key.numUserIDs() > 0 && key.userID(0).name()) { | ||
865 | mMetaData.signer = prettifyDN(key.userID(0).name()); | ||
866 | } | ||
867 | if (!mMetaData.signerMailAddresses.empty()) { | ||
868 | if (mMetaData.signer.isEmpty()) { | ||
869 | mMetaData.signer = mMetaData.signerMailAddresses.front(); | ||
870 | } else { | ||
871 | mMetaData.signer += QLatin1String(" <") + mMetaData.signerMailAddresses.front() + QLatin1Char('>'); | ||
872 | } | ||
873 | } | ||
874 | } | ||
875 | } | ||
876 | } | ||
877 | |||
878 | void SignedMessagePart::startVerification() | ||
879 | { | ||
880 | if (mSignedData) { | ||
881 | const QByteArray cleartext = KMime::LFtoCRLF(mSignedData->encodedContent()); | ||
882 | const QTextCodec *aCodec(mOtp->codecFor(mSignedData)); | ||
883 | |||
884 | //The case for pkcs7 | ||
885 | if (mNode == mSignedData) { | ||
886 | startVerificationDetached(cleartext, nullptr, {}); | ||
887 | } else { | ||
888 | startVerificationDetached(cleartext, mSignedData, mNode->decodedContent()); | ||
889 | } | ||
890 | } | ||
891 | } | ||
892 | |||
893 | void SignedMessagePart::startVerification(const QByteArray &text, const QTextCodec *aCodec) | ||
894 | { | ||
895 | startVerificationDetached(text, nullptr, QByteArray()); | ||
896 | |||
897 | if (!mNode && mMetaData.isSigned) { | ||
898 | setText(aCodec->toUnicode(mVerifiedText)); | ||
899 | } | ||
900 | } | ||
901 | |||
902 | void SignedMessagePart::startVerificationDetached(const QByteArray &text, KMime::Content *textNode, const QByteArray &signature) | ||
903 | { | ||
904 | mMetaData.isEncrypted = false; | ||
905 | mMetaData.isDecryptable = false; | ||
906 | |||
907 | if (textNode) { | ||
908 | parseInternal(textNode, false); | ||
909 | } | ||
910 | |||
911 | okVerify(text, signature, textNode); | ||
912 | |||
913 | if (!mMetaData.isSigned) { | ||
914 | mMetaData.creationTime = QDateTime(); | ||
915 | } | ||
916 | } | ||
917 | |||
918 | void SignedMessagePart::setVerificationResult(const CryptoBodyPartMemento *m, KMime::Content *textNode) | ||
919 | { | ||
920 | if (const auto vm = dynamic_cast<const VerifyDetachedBodyPartMemento *>(m)) { | ||
921 | mSignatures = vm->verifyResult().signatures(); | ||
922 | } | ||
923 | if (const auto vm = dynamic_cast<const VerifyOpaqueBodyPartMemento *>(m)) { | ||
924 | mVerifiedText = vm->plainText(); | ||
925 | mSignatures = vm->verifyResult().signatures(); | ||
926 | } | ||
927 | if (const auto vm = dynamic_cast<const DecryptVerifyBodyPartMemento *>(m)) { | ||
928 | mVerifiedText = vm->plainText(); | ||
929 | mSignatures = vm->verifyResult().signatures(); | ||
930 | } | ||
931 | mMetaData.auditLogError = m->auditLogError(); | ||
932 | mMetaData.auditLog = m->auditLogAsHtml(); | ||
933 | mMetaData.isSigned = !mSignatures.empty(); | ||
934 | |||
935 | if (mMetaData.isSigned) { | ||
936 | sigStatusToMetaData(); | ||
937 | if (mNode) { | ||
938 | if (!textNode) { | ||
939 | mOtp->mNodeHelper->setPartMetaData(mNode, mMetaData); | ||
940 | |||
941 | if (!mVerifiedText.isEmpty()) { | ||
942 | auto tempNode = new KMime::Content(); | ||
943 | tempNode->setContent(KMime::CRLFtoLF(mVerifiedText.constData())); | ||
944 | tempNode->parse(); | ||
945 | |||
946 | if (!tempNode->head().isEmpty()) { | ||
947 | tempNode->contentDescription()->from7BitString("signed data"); | ||
948 | } | ||
949 | mOtp->mNodeHelper->attachExtraContent(mNode, tempNode); | ||
950 | |||
951 | parseInternal(tempNode, false); | ||
952 | } | ||
953 | } | ||
954 | } | ||
955 | } | ||
956 | } | ||
957 | |||
958 | QString SignedMessagePart::plaintextContent() const | ||
959 | { | ||
960 | if (!mNode) { | ||
961 | return MessagePart::text(); | ||
962 | } else { | ||
963 | return QString(); | ||
964 | } | ||
965 | } | ||
966 | |||
967 | QString SignedMessagePart::htmlContent() const | ||
968 | { | ||
969 | if (!mNode) { | ||
970 | return MessagePart::text(); | ||
971 | } else { | ||
972 | return QString(); | ||
973 | } | ||
974 | } | ||
975 | |||
976 | //-----CryptMessageBlock--------------------- | ||
977 | EncryptedMessagePart::EncryptedMessagePart(ObjectTreeParser *otp, | ||
978 | const QString &text, | ||
979 | const QGpgME::Protocol *cryptoProto, | ||
980 | const QString &fromAddress, | ||
981 | KMime::Content *node, KMime::Content *encryptedNode) | ||
982 | : MessagePart(otp, text, node) | ||
983 | , mPassphraseError(false) | ||
984 | , mNoSecKey(false) | ||
985 | , mCryptoProto(cryptoProto) | ||
986 | , mFromAddress(fromAddress) | ||
987 | , mEncryptedNode(encryptedNode) | ||
988 | { | ||
989 | mMetaData.technicalProblem = (mCryptoProto == nullptr); | ||
990 | mMetaData.isSigned = false; | ||
991 | mMetaData.isGoodSignature = false; | ||
992 | mMetaData.isEncrypted = false; | ||
993 | mMetaData.isDecryptable = false; | ||
994 | mMetaData.keyTrust = GpgME::Signature::Unknown; | ||
995 | mMetaData.status = i18n("Wrong Crypto Plug-In."); | ||
996 | mMetaData.status_code = GPGME_SIG_STAT_NONE; | ||
997 | } | ||
998 | |||
999 | EncryptedMessagePart::~EncryptedMessagePart() | ||
1000 | { | ||
1001 | |||
1002 | } | ||
1003 | |||
1004 | void EncryptedMessagePart::setIsEncrypted(bool encrypted) | ||
1005 | { | ||
1006 | mMetaData.isEncrypted = encrypted; | ||
1007 | } | ||
1008 | |||
1009 | bool EncryptedMessagePart::isEncrypted() const | ||
1010 | { | ||
1011 | return mMetaData.isEncrypted; | ||
1012 | } | ||
1013 | |||
1014 | bool EncryptedMessagePart::isDecryptable() const | ||
1015 | { | ||
1016 | return mMetaData.isDecryptable; | ||
1017 | } | ||
1018 | |||
1019 | bool EncryptedMessagePart::passphraseError() const | ||
1020 | { | ||
1021 | return mPassphraseError; | ||
1022 | } | ||
1023 | |||
1024 | void EncryptedMessagePart::startDecryption(const QByteArray &text, const QTextCodec *aCodec) | ||
1025 | { | ||
1026 | KMime::Content *content = new KMime::Content; | ||
1027 | content->setBody(text); | ||
1028 | content->parse(); | ||
1029 | |||
1030 | startDecryption(content); | ||
1031 | |||
1032 | auto code = aCodec ? aCodec : mOtp->codecFor(mNode); | ||
1033 | if (!mMetaData.inProgress && mMetaData.isDecryptable) { | ||
1034 | if (hasSubParts()) { | ||
1035 | auto _mp = (subParts()[0]).dynamicCast<SignedMessagePart>(); | ||
1036 | if (_mp) { | ||
1037 | _mp->setText(aCodec->toUnicode(mDecryptedData)); | ||
1038 | } else { | ||
1039 | setText(aCodec->toUnicode(mDecryptedData)); | ||
1040 | } | ||
1041 | } else { | ||
1042 | setText(aCodec->toUnicode(mDecryptedData)); | ||
1043 | } | ||
1044 | } | ||
1045 | } | ||
1046 | |||
1047 | bool EncryptedMessagePart::okDecryptMIME(KMime::Content &data) | ||
1048 | { | ||
1049 | mPassphraseError = false; | ||
1050 | mMetaData.inProgress = false; | ||
1051 | mMetaData.errorText.clear(); | ||
1052 | mMetaData.auditLogError = GpgME::Error(); | ||
1053 | mMetaData.auditLog.clear(); | ||
1054 | bool bDecryptionOk = false; | ||
1055 | bool cannotDecrypt = false; | ||
1056 | NodeHelper *nodeHelper = mOtp->nodeHelper(); | ||
1057 | |||
1058 | // TODO in the async case remember the memento: | ||
1059 | const DecryptVerifyBodyPartMemento *m = nullptr; | ||
1060 | |||
1061 | Q_ASSERT(!m || mCryptoProto); //No CryptoPlugin and having a bodyPartMemento -> there is something completely wrong | ||
1062 | |||
1063 | if (!m && mCryptoProto) { | ||
1064 | QGpgME::DecryptVerifyJob *job = mCryptoProto->decryptVerifyJob(); | ||
1065 | if (!job) { | ||
1066 | cannotDecrypt = true; | ||
1067 | } else { | ||
1068 | const QByteArray ciphertext = data.decodedContent(); | ||
1069 | DecryptVerifyBodyPartMemento *newM = new DecryptVerifyBodyPartMemento(job, ciphertext); | ||
1070 | if (mOtp->allowAsync()) { | ||
1071 | QObject::connect(newM, &CryptoBodyPartMemento::update, | ||
1072 | nodeHelper, &NodeHelper::update); | ||
1073 | // QObject::connect(newM, SIGNAL(update(MimeTreeParser::UpdateMode)), _source->sourceObject(), | ||
1074 | // SLOT(update(MimeTreeParser::UpdateMode))); | ||
1075 | if (newM->start()) { | ||
1076 | mMetaData.inProgress = true; | ||
1077 | mOtp->mHasPendingAsyncJobs = true; | ||
1078 | } else { | ||
1079 | m = newM; | ||
1080 | } | ||
1081 | } else { | ||
1082 | newM->exec(); | ||
1083 | m = newM; | ||
1084 | } | ||
1085 | } | ||
1086 | //Only relevant in the async case | ||
1087 | // } else if (m->isRunning()) { | ||
1088 | // mMetaData.inProgress = true; | ||
1089 | // mOtp->mHasPendingAsyncJobs = true; | ||
1090 | // m = nullptr; | ||
1091 | } | ||
1092 | |||
1093 | if (m) { | ||
1094 | const QByteArray &plainText = m->plainText(); | ||
1095 | const GpgME::DecryptionResult &decryptResult = m->decryptResult(); | ||
1096 | const GpgME::VerificationResult &verifyResult = m->verifyResult(); | ||
1097 | mMetaData.isSigned = verifyResult.signatures().size() > 0; | ||
1098 | |||
1099 | if (verifyResult.signatures().size() > 0) { | ||
1100 | //We simply attach a signed message part to indicate that this content is also signed | ||
1101 | auto subPart = SignedMessagePart::Ptr(new SignedMessagePart(mOtp, QString::fromUtf8(plainText), mCryptoProto, mFromAddress, nullptr, nullptr)); | ||
1102 | subPart->setVerificationResult(m, nullptr); | ||
1103 | appendSubPart(subPart); | ||
1104 | } | ||
1105 | |||
1106 | mDecryptRecipients = decryptResult.recipients(); | ||
1107 | bDecryptionOk = !decryptResult.error(); | ||
1108 | // std::stringstream ss; | ||
1109 | // ss << decryptResult << '\n' << verifyResult; | ||
1110 | // qCDebug(MIMETREEPARSER_LOG) << ss.str().c_str(); | ||
1111 | |||
1112 | if (!bDecryptionOk && mMetaData.isSigned) { | ||
1113 | //Only a signed part | ||
1114 | mMetaData.isEncrypted = false; | ||
1115 | bDecryptionOk = true; | ||
1116 | mDecryptedData = plainText; | ||
1117 | } else { | ||
1118 | mPassphraseError = decryptResult.error().isCanceled() || decryptResult.error().code() == GPG_ERR_NO_SECKEY; | ||
1119 | mMetaData.isEncrypted = decryptResult.error().code() != GPG_ERR_NO_DATA; | ||
1120 | mMetaData.errorText = QString::fromLocal8Bit(decryptResult.error().asString()); | ||
1121 | if (mMetaData.isEncrypted && decryptResult.numRecipients() > 0) { | ||
1122 | mMetaData.keyId = decryptResult.recipient(0).keyID(); | ||
1123 | } | ||
1124 | |||
1125 | if (bDecryptionOk) { | ||
1126 | mDecryptedData = plainText; | ||
1127 | setText(QString::fromUtf8(mDecryptedData.constData())); | ||
1128 | } else { | ||
1129 | mNoSecKey = true; | ||
1130 | foreach (const GpgME::DecryptionResult::Recipient &recipient, decryptResult.recipients()) { | ||
1131 | mNoSecKey &= (recipient.status().code() == GPG_ERR_NO_SECKEY); | ||
1132 | } | ||
1133 | if (!mPassphraseError && !mNoSecKey) { // GpgME do not detect passphrase error correctly | ||
1134 | mPassphraseError = true; | ||
1135 | } | ||
1136 | } | ||
1137 | } | ||
1138 | } | ||
1139 | |||
1140 | if (!bDecryptionOk) { | ||
1141 | if (!mCryptoProto) { | ||
1142 | mMetaData.errorText = i18n("No appropriate crypto plug-in was found."); | ||
1143 | } else if (cannotDecrypt) { | ||
1144 | mMetaData.errorText = i18n("Crypto plug-in \"%1\" cannot decrypt messages.", mCryptoProto->name()); | ||
1145 | } else if (!passphraseError()) { | ||
1146 | mMetaData.errorText = i18n("Crypto plug-in \"%1\" could not decrypt the data.", mCryptoProto->name()) | ||
1147 | + i18n("Error: %1", mMetaData.errorText); | ||
1148 | } | ||
1149 | } | ||
1150 | //TODO don't delete in async case | ||
1151 | if (m) { | ||
1152 | delete m; | ||
1153 | } | ||
1154 | return bDecryptionOk; | ||
1155 | } | ||
1156 | |||
1157 | void EncryptedMessagePart::startDecryption(KMime::Content *data) | ||
1158 | { | ||
1159 | |||
1160 | if (!data) { | ||
1161 | data = mEncryptedNode; | ||
1162 | if (!data) { | ||
1163 | data = mNode; | ||
1164 | } | ||
1165 | } | ||
1166 | |||
1167 | mMetaData.isEncrypted = true; | ||
1168 | |||
1169 | bool bOkDecrypt = okDecryptMIME(*data); | ||
1170 | |||
1171 | if (mMetaData.inProgress) { | ||
1172 | return; | ||
1173 | } | ||
1174 | mMetaData.isDecryptable = bOkDecrypt; | ||
1175 | |||
1176 | if (!mMetaData.isDecryptable) { | ||
1177 | setText(QString::fromUtf8(mDecryptedData.constData())); | ||
1178 | } | ||
1179 | |||
1180 | // if (mMetaData.isEncrypted && !decryptMessage()) { | ||
1181 | // mMetaData.isDecryptable = true; | ||
1182 | // } | ||
1183 | |||
1184 | if (mNode && !mMetaData.isSigned) { | ||
1185 | mOtp->mNodeHelper->setPartMetaData(mNode, mMetaData); | ||
1186 | auto tempNode = new KMime::Content(); | ||
1187 | tempNode->setContent(KMime::CRLFtoLF(mDecryptedData.constData())); | ||
1188 | tempNode->parse(); | ||
1189 | |||
1190 | if (!tempNode->head().isEmpty()) { | ||
1191 | tempNode->contentDescription()->from7BitString("encrypted data"); | ||
1192 | } | ||
1193 | mOtp->mNodeHelper->attachExtraContent(mNode, tempNode); | ||
1194 | |||
1195 | parseInternal(tempNode, false); | ||
1196 | } | ||
1197 | } | ||
1198 | |||
1199 | QString EncryptedMessagePart::plaintextContent() const | ||
1200 | { | ||
1201 | if (!mNode) { | ||
1202 | return MessagePart::text(); | ||
1203 | } else { | ||
1204 | return QString(); | ||
1205 | } | ||
1206 | } | ||
1207 | |||
1208 | QString EncryptedMessagePart::htmlContent() const | ||
1209 | { | ||
1210 | if (!mNode) { | ||
1211 | return MessagePart::text(); | ||
1212 | } else { | ||
1213 | return QString(); | ||
1214 | } | ||
1215 | } | ||
1216 | |||
1217 | QString EncryptedMessagePart::text() const | ||
1218 | { | ||
1219 | if (hasSubParts()) { | ||
1220 | auto _mp = (subParts()[0]).dynamicCast<SignedMessagePart>(); | ||
1221 | if (_mp) { | ||
1222 | return _mp->text(); | ||
1223 | } else { | ||
1224 | return MessagePart::text(); | ||
1225 | } | ||
1226 | } else { | ||
1227 | return MessagePart::text(); | ||
1228 | } | ||
1229 | } | ||
1230 | |||
1231 | EncapsulatedRfc822MessagePart::EncapsulatedRfc822MessagePart(ObjectTreeParser *otp, KMime::Content *node, const KMime::Message::Ptr &message) | ||
1232 | : MessagePart(otp, QString(), node) | ||
1233 | , mMessage(message) | ||
1234 | { | ||
1235 | mMetaData.isEncrypted = false; | ||
1236 | mMetaData.isSigned = false; | ||
1237 | mMetaData.isEncapsulatedRfc822Message = true; | ||
1238 | |||
1239 | mOtp->nodeHelper()->setPartMetaData(mNode, mMetaData); | ||
1240 | |||
1241 | if (!mMessage) { | ||
1242 | qCWarning(MIMETREEPARSER_LOG) << "Node is of type message/rfc822 but doesn't have a message!"; | ||
1243 | return; | ||
1244 | } | ||
1245 | |||
1246 | parseInternal(message.data(), false); | ||
1247 | } | ||
1248 | |||
1249 | EncapsulatedRfc822MessagePart::~EncapsulatedRfc822MessagePart() | ||
1250 | { | ||
1251 | |||
1252 | } | ||
1253 | |||
1254 | QString EncapsulatedRfc822MessagePart::text() const | ||
1255 | { | ||
1256 | return renderInternalText(); | ||
1257 | } | ||
1258 | |||