summaryrefslogtreecommitdiffstats
path: root/framework/domain/mimetreeparser/interface.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'framework/domain/mimetreeparser/interface.cpp')
-rw-r--r--framework/domain/mimetreeparser/interface.cpp821
1 files changed, 821 insertions, 0 deletions
diff --git a/framework/domain/mimetreeparser/interface.cpp b/framework/domain/mimetreeparser/interface.cpp
new file mode 100644
index 00000000..c3ecf79c
--- /dev/null
+++ b/framework/domain/mimetreeparser/interface.cpp
@@ -0,0 +1,821 @@
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 <KMime/Content>
27#include <MimeTreeParser/ObjectTreeParser>
28#include <MimeTreeParser/MessagePart>
29#include <MimeTreeParser/NodeHelper>
30
31#include <QMimeDatabase>
32#include <QMimeType>
33#include <QDebug>
34
35class MailMimePrivate
36{
37public:
38 KMime::Content *mNode;
39 MailMime *q;
40};
41
42MailMime::MailMime()
43 : d(std::unique_ptr<MailMimePrivate>(new MailMimePrivate()))
44{
45 d->q = this;
46}
47
48bool MailMime::isFirstTextPart() const
49{
50 if (!d->mNode || !d->mNode->topLevel()) {
51 return false;
52 }
53 return (d->mNode->topLevel()->textContent() == d->mNode);
54}
55
56bool MailMime::isTopLevelPart() const
57{
58 if (!d->mNode) {
59 return false;
60 }
61 return (d->mNode->topLevel() == d->mNode);
62}
63
64MailMime::Disposition MailMime::disposition() const
65{
66 if (!d->mNode) {
67 return Invalid;
68 }
69 const auto cd = d->mNode->contentDisposition(false);
70 if (!cd) {
71 return Invalid;
72 }
73 switch (cd->disposition()){
74 case KMime::Headers::CDinline:
75 return Inline;
76 case KMime::Headers::CDattachment:
77 return Attachment;
78 default:
79 return Invalid;
80 }
81}
82
83QString MailMime::filename() const
84{
85 if (!d->mNode) {
86 return QString();
87 }
88 const auto cd = d->mNode->contentDisposition(false);
89 if (!cd) {
90 return QString();
91 }
92 return cd->filename();
93}
94
95QMimeType MailMime::mimetype() const
96{
97 if (!d->mNode) {
98 return QMimeType();
99 }
100
101 const auto ct = d->mNode->contentType(false);
102 if (!ct) {
103 return QMimeType();
104 }
105
106 QMimeDatabase mimeDb;
107 return mimeDb.mimeTypeForName(ct->mimeType());
108}
109
110class PartPrivate
111{
112public:
113 PartPrivate(Part *part);
114 void appendSubPart(Part::Ptr subpart);
115
116 QVector<Part::Ptr> subParts();
117
118 Part *parent() const;
119
120 const MailMime::Ptr &mailMime() const;
121 void createMailMime(const MimeTreeParser::MimeMessagePart::Ptr &part);
122 void createMailMime(const MimeTreeParser::TextMessagePart::Ptr &part);
123 void createMailMime(const MimeTreeParser::AlternativeMessagePart::Ptr &part);
124 void createMailMime(const MimeTreeParser::HtmlMessagePart::Ptr &part);
125
126 void appendEncryption(const MimeTreeParser::EncryptedMessagePart::Ptr &part);
127 void appendSignature(const MimeTreeParser::SignedMessagePart::Ptr &part);
128
129 void setSignatures(const QVector<Signature::Ptr> &sigs);
130 void setEncryptions(const QVector<Encryption::Ptr> &encs);
131
132 const QVector<Encryption::Ptr> &encryptions() const;
133 const QVector<Signature::Ptr> &signatures() const;
134private:
135 Part *q;
136 Part *mParent;
137 QVector<Part::Ptr> mSubParts;
138 QVector<Encryption::Ptr> mEncryptions;
139 QVector<Signature::Ptr> mSignatures;
140 MailMime::Ptr mMailMime;
141};
142
143PartPrivate::PartPrivate(Part* part)
144 : q(part)
145 , mParent(Q_NULLPTR)
146{
147
148}
149
150void PartPrivate::createMailMime(const MimeTreeParser::HtmlMessagePart::Ptr& part)
151{
152 mMailMime = MailMime::Ptr(new MailMime);
153 mMailMime->d->mNode = part->mNode;
154}
155
156void PartPrivate::createMailMime(const MimeTreeParser::AlternativeMessagePart::Ptr& part)
157{
158 mMailMime = MailMime::Ptr(new MailMime);
159 mMailMime->d->mNode = part->mNode;
160}
161
162void PartPrivate::createMailMime(const MimeTreeParser::TextMessagePart::Ptr& part)
163{
164 mMailMime = MailMime::Ptr(new MailMime);
165 mMailMime->d->mNode = part->mNode;
166}
167
168void PartPrivate::createMailMime(const MimeTreeParser::MimeMessagePart::Ptr& part)
169{
170 mMailMime = MailMime::Ptr(new MailMime);
171 mMailMime->d->mNode = part->mNode;
172}
173
174void PartPrivate::appendSubPart(Part::Ptr subpart)
175{
176 subpart->d->mParent = q;
177 mSubParts.append(subpart);
178}
179
180void PartPrivate::appendEncryption(const MimeTreeParser::EncryptedMessagePart::Ptr& part)
181{
182 mEncryptions.append(Encryption::Ptr(new Encryption));
183}
184
185void PartPrivate::setEncryptions(const QVector< Encryption::Ptr >& encs)
186{
187 mEncryptions = encs;
188}
189
190void PartPrivate::appendSignature(const MimeTreeParser::SignedMessagePart::Ptr& part)
191{
192 mSignatures.append(Signature::Ptr(new Signature));
193}
194
195
196void PartPrivate::setSignatures(const QVector< Signature::Ptr >& sigs)
197{
198 mSignatures = sigs;
199}
200
201Part *PartPrivate::parent() const
202{
203 return mParent;
204}
205
206QVector< Part::Ptr > PartPrivate::subParts()
207{
208 return mSubParts;
209}
210
211const MailMime::Ptr& PartPrivate::mailMime() const
212{
213 return mMailMime;
214}
215
216const QVector< Encryption::Ptr >& PartPrivate::encryptions() const
217{
218 return mEncryptions;
219}
220
221const QVector< Signature::Ptr >& PartPrivate::signatures() const
222{
223 return mSignatures;
224}
225
226Part::Part()
227 : d(std::unique_ptr<PartPrivate>(new PartPrivate(this)))
228{
229
230}
231
232bool Part::hasSubParts() const
233{
234 return !subParts().isEmpty();
235}
236
237QVector<Part::Ptr> Part::subParts() const
238{
239 return d->subParts();
240}
241
242QByteArray Part::type() const
243{
244 return "Part";
245}
246
247QVector<QByteArray> Part::availableContents() const
248{
249 return QVector<QByteArray>();
250}
251
252QVector<Content::Ptr> Part::content() const
253{
254 return content(availableContents().first());
255}
256
257QVector<Content::Ptr> Part::content(const QByteArray& ct) const
258{
259 return QVector<Content::Ptr>();
260}
261
262QVector<Encryption::Ptr> Part::encryptions() const
263{
264 auto ret = d->encryptions();
265 auto parent = d->parent();
266 if (parent) {
267 ret.append(parent->encryptions());
268 }
269 return ret;
270}
271
272QVector<Signature::Ptr> Part::signatures() const
273{
274 auto ret = d->signatures();
275 auto parent = d->parent();
276 if (parent) {
277 ret.append(parent->signatures());
278 }
279 return ret;
280}
281
282MailMime::Ptr Part::mailMime() const
283{
284 return d->mailMime();
285}
286
287class ContentPrivate
288{
289public:
290 QByteArray mContent;
291 QByteArray mCodec;
292 Part *mParent;
293 Content *q;
294 MailMime::Ptr mMailMime;
295 QVector<Encryption::Ptr> mEncryptions;
296 QVector<Signature::Ptr> mSignatures;
297 void appendSignature(const MimeTreeParser::SignedMessagePart::Ptr &sig);
298 void appendEncryption(const MimeTreeParser::EncryptedMessagePart::Ptr &enc);
299};
300
301void ContentPrivate::appendEncryption(const MimeTreeParser::EncryptedMessagePart::Ptr& enc)
302{
303 mEncryptions.append(Encryption::Ptr(new Encryption));
304}
305
306void ContentPrivate::appendSignature(const MimeTreeParser::SignedMessagePart::Ptr& sig)
307{
308 mSignatures.append(Signature::Ptr(new Signature));
309}
310
311
312Content::Content(const QByteArray& content, Part *parent)
313 : d(std::unique_ptr<ContentPrivate>(new ContentPrivate))
314{
315 d->q = this;
316 d->mContent = content;
317 d->mCodec = "utf-8";
318 d->mParent = parent;
319}
320
321Content::Content(ContentPrivate* d_ptr)
322 : d(std::unique_ptr<ContentPrivate>(d_ptr))
323{
324 d->q = this;
325}
326
327Content::~Content()
328{
329}
330
331QVector<Encryption::Ptr> Content::encryptions() const
332{
333 auto ret = d->mEncryptions;
334 if (d->mParent) {
335 ret.append(d->mParent->encryptions());
336 }
337 return ret;
338}
339
340QVector<Signature::Ptr> Content::signatures() const
341{
342 auto ret = d->mSignatures;
343 if (d->mParent) {
344 ret.append(d->mParent->signatures());
345 }
346 return ret;
347}
348
349QByteArray Content::content() const
350{
351 return d->mContent;
352}
353
354QByteArray Content::charset() const
355{
356 return d->mCodec;
357}
358
359QByteArray Content::type() const
360{
361 return "Content";
362}
363
364MailMime::Ptr Content::mailMime() const
365{
366 if (d->mMailMime) {
367 return d->mMailMime;
368 } else {
369 return d->mParent->mailMime();
370 }
371}
372
373Part *Content::parent() const
374{
375 return d->mParent;
376}
377
378HtmlContent::HtmlContent(const QByteArray& content, Part* parent)
379 : Content(content, parent)
380{
381
382}
383
384QByteArray HtmlContent::type() const
385{
386 return "HtmlContent";
387}
388
389PlainTextContent::PlainTextContent(const QByteArray& content, Part* parent)
390 : Content(content, parent)
391{
392
393}
394
395PlainTextContent::PlainTextContent(ContentPrivate* d_ptr)
396 : Content(d_ptr)
397{
398
399}
400
401HtmlContent::HtmlContent(ContentPrivate* d_ptr)
402 : Content(d_ptr)
403{
404
405}
406
407
408QByteArray PlainTextContent::type() const
409{
410 return "PlainTextContent";
411}
412
413class AlternativePartPrivate
414{
415public:
416 void fillFrom(MimeTreeParser::AlternativeMessagePart::Ptr part);
417
418 QVector<Content::Ptr> content(const QByteArray &ct) const;
419
420 AlternativePart *q;
421
422 QVector<QByteArray> types() const;
423
424private:
425 QMap<QByteArray, QVector<Content::Ptr>> mContent;
426 QVector<QByteArray> mTypes;
427};
428
429void AlternativePartPrivate::fillFrom(MimeTreeParser::AlternativeMessagePart::Ptr part)
430{
431 mTypes = QVector<QByteArray>() << "html" << "plaintext";
432
433 Content::Ptr content = std::make_shared<HtmlContent>(part->htmlContent().toLocal8Bit(), q);
434 mContent["html"].append(content);
435 content = std::make_shared<PlainTextContent>(part->plaintextContent().toLocal8Bit(), q);
436 mContent["plaintext"].append(content);
437 q->reachParentD()->createMailMime(part);
438}
439
440QVector<QByteArray> AlternativePartPrivate::types() const
441{
442 return mTypes;
443}
444
445QVector<Content::Ptr> AlternativePartPrivate::content(const QByteArray& ct) const
446{
447 return mContent[ct];
448}
449
450AlternativePart::AlternativePart()
451 : d(std::unique_ptr<AlternativePartPrivate>(new AlternativePartPrivate))
452{
453 d->q = this;
454}
455
456AlternativePart::~AlternativePart()
457{
458
459}
460
461QByteArray AlternativePart::type() const
462{
463 return "AlternativePart";
464}
465
466QVector<QByteArray> AlternativePart::availableContents() const
467{
468 return d->types();
469}
470
471QVector<Content::Ptr> AlternativePart::content(const QByteArray& ct) const
472{
473 return d->content(ct);
474}
475
476PartPrivate* AlternativePart::reachParentD() const
477{
478 return Part::d.get();
479}
480
481class SinglePartPrivate
482{
483public:
484 void fillFrom(MimeTreeParser::TextMessagePart::Ptr part);
485 void fillFrom(MimeTreeParser::HtmlMessagePart::Ptr part);
486 void fillFrom(MimeTreeParser::AttachmentMessagePart::Ptr part);
487 SinglePart *q;
488
489 QVector<Content::Ptr> mContent;
490 QByteArray mType;
491};
492
493void SinglePartPrivate::fillFrom(MimeTreeParser::TextMessagePart::Ptr part)
494{
495 mType = "plaintext";
496 mContent.clear();
497 foreach (const auto &mp, part->subParts()) {
498 auto d_ptr = new ContentPrivate;
499 d_ptr->mContent = part->text().toLocal8Bit();
500 d_ptr->mParent = q;
501 d_ptr->mCodec = "utf-8";
502 const auto enc = mp.dynamicCast<MimeTreeParser::EncryptedMessagePart>();
503 auto sig = mp.dynamicCast<MimeTreeParser::SignedMessagePart>();
504 if (enc) {
505 d_ptr->appendEncryption(enc);
506 const auto s = enc->subParts();
507 if (s.size() == 1) {
508 sig = s[0].dynamicCast<MimeTreeParser::SignedMessagePart>();
509 }
510 }
511 if (sig) {
512 d_ptr->appendSignature(sig);
513 }
514 mContent.append(std::make_shared<PlainTextContent>(d_ptr));
515 q->reachParentD()->createMailMime(part);
516 }
517}
518
519void SinglePartPrivate::fillFrom(MimeTreeParser::HtmlMessagePart::Ptr part)
520{
521 mType = "html";
522 mContent.clear();
523 mContent.append(std::make_shared<HtmlContent>(part->text().toLocal8Bit(), q));
524 q->reachParentD()->createMailMime(part);
525}
526
527void SinglePartPrivate::fillFrom(MimeTreeParser::AttachmentMessagePart::Ptr part)
528{
529 q->reachParentD()->createMailMime(part.staticCast<MimeTreeParser::TextMessagePart>());
530 mType = q->mailMime()->mimetype().name().toUtf8();
531 mContent.clear();
532 mContent.append(std::make_shared<Content>(part->text().toLocal8Bit(), q));
533}
534
535SinglePart::SinglePart()
536 : d(std::unique_ptr<SinglePartPrivate>(new SinglePartPrivate))
537{
538 d->q = this;
539}
540
541SinglePart::~SinglePart()
542{
543
544}
545
546QVector<QByteArray> SinglePart::availableContents() const
547{
548 return QVector<QByteArray>() << d->mType;
549}
550
551QVector< Content::Ptr > SinglePart::content(const QByteArray &ct) const
552{
553 if (ct == d->mType) {
554 return d->mContent;
555 }
556 return QVector<Content::Ptr>();
557}
558
559QByteArray SinglePart::type() const
560{
561 return "SinglePart";
562}
563
564PartPrivate* SinglePart::reachParentD() const
565{
566 return Part::d.get();
567}
568
569class SignaturePrivate
570{
571public:
572 Signature *q;
573};
574
575Signature::Signature()
576 :d(std::unique_ptr<SignaturePrivate>(new SignaturePrivate))
577{
578 d->q = this;
579}
580
581
582Signature::Signature(SignaturePrivate *d_ptr)
583 :d(std::unique_ptr<SignaturePrivate>(d_ptr))
584{
585 d->q = this;
586}
587
588Signature::~Signature()
589{
590
591}
592
593
594class EncryptionPrivate
595{
596public:
597 Encryption *q;
598};
599
600Encryption::Encryption(EncryptionPrivate *d_ptr)
601 :d(std::unique_ptr<EncryptionPrivate>(d_ptr))
602{
603 d->q = this;
604}
605
606Encryption::Encryption()
607 :d(std::unique_ptr<EncryptionPrivate>(new EncryptionPrivate))
608{
609 d->q = this;
610}
611
612Encryption::~Encryption()
613{
614
615}
616
617ParserPrivate::ParserPrivate(Parser* parser)
618 : q(parser)
619 , mNodeHelper(std::make_shared<MimeTreeParser::NodeHelper>())
620{
621
622}
623
624void ParserPrivate::setMessage(const QByteArray& mimeMessage)
625{
626 const auto mailData = KMime::CRLFtoLF(mimeMessage);
627 mMsg = KMime::Message::Ptr(new KMime::Message);
628 mMsg->setContent(mailData);
629 mMsg->parse();
630
631 // render the mail
632 StringHtmlWriter htmlWriter;
633 ObjectTreeSource source(&htmlWriter);
634 MimeTreeParser::ObjectTreeParser otp(&source, mNodeHelper.get());
635
636 otp.parseObjectTree(mMsg.data());
637 mPartTree = otp.parsedPart().dynamicCast<MimeTreeParser::MessagePart>();
638
639 mEmbeddedPartMap = htmlWriter.embeddedParts();
640 mHtml = htmlWriter.html();
641
642 mTree = std::make_shared<Part>();
643 createTree(mPartTree, mTree);
644}
645
646
647void ParserPrivate::createTree(const MimeTreeParser::MessagePart::Ptr &start, const Part::Ptr &tree)
648{
649 foreach (const auto &mp, start->subParts()) {
650 const auto m = mp.dynamicCast<MimeTreeParser::MessagePart>();
651 const auto text = mp.dynamicCast<MimeTreeParser::TextMessagePart>();
652 const auto alternative = mp.dynamicCast<MimeTreeParser::AlternativeMessagePart>();
653 const auto html = mp.dynamicCast<MimeTreeParser::HtmlMessagePart>();
654 const auto attachment = mp.dynamicCast<MimeTreeParser::AttachmentMessagePart>();
655 if (attachment) {
656 auto part = std::make_shared<SinglePart>();
657 part->d->fillFrom(attachment);
658 tree->d->appendSubPart(part);
659 } else if (text) {
660 auto part = std::make_shared<SinglePart>();
661 part->d->fillFrom(text);
662 tree->d->appendSubPart(part);
663 } else if (alternative) {
664 auto part = std::make_shared<AlternativePart>();
665 part->d->fillFrom(alternative);
666 tree->d->appendSubPart(part);
667 } else if (html) {
668 auto part = std::make_shared<SinglePart>();
669 part->d->fillFrom(html);
670 tree->d->appendSubPart(part);
671 } else {
672 const auto enc = mp.dynamicCast<MimeTreeParser::EncryptedMessagePart>();
673 const auto sig = mp.dynamicCast<MimeTreeParser::SignedMessagePart>();
674 if (enc || sig) {
675 auto subTree = std::make_shared<Part>();
676 if (enc) {
677 subTree->d->appendEncryption(enc);
678 }
679 if (sig) {
680 subTree->d->appendSignature(sig);
681 }
682 createTree(m, subTree);
683 foreach(const auto &p, subTree->subParts()) {
684 tree->d->appendSubPart(p);
685 if (enc) {
686 p->d->setEncryptions(subTree->d->encryptions());
687 }
688 if (sig) {
689 p->d->setSignatures(subTree->d->signatures());
690 }
691 }
692 } else {
693 createTree(m, tree);
694 }
695 }
696 }
697}
698
699Parser::Parser(const QByteArray& mimeMessage)
700 :d(std::unique_ptr<ParserPrivate>(new ParserPrivate(this)))
701{
702 d->setMessage(mimeMessage);
703}
704
705Parser::~Parser()
706{
707}
708
709QUrl Parser::getPart(const QByteArray &cid)
710{
711 return d->mEmbeddedPartMap.value(cid);
712}
713
714QVector<Part::Ptr> Parser::collectContentParts() const
715{
716 return collect(d->mTree, [](const Part::Ptr &p){return p->type() != "EncapsulatedPart";},
717 [](const Content::Ptr &content){
718 const auto mime = content->mailMime();
719
720 if (!mime) {
721 return true;
722 }
723
724 if (mime->isFirstTextPart()) {
725 return true;
726 }
727
728 {
729 const auto parent = content->parent();
730 if (parent) {
731 const auto _mime = parent->mailMime();
732 if (_mime && (_mime->isTopLevelPart() || _mime->isFirstTextPart())) {
733 return true;
734 }
735 }
736 }
737 const auto cd = mime->disposition();
738 if (cd && cd == MailMime::Inline) {
739 // explict "inline" disposition:
740 return true;
741 }
742 if (cd && cd == MailMime::Attachment) {
743 // explicit "attachment" disposition:
744 return false;
745 }
746
747 const auto ct = mime->mimetype();
748 if (ct.name().trimmed().toLower() == "text" && ct.name().trimmed().isEmpty() &&
749 (!mime || mime->filename().trimmed().isEmpty())) {
750 // text/* w/o filename parameter:
751 return true;
752 }
753 return false;
754 });
755}
756
757
758QVector<Part::Ptr> Parser::collectAttachmentParts() const
759{
760 return collect(d->mTree, [](const Part::Ptr &p){return p->type() != "EncapsulatedPart";},
761 [](const Content::Ptr &content){
762 const auto mime = content->mailMime();
763
764 if (!mime) {
765 return false;
766 }
767
768 if (mime->isFirstTextPart()) {
769 return false;
770 }
771
772 {
773 const auto parent = content->parent();
774 if (parent) {
775 const auto _mime = parent->mailMime();
776 if (_mime && (_mime->isTopLevelPart() || _mime->isFirstTextPart())) {
777 return false;
778 }
779 }
780 }
781 const auto cd = mime->disposition();
782 if (cd && cd == MailMime::Inline) {
783 // explict "inline" disposition:
784 return false;
785 }
786 if (cd && cd == MailMime::Attachment) {
787 // explicit "attachment" disposition:
788 return true;
789 }
790
791 const auto ct = mime->mimetype();
792 if (ct.name().trimmed().toLower() == "text" && ct.name().trimmed().isEmpty() &&
793 (!mime || mime->filename().trimmed().isEmpty())) {
794 // text/* w/o filename parameter:
795 return false;
796 }
797 return true;
798 });
799}
800QVector<Part::Ptr> Parser::collect(const Part::Ptr &start, std::function<bool(const Part::Ptr &)> select, std::function<bool(const Content::Ptr &)> filter) const
801{
802 QVector<Part::Ptr> ret;
803 foreach (const auto &part, start->subParts()) {
804 QVector<QByteArray> contents;
805 foreach(const auto &ct, part->availableContents()) {
806 foreach(const auto &content, part->content(ct)) {
807 if (filter(content)) {
808 contents.append(ct);
809 break;
810 }
811 }
812 }
813 if (!contents.isEmpty()) {
814 ret.append(part);
815 }
816 if (select(part)){
817 ret += collect(part, select, filter);
818 }
819 }
820 return ret;
821} \ No newline at end of file