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