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