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