summaryrefslogtreecommitdiffstats
path: root/framework/src/domain/mimetreeparser/interface.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'framework/src/domain/mimetreeparser/interface.cpp')
-rw-r--r--framework/src/domain/mimetreeparser/interface.cpp1179
1 files changed, 1179 insertions, 0 deletions
diff --git a/framework/src/domain/mimetreeparser/interface.cpp b/framework/src/domain/mimetreeparser/interface.cpp
new file mode 100644
index 00000000..663a2013
--- /dev/null
+++ b/framework/src/domain/mimetreeparser/interface.cpp
@@ -0,0 +1,1179 @@
1/*
2 Copyright (c) 2016 Sandro Knauß <knauss@kolabsystems.com>
3
4 This library is free software; you can redistribute it and/or modify it
5 under the terms of the GNU Library General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or (at your
7 option) any later version.
8
9 This library is distributed in the hope that it will be useful, but WITHOUT
10 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
12 License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to the
16 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17 02110-1301, USA.
18*/
19
20#include "interface.h"
21#include "interface_p.h"
22
23#include "stringhtmlwriter.h"
24#include "objecttreesource.h"
25
26#include <QGpgME/KeyListJob>
27#include <QGpgME/Protocol>
28#include <gpgme++/key.h>
29#include <gpgme++/keylistresult.h>
30
31#include <KMime/Content>
32#include <MimeTreeParser/ObjectTreeParser>
33#include <MimeTreeParser/MessagePart>
34#include <MimeTreeParser/NodeHelper>
35
36#include <QMimeDatabase>
37#include <QMimeType>
38#include <QTextCodec>
39#include <QDebug>
40
41class MailMimePrivate
42{
43public:
44 MailMimePrivate(MailMime *p);
45
46 MailMime *q;
47 KMime::Content *mNode;
48 std::shared_ptr<MailMime> parent;
49};
50
51MailMimePrivate::MailMimePrivate(MailMime* p)
52 : q(p)
53 , mNode(nullptr)
54 , parent(nullptr)
55{
56}
57
58
59MailMime::MailMime()
60 : d(std::unique_ptr<MailMimePrivate>(new MailMimePrivate(this)))
61{
62}
63
64QByteArray MailMime::cid() const
65{
66 if (!d->mNode || !d->mNode->contentID()) {
67 return QByteArray();
68 }
69 return d->mNode->contentID()->identifier();
70}
71
72QByteArray MailMime::charset() const
73{
74 if(!d->mNode || !d->mNode->contentType(false)) {
75 return QByteArray();
76 }
77 if (d->mNode->contentType(false)) {
78 return d->mNode->contentType(false)->charset();
79 }
80 return d->mNode->defaultCharset();
81}
82
83bool MailMime::isFirstTextPart() const
84{
85 if (!d->mNode || !d->mNode->topLevel()) {
86 return false;
87 }
88 return (d->mNode->topLevel()->textContent() == d->mNode);
89}
90
91bool MailMime::isFirstPart() const
92{
93 if (!d->mNode || !d->mNode->parent()) {
94 return false;
95 }
96 return (d->mNode->parent()->contents().first() == d->mNode);
97}
98
99bool MailMime::isTopLevelPart() const
100{
101 if (!d->mNode) {
102 return false;
103 }
104 return (d->mNode->topLevel() == d->mNode);
105}
106
107MailMime::Disposition MailMime::disposition() const
108{
109 if (!d->mNode) {
110 return Invalid;
111 }
112 const auto cd = d->mNode->contentDisposition(false);
113 if (!cd) {
114 return Invalid;
115 }
116 switch (cd->disposition()){
117 case KMime::Headers::CDinline:
118 return Inline;
119 case KMime::Headers::CDattachment:
120 return Attachment;
121 default:
122 return Invalid;
123 }
124}
125
126QString MailMime::filename() const
127{
128 if (!d->mNode) {
129 return QString();
130 }
131 const auto cd = d->mNode->contentDisposition(false);
132 if (!cd) {
133 return QString();
134 }
135 return cd->filename();
136}
137
138QMimeType MailMime::mimetype() const
139{
140 if (!d->mNode) {
141 return QMimeType();
142 }
143
144 const auto ct = d->mNode->contentType(false);
145 if (!ct) {
146 return QMimeType();
147 }
148
149 QMimeDatabase mimeDb;
150 return mimeDb.mimeTypeForName(ct->mimeType());
151}
152
153MailMime::Ptr MailMime::parent() const
154{
155 if (!d->parent) {
156 d->parent = std::shared_ptr<MailMime>(new MailMime());
157 d->parent->d->mNode = d->mNode->parent();
158 }
159 return d->parent;
160}
161
162QByteArray MailMime::decodedContent() const
163{
164 if (!d->mNode) {
165 return QByteArray();
166 }
167 return d->mNode->decodedContent();
168}
169
170class KeyPrivate
171{
172public:
173 Key *q;
174 GpgME::Key mKey;
175 QByteArray mKeyID;
176};
177
178Key::Key()
179 :d(std::unique_ptr<KeyPrivate>(new KeyPrivate))
180{
181 d->q = this;
182}
183
184
185Key::Key(KeyPrivate *d_ptr)
186 :d(std::unique_ptr<KeyPrivate>(d_ptr))
187{
188 d->q = this;
189}
190
191Key::~Key()
192{
193
194}
195
196QString Key::keyid() const
197{
198 if (!d->mKey.isNull()) {
199 return d->mKey.keyID();
200 }
201
202 return d->mKeyID;
203}
204
205QString Key::name() const
206{
207 //FIXME: is this the correct way to get the primary UID?
208 if (!d->mKey.isNull()) {
209 return d->mKey.userID(0).name();
210 }
211
212 return QString();
213}
214
215QString Key::email() const
216{
217 if (!d->mKey.isNull()) {
218 return d->mKey.userID(0).email();
219 }
220 return QString();
221}
222
223QString Key::comment() const
224{
225 if (!d->mKey.isNull()) {
226 return d->mKey.userID(0).comment();
227 }
228 return QString();
229}
230
231class SignaturePrivate
232{
233public:
234 Signature *q;
235 GpgME::Signature mSignature;
236 Key::Ptr mKey;
237};
238
239Signature::Signature()
240 :d(std::unique_ptr<SignaturePrivate>(new SignaturePrivate))
241{
242 d->q = this;
243}
244
245
246Signature::Signature(SignaturePrivate *d_ptr)
247 :d(std::unique_ptr<SignaturePrivate>(d_ptr))
248{
249 d->q = this;
250}
251
252Signature::~Signature()
253{
254
255}
256
257QDateTime Signature::creationDateTime() const
258{
259 QDateTime dt;
260 dt.setTime_t(d->mSignature.creationTime());
261 return dt;
262}
263
264QDateTime Signature::expirationDateTime() const
265{
266 QDateTime dt;
267 dt.setTime_t(d->mSignature.expirationTime());
268 return dt;
269}
270
271bool Signature::neverExpires() const
272{
273 return d->mSignature.neverExpires();
274}
275
276Key::Ptr Signature::key() const
277{
278 return d->mKey;
279}
280
281class EncryptionPrivate
282{
283public:
284 Encryption *q;
285 std::vector<Key::Ptr> mRecipients;
286 Encryption::ErrorType mErrorType;
287 QString mErrorString;
288};
289
290Encryption::Encryption(EncryptionPrivate *d_ptr)
291 :d(std::unique_ptr<EncryptionPrivate>(d_ptr))
292{
293 d->q = this;
294}
295
296Encryption::Encryption()
297 :d(std::unique_ptr<EncryptionPrivate>(new EncryptionPrivate))
298{
299 d->q = this;
300 d->mErrorType = Encryption::NoError;
301}
302
303Encryption::~Encryption()
304{
305
306}
307
308std::vector<Key::Ptr> Encryption::recipients() const
309{
310 return d->mRecipients;
311}
312
313QString Encryption::errorString()
314{
315 return d->mErrorString;
316}
317
318Encryption::ErrorType Encryption::errorType()
319{
320 return d->mErrorType;
321}
322
323
324class PartPrivate
325{
326public:
327 PartPrivate(Part *part);
328 void appendSubPart(Part::Ptr subpart);
329
330 QVector<Part::Ptr> subParts();
331
332 Part *parent() const;
333
334 const MailMime::Ptr &mailMime() const;
335 void createMailMime(const MimeTreeParser::MimeMessagePart::Ptr &part);
336 void createMailMime(const MimeTreeParser::TextMessagePart::Ptr &part);
337 void createMailMime(const MimeTreeParser::AlternativeMessagePart::Ptr &part);
338 void createMailMime(const MimeTreeParser::HtmlMessagePart::Ptr &part);
339 void createMailMime(const MimeTreeParser::EncryptedMessagePart::Ptr &part);
340
341 static Encryption::Ptr createEncryption(const MimeTreeParser::EncryptedMessagePart::Ptr& part);
342 void appendEncryption(const MimeTreeParser::EncryptedMessagePart::Ptr &part);
343 static QVector<Signature::Ptr> createSignature(const MimeTreeParser::SignedMessagePart::Ptr& part);
344 void appendSignature(const MimeTreeParser::SignedMessagePart::Ptr &part);
345
346 void setSignatures(const QVector<Signature::Ptr> &sigs);
347 void setEncryptions(const QVector<Encryption::Ptr> &encs);
348
349 const QVector<Encryption::Ptr> &encryptions() const;
350 const QVector<Signature::Ptr> &signatures() const;
351private:
352 Part *q;
353 Part *mParent;
354 QVector<Part::Ptr> mSubParts;
355 QVector<Encryption::Ptr> mEncryptions;
356 QVector<Signature::Ptr> mSignatures;
357 MailMime::Ptr mMailMime;
358};
359
360PartPrivate::PartPrivate(Part* part)
361 : q(part)
362 , mParent(Q_NULLPTR)
363{
364
365}
366
367void PartPrivate::createMailMime(const MimeTreeParser::HtmlMessagePart::Ptr& part)
368{
369 mMailMime = MailMime::Ptr(new MailMime);
370 mMailMime->d->mNode = part->mNode;
371}
372
373void PartPrivate::createMailMime(const MimeTreeParser::AlternativeMessagePart::Ptr& part)
374{
375 mMailMime = MailMime::Ptr(new MailMime);
376 mMailMime->d->mNode = part->mNode;
377}
378
379void PartPrivate::createMailMime(const MimeTreeParser::TextMessagePart::Ptr& part)
380{
381 mMailMime = MailMime::Ptr(new MailMime);
382 mMailMime->d->mNode = part->mNode;
383}
384
385void PartPrivate::createMailMime(const MimeTreeParser::MimeMessagePart::Ptr& part)
386{
387 mMailMime = MailMime::Ptr(new MailMime);
388 mMailMime->d->mNode = part->mNode;
389}
390
391void PartPrivate::createMailMime(const MimeTreeParser::EncryptedMessagePart::Ptr& part)
392{
393 mMailMime = MailMime::Ptr(new MailMime);
394 mMailMime->d->mNode = part->mNode;
395}
396
397void PartPrivate::appendSubPart(Part::Ptr subpart)
398{
399 subpart->d->mParent = q;
400 mSubParts.append(subpart);
401}
402
403Encryption::Ptr PartPrivate::createEncryption(const MimeTreeParser::EncryptedMessagePart::Ptr& part)
404{
405 QGpgME::KeyListJob *job = part->mCryptoProto->keyListJob(false); // local, no sigs
406 if (!job) {
407 qWarning() << "The Crypto backend does not support listing keys. ";
408 return Encryption::Ptr();
409 }
410
411 auto encpriv = new EncryptionPrivate();
412 if (part->passphraseError()) {
413 encpriv->mErrorType = Encryption::PassphraseError;
414 encpriv->mErrorString = part->mMetaData.errorText;
415 } else if (part->isEncrypted() && !part->isDecryptable()) {
416 encpriv->mErrorType = Encryption::KeyMissing;
417 encpriv->mErrorString = part->mMetaData.errorText;
418 } else if (!part->isEncrypted() && !part->isDecryptable()) {
419 encpriv->mErrorType = Encryption::UnknownError;
420 encpriv->mErrorString = part->mMetaData.errorText;
421 } else {
422 encpriv->mErrorType = Encryption::NoError;
423 }
424
425 foreach(const auto &recipient, part->mDecryptRecipients) {
426 std::vector<GpgME::Key> found_keys;
427 const auto &keyid = recipient.keyID();
428 GpgME::KeyListResult res = job->exec(QStringList(QLatin1String(keyid)), false, found_keys);
429 if (res.error()) {
430 qWarning() << "Error while searching key for Fingerprint: " << keyid;
431 continue;
432 }
433 if (found_keys.size() > 1) {
434 // Should not Happen
435 qWarning() << "Oops: Found more then one Key for Fingerprint: " << keyid;
436 }
437 if (found_keys.size() != 1) {
438 // Should not Happen at this point
439 qWarning() << "Oops: Found no Key for Fingerprint: " << keyid;
440 auto keypriv = new KeyPrivate;
441 keypriv->mKeyID = keyid;
442 encpriv->mRecipients.push_back(Key::Ptr(new Key(keypriv)));
443 } else {
444 auto key = found_keys[0];
445 auto keypriv = new KeyPrivate;
446 keypriv->mKey = key;
447 encpriv->mRecipients.push_back(Key::Ptr(new Key(keypriv)));
448 }
449 }
450 return Encryption::Ptr(new Encryption(encpriv));
451}
452
453void PartPrivate::appendEncryption(const MimeTreeParser::EncryptedMessagePart::Ptr& part)
454{
455 mEncryptions.append(createEncryption(part));
456}
457
458void PartPrivate::setEncryptions(const QVector< Encryption::Ptr >& encs)
459{
460 mEncryptions = encs;
461}
462
463QVector<Signature::Ptr> PartPrivate::createSignature(const MimeTreeParser::SignedMessagePart::Ptr& part)
464{
465 QVector<Signature::Ptr> sigs;
466 QGpgME::KeyListJob *job = part->mCryptoProto->keyListJob(false); // local, no sigs
467 if (!job) {
468 qWarning() << "The Crypto backend does not support listing keys. ";
469 return sigs;
470 }
471
472 foreach(const auto &sig, part->mSignatures) {
473 auto sigpriv = new SignaturePrivate();
474 sigpriv->mSignature = sig;
475 auto signature = std::make_shared<Signature>(sigpriv);
476 sigs.append(signature);
477
478 std::vector<GpgME::Key> found_keys;
479 const auto &keyid = sig.fingerprint();
480 GpgME::KeyListResult res = job->exec(QStringList(QLatin1String(keyid)), false, found_keys);
481 if (res.error()) {
482 qWarning() << "Error while searching key for Fingerprint: " << keyid;
483 continue;
484 }
485 if (found_keys.size() > 1) {
486 // Should not Happen
487 qWarning() << "Oops: Found more then one Key for Fingerprint: " << keyid;
488 continue;
489 }
490 if (found_keys.size() != 1) {
491 // Should not Happen at this point
492 qWarning() << "Oops: Found no Key for Fingerprint: " << keyid;
493 continue;
494 } else {
495 auto key = found_keys[0];
496 auto keypriv = new KeyPrivate;
497 keypriv->mKey = key;
498 sigpriv->mKey = Key::Ptr(new Key(keypriv));
499 }
500 }
501 return sigs;
502}
503
504void PartPrivate::appendSignature(const MimeTreeParser::SignedMessagePart::Ptr& part)
505{
506 mSignatures.append(createSignature(part));
507}
508
509
510void PartPrivate::setSignatures(const QVector< Signature::Ptr >& sigs)
511{
512 mSignatures = sigs;
513}
514
515Part *PartPrivate::parent() const
516{
517 return mParent;
518}
519
520QVector< Part::Ptr > PartPrivate::subParts()
521{
522 return mSubParts;
523}
524
525const MailMime::Ptr& PartPrivate::mailMime() const
526{
527 return mMailMime;
528}
529
530const QVector< Encryption::Ptr >& PartPrivate::encryptions() const
531{
532 return mEncryptions;
533}
534
535const QVector< Signature::Ptr >& PartPrivate::signatures() const
536{
537 return mSignatures;
538}
539
540Part::Part()
541 : d(std::unique_ptr<PartPrivate>(new PartPrivate(this)))
542{
543
544}
545
546bool Part::hasSubParts() const
547{
548 return !subParts().isEmpty();
549}
550
551QVector<Part::Ptr> Part::subParts() const
552{
553 return d->subParts();
554}
555
556QByteArray Part::type() const
557{
558 return "Part";
559}
560
561QVector<QByteArray> Part::availableContents() const
562{
563 return QVector<QByteArray>();
564}
565
566QVector<Content::Ptr> Part::content() const
567{
568 return content(availableContents().first());
569}
570
571QVector<Content::Ptr> Part::content(const QByteArray& ct) const
572{
573 return QVector<Content::Ptr>();
574}
575
576QVector<Encryption::Ptr> Part::encryptions() const
577{
578 auto ret = d->encryptions();
579 auto parent = d->parent();
580 if (parent) {
581 ret.append(parent->encryptions());
582 }
583 return ret;
584}
585
586QVector<Signature::Ptr> Part::signatures() const
587{
588 auto ret = d->signatures();
589 auto parent = d->parent();
590 if (parent) {
591 ret.append(parent->signatures());
592 }
593 return ret;
594}
595
596MailMime::Ptr Part::mailMime() const
597{
598 return d->mailMime();
599}
600
601Part *Part::parent() const
602{
603 return d->parent();
604}
605
606class ContentPrivate
607{
608public:
609 QByteArray mContent;
610 QByteArray mCodec;
611 Part *mParent;
612 Content *q;
613 MailMime::Ptr mMailMime;
614 QVector<Encryption::Ptr> mEncryptions;
615 QVector<Signature::Ptr> mSignatures;
616 void appendSignature(const MimeTreeParser::SignedMessagePart::Ptr &sig);
617 void appendEncryption(const MimeTreeParser::EncryptedMessagePart::Ptr &enc);
618};
619
620void ContentPrivate::appendEncryption(const MimeTreeParser::EncryptedMessagePart::Ptr& enc)
621{
622 mEncryptions.append(PartPrivate::createEncryption(enc));
623}
624
625void ContentPrivate::appendSignature(const MimeTreeParser::SignedMessagePart::Ptr& sig)
626{
627 mSignatures.append(PartPrivate::createSignature(sig));
628}
629
630
631Content::Content(const QByteArray& content, Part *parent)
632 : d(std::unique_ptr<ContentPrivate>(new ContentPrivate))
633{
634 d->q = this;
635 d->mContent = content;
636 d->mCodec = "utf-8";
637 d->mParent = parent;
638}
639
640Content::Content(ContentPrivate* d_ptr)
641 : d(std::unique_ptr<ContentPrivate>(d_ptr))
642{
643 d->q = this;
644}
645
646Content::~Content()
647{
648}
649
650QVector<Encryption::Ptr> Content::encryptions() const
651{
652 auto ret = d->mEncryptions;
653 if (d->mParent) {
654 ret.append(d->mParent->encryptions());
655 }
656 return ret;
657}
658
659QVector<Signature::Ptr> Content::signatures() const
660{
661 auto ret = d->mSignatures;
662 if (d->mParent) {
663 ret.append(d->mParent->signatures());
664 }
665 return ret;
666}
667
668QByteArray Content::content() const
669{
670 return d->mContent;
671}
672
673QByteArray Content::charset() const
674{
675 return d->mCodec;
676}
677
678QString Content::encodedContent() const
679{
680 return QString::fromUtf8(content());
681 // TODO: should set "raw" content not the already utf8 encoded content
682 // return encodedContent(charset());
683}
684
685QString Content::encodedContent(const QByteArray &charset) const
686{
687 QTextCodec *codec = QTextCodec::codecForName(charset);
688 return codec->toUnicode(content());
689}
690
691QByteArray Content::type() const
692{
693 return "Content";
694}
695
696MailMime::Ptr Content::mailMime() const
697{
698 if (d->mMailMime) {
699 return d->mMailMime;
700 } else {
701 return d->mParent->mailMime();
702 }
703}
704
705Part *Content::parent() const
706{
707 return d->mParent;
708}
709
710HtmlContent::HtmlContent(const QByteArray& content, Part* parent)
711 : Content(content, parent)
712{
713
714}
715
716QByteArray HtmlContent::type() const
717{
718 return "HtmlContent";
719}
720
721PlainTextContent::PlainTextContent(const QByteArray& content, Part* parent)
722 : Content(content, parent)
723{
724
725}
726
727PlainTextContent::PlainTextContent(ContentPrivate* d_ptr)
728 : Content(d_ptr)
729{
730
731}
732
733HtmlContent::HtmlContent(ContentPrivate* d_ptr)
734 : Content(d_ptr)
735{
736
737}
738
739
740QByteArray PlainTextContent::type() const
741{
742 return "PlainTextContent";
743}
744
745class AlternativePartPrivate
746{
747public:
748 void fillFrom(MimeTreeParser::AlternativeMessagePart::Ptr part);
749
750 QVector<Content::Ptr> content(const QByteArray &ct) const;
751
752 AlternativePart *q;
753
754 QVector<QByteArray> types() const;
755
756private:
757 QMap<QByteArray, QVector<Content::Ptr>> mContent;
758 QVector<QByteArray> mTypes;
759};
760
761void AlternativePartPrivate::fillFrom(MimeTreeParser::AlternativeMessagePart::Ptr part)
762{
763 mTypes = QVector<QByteArray>() << "html" << "plaintext";
764
765 Content::Ptr content = std::make_shared<HtmlContent>(part->htmlContent().toLocal8Bit(), q);
766 mContent["html"].append(content);
767 content = std::make_shared<PlainTextContent>(part->plaintextContent().toLocal8Bit(), q);
768 mContent["plaintext"].append(content);
769 q->reachParentD()->createMailMime(part);
770}
771
772QVector<QByteArray> AlternativePartPrivate::types() const
773{
774 return mTypes;
775}
776
777QVector<Content::Ptr> AlternativePartPrivate::content(const QByteArray& ct) const
778{
779 return mContent[ct];
780}
781
782AlternativePart::AlternativePart()
783 : d(std::unique_ptr<AlternativePartPrivate>(new AlternativePartPrivate))
784{
785 d->q = this;
786}
787
788AlternativePart::~AlternativePart()
789{
790
791}
792
793QByteArray AlternativePart::type() const
794{
795 return "AlternativePart";
796}
797
798QVector<QByteArray> AlternativePart::availableContents() const
799{
800 return d->types();
801}
802
803QVector<Content::Ptr> AlternativePart::content(const QByteArray& ct) const
804{
805 return d->content(ct);
806}
807
808PartPrivate* AlternativePart::reachParentD() const
809{
810 return Part::d.get();
811}
812
813class SinglePartPrivate
814{
815public:
816 void fillFrom(const MimeTreeParser::TextMessagePart::Ptr &part);
817 void fillFrom(const MimeTreeParser::HtmlMessagePart::Ptr &part);
818 void fillFrom(const MimeTreeParser::AttachmentMessagePart::Ptr &part);
819 void createEncryptionFailBlock(const MimeTreeParser::EncryptedMessagePart::Ptr &part);
820 SinglePart *q;
821
822 QVector<Content::Ptr> mContent;
823 QByteArray mType;
824};
825
826void SinglePartPrivate::fillFrom(const MimeTreeParser::TextMessagePart::Ptr &part)
827{
828 mType = "plaintext";
829 mContent.clear();
830 foreach (const auto &mp, part->subParts()) {
831 auto d_ptr = new ContentPrivate;
832 d_ptr->mContent = mp->text().toUtf8(); // TODO: should set "raw" content not the already utf8 encoded content
833 d_ptr->mParent = q;
834 const auto enc = mp.dynamicCast<MimeTreeParser::EncryptedMessagePart>();
835 auto sig = mp.dynamicCast<MimeTreeParser::SignedMessagePart>();
836 if (enc) {
837 d_ptr->appendEncryption(enc);
838 if (!enc->isDecryptable()) {
839 d_ptr->mContent = QByteArray();
840 }
841 const auto s = enc->subParts();
842 if (s.size() == 1) {
843 sig = s[0].dynamicCast<MimeTreeParser::SignedMessagePart>();
844 }
845 }
846 if (sig) {
847 d_ptr->appendSignature(sig);
848 }
849 mContent.append(std::make_shared<PlainTextContent>(d_ptr));
850 q->reachParentD()->createMailMime(part);
851 d_ptr->mCodec = q->mailMime()->charset();
852 }
853}
854
855void SinglePartPrivate::fillFrom(const MimeTreeParser::HtmlMessagePart::Ptr &part)
856{
857 mType = "html";
858 mContent.clear();
859 mContent.append(std::make_shared<HtmlContent>(part->text().toUtf8(), q));
860 q->reachParentD()->createMailMime(part);
861}
862
863void SinglePartPrivate::fillFrom(const MimeTreeParser::AttachmentMessagePart::Ptr &part)
864{
865 QMimeDatabase mimeDb;
866 q->reachParentD()->createMailMime(part.staticCast<MimeTreeParser::TextMessagePart>());
867 const auto mimetype = q->mailMime()->mimetype();
868 const auto content = q->mailMime()->decodedContent();
869 mContent.clear();
870 if (mimetype == mimeDb.mimeTypeForName("text/plain")) {
871 mType = "plaintext";
872 mContent.append(std::make_shared<PlainTextContent>(content, q));
873 } else if (mimetype == mimeDb.mimeTypeForName("text/html")) {
874 mType = "html";
875 mContent.append(std::make_shared<HtmlContent>(content, q));
876 } else {
877 mType = mimetype.name().toUtf8();
878 mContent.append(std::make_shared<Content>(content, q));
879 }
880}
881
882void SinglePartPrivate::createEncryptionFailBlock(const MimeTreeParser::EncryptedMessagePart::Ptr &part)
883{
884 mType = "plaintext";
885 mContent.clear();
886 mContent.append(std::make_shared<PlainTextContent>(QByteArray(), q));
887 q->reachParentD()->createMailMime(part);
888}
889
890SinglePart::SinglePart()
891 : d(std::unique_ptr<SinglePartPrivate>(new SinglePartPrivate))
892{
893 d->q = this;
894}
895
896SinglePart::~SinglePart()
897{
898
899}
900
901QVector<QByteArray> SinglePart::availableContents() const
902{
903 return QVector<QByteArray>() << d->mType;
904}
905
906QVector< Content::Ptr > SinglePart::content(const QByteArray &ct) const
907{
908 if (ct == d->mType) {
909 return d->mContent;
910 }
911 return QVector<Content::Ptr>();
912}
913
914QByteArray SinglePart::type() const
915{
916 return "SinglePart";
917}
918
919PartPrivate* SinglePart::reachParentD() const
920{
921 return Part::d.get();
922}
923
924ParserPrivate::ParserPrivate(Parser* parser)
925 : q(parser)
926 , mNodeHelper(std::make_shared<MimeTreeParser::NodeHelper>())
927{
928
929}
930
931void ParserPrivate::setMessage(const QByteArray& mimeMessage)
932{
933 const auto mailData = KMime::CRLFtoLF(mimeMessage);
934 mMsg = KMime::Message::Ptr(new KMime::Message);
935 mMsg->setContent(mailData);
936 mMsg->parse();
937
938 // render the mail
939 StringHtmlWriter htmlWriter;
940 ObjectTreeSource source(&htmlWriter);
941 MimeTreeParser::ObjectTreeParser otp(&source, mNodeHelper.get());
942
943 otp.parseObjectTree(mMsg.data());
944 mPartTree = otp.parsedPart().dynamicCast<MimeTreeParser::MessagePart>();
945
946 mEmbeddedPartMap = htmlWriter.embeddedParts();
947 mHtml = htmlWriter.html();
948
949 mTree = std::make_shared<Part>();
950 createTree(mPartTree, mTree);
951}
952
953
954void ParserPrivate::createTree(const MimeTreeParser::MessagePart::Ptr &start, const Part::Ptr &tree)
955{
956 foreach (const auto &mp, start->subParts()) {
957 const auto m = mp.dynamicCast<MimeTreeParser::MessagePart>();
958 const auto text = mp.dynamicCast<MimeTreeParser::TextMessagePart>();
959 const auto alternative = mp.dynamicCast<MimeTreeParser::AlternativeMessagePart>();
960 const auto html = mp.dynamicCast<MimeTreeParser::HtmlMessagePart>();
961 const auto attachment = mp.dynamicCast<MimeTreeParser::AttachmentMessagePart>();
962 if (attachment) {
963 auto part = std::make_shared<SinglePart>();
964 part->d->fillFrom(attachment);
965 tree->d->appendSubPart(part);
966 } else if (text) {
967 auto part = std::make_shared<SinglePart>();
968 part->d->fillFrom(text);
969 tree->d->appendSubPart(part);
970 } else if (alternative) {
971 auto part = std::make_shared<AlternativePart>();
972 part->d->fillFrom(alternative);
973 tree->d->appendSubPart(part);
974 } else if (html) {
975 auto part = std::make_shared<SinglePart>();
976 part->d->fillFrom(html);
977 tree->d->appendSubPart(part);
978 } else {
979 const auto enc = mp.dynamicCast<MimeTreeParser::EncryptedMessagePart>();
980 const auto sig = mp.dynamicCast<MimeTreeParser::SignedMessagePart>();
981 if (enc || sig) {
982 auto subTree = std::make_shared<Part>();
983 if (enc) {
984 subTree->d->appendEncryption(enc);
985 if (!enc->isDecryptable()) {
986 auto part = std::make_shared<SinglePart>();
987 part->d->createEncryptionFailBlock(enc);
988 part->reachParentD()->setEncryptions(subTree->d->encryptions());
989 tree->d->appendSubPart(part);
990 return;
991 }
992 }
993 if (sig) {
994 subTree->d->appendSignature(sig);
995 }
996 createTree(m, subTree);
997 foreach(const auto &p, subTree->subParts()) {
998 tree->d->appendSubPart(p);
999 if (enc) {
1000 p->d->setEncryptions(subTree->d->encryptions());
1001 }
1002 if (sig) {
1003 p->d->setSignatures(subTree->d->signatures());
1004 }
1005 }
1006 } else {
1007 createTree(m, tree);
1008 }
1009 }
1010 }
1011}
1012
1013Parser::Parser(const QByteArray& mimeMessage)
1014 :d(std::unique_ptr<ParserPrivate>(new ParserPrivate(this)))
1015{
1016 d->setMessage(mimeMessage);
1017}
1018
1019Parser::~Parser()
1020{
1021}
1022
1023Part::Ptr Parser::getPart(const QUrl &url)
1024{
1025 if (url.scheme() == QStringLiteral("cid") && !url.path().isEmpty()) {
1026 const auto cid = url.path();
1027 return find(d->mTree, [&cid](const Part::Ptr &p){
1028 const auto mime = p->mailMime();
1029 return mime->cid() == cid;
1030 });
1031 }
1032 return Part::Ptr();
1033}
1034
1035QVector<Part::Ptr> Parser::collectContentParts() const
1036{
1037 return collect(d->mTree, [](const Part::Ptr &p){return p->type() != "EncapsulatedPart";},
1038 [](const Content::Ptr &content){
1039 const auto mime = content->mailMime();
1040
1041 if (!mime) {
1042 return true;
1043 }
1044
1045 if (mime->isFirstTextPart()) {
1046 return true;
1047 }
1048
1049 {
1050 auto _mime = content->parent()->mailMime();
1051 while (_mime) {
1052 if (_mime && (_mime->isTopLevelPart() || _mime->isFirstTextPart())) {
1053 return true;
1054 }
1055 if (_mime->isFirstPart()) {
1056 _mime = _mime->parent();
1057 } else {
1058 break;
1059 }
1060 }
1061 }
1062
1063 const auto ctname = mime->mimetype().name().trimmed().toLower();
1064 bool mightContent = (content->type() != "Content"); //Content we understand
1065
1066 const auto cd = mime->disposition();
1067 if (cd && cd == MailMime::Inline) {
1068 return mightContent;
1069 }
1070
1071 if (cd && cd == MailMime::Attachment) {
1072 return false;
1073 }
1074
1075 if ((ctname.startsWith("text/") || ctname.isEmpty()) &&
1076 (!mime || mime->filename().trimmed().isEmpty())) {
1077 // text/* w/o filename parameter:
1078 return true;
1079 }
1080 return false;
1081 });
1082}
1083
1084
1085QVector<Part::Ptr> Parser::collectAttachmentParts() const
1086{
1087 return collect(d->mTree, [](const Part::Ptr &p){return p->type() != "EncapsulatedPart";},
1088 [](const Content::Ptr &content){
1089 const auto mime = content->mailMime();
1090
1091 if (!mime) {
1092 return false;
1093 }
1094
1095 if (mime->isFirstTextPart()) {
1096 return false;
1097 }
1098
1099 {
1100 QMimeDatabase mimeDb;
1101 auto _mime = content->parent()->mailMime();
1102 const auto parent = _mime->parent();
1103 if (parent) {
1104 const auto mimetype = parent->mimetype();
1105 if (mimetype == mimeDb.mimeTypeForName("multipart/related")) {
1106 return false;
1107 }
1108 }
1109 while (_mime) {
1110 if (_mime && (_mime->isTopLevelPart() || _mime->isFirstTextPart())) {
1111 return false;
1112 }
1113 if (_mime->isFirstPart()) {
1114 _mime = _mime->parent();
1115 } else {
1116 break;
1117 }
1118 }
1119 }
1120
1121 const auto ctname = mime->mimetype().name().trimmed().toLower();
1122 bool mightContent = (content->type() != "Content"); //Content we understand
1123
1124 const auto cd = mime->disposition();
1125 if (cd && cd == MailMime::Inline) {
1126 // explict "inline" disposition:
1127 return !mightContent;
1128 }
1129 if (cd && cd == MailMime::Attachment) {
1130 // explicit "attachment" disposition:
1131 return true;
1132 }
1133
1134 const auto ct = mime->mimetype();
1135 if ((ctname.startsWith("text/") || ctname.isEmpty()) &&
1136 (!mime || mime->filename().trimmed().isEmpty())) {
1137 // text/* w/o filename parameter:
1138 return false;
1139 }
1140 return true;
1141 });
1142}
1143
1144QVector<Part::Ptr> Parser::collect(const Part::Ptr &start, std::function<bool(const Part::Ptr &)> select, std::function<bool(const Content::Ptr &)> filter) const
1145{
1146 QVector<Part::Ptr> ret;
1147 foreach (const auto &part, start->subParts()) {
1148 QVector<QByteArray> contents;
1149 foreach(const auto &ct, part->availableContents()) {
1150 foreach(const auto &content, part->content(ct)) {
1151 if (filter(content)) {
1152 contents.append(ct);
1153 break;
1154 }
1155 }
1156 }
1157 if (!contents.isEmpty()) {
1158 ret.append(part);
1159 }
1160 if (select(part)){
1161 ret += collect(part, select, filter);
1162 }
1163 }
1164 return ret;
1165}
1166
1167Part::Ptr Parser::find(const Part::Ptr &start, std::function<bool(const Part::Ptr &)> select) const
1168{
1169 foreach (const auto &part, start->subParts()) {
1170 if (select(part)) {
1171 return part;
1172 }
1173 const auto ret = find(part, select);
1174 if (ret) {
1175 return ret;
1176 }
1177 }
1178 return Part::Ptr();
1179}