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