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