summaryrefslogtreecommitdiffstats
path: root/framework/src/domain/mime/mimetreeparser/messagepart.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'framework/src/domain/mime/mimetreeparser/messagepart.cpp')
-rw-r--r--framework/src/domain/mime/mimetreeparser/messagepart.cpp1258
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
50using namespace MimeTreeParser;
51
52//------MessagePart-----------------------
53MessagePart::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
62MessagePart::~MessagePart()
63{
64}
65
66/*
67QByteArray 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/*
77bool 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
85bool 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
93bool MailMime::isTopLevelPart() const
94{
95 if (!d->mNode) {
96 return false;
97 }
98 return (d->mNode->topLevel() == d->mNode);
99}
100*/
101
102MessagePart::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
121QString 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
133static KMime::Headers::ContentType *contentType(KMime::Content *node)
134{
135 if (node) {
136 return node->contentType(false);
137 }
138 return nullptr;
139}
140
141QByteArray MessagePart::charset() const
142{
143 if (auto ct = contentType(mNode)) {
144 return ct->charset();
145 }
146 return mNode->defaultCharset();
147}
148
149QByteArray MessagePart::mimeType() const
150{
151 if (auto ct = contentType(mNode)) {
152 return ct->mimeType();
153 }
154 return {};
155}
156
157bool MessagePart::isText() const
158{
159 if (auto ct = contentType(mNode)) {
160 return ct->isText();
161 }
162 return false;
163}
164
165int 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
176QString MessagePart::errorString() const
177{
178 return mMetaData.errorText;
179}
180
181PartMetaData *MessagePart::partMetaData()
182{
183 return &mMetaData;
184}
185
186bool MessagePart::isAttachment() const
187{
188 return true;
189}
190
191KMime::Content *MessagePart::node() const
192{
193 return mNode;
194}
195
196void MessagePart::setIsRoot(bool root)
197{
198 mRoot = root;
199}
200
201bool MessagePart::isRoot() const
202{
203 return mRoot;
204}
205
206QString MessagePart::text() const
207{
208 return mText;
209}
210
211void MessagePart::setText(const QString &text)
212{
213 mText = text;
214}
215
216bool MessagePart::isHtml() const
217{
218 return false;
219}
220
221MessagePart *MessagePart::parentPart() const
222{
223 return mParentPart;
224}
225
226void MessagePart::setParentPart(MessagePart *parentPart)
227{
228 mParentPart = parentPart;
229}
230
231QString MessagePart::htmlContent() const
232{
233 return text();
234}
235
236QString MessagePart::plaintextContent() const
237{
238 return text();
239}
240
241
242
243void 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
252QString MessagePart::renderInternalText() const
253{
254 QString text;
255 foreach (const auto &mp, subParts()) {
256 text += mp->text();
257 }
258 return text;
259}
260
261void MessagePart::appendSubPart(const MessagePart::Ptr &messagePart)
262{
263 messagePart->setParentPart(this);
264 mBlocks.append(messagePart);
265}
266
267const QVector<MessagePart::Ptr> &MessagePart::subParts() const
268{
269 return mBlocks;
270}
271
272bool MessagePart::hasSubParts() const
273{
274 return !mBlocks.isEmpty();
275}
276
277QVector<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
293QVector<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----------------------
310MessagePartList::MessagePartList(ObjectTreeParser *otp, KMime::Content *node)
311 : MessagePart(otp, QString(), node)
312{
313}
314
315MessagePartList::~MessagePartList()
316{
317
318}
319
320QString MessagePartList::text() const
321{
322 return renderInternalText();
323}
324
325QString MessagePartList::plaintextContent() const
326{
327 return QString();
328}
329
330QString MessagePartList::htmlContent() const
331{
332 return QString();
333}
334
335//-----TextMessageBlock----------------------
336
337TextMessagePart::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
349TextMessagePart::~TextMessagePart()
350{
351
352}
353
354void 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
433KMMsgEncryptionState TextMessagePart::encryptionState() const
434{
435 return mEncryptionState;
436}
437
438KMMsgSignatureState TextMessagePart::signatureState() const
439{
440 return mSignatureState;
441}
442
443//-----AttachmentMessageBlock----------------------
444
445AttachmentMessagePart::AttachmentMessagePart(ObjectTreeParser *otp, KMime::Content *node)
446 : TextMessagePart(otp, node)
447{
448
449}
450
451AttachmentMessagePart::~AttachmentMessagePart()
452{
453
454}
455
456
457//-----HtmlMessageBlock----------------------
458
459HtmlMessagePart::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
471HtmlMessagePart::~HtmlMessagePart()
472{
473}
474
475QString HtmlMessagePart::text() const
476{
477 return mBodyHTML;
478}
479
480bool HtmlMessagePart::isHtml() const
481{
482 return true;
483}
484
485//-----MimeMessageBlock----------------------
486
487MimeMessagePart::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
498MimeMessagePart::~MimeMessagePart()
499{
500
501}
502
503QString MimeMessagePart::text() const
504{
505 return renderInternalText();
506}
507
508QString MimeMessagePart::plaintextContent() const
509{
510 return QString();
511}
512
513QString MimeMessagePart::htmlContent() const
514{
515 return QString();
516}
517
518//-----AlternativeMessagePart----------------------
519
520AlternativeMessagePart::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
569AlternativeMessagePart::~AlternativeMessagePart()
570{
571
572}
573
574Util::HtmlMode AlternativeMessagePart::preferredMode() const
575{
576 return mPreferredMode;
577}
578
579QList<Util::HtmlMode> AlternativeMessagePart::availableModes()
580{
581 return mChildParts.keys();
582}
583
584QString AlternativeMessagePart::text() const
585{
586 if (mChildParts.contains(Util::MultipartPlain)) {
587 return mChildParts[Util::MultipartPlain]->text();
588 }
589 return QString();
590}
591
592bool AlternativeMessagePart::isHtml() const
593{
594 return mChildParts.contains(Util::MultipartHtml);
595}
596
597QString AlternativeMessagePart::plaintextContent() const
598{
599 return text();
600}
601
602QString 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
613CertMessagePart::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
623CertMessagePart::~CertMessagePart()
624{
625
626}
627
628void 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
636QString CertMessagePart::text() const
637{
638 return QString();
639}
640
641//-----SignedMessageBlock---------------------
642SignedMessagePart::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
660SignedMessagePart::~SignedMessagePart()
661{
662
663}
664
665void SignedMessagePart::setIsSigned(bool isSigned)
666{
667 mMetaData.isSigned = isSigned;
668}
669
670bool SignedMessagePart::isSigned() const
671{
672 return mMetaData.isSigned;
673}
674
675bool 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
770static 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
790QString prettifyDN(const char *uid)
791{
792 return QGpgME::DN(uid).prettyDN();
793}
794
795void 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
878void 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
893void 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
902void 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
918void 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
958QString SignedMessagePart::plaintextContent() const
959{
960 if (!mNode) {
961 return MessagePart::text();
962 } else {
963 return QString();
964 }
965}
966
967QString SignedMessagePart::htmlContent() const
968{
969 if (!mNode) {
970 return MessagePart::text();
971 } else {
972 return QString();
973 }
974}
975
976//-----CryptMessageBlock---------------------
977EncryptedMessagePart::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
999EncryptedMessagePart::~EncryptedMessagePart()
1000{
1001
1002}
1003
1004void EncryptedMessagePart::setIsEncrypted(bool encrypted)
1005{
1006 mMetaData.isEncrypted = encrypted;
1007}
1008
1009bool EncryptedMessagePart::isEncrypted() const
1010{
1011 return mMetaData.isEncrypted;
1012}
1013
1014bool EncryptedMessagePart::isDecryptable() const
1015{
1016 return mMetaData.isDecryptable;
1017}
1018
1019bool EncryptedMessagePart::passphraseError() const
1020{
1021 return mPassphraseError;
1022}
1023
1024void 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
1047bool 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
1157void 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
1199QString EncryptedMessagePart::plaintextContent() const
1200{
1201 if (!mNode) {
1202 return MessagePart::text();
1203 } else {
1204 return QString();
1205 }
1206}
1207
1208QString EncryptedMessagePart::htmlContent() const
1209{
1210 if (!mNode) {
1211 return MessagePart::text();
1212 } else {
1213 return QString();
1214 }
1215}
1216
1217QString 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
1231EncapsulatedRfc822MessagePart::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
1249EncapsulatedRfc822MessagePart::~EncapsulatedRfc822MessagePart()
1250{
1251
1252}
1253
1254QString EncapsulatedRfc822MessagePart::text() const
1255{
1256 return renderInternalText();
1257}
1258