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.cpp538
1 files changed, 538 insertions, 0 deletions
diff --git a/framework/domain/mimetreeparser/interface.cpp b/framework/domain/mimetreeparser/interface.cpp
new file mode 100644
index 00000000..a76a6cde
--- /dev/null
+++ b/framework/domain/mimetreeparser/interface.cpp
@@ -0,0 +1,538 @@
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) {
51 return false;
52 }
53 return (d->mNode->topLevel()->textContent() == d->mNode);
54}
55
56MailMime::Disposition MailMime::disposition() const
57{
58 if (!d->mNode) {
59 return Invalid;
60 }
61 const auto cd = d->mNode->contentDisposition(false);
62 if (!cd) {
63 return Invalid;
64 }
65 switch (cd->disposition()){
66 case KMime::Headers::CDinline:
67 return Inline;
68 case KMime::Headers::CDattachment:
69 return Attachment;
70 default:
71 return Invalid;
72 }
73}
74
75QString MailMime::filename() const
76{
77 if (!d->mNode) {
78 return QString();
79 }
80 const auto cd = d->mNode->contentDisposition(false);
81 if (!cd) {
82 return QString();
83 }
84 return cd->filename();
85}
86
87QMimeType MailMime::mimetype() const
88{
89 if (!d->mNode) {
90 return QMimeType();
91 }
92
93 const auto ct = d->mNode->contentType(false);
94 if (!ct) {
95 return QMimeType();
96 }
97
98 QMimeDatabase mimeDb;
99 return mimeDb.mimeTypeForName(ct->mimeType());
100}
101
102class PartPrivate
103{
104public:
105 PartPrivate(Part *part);
106 void appendSubPart(Part::Ptr subpart);
107
108 QVector<Part::Ptr> subParts();
109
110 Part *parent() const;
111
112 const MailMime::Ptr &mailMime() const;
113private:
114 Part *q;
115 Part *mParent;
116 QVector<Part::Ptr> mSubParts;
117 MailMime::Ptr mMailMime;
118};
119
120PartPrivate::PartPrivate(Part* part)
121 : q(part)
122 , mParent(Q_NULLPTR)
123{
124
125}
126
127void PartPrivate::appendSubPart(Part::Ptr subpart)
128{
129 subpart->d->mParent = q;
130 mSubParts.append(subpart);
131}
132
133Part *PartPrivate::parent() const
134{
135 return mParent;
136}
137
138QVector< Part::Ptr > PartPrivate::subParts()
139{
140 return mSubParts;
141}
142
143const MailMime::Ptr& PartPrivate::mailMime() const
144{
145 return mMailMime;
146}
147
148Part::Part()
149 : d(std::unique_ptr<PartPrivate>(new PartPrivate(this)))
150{
151
152}
153
154bool Part::hasSubParts() const
155{
156 return !subParts().isEmpty();
157}
158
159QVector<Part::Ptr> Part::subParts() const
160{
161 return d->subParts();
162}
163
164QByteArray Part::type() const
165{
166 return "Part";
167}
168
169QVector<QByteArray> Part::availableContents() const
170{
171 return QVector<QByteArray>();
172}
173
174QVector<Content::Ptr> Part::content() const
175{
176 return content(availableContents().first());
177}
178
179QVector<Content::Ptr> Part::content(const QByteArray& ct) const
180{
181 return QVector<Content::Ptr>();
182}
183
184QVector<Encryption> Part::encryptions() const
185{
186 auto parent = d->parent();
187 if (parent) {
188 return parent->encryptions();
189 } else {
190 return QVector<Encryption>();
191 }
192}
193
194QVector<Signature> Part::signatures() const
195{
196 auto parent = d->parent();
197 if (parent) {
198 return parent->signatures();
199 } else {
200 return QVector<Signature>();
201 }
202}
203
204MailMime::Ptr Part::mailMime() const
205{
206 return d->mailMime();
207}
208
209class ContentPrivate
210{
211public:
212 QByteArray mContent;
213 QByteArray mCodec;
214 Part *mParent;
215 Content *q;
216 MailMime::Ptr mMailMime;
217};
218
219Content::Content(const QByteArray& content, Part *parent)
220 : d(std::unique_ptr<ContentPrivate>(new ContentPrivate))
221{
222 d->q = this;
223 d->mContent = content;
224 d->mCodec = "utf-8";
225 d->mParent = parent;
226}
227
228Content::~Content()
229{
230}
231
232QVector<Encryption> Content::encryptions() const
233{
234 if (d->mParent) {
235 return d->mParent->encryptions();
236 }
237 return QVector<Encryption>();
238}
239
240QVector<Signature> Content::signatures() const
241{
242 if (d->mParent) {
243 return d->mParent->signatures();
244 }
245 return QVector<Signature>();
246}
247
248QByteArray Content::content() const
249{
250 return d->mContent;
251}
252
253QByteArray Content::charset() const
254{
255 return d->mCodec;
256}
257
258QByteArray Content::type() const
259{
260 return "Content";
261}
262
263MailMime::Ptr Content::mailMime() const
264{
265 return d->mMailMime;
266}
267
268HtmlContent::HtmlContent(const QByteArray& content, Part* parent)
269 : Content(content, parent)
270{
271
272}
273
274QByteArray HtmlContent::type() const
275{
276 return "HtmlContent";
277}
278
279PlainTextContent::PlainTextContent(const QByteArray& content, Part* parent)
280 : Content(content, parent)
281{
282
283}
284
285QByteArray PlainTextContent::type() const
286{
287 return "PlainTextContent";
288}
289
290class AlternativePartPrivate
291{
292public:
293 void fillFrom(MimeTreeParser::AlternativeMessagePart::Ptr part);
294
295 QVector<Content::Ptr> content(const QByteArray &ct) const;
296
297 AlternativePart *q;
298
299 QVector<QByteArray> types() const;
300
301private:
302 QMap<QByteArray, QVector<Content::Ptr>> mContent;
303 QVector<QByteArray> mTypes;
304};
305
306void AlternativePartPrivate::fillFrom(MimeTreeParser::AlternativeMessagePart::Ptr part)
307{
308 mTypes = QVector<QByteArray>() << "html" << "plaintext";
309
310 Content::Ptr content = std::make_shared<HtmlContent>(part->htmlContent().toLocal8Bit(), q);
311 mContent["html"].append(content);
312 content = std::make_shared<PlainTextContent>(part->plaintextContent().toLocal8Bit(), q);
313 mContent["plaintext"].append(content);
314}
315
316QVector<QByteArray> AlternativePartPrivate::types() const
317{
318 return mTypes;
319}
320
321QVector<Content::Ptr> AlternativePartPrivate::content(const QByteArray& ct) const
322{
323 return mContent[ct];
324}
325
326AlternativePart::AlternativePart()
327 : d(std::unique_ptr<AlternativePartPrivate>(new AlternativePartPrivate))
328{
329 d->q = this;
330}
331
332AlternativePart::~AlternativePart()
333{
334
335}
336
337QByteArray AlternativePart::type() const
338{
339 return "AlternativePart";
340}
341
342QVector<QByteArray> AlternativePart::availableContents() const
343{
344 return d->types();
345}
346
347QVector<Content::Ptr> AlternativePart::content(const QByteArray& ct) const
348{
349 return d->content(ct);
350}
351
352class SinglePartPrivate
353{
354public:
355 void fillFrom(MimeTreeParser::TextMessagePart::Ptr part);
356 void fillFrom(MimeTreeParser::HtmlMessagePart::Ptr part);
357 void fillFrom(MimeTreeParser::AttachmentMessagePart::Ptr part);
358 SinglePart *q;
359
360 QVector<Content::Ptr> mContent;
361 QByteArray mType;
362};
363
364void SinglePartPrivate::fillFrom(MimeTreeParser::TextMessagePart::Ptr part)
365{
366 mType = "plaintext";
367 mContent.clear();
368 foreach (const auto &mp, part->subParts()) {
369 mContent.append(std::make_shared<PlainTextContent>(mp->text().toLocal8Bit(), q));
370 }
371}
372
373void SinglePartPrivate::fillFrom(MimeTreeParser::HtmlMessagePart::Ptr part)
374{
375 mType = "html";
376 mContent.clear();
377 mContent.append(std::make_shared<HtmlContent>(part->text().toLocal8Bit(), q));
378}
379
380void SinglePartPrivate::fillFrom(MimeTreeParser::AttachmentMessagePart::Ptr part)
381{
382
383}
384
385SinglePart::SinglePart()
386 : d(std::unique_ptr<SinglePartPrivate>(new SinglePartPrivate))
387{
388 d->q = this;
389}
390
391SinglePart::~SinglePart()
392{
393
394}
395
396QVector<QByteArray> SinglePart::availableContents() const
397{
398 return QVector<QByteArray>() << d->mType;
399}
400
401QVector< Content::Ptr > SinglePart::content(const QByteArray &ct) const
402{
403 if (ct == d->mType) {
404 return d->mContent;
405 }
406 return QVector<Content::Ptr>();
407}
408
409QByteArray SinglePart::type() const
410{
411 return "SinglePart";
412}
413
414ParserPrivate::ParserPrivate(Parser* parser)
415 : q(parser)
416 , mNodeHelper(std::make_shared<MimeTreeParser::NodeHelper>())
417{
418
419}
420
421void ParserPrivate::setMessage(const QByteArray& mimeMessage)
422{
423 const auto mailData = KMime::CRLFtoLF(mimeMessage);
424 KMime::Message::Ptr msg(new KMime::Message);
425 msg->setContent(mailData);
426 msg->parse();
427
428 // render the mail
429 StringHtmlWriter htmlWriter;
430 ObjectTreeSource source(&htmlWriter);
431 MimeTreeParser::ObjectTreeParser otp(&source, mNodeHelper.get());
432
433 otp.parseObjectTree(msg.data());
434 mPartTree = otp.parsedPart().dynamicCast<MimeTreeParser::MessagePart>();
435
436 mEmbeddedPartMap = htmlWriter.embeddedParts();
437 mHtml = htmlWriter.html();
438
439 mTree = std::make_shared<Part>();
440 createTree(mPartTree, mTree);
441}
442
443
444void ParserPrivate::createTree(const MimeTreeParser::MessagePart::Ptr &start, const Part::Ptr &tree)
445{
446 foreach (const auto &mp, start->subParts()) {
447 const auto m = mp.dynamicCast<MimeTreeParser::MessagePart>();
448 const auto text = mp.dynamicCast<MimeTreeParser::TextMessagePart>();
449 const auto alternative = mp.dynamicCast<MimeTreeParser::AlternativeMessagePart>();
450 const auto html = mp.dynamicCast<MimeTreeParser::HtmlMessagePart>();
451 const auto attachment = mp.dynamicCast<MimeTreeParser::AttachmentMessagePart>();
452 if (attachment) {
453 auto part = std::make_shared<SinglePart>();
454 part->d->fillFrom(attachment);
455 mTree->d->appendSubPart(part);
456 } else if (text) {
457 auto part = std::make_shared<SinglePart>();
458 part->d->fillFrom(text);
459 mTree->d->appendSubPart(part);
460 } else if (alternative) {
461 auto part = std::make_shared<AlternativePart>();
462 part->d->fillFrom(alternative);
463 mTree->d->appendSubPart(part);
464 } else if (html) {
465 auto part = std::make_shared<SinglePart>();
466 part->d->fillFrom(html);
467 mTree->d->appendSubPart(part);
468 } else {
469 createTree(m, tree);
470 }
471 }
472}
473
474Parser::Parser(const QByteArray& mimeMessage)
475 :d(std::unique_ptr<ParserPrivate>(new ParserPrivate(this)))
476{
477 d->setMessage(mimeMessage);
478}
479
480Parser::~Parser()
481{
482}
483
484QVector<Part::Ptr> Parser::collectContentParts() const
485{
486 return collect(d->mTree, [](const Part::Ptr &p){return p->type() != "EncapsulatedPart";},
487 [](const Content::Ptr &content){
488 const auto mime = content->mailMime();
489
490 if (!mime) {
491 return true;
492 }
493
494 if (mime->isFirstTextPart()) {
495 return true;
496 }
497 const auto cd = mime->disposition();
498 if (cd && cd == MailMime::Inline) {
499 // explict "inline" disposition:
500 return true;
501 }
502 if (cd && cd == MailMime::Attachment) {
503 // explicit "attachment" disposition:
504 return false;
505 }
506
507 const auto ct = mime->mimetype();
508 if (ct.name().trimmed().toLower() == "text" && ct.name().trimmed().isEmpty() &&
509 (!mime || mime->filename().trimmed().isEmpty())) {
510 // text/* w/o filename parameter:
511 return true;
512 }
513 return false;
514 });
515}
516
517QVector<Part::Ptr> Parser::collect(const Part::Ptr &start, std::function<bool(const Part::Ptr &)> select, std::function<bool(const Content::Ptr &)> filter) const
518{
519 QVector<Part::Ptr> ret;
520 foreach (const auto &part, start->subParts()) {
521 QVector<QByteArray> contents;
522 foreach(const auto &ct, part->availableContents()) {
523 foreach(const auto &content, part->content(ct)) {
524 if (filter(content)) {
525 contents.append(ct);
526 break;
527 }
528 }
529 }
530 if (!contents.isEmpty()) {
531 ret.append(part);
532 }
533 if (select(part)){
534 ret += collect(part, select, filter);
535 }
536 }
537 return ret;
538} \ No newline at end of file