summaryrefslogtreecommitdiffstats
path: root/framework/src/domain/mime
diff options
context:
space:
mode:
Diffstat (limited to 'framework/src/domain/mime')
-rw-r--r--framework/src/domain/mime/attachmentmodel.cpp145
-rw-r--r--framework/src/domain/mime/htmlutils.cpp286
-rw-r--r--framework/src/domain/mime/htmlutils.h25
-rw-r--r--framework/src/domain/mime/mailtemplates.cpp840
-rw-r--r--framework/src/domain/mime/mailtemplates.h29
-rw-r--r--framework/src/domain/mime/messageparser.cpp73
-rw-r--r--framework/src/domain/mime/messageparser.h116
-rw-r--r--framework/src/domain/mime/messageparser_new.cpp513
-rw-r--r--framework/src/domain/mime/mimetreeparser/CMakeLists.txt17
-rw-r--r--framework/src/domain/mime/mimetreeparser/interface.cpp1179
-rw-r--r--framework/src/domain/mime/mimetreeparser/interface.h378
-rw-r--r--framework/src/domain/mime/mimetreeparser/interface_p.h56
-rw-r--r--framework/src/domain/mime/mimetreeparser/objecttreesource.cpp152
-rw-r--r--framework/src/domain/mime/mimetreeparser/objecttreesource.h57
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/CMakeLists.txt72
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/applicationpgpencrypted.cpp98
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/applicationpgpencrypted.h41
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/applicationpkcs7mime.cpp178
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/applicationpkcs7mime.h41
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/attachmentstrategy.cpp343
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/attachmentstrategy.h86
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/attachmenttemporaryfilesdirs.cpp108
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/attachmenttemporaryfilesdirs.h57
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/CMakeLists.txt46
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/attachmenttest.cpp68
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/cryptohelpertest.cpp144
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/cryptohelpertest.h42
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/alternative-notext.mbox22
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/alternative-notext.mbox.html17
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/alternative-notext.mbox.tree2
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/alternative.mbox34
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/alternative.mbox.html17
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/alternative.mbox.tree2
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/forward-openpgp-signed-encrypted.mbox.html84
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/openpgp-encoded.mbox.html38
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/openpgp-encrypted+signed.mbox.html55
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/openpgp-encrypted-attachment-and-non-encrypted-attachment.mbox.html80
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/openpgp-encrypted-attachment.mbox.html69
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/openpgp-encrypted-non-encrypted-attachment.mbox.html72
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/openpgp-encrypted-partially-signed-attachments.mbox.html102
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/openpgp-inline-charset-encrypted.mbox.html50
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/openpgp-inline-signed.mbox.html38
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/openpgp-signed-base64-mailman-footer.mbox.html81
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/openpgp-signed-encrypted-two-attachments.mbox.html91
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/openpgp-signed-encrypted.mbox.html58
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/openpgp-signed-mailinglist+old.mbox.html97
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/openpgp-signed-mailinglist.mbox.html68
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/openpgp-signed-two-attachments.mbox.html74
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/signed-forward-openpgp-signed-encrypted.mbox.html111
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/smime-opaque-enc+sign.mbox.html60
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/smime-opaque-sign.mbox.html43
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/smime-signed-encrypted.mbox.html58
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/encapsulated-with-attachment.mbox53
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/encapsulated-with-attachment.mbox.html51
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/encapsulated-with-attachment.mbox.tree10
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/forward-openpgp-signed-encrypted.mbox69
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/forward-openpgp-signed-encrypted.mbox.html81
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/forward-openpgp-signed-encrypted.mbox.tree9
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/headeronly/openpgp-encrypted-attachment-and-non-encrypted-attachment.mbox.html61
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/headeronly/openpgp-encrypted-attachment.mbox.html58
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/headeronly/openpgp-encrypted-non-encrypted-attachment.mbox.html61
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/headeronly/openpgp-encrypted-partially-signed-attachments.mbox.html84
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/headeronly/openpgp-encrypted-two-attachments.mbox.html34
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/headeronly/openpgp-signed-apple.mbox.html50
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/headeronly/openpgp-signed-encrypted-two-attachments.mbox.html58
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/headeronly/openpgp-signed-mailinglist+additional-children.mbox.html52
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/headeronly/openpgp-signed-no-protocol.mbox.html28
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/headeronly/openpgp-signed-two-attachments.mbox.html41
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/headeronly/smime-signed-apple.mbox.html50
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/hidden/encapsulated-with-attachment.mbox.html43
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/hidden/openpgp-encrypted-attachment-and-non-encrypted-attachment.mbox.html61
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/hidden/openpgp-encrypted-attachment.mbox.html58
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/hidden/openpgp-encrypted-noData.mbox.html10
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/hidden/openpgp-encrypted-non-encrypted-attachment.mbox.html61
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/hidden/openpgp-encrypted-partially-signed-attachments.mbox.html84
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/hidden/openpgp-encrypted-two-attachments.mbox.html34
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/hidden/openpgp-signed-apple.mbox.html50
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/hidden/openpgp-signed-encrypted-two-attachments.mbox.html58
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/hidden/openpgp-signed-mailinglist+additional-children.mbox.html44
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/hidden/openpgp-signed-no-protocol.mbox.html28
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/hidden/openpgp-signed-two-attachments.mbox.html41
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/hidden/smime-signed-apple.mbox.html50
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/html.mbox31
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/html.mbox.html17
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/html.mbox.tree2
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/htmlonly.mbox21
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/htmlonly.mbox.html12
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/htmlonly.mbox.tree2
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/htmlonlyexternal.mbox21
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/iconic/openpgp-encrypted-partially-signed-attachments.mbox.html92
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/iconic/openpgp-encrypted-two-attachments.mbox.html50
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/iconic/openpgp-signed-encrypted-two-attachments.mbox.html74
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/iconic/openpgp-signed-two-attachments.mbox.html57
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlined/encapsulated-with-attachment.mbox.html58
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlined/openpgp-encrypted-attachment-and-non-encrypted-attachment.mbox.html90
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlined/openpgp-encrypted-attachment.mbox.html74
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlined/openpgp-encrypted-non-encrypted-attachment.mbox.html74
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlined/openpgp-signed-apple.mbox.html63
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlined/openpgp-signed-mailinglist+additional-children.mbox.html67
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlined/smime-signed-apple.mbox.html63
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlinepgpencrypted-appendix.mbox33
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlinepgpencrypted-appendix.mbox.html36
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlinepgpencrypted-appendix.mbox.tree4
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlinepgpencrypted-error.mbox55
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlinepgpencrypted-error.mbox.html24
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlinepgpencrypted-error.mbox.tree3
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlinepgpencrypted.mbox29
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlinepgpencrypted.mbox.html26
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlinepgpencrypted.mbox.inProgress.html24
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlinepgpencrypted.mbox.tree3
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/mailheader.css512
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/no-content-type.mbox7
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/no-content-type.mbox.html14
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/no-content-type.mbox.tree3
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encoded.mbox33
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encoded.mbox.html35
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encoded.mbox.tree4
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted+signed.mbox46
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted+signed.mbox.html52
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted+signed.mbox.tree5
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-applemail.mbox52
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-applemail.mbox.html39
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-applemail.mbox.tree7
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-attachment-and-non-encrypted-attachment.mbox115
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-attachment-and-non-encrypted-attachment.mbox.html77
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-attachment-and-non-encrypted-attachment.mbox.tree11
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-attachment.mbox57
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-attachment.mbox.html66
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-attachment.mbox.tree8
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-enigmail1.6.mbox48
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-enigmail1.6.mbox.html34
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-enigmail1.6.mbox.tree5
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-noData.mbox17
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-noData.mbox.html19
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-noData.mbox.tree4
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-non-encrypted-attachment.mbox114
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-non-encrypted-attachment.mbox.html69
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-non-encrypted-attachment.mbox.tree9
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-partially-signed-attachments.mbox91
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-partially-signed-attachments.mbox.html99
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-partially-signed-attachments.mbox.tree12
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-two-attachments.mbox42
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-two-attachments.mbox.html64
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-two-attachments.mbox.tree9
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted.mbox36
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted.mbox.html31
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted.mbox.inProgress.html24
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted.mbox.tree4
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-inline-charset-encrypted.mbox40
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-inline-charset-encrypted.mbox.html47
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-inline-charset-encrypted.mbox.inProgress.html24
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-inline-charset-encrypted.mbox.tree4
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-inline-signed-broken.mbox26
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-inline-signed.mbox26
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-inline-signed.mbox.html35
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-inline-signed.mbox.inProgress.html22
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-inline-signed.mbox.tree3
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-inline-wrong-charset-encrypted.mbox34
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-inline-wrong-charset-encrypted.mbox.html47
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-inline-wrong-charset-encrypted.mbox.tree4
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-apple.mbox129
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-apple.mbox.html58
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-apple.mbox.inProgress.html49
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-apple.mbox.tree3
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-base64-mailman-footer.mbox117
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-base64-mailman-footer.mbox.html78
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-base64-mailman-footer.mbox.tree7
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-encrypted-two-attachments.mbox52
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-encrypted-two-attachments.mbox.html88
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-encrypted-two-attachments.mbox.tree10
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-encrypted.mbox47
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-encrypted.mbox.html55
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-encrypted.mbox.tree5
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-mailinglist+additional-children.mbox126
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-mailinglist+additional-children.mbox.html60
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-mailinglist+additional-children.mbox.tree10
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-mailinglist+old.mbox67
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-mailinglist+old.mbox.html94
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-mailinglist+old.mbox.tree13
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-mailinglist.mbox121
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-mailinglist.mbox.html65
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-mailinglist.mbox.inProgress.html56
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-mailinglist.mbox.tree7
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-no-protocol.mbox35
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-no-protocol.mbox.html36
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-no-protocol.mbox.tree5
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-two-attachments.mbox54
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-two-attachments.mbox.html71
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-two-attachments.mbox.tree9
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/signed-forward-openpgp-signed-encrypted.mbox92
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/signed-forward-openpgp-signed-encrypted.mbox.html105
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/signed-forward-openpgp-signed-encrypted.mbox.tree10
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-cert.mbox24
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-cert.mbox.html10
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-cert.mbox.tree2
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-encrypted-octet-stream.mbox23
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-encrypted-octet-stream.mbox.html31
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-encrypted-octet-stream.mbox.tree4
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-encrypted.mbox22
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-encrypted.mbox.html31
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-encrypted.mbox.inProgress.html24
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-encrypted.mbox.tree4
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-opaque-enc+sign.mbox37
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-opaque-enc+sign.mbox.html57
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-opaque-enc+sign.mbox.tree5
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-opaque-sign.mbox25
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-opaque-sign.mbox.html40
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-opaque-sign.mbox.inProgress.html22
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-opaque-sign.mbox.tree4
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-opaque-signed-encrypted-attachment.mbox50
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-signed-apple.mbox197
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-signed-apple.mbox.html58
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-signed-apple.mbox.inProgress.html49
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-signed-apple.mbox.tree3
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-signed-encrypted.mbox38
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-signed-encrypted.mbox.html55
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-signed-encrypted.mbox.tree5
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/text+html-maillinglist.mbox163
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/text+html-maillinglist.mbox.html38
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/data/text+html-maillinglist.mbox.tree5
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/kdepim_add_gpg_crypto_test.cmake61
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/kdepim_generate_crypto_test_wrapper.cmake45
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/nodehelpertest.cpp275
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/nodehelpertest.h45
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/setupenv.cpp34
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/setupenv.h175
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/testcsshelper.cpp106
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/testcsshelper.h50
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/util.cpp34
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/autotests/util.h43
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/bodypart.cpp41
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/bodypart.h209
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/bodypartformatter.cpp147
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/bodypartformatter.h149
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/bodypartformatter_impl.cpp193
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/bodypartformatterbasefactory.cpp179
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/bodypartformatterbasefactory.h85
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/bodypartformatterbasefactory_p.h57
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/cryptobodypartmemento.cpp56
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/cryptobodypartmemento.h75
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/cryptohelper.cpp150
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/cryptohelper.h62
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/decryptverifybodypartmemento.cpp86
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/decryptverifybodypartmemento.h81
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/enums.h54
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/filehtmlwriter.cpp119
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/filehtmlwriter.h70
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/htmlwriter.cpp40
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/htmlwriter.h125
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/mailman.cpp183
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/mailman.h44
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/messagepart.cpp1352
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/messagepart.h422
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/messagepartrenderer.cpp23
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/messagepartrenderer.h43
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/mimetreeparser_debug.cpp3
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/mimetreeparser_debug.h4
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/multipartalternative.cpp94
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/multipartalternative.h41
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/multipartencrypted.cpp111
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/multipartencrypted.h41
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/multipartmixed.cpp61
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/multipartmixed.h41
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/multipartsigned.cpp114
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/multipartsigned.h41
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/nodehelper.cpp1069
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/nodehelper.h290
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/objecttreeparser.cpp495
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/objecttreeparser.h406
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/objecttreesource.cpp28
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/objecttreesource.h109
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/partmetadata.h67
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/partnodebodypart.cpp125
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/partnodebodypart.h108
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/qgpgmejobexecutor.cpp158
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/qgpgmejobexecutor.h86
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/queuehtmlwriter.cpp136
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/queuehtmlwriter.h75
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/texthtml.cpp58
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/texthtml.h41
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/textplain.cpp78
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/textplain.h41
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/util.cpp136
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/util.h67
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/utils.cpp70
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/utils.h42
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/verifydetachedbodypartmemento.cpp177
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/verifydetachedbodypartmemento.h87
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/verifyopaquebodypartmemento.cpp179
-rw-r--r--framework/src/domain/mime/mimetreeparser/otp/verifyopaquebodypartmemento.h93
-rw-r--r--framework/src/domain/mime/mimetreeparser/stringhtmlwriter.cpp150
-rw-r--r--framework/src/domain/mime/mimetreeparser/stringhtmlwriter.h71
-rw-r--r--framework/src/domain/mime/mimetreeparser/tests/CMakeLists.txt23
-rw-r--r--framework/src/domain/mime/mimetreeparser/tests/data/alternative.mbox28
-rw-r--r--framework/src/domain/mime/mimetreeparser/tests/data/cid-links.mbox1384
-rw-r--r--framework/src/domain/mime/mimetreeparser/tests/data/html.mbox15
-rw-r--r--framework/src/domain/mime/mimetreeparser/tests/data/openpgp-encrypted-attachment-and-non-encrypted-attachment.mbox115
-rw-r--r--framework/src/domain/mime/mimetreeparser/tests/data/openpgp-inline-charset-encrypted.mbox40
-rw-r--r--framework/src/domain/mime/mimetreeparser/tests/data/openpgp-inline-encrypted+nonenc.mbox31
-rw-r--r--framework/src/domain/mime/mimetreeparser/tests/data/plaintext.mbox13
-rw-r--r--framework/src/domain/mime/mimetreeparser/tests/data/smime-encrypted.mbox22
-rw-r--r--framework/src/domain/mime/mimetreeparser/tests/gnupg_home/CMakeLists.txt10
-rw-r--r--framework/src/domain/mime/mimetreeparser/tests/gnupg_home/dirmngr-cache.d/DIR.txt3
-rw-r--r--framework/src/domain/mime/mimetreeparser/tests/gnupg_home/dirmngr-cache.d/crl-4E31CEB57DDD4A7B9991AB05507B1ED4293FF952.dbbin0 -> 2130 bytes
-rw-r--r--framework/src/domain/mime/mimetreeparser/tests/gnupg_home/dirmngr-cache.d/crl-7F2A402CBB016A9146D613568C89D3596A4111AA.dbbin0 -> 2048 bytes
-rw-r--r--framework/src/domain/mime/mimetreeparser/tests/gnupg_home/dirmngr.conf8
-rw-r--r--framework/src/domain/mime/mimetreeparser/tests/gnupg_home/gpg-agent.conf.in10
-rw-r--r--framework/src/domain/mime/mimetreeparser/tests/gnupg_home/gpg.conf244
-rw-r--r--framework/src/domain/mime/mimetreeparser/tests/gnupg_home/gpgsm.conf.in10
-rwxr-xr-xframework/src/domain/mime/mimetreeparser/tests/gnupg_home/pinentry-fake.sh9
-rw-r--r--framework/src/domain/mime/mimetreeparser/tests/gnupg_home/private-keys-v1.d/1AA8BA52430E51AE249AF0DA97D59F869E4101A8.keybin0 -> 528 bytes
-rw-r--r--framework/src/domain/mime/mimetreeparser/tests/gnupg_home/pubring.gpgbin0 -> 6757 bytes
-rw-r--r--framework/src/domain/mime/mimetreeparser/tests/gnupg_home/pubring.kbxbin0 -> 2017 bytes
-rw-r--r--framework/src/domain/mime/mimetreeparser/tests/gnupg_home/scdaemon.conf8
-rw-r--r--framework/src/domain/mime/mimetreeparser/tests/gnupg_home/secring.gpgbin0 -> 5163 bytes
-rw-r--r--framework/src/domain/mime/mimetreeparser/tests/gnupg_home/trustdb.gpgbin0 -> 1440 bytes
-rw-r--r--framework/src/domain/mime/mimetreeparser/tests/gnupg_home/trustlist.txt11
-rw-r--r--framework/src/domain/mime/mimetreeparser/tests/gpgerrortest.cpp214
-rw-r--r--framework/src/domain/mime/mimetreeparser/tests/interfacetest.cpp310
-rw-r--r--framework/src/domain/mime/mimetreeparser/tests/kdepim_add_gpg_crypto_test.cmake61
-rw-r--r--framework/src/domain/mime/mimetreeparser/tests/kdepim_generate_crypto_test_wrapper.cmake45
-rw-r--r--framework/src/domain/mime/mimetreeparser/thoughts.txt148
-rw-r--r--framework/src/domain/mime/objecttreesource.cpp152
-rw-r--r--framework/src/domain/mime/objecttreesource.h57
-rw-r--r--framework/src/domain/mime/stringhtmlwriter.cpp150
-rw-r--r--framework/src/domain/mime/stringhtmlwriter.h71
326 files changed, 27234 insertions, 0 deletions
diff --git a/framework/src/domain/mime/attachmentmodel.cpp b/framework/src/domain/mime/attachmentmodel.cpp
new file mode 100644
index 00000000..e98c6fc0
--- /dev/null
+++ b/framework/src/domain/mime/attachmentmodel.cpp
@@ -0,0 +1,145 @@
1/*
2 Copyright (c) 2016 Sandro Knauß <knauss@kolabsys.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 "messageparser.h"
21#include "mimetreeparser/interface.h"
22
23#include <QIcon>
24#include <QDebug>
25
26QString sizeHuman(const Content::Ptr &content)
27{
28 float num = content->content().size();
29 QStringList list;
30 list << "KB" << "MB" << "GB" << "TB";
31
32 QStringListIterator i(list);
33 QString unit("Bytes");
34
35 while(num >= 1024.0 && i.hasNext())
36 {
37 unit = i.next();
38 num /= 1024.0;
39 }
40
41 if (unit == "Bytes") {
42 return QString().setNum(num) + " " + unit;
43 } else {
44 return QString().setNum(num,'f',2)+" "+unit;
45 }
46}
47
48class AttachmentModelPrivate
49{
50public:
51 AttachmentModelPrivate(AttachmentModel *q_ptr, const std::shared_ptr<Parser> &parser);
52
53 AttachmentModel *q;
54 std::shared_ptr<Parser> mParser;
55 QVector<Part::Ptr> mAttachments;
56};
57
58AttachmentModelPrivate::AttachmentModelPrivate(AttachmentModel* q_ptr, const std::shared_ptr<Parser>& parser)
59 : q(q_ptr)
60 , mParser(parser)
61{
62 mAttachments = mParser->collectAttachmentParts();
63}
64
65AttachmentModel::AttachmentModel(std::shared_ptr<Parser> parser)
66 : d(std::unique_ptr<AttachmentModelPrivate>(new AttachmentModelPrivate(this, parser)))
67{
68}
69
70AttachmentModel::~AttachmentModel()
71{
72}
73
74QHash<int, QByteArray> AttachmentModel::roleNames() const
75{
76 QHash<int, QByteArray> roles;
77 roles[TypeRole] = "type";
78 roles[NameRole] = "name";
79 roles[SizeRole] = "size";
80 roles[IconRole] = "icon";
81 roles[IsEncryptedRole] = "encrypted";
82 roles[IsSignedRole] = "signed";
83 return roles;
84}
85
86QModelIndex AttachmentModel::index(int row, int column, const QModelIndex &parent) const
87{
88 if (row < 0 || column != 0) {
89 return QModelIndex();
90 }
91
92 if (row < d->mAttachments.size()) {
93 return createIndex(row, column, d->mAttachments.at(row).get());
94 }
95 return QModelIndex();
96}
97
98QVariant AttachmentModel::data(const QModelIndex &index, int role) const
99{
100 if (!index.isValid()) {
101 switch (role) {
102 case Qt::DisplayRole:
103 return QString("root");
104 }
105 return QVariant();
106 }
107
108 if (index.internalPointer()) {
109 const auto entry = static_cast<Part *>(index.internalPointer());
110 const auto content = entry->content().at(0);
111 switch(role) {
112 case TypeRole:
113 return content->mailMime()->mimetype().name();
114 case NameRole:
115 return entry->mailMime()->filename();
116 case IconRole:
117 return QIcon::fromTheme(content->mailMime()->mimetype().iconName());
118 case SizeRole:
119 return sizeHuman(content);
120 case IsEncryptedRole:
121 return content->encryptions().size() > 0;
122 case IsSignedRole:
123 return content->signatures().size() > 0;
124 }
125 }
126 return QVariant();
127}
128
129QModelIndex AttachmentModel::parent(const QModelIndex &index) const
130{
131 return QModelIndex();
132}
133
134int AttachmentModel::rowCount(const QModelIndex &parent) const
135{
136 if (!parent.isValid()) {
137 return d->mAttachments.size();
138 }
139 return 0;
140}
141
142int AttachmentModel::columnCount(const QModelIndex &parent) const
143{
144 return 1;
145}
diff --git a/framework/src/domain/mime/htmlutils.cpp b/framework/src/domain/mime/htmlutils.cpp
new file mode 100644
index 00000000..156bcc48
--- /dev/null
+++ b/framework/src/domain/mime/htmlutils.cpp
@@ -0,0 +1,286 @@
1/*
2 Copyright (c) 2017 Christian Mollekopf <mollekopf@kolabsys.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#include "htmlutils.h"
20
21#include <QTextDocument>
22
23static QString resolveEntities(const QString &in)
24{
25 QString out;
26
27 for(int i = 0; i < (int)in.length(); ++i) {
28 if(in[i] == '&') {
29 // find a semicolon
30 ++i;
31 int n = in.indexOf(';', i);
32 if(n == -1)
33 break;
34 QString type = in.mid(i, (n-i));
35 i = n; // should be n+1, but we'll let the loop increment do it
36
37 if(type == "amp")
38 out += '&';
39 else if(type == "lt")
40 out += '<';
41 else if(type == "gt")
42 out += '>';
43 else if(type == "quot")
44 out += '\"';
45 else if(type == "apos")
46 out += '\'';
47 else if(type == "nbsp")
48 out += 0xa0;
49 } else {
50 out += in[i];
51 }
52 }
53
54 return out;
55}
56
57
58static bool linkify_pmatch(const QString &str1, int at, const QString &str2)
59{
60 if(str2.length() > (str1.length()-at))
61 return false;
62
63 for(int n = 0; n < (int)str2.length(); ++n) {
64 if(str1.at(n+at).toLower() != str2.at(n).toLower())
65 return false;
66 }
67
68 return true;
69}
70
71static bool linkify_isOneOf(const QChar &c, const QString &charlist)
72{
73 for(int i = 0; i < (int)charlist.length(); ++i) {
74 if(c == charlist.at(i))
75 return true;
76 }
77
78 return false;
79}
80
81// encodes a few dangerous html characters
82static QString linkify_htmlsafe(const QString &in)
83{
84 QString out;
85
86 for(int n = 0; n < in.length(); ++n) {
87 if(linkify_isOneOf(in.at(n), "\"\'`<>")) {
88 // hex encode
89 QString hex;
90 hex.sprintf("%%%02X", in.at(n).toLatin1());
91 out.append(hex);
92 } else {
93 out.append(in.at(n));
94 }
95 }
96
97 return out;
98}
99
100static bool linkify_okUrl(const QString &url)
101{
102 if(url.at(url.length()-1) == '.')
103 return false;
104
105 return true;
106}
107
108static bool linkify_okEmail(const QString &addy)
109{
110 // this makes sure that there is an '@' and a '.' after it, and that there is
111 // at least one char for each of the three sections
112 int n = addy.indexOf('@');
113 if(n == -1 || n == 0)
114 return false;
115 int d = addy.indexOf('.', n+1);
116 if(d == -1 || d == 0)
117 return false;
118 if((addy.length()-1) - d <= 0)
119 return false;
120 if(addy.indexOf("..") != -1)
121 return false;
122
123 return true;
124}
125
126/**
127 * takes a richtext string and heuristically adds links for uris of common protocols
128 * @return a richtext string with link markup added
129 */
130QString HtmlUtils::linkify(const QString &in)
131{
132 QString out = in;
133 int x1, x2;
134 bool isUrl, isAtStyle;
135 QString linked, link, href;
136
137 for(int n = 0; n < (int)out.length(); ++n) {
138 isUrl = false;
139 isAtStyle = false;
140 x1 = n;
141
142 if(linkify_pmatch(out, n, "xmpp:")) {
143 n += 5;
144 isUrl = true;
145 href = "";
146 }
147 else if(linkify_pmatch(out, n, "mailto:")) {
148 n += 7;
149 isUrl = true;
150 href = "";
151 }
152 else if(linkify_pmatch(out, n, "http://")) {
153 n += 7;
154 isUrl = true;
155 href = "";
156 }
157 else if(linkify_pmatch(out, n, "https://")) {
158 n += 8;
159 isUrl = true;
160 href = "";
161 }
162 else if(linkify_pmatch(out, n, "ftp://")) {
163 n += 6;
164 isUrl = true;
165 href = "";
166 }
167 else if(linkify_pmatch(out, n, "news://")) {
168 n += 7;
169 isUrl = true;
170 href = "";
171 }
172 else if (linkify_pmatch(out, n, "ed2k://")) {
173 n += 7;
174 isUrl = true;
175 href = "";
176 }
177 else if (linkify_pmatch(out, n, "magnet:")) {
178 n += 7;
179 isUrl = true;
180 href = "";
181 }
182 else if(linkify_pmatch(out, n, "www.")) {
183 isUrl = true;
184 href = "http://";
185 }
186 else if(linkify_pmatch(out, n, "ftp.")) {
187 isUrl = true;
188 href = "ftp://";
189 }
190 else if(linkify_pmatch(out, n, "@")) {
191 isAtStyle = true;
192 href = "x-psi-atstyle:";
193 }
194
195 if(isUrl) {
196 // make sure the previous char is not alphanumeric
197 if(x1 > 0 && out.at(x1-1).isLetterOrNumber())
198 continue;
199
200 // find whitespace (or end)
201 QMap<QChar, int> brackets;
202 brackets['('] = brackets[')'] = brackets['['] = brackets[']'] = brackets['{'] = brackets['}'] = 0;
203 QMap<QChar, QChar> openingBracket;
204 openingBracket[')'] = '(';
205 openingBracket[']'] = '[';
206 openingBracket['}'] = '{';
207 for(x2 = n; x2 < (int)out.length(); ++x2) {
208 if(out.at(x2).isSpace() || linkify_isOneOf(out.at(x2), "\"\'`<>")
209 || linkify_pmatch(out, x2, "&quot;") || linkify_pmatch(out, x2, "&apos;")
210 || linkify_pmatch(out, x2, "&gt;") || linkify_pmatch(out, x2, "&lt;") ) {
211 break;
212 }
213 if(brackets.keys().contains(out.at(x2))) {
214 ++brackets[out.at(x2)];
215 }
216 }
217 int len = x2-x1;
218 QString pre = resolveEntities(out.mid(x1, x2-x1));
219
220 // go backward hacking off unwanted punctuation
221 int cutoff;
222 for(cutoff = pre.length()-1; cutoff >= 0; --cutoff) {
223 if(!linkify_isOneOf(pre.at(cutoff), "!?,.()[]{}<>\""))
224 break;
225 if(linkify_isOneOf(pre.at(cutoff), ")]}")
226 && brackets[pre.at(cutoff)] - brackets[openingBracket[pre.at(cutoff)]] <= 0 ) {
227 break; // in theory, there could be == above, but these are urls, not math ;)
228 }
229 if(brackets.keys().contains(pre.at(cutoff))) {
230 --brackets[pre.at(cutoff)];
231 }
232
233 }
234 ++cutoff;
235 //++x2;
236
237 link = pre.mid(0, cutoff);
238 if(!linkify_okUrl(link)) {
239 n = x1 + link.length();
240 continue;
241 }
242 href += link;
243 // attributes need to be encoded too.
244 href = href.toHtmlEscaped();
245 href = linkify_htmlsafe(href);
246 //printf("link: [%s], href=[%s]\n", link.latin1(), href.latin1());
247 linked = QString("<a href=\"%1\">").arg(href) + link.toHtmlEscaped() + "</a>" + pre.mid(cutoff).toHtmlEscaped();
248 out.replace(x1, len, linked);
249 n = x1 + linked.length() - 1;
250 } else if(isAtStyle) {
251 // go backward till we find the beginning
252 if(x1 == 0)
253 continue;
254 --x1;
255 for(; x1 >= 0; --x1) {
256 if(!linkify_isOneOf(out.at(x1), "_.-+") && !out.at(x1).isLetterOrNumber())
257 break;
258 }
259 ++x1;
260
261 // go forward till we find the end
262 x2 = n + 1;
263 for(; x2 < (int)out.length(); ++x2) {
264 if(!linkify_isOneOf(out.at(x2), "_.-+") && !out.at(x2).isLetterOrNumber())
265 break;
266 }
267
268 int len = x2-x1;
269 link = out.mid(x1, len);
270 //link = resolveEntities(link);
271
272 if(!linkify_okEmail(link)) {
273 n = x1 + link.length();
274 continue;
275 }
276
277 href += link;
278 //printf("link: [%s], href=[%s]\n", link.latin1(), href.latin1());
279 linked = QString("<a href=\"%1\">").arg(href) + link + "</a>";
280 out.replace(x1, len, linked);
281 n = x1 + linked.length() - 1;
282 }
283 }
284
285 return out;
286}
diff --git a/framework/src/domain/mime/htmlutils.h b/framework/src/domain/mime/htmlutils.h
new file mode 100644
index 00000000..b59da1dc
--- /dev/null
+++ b/framework/src/domain/mime/htmlutils.h
@@ -0,0 +1,25 @@
1/*
2 Copyright (c) 2017 Christian Mollekopf <mollekopf@kolabsys.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#pragma once
20
21#include <QString>
22
23namespace HtmlUtils {
24 QString linkify(const QString &in);
25}
diff --git a/framework/src/domain/mime/mailtemplates.cpp b/framework/src/domain/mime/mailtemplates.cpp
new file mode 100644
index 00000000..254dbba3
--- /dev/null
+++ b/framework/src/domain/mime/mailtemplates.cpp
@@ -0,0 +1,840 @@
1/*
2 Copyright (C) 2010 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
3 Copyright (c) 2010 Leo Franchi <lfranchi@kde.org>
4 Copyright (c) 2016 Christian Mollekopf <mollekopf@kolabsys.com>
5
6 This library is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Library General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or (at your
9 option) any later version.
10
11 This library is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
14 License for more details.
15
16 You should have received a copy of the GNU Library General Public License
17 along with this library; see the file COPYING.LIB. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301, USA.
20*/
21#include "mailtemplates.h"
22
23#include <functional>
24#include <QByteArray>
25#include <QList>
26#include <QDebug>
27#include <QWebEnginePage>
28#include <QWebEngineProfile>
29#include <QWebEngineSettings>
30#include <QWebEngineScript>
31#include <QSysInfo>
32#include <QTextCodec>
33
34#include <KCodecs/KCharsets>
35#include <KMime/Types>
36
37#include "stringhtmlwriter.h"
38#include "objecttreesource.h"
39
40#include <otp/objecttreeparser.h>
41
42namespace KMime {
43 namespace Types {
44static bool operator==(const KMime::Types::AddrSpec &left, const KMime::Types::AddrSpec &right)
45{
46 return (left.asString() == right.asString());
47}
48
49static bool operator==(const KMime::Types::Mailbox &left, const KMime::Types::Mailbox &right)
50{
51 return (left.addrSpec().asString() == right.addrSpec().asString());
52}
53 }
54}
55
56static KMime::Types::Mailbox::List stripMyAddressesFromAddressList(const KMime::Types::Mailbox::List &list, const KMime::Types::AddrSpecList me)
57{
58 KMime::Types::Mailbox::List addresses(list);
59 for (KMime::Types::Mailbox::List::Iterator it = addresses.begin(); it != addresses.end();) {
60 if (me.contains(it->addrSpec())) {
61 it = addresses.erase(it);
62 } else {
63 ++it;
64 }
65 }
66
67 return addresses;
68}
69
70void initHeader(const KMime::Message::Ptr &message)
71{
72 message->removeHeader<KMime::Headers::To>();
73 message->removeHeader<KMime::Headers::Subject>();
74 message->date()->setDateTime(QDateTime::currentDateTime());
75
76 const QStringList extraInfo = QStringList() << QSysInfo::prettyProductName();
77 message->userAgent()->fromUnicodeString(QString("%1/%2(%3)").arg(QString::fromLocal8Bit("Kube")).arg("0.1").arg(extraInfo.join(",")), "utf-8");
78 // This will allow to change Content-Type:
79 message->contentType()->setMimeType("text/plain");
80}
81
82QString replacePrefixes(const QString &str, const QStringList &prefixRegExps,
83 bool replace, const QString &newPrefix)
84{
85 bool recognized = false;
86 // construct a big regexp that
87 // 1. is anchored to the beginning of str (sans whitespace)
88 // 2. matches at least one of the part regexps in prefixRegExps
89 QString bigRegExp = QStringLiteral("^(?:\\s+|(?:%1))+\\s*").arg(prefixRegExps.join(QStringLiteral(")|(?:")));
90 QRegExp rx(bigRegExp, Qt::CaseInsensitive);
91 if (rx.isValid()) {
92 QString tmp = str;
93 if (rx.indexIn(tmp) == 0) {
94 recognized = true;
95 if (replace) {
96 return tmp.replace(0, rx.matchedLength(), newPrefix + QLatin1String(" "));
97 }
98 }
99 } else {
100 qWarning() << "bigRegExp = \""
101 << bigRegExp << "\"\n"
102 << "prefix regexp is invalid!";
103 // try good ole Re/Fwd:
104 recognized = str.startsWith(newPrefix);
105 }
106
107 if (!recognized) {
108 return newPrefix + QLatin1String(" ") + str;
109 } else {
110 return str;
111 }
112}
113
114QString cleanSubject(const KMime::Message::Ptr &msg, const QStringList &prefixRegExps, bool replace, const QString &newPrefix)
115{
116 return replacePrefixes(msg->subject()->asUnicodeString(), prefixRegExps, replace, newPrefix);
117}
118
119QString forwardSubject(const KMime::Message::Ptr &msg)
120{
121 bool replaceForwardPrefix = true;
122 QStringList forwardPrefixes;
123 forwardPrefixes << "Fwd:";
124 forwardPrefixes << "FW:";
125 return cleanSubject(msg, forwardPrefixes, replaceForwardPrefix, QStringLiteral("Fwd:"));
126}
127
128QString replySubject(const KMime::Message::Ptr &msg)
129{
130 bool replaceReplyPrefix = true;
131 QStringList replyPrefixes;
132 //We're escaping the regex escape sequences. awesome
133 replyPrefixes << "Re\\\\s*:";
134 replyPrefixes << "Re[\\\\d+\\\\]:";
135 replyPrefixes << "Re\\\\d+:";
136 return cleanSubject(msg, replyPrefixes, replaceReplyPrefix, QStringLiteral("Re:"));
137}
138
139QByteArray getRefStr(const KMime::Message::Ptr &msg)
140{
141 QByteArray firstRef, lastRef, refStr, retRefStr;
142 int i, j;
143
144 if (auto hdr = msg->references(false)) {
145 refStr = hdr->as7BitString(false).trimmed();
146 }
147
148 if (refStr.isEmpty()) {
149 return msg->messageID()->as7BitString(false);
150 }
151
152 i = refStr.indexOf('<');
153 j = refStr.indexOf('>');
154 firstRef = refStr.mid(i, j - i + 1);
155 if (!firstRef.isEmpty()) {
156 retRefStr = firstRef + ' ';
157 }
158
159 i = refStr.lastIndexOf('<');
160 j = refStr.lastIndexOf('>');
161
162 lastRef = refStr.mid(i, j - i + 1);
163 if (!lastRef.isEmpty() && lastRef != firstRef) {
164 retRefStr += lastRef + ' ';
165 }
166
167 retRefStr += msg->messageID()->as7BitString(false);
168 return retRefStr;
169}
170
171KMime::Content *createPlainPartContent(const KMime::Message::Ptr &msg, const QString &plainBody)
172{
173 KMime::Content *textPart = new KMime::Content(msg.data());
174 textPart->contentType()->setMimeType("text/plain");
175 //FIXME This is supposed to select a charset out of the available charsets that contains all necessary characters to render the text
176 // QTextCodec *charset = selectCharset(m_charsets, plainBody);
177 // textPart->contentType()->setCharset(charset->name());
178 textPart->contentType()->setCharset("utf-8");
179 textPart->contentTransferEncoding()->setEncoding(KMime::Headers::CE8Bit);
180 textPart->fromUnicodeString(plainBody);
181 return textPart;
182}
183
184KMime::Content *createMultipartAlternativeContent(const KMime::Message::Ptr &msg, const QString &plainBody, const QString &htmlBody)
185{
186 KMime::Content *multipartAlternative = new KMime::Content(msg.data());
187 multipartAlternative->contentType()->setMimeType("multipart/alternative");
188 const QByteArray boundary = KMime::multiPartBoundary();
189 multipartAlternative->contentType()->setBoundary(boundary);
190
191 KMime::Content *textPart = createPlainPartContent(msg, plainBody);
192 multipartAlternative->addContent(textPart);
193
194 KMime::Content *htmlPart = new KMime::Content(msg.data());
195 htmlPart->contentType()->setMimeType("text/html");
196 //FIXME This is supposed to select a charset out of the available charsets that contains all necessary characters to render the text
197 // QTextCodec *charset = selectCharset(m_charsets, htmlBody);
198 // htmlPart->contentType()->setCharset(charset->name());
199 textPart->contentType()->setCharset("utf-8");
200 htmlPart->contentTransferEncoding()->setEncoding(KMime::Headers::CE8Bit);
201 htmlPart->fromUnicodeString(htmlBody);
202 multipartAlternative->addContent(htmlPart);
203
204 return multipartAlternative;
205}
206
207void addProcessedBodyToMessage(const KMime::Message::Ptr &msg, const QString &plainBody, const QString &htmlBody, bool forward)
208{
209 //FIXME
210 // MessageCore::ImageCollector ic;
211 // ic.collectImagesFrom(mOrigMsg.data());
212
213 // Now, delete the old content and set the new content, which
214 // is either only the new text or the new text with some attachments.
215 auto parts = msg->contents();
216 foreach (KMime::Content *content, parts) {
217 msg->removeContent(content, true/*delete*/);
218 }
219
220 msg->contentType()->clear(); // to get rid of old boundary
221
222 const QByteArray boundary = KMime::multiPartBoundary();
223 KMime::Content *const mainTextPart =
224 htmlBody.isEmpty() ?
225 createPlainPartContent(msg, plainBody) :
226 createMultipartAlternativeContent(msg, plainBody, htmlBody);
227 mainTextPart->assemble();
228
229 KMime::Content *textPart = mainTextPart;
230 // if (!ic.images().empty()) {
231 // textPart = createMultipartRelated(ic, mainTextPart);
232 // textPart->assemble();
233 // }
234
235 // If we have some attachments, create a multipart/mixed mail and
236 // add the normal body as well as the attachments
237 KMime::Content *mainPart = textPart;
238 //FIXME
239 // if (forward) {
240 // auto attachments = mOrigMsg->attachments();
241 // attachments += mOtp->nodeHelper()->attachmentsOfExtraContents();
242 // if (!attachments.isEmpty()) {
243 // mainPart = createMultipartMixed(attachments, textPart);
244 // mainPart->assemble();
245 // }
246 // }
247
248 msg->setBody(mainPart->encodedBody());
249 msg->setHeader(mainPart->contentType());
250 msg->setHeader(mainPart->contentTransferEncoding());
251 msg->assemble();
252 msg->parse();
253}
254
255QString plainToHtml(const QString &body)
256{
257 QString str = body;
258 str = str.toHtmlEscaped();
259 str.replace(QStringLiteral("\n"), QStringLiteral("<br />\n"));
260 return str;
261}
262
263//TODO implement this function using a DOM tree parser
264void makeValidHtml(QString &body, const QString &headElement)
265{
266 QRegExp regEx;
267 regEx.setMinimal(true);
268 regEx.setPattern(QStringLiteral("<html.*>"));
269
270 if (!body.isEmpty() && !body.contains(regEx)) {
271 regEx.setPattern(QStringLiteral("<body.*>"));
272 if (!body.contains(regEx)) {
273 body = QLatin1String("<body>") + body + QLatin1String("<br/></body>");
274 }
275 regEx.setPattern(QStringLiteral("<head.*>"));
276 if (!body.contains(regEx)) {
277 body = QLatin1String("<head>") + headElement + QLatin1String("</head>") + body;
278 }
279 body = QLatin1String("<html>") + body + QLatin1String("</html>");
280 }
281}
282
283//FIXME strip signature works partially for HTML mails
284QString stripSignature(const QString &msg)
285{
286 // Following RFC 3676, only > before --
287 // I prefer to not delete a SB instead of delete good mail content.
288 const QRegExp sbDelimiterSearch = QRegExp(QLatin1String("(^|\n)[> ]*-- \n"));
289 // The regular expression to look for prefix change
290 const QRegExp commonReplySearch = QRegExp(QLatin1String("^[ ]*>"));
291
292 QString res = msg;
293 int posDeletingStart = 1; // to start looking at 0
294
295 // While there are SB delimiters (start looking just before the deleted SB)
296 while ((posDeletingStart = res.indexOf(sbDelimiterSearch, posDeletingStart - 1)) >= 0) {
297 QString prefix; // the current prefix
298 QString line; // the line to check if is part of the SB
299 int posNewLine = -1;
300
301 // Look for the SB beginning
302 int posSignatureBlock = res.indexOf(QLatin1Char('-'), posDeletingStart);
303 // The prefix before "-- "$
304 if (res.at(posDeletingStart) == QLatin1Char('\n')) {
305 ++posDeletingStart;
306 }
307
308 prefix = res.mid(posDeletingStart, posSignatureBlock - posDeletingStart);
309 posNewLine = res.indexOf(QLatin1Char('\n'), posSignatureBlock) + 1;
310
311 // now go to the end of the SB
312 while (posNewLine < res.size() && posNewLine > 0) {
313 // handle the undefined case for mid ( x , -n ) where n>1
314 int nextPosNewLine = res.indexOf(QLatin1Char('\n'), posNewLine);
315
316 if (nextPosNewLine < 0) {
317 nextPosNewLine = posNewLine - 1;
318 }
319
320 line = res.mid(posNewLine, nextPosNewLine - posNewLine);
321
322 // check when the SB ends:
323 // * does not starts with prefix or
324 // * starts with prefix+(any substring of prefix)
325 if ((prefix.isEmpty() && line.indexOf(commonReplySearch) < 0) ||
326 (!prefix.isEmpty() && line.startsWith(prefix) &&
327 line.mid(prefix.size()).indexOf(commonReplySearch) < 0)) {
328 posNewLine = res.indexOf(QLatin1Char('\n'), posNewLine) + 1;
329 } else {
330 break; // end of the SB
331 }
332 }
333
334 // remove the SB or truncate when is the last SB
335 if (posNewLine > 0) {
336 res.remove(posDeletingStart, posNewLine - posDeletingStart);
337 } else {
338 res.truncate(posDeletingStart);
339 }
340 }
341
342 return res;
343}
344
345void setupPage(QWebEnginePage *page)
346{
347 page->profile()->setHttpCacheType(QWebEngineProfile::MemoryHttpCache);
348 page->profile()->setPersistentCookiesPolicy(QWebEngineProfile::NoPersistentCookies);
349 page->settings()->setAttribute(QWebEngineSettings::JavascriptEnabled, false);
350 page->settings()->setAttribute(QWebEngineSettings::PluginsEnabled, false);
351 page->settings()->setAttribute(QWebEngineSettings::JavascriptCanOpenWindows, false);
352 page->settings()->setAttribute(QWebEngineSettings::JavascriptCanAccessClipboard, false);
353 page->settings()->setAttribute(QWebEngineSettings::LocalStorageEnabled, false);
354 page->settings()->setAttribute(QWebEngineSettings::XSSAuditingEnabled, false);
355 page->settings()->setAttribute(QWebEngineSettings::ErrorPageEnabled, false);
356 page->settings()->setAttribute(QWebEngineSettings::LocalContentCanAccessRemoteUrls, false);
357 page->settings()->setAttribute(QWebEngineSettings::LocalContentCanAccessFileUrls, false);
358 page->settings()->setAttribute(QWebEngineSettings::HyperlinkAuditingEnabled, false);
359 page->settings()->setAttribute(QWebEngineSettings::FullScreenSupportEnabled, false);
360 page->settings()->setAttribute(QWebEngineSettings::ScreenCaptureEnabled, false);
361 page->settings()->setAttribute(QWebEngineSettings::WebGLEnabled, false);
362 page->settings()->setAttribute(QWebEngineSettings::AutoLoadIconsForPage, false);
363 page->settings()->setAttribute(QWebEngineSettings::Accelerated2dCanvasEnabled, false);
364 page->settings()->setAttribute(QWebEngineSettings::WebGLEnabled, false);
365
366#if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0)
367 page->settings()->setAttribute(QWebEngineSettings::FocusOnNavigationEnabled, false);
368 page->settings()->setAttribute(QWebEngineSettings::AllowRunningInsecureContent, false);
369#endif
370}
371
372void plainMessageText(const QString &plainTextContent, const QString &htmlContent, bool aStripSignature, const std::function<void(const QString &)> &callback)
373{
374 QString result = plainTextContent;
375 if (plainTextContent.isEmpty()) { //HTML-only mails
376 auto page = new QWebEnginePage;
377 setupPage(page);
378 page->setHtml(htmlContent);
379 page->toPlainText([=] (const QString &plaintext) {
380 page->deleteLater();
381 callback(plaintext);
382 });
383 return;
384 }
385
386 if (aStripSignature) {
387 result = stripSignature(result);
388 }
389 callback(result);
390}
391
392QString extractHeaderBodyScript()
393{
394 const QString source = QStringLiteral("(function() {"
395 "var res = {"
396 " body: document.getElementsByTagName('body')[0].innerHTML,"
397 " header: document.getElementsByTagName('head')[0].innerHTML"
398 "};"
399 "return res;"
400 "})()");
401 return source;
402}
403
404void htmlMessageText(const QString &plainTextContent, const QString &htmlContent, bool aStripSignature, const std::function<void(const QString &body, QString &head)> &callback)
405{
406 QString htmlElement = htmlContent;
407
408 if (htmlElement.isEmpty()) { //plain mails only
409 QString htmlReplace = plainTextContent.toHtmlEscaped();
410 htmlReplace = htmlReplace.replace(QStringLiteral("\n"), QStringLiteral("<br />"));
411 htmlElement = QStringLiteral("<html><head></head><body>%1</body></html>\n").arg(htmlReplace);
412 }
413
414 auto page = new QWebEnginePage;
415 setupPage(page);
416
417 page->setHtml(htmlElement);
418 page->runJavaScript(extractHeaderBodyScript(), QWebEngineScript::ApplicationWorld, [=](const QVariant &result){
419 page->deleteLater();
420 const QVariantMap map = result.toMap();
421 auto bodyElement = map.value(QStringLiteral("body")).toString();
422 auto headerElement = map.value(QStringLiteral("header")).toString();
423 if (!bodyElement.isEmpty()) {
424 if (aStripSignature) {
425 callback(stripSignature(bodyElement), headerElement);
426 }
427 return callback(bodyElement, headerElement);
428 }
429
430 if (aStripSignature) {
431 return callback(stripSignature(htmlElement), headerElement);
432 }
433 return callback(htmlElement, headerElement);
434 });
435}
436
437QString formatQuotePrefix(const QString &wildString, const QString &fromDisplayString)
438{
439 QString result;
440
441 if (wildString.isEmpty()) {
442 return wildString;
443 }
444
445 unsigned int strLength(wildString.length());
446 for (uint i = 0; i < strLength;) {
447 QChar ch = wildString[i++];
448 if (ch == QLatin1Char('%') && i < strLength) {
449 ch = wildString[i++];
450 switch (ch.toLatin1()) {
451 case 'f': { // sender's initals
452 if (fromDisplayString.isEmpty()) {
453 break;
454 }
455
456 uint j = 0;
457 const unsigned int strLength(fromDisplayString.length());
458 for (; j < strLength && fromDisplayString[j] > QLatin1Char(' '); ++j)
459 ;
460 for (; j < strLength && fromDisplayString[j] <= QLatin1Char(' '); ++j)
461 ;
462 result += fromDisplayString[0];
463 if (j < strLength && fromDisplayString[j] > QLatin1Char(' ')) {
464 result += fromDisplayString[j];
465 } else if (strLength > 1) {
466 if (fromDisplayString[1] > QLatin1Char(' ')) {
467 result += fromDisplayString[1];
468 }
469 }
470 }
471 break;
472 case '_':
473 result += QLatin1Char(' ');
474 break;
475 case '%':
476 result += QLatin1Char('%');
477 break;
478 default:
479 result += QLatin1Char('%');
480 result += ch;
481 break;
482 }
483 } else {
484 result += ch;
485 }
486 }
487 return result;
488}
489
490QString quotedPlainText(const QString &selection, const QString &fromDisplayString)
491{
492 QString content = selection;
493 // Remove blank lines at the beginning:
494 const int firstNonWS = content.indexOf(QRegExp(QLatin1String("\\S")));
495 const int lineStart = content.lastIndexOf(QLatin1Char('\n'), firstNonWS);
496 if (lineStart >= 0) {
497 content.remove(0, static_cast<unsigned int>(lineStart));
498 }
499
500 const auto quoteString = QStringLiteral("> ");
501 const QString indentStr = formatQuotePrefix(quoteString, fromDisplayString);
502 //FIXME
503 // if (TemplateParserSettings::self()->smartQuote() && mWrap) {
504 // content = MessageCore::StringUtil::smartQuote(content, mColWrap - indentStr.length());
505 // }
506 content.replace(QLatin1Char('\n'), QLatin1Char('\n') + indentStr);
507 content.prepend(indentStr);
508 content += QLatin1Char('\n');
509
510 return content;
511}
512
513QString quotedHtmlText(const QString &selection)
514{
515 QString content = selection;
516 //TODO 1) look for all the variations of <br> and remove the blank lines
517 //2) implement vertical bar for quoted HTML mail.
518 //3) After vertical bar is implemented, If a user wants to edit quoted message,
519 // then the <blockquote> tags below should open and close as when required.
520
521 //Add blockquote tag, so that quoted message can be differentiated from normal message
522 content = QLatin1String("<blockquote>") + content + QLatin1String("</blockquote>");
523 return content;
524}
525
526void applyCharset(const KMime::Message::Ptr msg, const KMime::Message::Ptr &origMsg)
527{
528 // first convert the body from its current encoding to unicode representation
529 QTextCodec *bodyCodec = KCharsets::charsets()->codecForName(QString::fromLatin1(msg->contentType()->charset()));
530 if (!bodyCodec) {
531 bodyCodec = KCharsets::charsets()->codecForName(QStringLiteral("UTF-8"));
532 }
533
534 const QString body = bodyCodec->toUnicode(msg->body());
535
536 // then apply the encoding of the original message
537 msg->contentType()->setCharset(origMsg->contentType()->charset());
538
539 QTextCodec *codec = KCharsets::charsets()->codecForName(QString::fromLatin1(msg->contentType()->charset()));
540 if (!codec) {
541 qCritical() << "Could not get text codec for charset" << msg->contentType()->charset();
542 } else if (!codec->canEncode(body)) { // charset can't encode body, fall back to preferred
543 const QStringList charsets /*= preferredCharsets() */;
544
545 QList<QByteArray> chars;
546 chars.reserve(charsets.count());
547 foreach (const QString &charset, charsets) {
548 chars << charset.toLatin1();
549 }
550
551 //FIXME
552 QByteArray fallbackCharset/* = selectCharset(chars, body)*/;
553 if (fallbackCharset.isEmpty()) { // UTF-8 as fall-through
554 fallbackCharset = "UTF-8";
555 }
556
557 codec = KCharsets::charsets()->codecForName(QString::fromLatin1(fallbackCharset));
558 msg->setBody(codec->fromUnicode(body));
559 } else {
560 msg->setBody(codec->fromUnicode(body));
561 }
562}
563
564enum ReplyStrategy {
565 ReplyList,
566 ReplySmart,
567 ReplyAll,
568 ReplyAuthor,
569 ReplyNone
570};
571
572void MailTemplates::reply(const KMime::Message::Ptr &origMsg, const std::function<void(const KMime::Message::Ptr &result)> &callback)
573{
574 //FIXME
575 const bool alwaysPlain = true;
576 //FIXME
577 const ReplyStrategy replyStrategy = ReplySmart;
578 KMime::Message::Ptr msg(new KMime::Message);
579 //FIXME
580 //Personal email addresses
581 KMime::Types::AddrSpecList me;
582 KMime::Types::Mailbox::List toList;
583 KMime::Types::Mailbox::List replyToList;
584 KMime::Types::Mailbox::List mailingListAddresses;
585
586 // const uint originalIdentity = identityUoid(origMsg);
587 initHeader(msg);
588 replyToList = origMsg->replyTo()->mailboxes();
589
590 msg->contentType()->setCharset("utf-8");
591
592 if (origMsg->headerByType("List-Post") &&
593 origMsg->headerByType("List-Post")->asUnicodeString().contains(QStringLiteral("mailto:"), Qt::CaseInsensitive)) {
594
595 const QString listPost = origMsg->headerByType("List-Post")->asUnicodeString();
596 QRegExp rx(QStringLiteral("<mailto:([^@>]+)@([^>]+)>"), Qt::CaseInsensitive);
597 if (rx.indexIn(listPost, 0) != -1) { // matched
598 KMime::Types::Mailbox mailbox;
599 mailbox.fromUnicodeString(rx.cap(1) + QLatin1Char('@') + rx.cap(2));
600 mailingListAddresses << mailbox;
601 }
602 }
603
604 switch (replyStrategy) {
605 case ReplySmart: {
606 if (auto hdr = origMsg->headerByType("Mail-Followup-To")) {
607 toList << KMime::Types::Mailbox::listFrom7BitString(hdr->as7BitString(false));
608 } else if (!replyToList.isEmpty()) {
609 toList = replyToList;
610 } else if (!mailingListAddresses.isEmpty()) {
611 toList = (KMime::Types::Mailbox::List() << mailingListAddresses.at(0));
612 } else {
613 // doesn't seem to be a mailing list, reply to From: address
614 toList = origMsg->from()->mailboxes();
615
616 bool listContainsMe = false;
617 for (const auto &m : me) {
618 KMime::Types::Mailbox mailbox;
619 mailbox.setAddress(m);
620 if (toList.contains(mailbox)) {
621 listContainsMe = true;
622 }
623 }
624 if (listContainsMe) {
625 // sender seems to be one of our own identities, so we assume that this
626 // is a reply to a "sent" mail where the users wants to add additional
627 // information for the recipient.
628 toList = origMsg->to()->mailboxes();
629 }
630 }
631 // strip all my addresses from the list of recipients
632 const KMime::Types::Mailbox::List recipients = toList;
633
634 toList = stripMyAddressesFromAddressList(recipients, me);
635
636 // ... unless the list contains only my addresses (reply to self)
637 if (toList.isEmpty() && !recipients.isEmpty()) {
638 toList << recipients.first();
639 }
640 }
641 break;
642 case ReplyList: {
643 if (auto hdr = origMsg->headerByType("Mail-Followup-To")) {
644 KMime::Types::Mailbox mailbox;
645 mailbox.from7BitString(hdr->as7BitString(false));
646 toList << mailbox;
647 } else if (!mailingListAddresses.isEmpty()) {
648 toList << mailingListAddresses[ 0 ];
649 } else if (!replyToList.isEmpty()) {
650 // assume a Reply-To header mangling mailing list
651 toList = replyToList;
652 }
653
654 //FIXME
655 // strip all my addresses from the list of recipients
656 const KMime::Types::Mailbox::List recipients = toList;
657 toList = stripMyAddressesFromAddressList(recipients, me);
658 }
659 break;
660 case ReplyAll: {
661 KMime::Types::Mailbox::List recipients;
662 KMime::Types::Mailbox::List ccRecipients;
663
664 // add addresses from the Reply-To header to the list of recipients
665 if (!replyToList.isEmpty()) {
666 recipients = replyToList;
667
668 // strip all possible mailing list addresses from the list of Reply-To addresses
669 foreach (const KMime::Types::Mailbox &mailbox, mailingListAddresses) {
670 foreach (const KMime::Types::Mailbox &recipient, recipients) {
671 if (mailbox == recipient) {
672 recipients.removeAll(recipient);
673 }
674 }
675 }
676 }
677
678 if (!mailingListAddresses.isEmpty()) {
679 // this is a mailing list message
680 if (recipients.isEmpty() && !origMsg->from()->asUnicodeString().isEmpty()) {
681 // The sender didn't set a Reply-to address, so we add the From
682 // address to the list of CC recipients.
683 ccRecipients += origMsg->from()->mailboxes();
684 qDebug() << "Added" << origMsg->from()->asUnicodeString() << "to the list of CC recipients";
685 }
686
687 // if it is a mailing list, add the posting address
688 recipients.prepend(mailingListAddresses[ 0 ]);
689 } else {
690 // this is a normal message
691 if (recipients.isEmpty() && !origMsg->from()->asUnicodeString().isEmpty()) {
692 // in case of replying to a normal message only then add the From
693 // address to the list of recipients if there was no Reply-to address
694 recipients += origMsg->from()->mailboxes();
695 qDebug() << "Added" << origMsg->from()->asUnicodeString() << "to the list of recipients";
696 }
697 }
698
699 // strip all my addresses from the list of recipients
700 toList = stripMyAddressesFromAddressList(recipients, me);
701
702 // merge To header and CC header into a list of CC recipients
703 if (!origMsg->cc()->asUnicodeString().isEmpty() || !origMsg->to()->asUnicodeString().isEmpty()) {
704 KMime::Types::Mailbox::List list;
705 if (!origMsg->to()->asUnicodeString().isEmpty()) {
706 list += origMsg->to()->mailboxes();
707 }
708 if (!origMsg->cc()->asUnicodeString().isEmpty()) {
709 list += origMsg->cc()->mailboxes();
710 }
711
712 foreach (const KMime::Types::Mailbox &mailbox, list) {
713 if (!recipients.contains(mailbox) &&
714 !ccRecipients.contains(mailbox)) {
715 ccRecipients += mailbox;
716 qDebug() << "Added" << mailbox.prettyAddress() << "to the list of CC recipients";
717 }
718 }
719 }
720
721 if (!ccRecipients.isEmpty()) {
722 // strip all my addresses from the list of CC recipients
723 ccRecipients = stripMyAddressesFromAddressList(ccRecipients, me);
724
725 // in case of a reply to self, toList might be empty. if that's the case
726 // then propagate a cc recipient to To: (if there is any).
727 if (toList.isEmpty() && !ccRecipients.isEmpty()) {
728 toList << ccRecipients.at(0);
729 ccRecipients.pop_front();
730 }
731
732 foreach (const KMime::Types::Mailbox &mailbox, ccRecipients) {
733 msg->cc()->addAddress(mailbox);
734 }
735 }
736
737 if (toList.isEmpty() && !recipients.isEmpty()) {
738 // reply to self without other recipients
739 toList << recipients.at(0);
740 }
741 }
742 break;
743 case ReplyAuthor: {
744 if (!replyToList.isEmpty()) {
745 KMime::Types::Mailbox::List recipients = replyToList;
746
747 // strip the mailing list post address from the list of Reply-To
748 // addresses since we want to reply in private
749 foreach (const KMime::Types::Mailbox &mailbox, mailingListAddresses) {
750 foreach (const KMime::Types::Mailbox &recipient, recipients) {
751 if (mailbox == recipient) {
752 recipients.removeAll(recipient);
753 }
754 }
755 }
756
757 if (!recipients.isEmpty()) {
758 toList = recipients;
759 } else {
760 // there was only the mailing list post address in the Reply-To header,
761 // so use the From address instead
762 toList = origMsg->from()->mailboxes();
763 }
764 } else if (!origMsg->from()->asUnicodeString().isEmpty()) {
765 toList = origMsg->from()->mailboxes();
766 }
767 }
768 break;
769 case ReplyNone:
770 // the addressees will be set by the caller
771 break;
772 }
773
774 foreach (const KMime::Types::Mailbox &mailbox, toList) {
775 msg->to()->addAddress(mailbox);
776 }
777
778 const QByteArray refStr = getRefStr(origMsg);
779 if (!refStr.isEmpty()) {
780 msg->references()->fromUnicodeString(QString::fromLocal8Bit(refStr), "utf-8");
781 }
782
783 //In-Reply-To = original msg-id
784 msg->inReplyTo()->from7BitString(origMsg->messageID()->as7BitString(false));
785
786 msg->subject()->fromUnicodeString(replySubject(origMsg), "utf-8");
787
788 auto definedLocale = QLocale::system();
789
790 //TODO set empty source instead
791 StringHtmlWriter htmlWriter;
792 MimeTreeParser::NodeHelper nodeHelper;
793 ObjectTreeSource source(&htmlWriter);
794 MimeTreeParser::ObjectTreeParser otp(&source, &nodeHelper);
795 otp.setAllowAsync(false);
796 otp.parseObjectTree(origMsg.data());
797
798 //Add quoted body
799 QString plainBody;
800 QString htmlBody;
801
802 //On $datetime you wrote:
803 const QDateTime date = origMsg->date()->dateTime();
804 const auto dateTimeString = QString("%1 %2").arg(definedLocale.toString(date.date(), QLocale::LongFormat)).arg(definedLocale.toString(date.time(), QLocale::LongFormat));
805 const auto onDateYouWroteLine = QString("On %1 you wrote:").arg(dateTimeString);
806 plainBody.append(onDateYouWroteLine);
807 htmlBody.append(plainToHtml(onDateYouWroteLine));
808
809 //Strip signature for replies
810 const bool stripSignature = true;
811
812 const auto plainTextContent = otp.plainTextContent();
813 const auto htmlContent = otp.htmlContent();
814
815 plainMessageText(plainTextContent, htmlContent, stripSignature, [=] (const QString &body) {
816 //Quoted body
817 QString plainQuote = quotedPlainText(body, origMsg->from()->displayString());
818 if (plainQuote.endsWith(QLatin1Char('\n'))) {
819 plainQuote.chop(1);
820 }
821 //The plain body is complete
822 auto plainBodyResult = plainBody + plainQuote;
823 htmlMessageText(plainTextContent, htmlContent, stripSignature, [=] (const QString &body, const QString &headElement) {
824 //The html body is complete
825 auto htmlBodyResult = htmlBody + quotedHtmlText(body);
826 if (alwaysPlain) {
827 htmlBodyResult.clear();
828 } else {
829 makeValidHtml(htmlBodyResult, headElement);
830 }
831
832 //Assemble the message
833 addProcessedBodyToMessage(msg, plainBodyResult, htmlBodyResult, false);
834 applyCharset(msg, origMsg);
835 msg->assemble();
836 //We're done
837 callback(msg);
838 });
839 });
840}
diff --git a/framework/src/domain/mime/mailtemplates.h b/framework/src/domain/mime/mailtemplates.h
new file mode 100644
index 00000000..db269c96
--- /dev/null
+++ b/framework/src/domain/mime/mailtemplates.h
@@ -0,0 +1,29 @@
1/*
2 Copyright (c) 2016 Christian Mollekopf <mollekopf@kolabsys.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#pragma once
21
22#include <QByteArray>
23#include <KMime/Message>
24#include <functional>
25
26namespace MailTemplates
27{
28 void reply(const KMime::Message::Ptr &origMsg, const std::function<void(const KMime::Message::Ptr &result)> &callback);
29};
diff --git a/framework/src/domain/mime/messageparser.cpp b/framework/src/domain/mime/messageparser.cpp
new file mode 100644
index 00000000..76c060f0
--- /dev/null
+++ b/framework/src/domain/mime/messageparser.cpp
@@ -0,0 +1,73 @@
1/*
2 Copyright (c) 2016 Christian Mollekopf <mollekopf@kolabsys.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#include "messageparser.h"
20
21#include "modeltest.h"
22#include "mimetreeparser/interface.h"
23
24#include <QDebug>
25
26class MessagePartPrivate
27{
28public:
29 std::shared_ptr<Parser> mParser;
30};
31
32MessageParser::MessageParser(QObject *parent)
33 : QObject(parent)
34 , d(std::unique_ptr<MessagePartPrivate>(new MessagePartPrivate))
35{
36
37}
38
39MessageParser::~MessageParser()
40{
41
42}
43
44QVariant MessageParser::message() const
45{
46 return QVariant();
47}
48
49void MessageParser::setMessage(const QVariant &message)
50{
51 d->mParser = std::shared_ptr<Parser>(new Parser(message.toByteArray()));
52 emit htmlChanged();
53}
54
55QAbstractItemModel *MessageParser::newTree() const
56{
57 if (!d->mParser) {
58 return nullptr;
59 }
60 const auto model = new NewModel(d->mParser);
61 // new ModelTest(model, model);
62 return model;
63}
64
65QAbstractItemModel *MessageParser::attachments() const
66{
67 if (!d->mParser) {
68 return nullptr;
69 }
70 const auto model = new AttachmentModel(d->mParser);
71 // new ModelTest(model, model);
72 return model;
73}
diff --git a/framework/src/domain/mime/messageparser.h b/framework/src/domain/mime/messageparser.h
new file mode 100644
index 00000000..de4d8838
--- /dev/null
+++ b/framework/src/domain/mime/messageparser.h
@@ -0,0 +1,116 @@
1/*
2 Copyright (c) 2016 Christian Mollekopf <mollekopf@kolabsys.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#pragma once
21
22#include <QObject>
23#include <QString>
24#include <QStringList>
25
26#include <QAbstractItemModel>
27#include <QModelIndex>
28
29#include <memory>
30
31class QAbstractItemModel;
32
33class Parser;
34class MessagePartPrivate;
35
36class NewModelPrivate;
37class AttachmentModelPrivate;
38
39class MessageParser : public QObject
40{
41 Q_OBJECT
42 Q_PROPERTY (QVariant message READ message WRITE setMessage)
43 Q_PROPERTY (QAbstractItemModel* newTree READ newTree NOTIFY htmlChanged)
44 Q_PROPERTY (QAbstractItemModel* attachments READ attachments NOTIFY htmlChanged)
45
46public:
47 explicit MessageParser(QObject *parent = Q_NULLPTR);
48 ~MessageParser();
49
50 QVariant message() const;
51 void setMessage(const QVariant &to);
52 QAbstractItemModel *newTree() const;
53 QAbstractItemModel *attachments() const;
54
55signals:
56 void htmlChanged();
57
58private:
59 std::unique_ptr<MessagePartPrivate> d;
60};
61
62class NewModel : public QAbstractItemModel {
63 Q_OBJECT
64public:
65 NewModel(std::shared_ptr<Parser> parser);
66 ~NewModel();
67
68public:
69 enum Roles {
70 TypeRole = Qt::UserRole + 1,
71 ContentsRole,
72 ContentRole,
73 IsComplexHtmlContentRole,
74 IsEmbededRole,
75 SecurityLevelRole,
76 EncryptionErrorType,
77 EncryptionErrorString
78 };
79
80 QHash<int, QByteArray> roleNames() const Q_DECL_OVERRIDE;
81 QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
82 QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE;
83 QModelIndex parent(const QModelIndex &index) const Q_DECL_OVERRIDE;
84 int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
85 int columnCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
86
87private:
88 std::unique_ptr<NewModelPrivate> d;
89};
90
91class AttachmentModel : public QAbstractItemModel {
92 Q_OBJECT
93public:
94 AttachmentModel(std::shared_ptr<Parser> parser);
95 ~AttachmentModel();
96
97public:
98 enum Roles {
99 TypeRole = Qt::UserRole + 1,
100 IconRole,
101 NameRole,
102 SizeRole,
103 IsEncryptedRole,
104 IsSignedRole
105 };
106
107 QHash<int, QByteArray> roleNames() const Q_DECL_OVERRIDE;
108 QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
109 QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE;
110 QModelIndex parent(const QModelIndex &index) const Q_DECL_OVERRIDE;
111 int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
112 int columnCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
113
114private:
115 std::unique_ptr<AttachmentModelPrivate> d;
116};
diff --git a/framework/src/domain/mime/messageparser_new.cpp b/framework/src/domain/mime/messageparser_new.cpp
new file mode 100644
index 00000000..7e7dbfa6
--- /dev/null
+++ b/framework/src/domain/mime/messageparser_new.cpp
@@ -0,0 +1,513 @@
1/*
2 Copyright (c) 2016 Sandro Knauß <knauss@kolabsys.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 "messageparser.h"
21#include "mimetreeparser/interface.h"
22#include "htmlutils.h"
23
24#include <QDebug>
25#include <QTextDocument>
26
27Q_DECLARE_METATYPE(Part *)
28Q_DECLARE_METATYPE(Content *)
29Q_DECLARE_METATYPE(Signature *)
30Q_DECLARE_METATYPE(Encryption *)
31
32class Entry;
33
34class NewModelPrivate
35{
36public:
37 NewModelPrivate(NewModel *q_ptr, const std::shared_ptr<Parser> &parser);
38 ~NewModelPrivate();
39
40 void createTree();
41
42 QSharedPointer<QVariant> getVar(const std::shared_ptr<Signature> &sig);
43 QSharedPointer<QVariant> getVar(const std::shared_ptr<Encryption> &enc);
44 QSharedPointer<QVariant> getVar(const std::shared_ptr<Part> &part);
45 QSharedPointer<QVariant> getVar(Part *part);
46 QSharedPointer<QVariant> getVar(const std::shared_ptr<Content> &content);
47 QSharedPointer<QVariant> getVar(Content *content);
48
49 int getPos(Signature *sig);
50 int getPos(Encryption *enc);
51 int getPos(Part *part);
52 int getPos(Content *content);
53
54 NewModel *q;
55 QVector<Part::Ptr> mParts;
56 std::unique_ptr<Entry> mRoot;
57
58 std::shared_ptr<Parser> mParser;
59private:
60 QMap<std::shared_ptr<Signature>, QSharedPointer<QVariant>> mSignatureMap;
61 QMap<std::shared_ptr<Encryption>, QSharedPointer<QVariant>> mEncryptionMap;
62 QMap<Part *, QSharedPointer<QVariant>> mPartMap;
63 QMap<Content *, QSharedPointer<QVariant>> mCMap;
64};
65
66class Entry
67{
68public:
69 Entry(NewModelPrivate *model)
70 : mParent(nullptr)
71 , mNewModelPrivate(model)
72 {
73 }
74
75 ~Entry()
76 {
77 foreach(auto child, mChildren) {
78 delete child;
79 }
80 mChildren.clear();
81 }
82
83 void addChild(Entry *entry)
84 {
85 mChildren.append(entry);
86 entry->mParent = this;
87 }
88
89 Entry *addSignatures(QVector<Signature::Ptr> signatures)
90 {
91 auto ret = this;
92 foreach(const auto &sig, signatures) {
93 auto entry = new Entry(mNewModelPrivate);
94 entry->mData = mNewModelPrivate->getVar(sig);
95 ret->addChild(entry);
96 ret = entry;
97 }
98 return ret;
99 }
100
101 Entry *addEncryptions(QVector<Encryption::Ptr> encryptions)
102 {
103 auto ret = this;
104 foreach(const auto &enc, encryptions) {
105 auto entry = new Entry(mNewModelPrivate);
106 entry->mData = mNewModelPrivate->getVar(enc);
107 ret->addChild(entry);
108 ret = entry;
109 }
110 return ret;
111 }
112
113 Entry *addPart(Part *part)
114 {
115 auto entry = new Entry(mNewModelPrivate);
116 entry->mData = mNewModelPrivate->getVar(part);
117 addChild(entry);
118
119 foreach(const auto &content, part->content()) {
120 auto _entry = entry;
121 _entry = _entry->addEncryptions(content->encryptions().mid(part->encryptions().size()));
122 _entry = _entry->addSignatures(content->signatures().mid(part->signatures().size()));
123 auto c = new Entry(mNewModelPrivate);
124 c->mData = mNewModelPrivate->getVar(content);
125 _entry->addChild(c);
126 }
127// foreach(const auto &content, part->availableContents()) {
128// foreach(const auto &contentPart, part->content(content)) {
129// auto _entry = entry;
130// _entry = _entry->addEncryptions(contentPart->encryptions().mid(part->encryptions().size()));
131// _entry = _entry->addSignatures(contentPart->signatures().mid(part->signatures().size()));
132// auto c = new Entry(mNewModelPrivate);
133// c->mData = mNewModelPrivate->getVar(contentPart);
134// _entry->addChild(c);
135// }
136// }
137 foreach(const auto &sp, part->subParts()) {
138 auto _entry = entry;
139 _entry = _entry->addEncryptions(sp->encryptions().mid(part->encryptions().size()));
140 _entry = _entry->addSignatures(sp->signatures().mid(part->signatures().size()));
141 _entry->addPart(sp.get());
142 }
143 return entry;
144 }
145
146 int pos()
147 {
148 if(!mParent) {
149 return -1;
150 }
151 int i=0;
152 foreach(const auto &child, mParent->mChildren) {
153 if (child == this) {
154 return i;
155 }
156 i++;
157 }
158 return -1;
159 }
160
161 QSharedPointer<QVariant> mData;
162
163 Entry *mParent;
164 QVector<Entry *> mChildren;
165 NewModelPrivate *mNewModelPrivate;
166};
167
168
169NewModelPrivate::NewModelPrivate(NewModel *q_ptr, const std::shared_ptr<Parser> &parser)
170 : q(q_ptr)
171 , mRoot(std::unique_ptr<Entry>(new Entry(this)))
172 , mParser(parser)
173{
174 mParts = mParser->collectContentParts();
175 createTree();
176}
177
178NewModelPrivate::~NewModelPrivate()
179{
180}
181
182void NewModelPrivate::createTree()
183{
184 auto root = mRoot.get();
185 auto parent = root;
186 Part *pPart = nullptr;
187 QVector<Signature::Ptr> signatures;
188 QVector<Encryption::Ptr> encryptions;
189 foreach(const auto part, mParts) {
190 auto _parent = parent;
191 if (pPart != part->parent()) {
192 auto _parent = root;
193 _parent = _parent->addEncryptions(part->parent()->encryptions());
194 _parent = _parent->addSignatures(part->parent()->signatures());
195 signatures = part->parent()->signatures();
196 encryptions = part->parent()->encryptions();
197 parent = _parent;
198 pPart = part->parent();
199 }
200 _parent = _parent->addEncryptions(part->encryptions().mid(encryptions.size()));
201 _parent = _parent->addSignatures(part->signatures().mid(signatures.size()));
202 _parent->addPart(part.get());
203 }
204}
205
206QSharedPointer<QVariant> NewModelPrivate::getVar(const std::shared_ptr<Signature> &sig)
207{
208 if (!mSignatureMap.contains(sig)) {
209 auto var = new QVariant();
210 var->setValue(sig.get());
211 mSignatureMap.insert(sig, QSharedPointer<QVariant>(var));
212 }
213 return mSignatureMap.value(sig);
214}
215
216QSharedPointer<QVariant> NewModelPrivate::getVar(const std::shared_ptr<Encryption> &enc)
217{
218 if (!mEncryptionMap.contains(enc)) {
219 auto var = new QVariant();
220 var->setValue(enc.get());
221 mEncryptionMap.insert(enc, QSharedPointer<QVariant>(var));
222 }
223 return mEncryptionMap.value(enc);
224}
225
226QSharedPointer<QVariant> NewModelPrivate::getVar(const std::shared_ptr<Part> &part)
227{
228 return getVar(part.get());
229}
230
231QSharedPointer<QVariant> NewModelPrivate::getVar(Part *part)
232{
233 if (!mPartMap.contains(part)) {
234 auto var = new QVariant();
235 var->setValue(part);
236 mPartMap.insert(part, QSharedPointer<QVariant>(var));
237 }
238 return mPartMap.value(part);
239}
240
241QSharedPointer<QVariant> NewModelPrivate::getVar(const std::shared_ptr<Content> &content)
242{
243 return getVar(content.get());
244}
245
246QSharedPointer<QVariant> NewModelPrivate::getVar(Content *content)
247{
248 if (!mCMap.contains(content)) {
249 auto var = new QVariant();
250 var->setValue(content);
251 mCMap.insert(content, QSharedPointer<QVariant>(var));
252 }
253 return mCMap.value(content);
254}
255
256int NewModelPrivate::getPos(Signature *signature)
257{
258 const auto first = mParts.first();
259 int i = 0;
260 foreach(const auto &sig, first->signatures()) {
261 if (sig.get() == signature) {
262 break;
263 }
264 i++;
265 }
266 return i;
267}
268
269int NewModelPrivate::getPos(Encryption *encryption)
270{
271 const auto first = mParts.first();
272 int i = 0;
273 foreach(const auto &enc, first->encryptions()) {
274 if (enc.get() == encryption) {
275 break;
276 }
277 i++;
278 }
279 return i;
280}
281
282int NewModelPrivate::getPos(Part *part)
283{
284 int i = 0;
285 foreach(const auto &p, mParts) {
286 if (p.get() == part) {
287 break;
288 }
289 i++;
290 }
291 return i;
292}
293
294int NewModelPrivate::getPos(Content *content)
295{
296 int i = 0;
297 foreach(const auto &c, content->parent()->content()) {
298 if (c.get() == content) {
299 break;
300 }
301 i++;
302 }
303 return i;
304}
305
306NewModel::NewModel(std::shared_ptr<Parser> parser)
307 : d(std::unique_ptr<NewModelPrivate>(new NewModelPrivate(this, parser)))
308{
309}
310
311NewModel::~NewModel()
312{
313}
314
315QHash<int, QByteArray> NewModel::roleNames() const
316{
317 QHash<int, QByteArray> roles;
318 roles[TypeRole] = "type";
319 roles[ContentRole] = "content";
320 roles[IsComplexHtmlContentRole] = "complexHtmlContent";
321 roles[IsEmbededRole] = "embeded";
322 roles[SecurityLevelRole] = "securityLevel";
323 roles[EncryptionErrorType] = "errorType";
324 roles[EncryptionErrorString] = "errorString";
325 return roles;
326}
327
328QModelIndex NewModel::index(int row, int column, const QModelIndex &parent) const
329{
330 if (row < 0 || column != 0) {
331 return QModelIndex();
332 }
333 Entry *entry = d->mRoot.get();
334 if (parent.isValid()) {
335 entry = static_cast<Entry *>(parent.internalPointer());
336 }
337
338 if (row < entry->mChildren.size()) {
339 return createIndex(row, column, entry->mChildren.at(row));
340 }
341 return QModelIndex();
342}
343
344QVariant NewModel::data(const QModelIndex &index, int role) const
345{
346 if (!index.isValid()) {
347 switch (role) {
348 case Qt::DisplayRole:
349 return QString("root");
350 case IsEmbededRole:
351 return false;
352 }
353 return QVariant();
354 }
355
356 if (index.internalPointer()) {
357 const auto entry = static_cast<Entry *>(index.internalPointer());
358 const auto _data = entry->mData;
359 if (entry == d->mRoot.get()|| !_data) {
360 switch (role) {
361 case Qt::DisplayRole:
362 return QString("root");
363 case IsEmbededRole:
364 return false;
365 }
366 return QVariant();
367 }
368 if (_data->userType() == qMetaTypeId<Signature *>()) {
369 const auto signature = _data->value<Signature *>();
370 int i = d->getPos(signature);
371 switch(role) {
372 case Qt::DisplayRole:
373 return QStringLiteral("Signature%1").arg(i);
374 case TypeRole:
375 return QStringLiteral("Signature");
376 case SecurityLevelRole:
377 return QStringLiteral("RED");
378 case IsEmbededRole:
379 return data(index.parent(), IsEmbededRole);
380 }
381 } else if (_data->userType() == qMetaTypeId<Encryption *>()) {
382 const auto encryption = _data->value<Encryption *>();
383 int i = d->getPos(encryption);
384 switch(role) {
385 case Qt::DisplayRole:
386 return QStringLiteral("Encryption%1").arg(i);
387 case TypeRole:
388 return QStringLiteral("Encryption");
389 case SecurityLevelRole:
390 return QStringLiteral("GREEN");
391 case IsEmbededRole:
392 return data(index.parent(), IsEmbededRole);
393 case EncryptionErrorType:
394 {
395 switch(encryption->errorType()) {
396 case Encryption::NoError:
397 return QString();
398 case Encryption::PassphraseError:
399 return QStringLiteral("PassphraseError");
400 case Encryption::KeyMissing:
401 return QStringLiteral("KeyMissing");
402 default:
403 return QStringLiteral("UnknownError");
404 }
405 }
406 case EncryptionErrorString:
407 return encryption->errorString();
408 }
409 } else if (_data->userType() == qMetaTypeId<Part *>()) {
410 const auto part = _data->value<Part *>();
411 switch (role) {
412 case Qt::DisplayRole:
413 case TypeRole:
414 return QString::fromLatin1(part->type());
415 case IsEmbededRole:
416 return data(index.parent(), IsEmbededRole);
417 }
418 } else if (_data->userType() == qMetaTypeId<Content *>()) {
419 const auto content = _data->value<Content *>();
420 int i = d->getPos(content);
421 switch(role) {
422 case Qt::DisplayRole:
423 return QStringLiteral("Content%1").arg(i);
424 case TypeRole:
425 return QString::fromLatin1(content->type());
426 case IsEmbededRole:
427 return data(index.parent(), IsEmbededRole);
428 case IsComplexHtmlContentRole: {
429 const auto contentType = data(index, TypeRole).toString();
430 if (contentType == "HtmlContent") {
431 const auto text = content->encodedContent();
432 if (text.contains("<!DOCTYPE html PUBLIC")) {
433 return true;
434 }
435 //Media queries are too advanced
436 if (text.contains("@media")) {
437 return true;
438 }
439 if (text.contains("<style")) {
440 return true;
441 }
442 return false;
443 } else {
444 return false;
445 }
446 break;
447 }
448 case ContentRole: {
449 auto text = content->encodedContent();
450 const auto contentType = data(index, TypeRole).toString();
451 if (contentType == "HtmlContent") {
452 const auto rx = QRegExp("(src)\\s*=\\s*(\"|')(cid:[^\"']+)\\2");
453 int pos = 0;
454 while ((pos = rx.indexIn(text, pos)) != -1) {
455 const auto link = QUrl(rx.cap(3).toUtf8());
456 pos += rx.matchedLength();
457 const auto repl = d->mParser->getPart(link);
458 if (!repl) {
459 continue;
460 }
461 const auto content = repl->content();
462 if(content.size() < 1) {
463 continue;
464 }
465 const auto mailMime = content.first()->mailMime();
466 const auto mimetype = mailMime->mimetype().name();
467 if (mimetype.startsWith("image/")) {
468 const auto data = content.first()->content();
469 text.replace(rx.cap(0), QString("src=\"data:%1;base64,%2\"").arg(mimetype, QString::fromLatin1(data.toBase64())));
470 }
471 }
472 } else { //We assume plain
473 //We alwas do richtext (so we get highlighted links and stuff).
474 return HtmlUtils::linkify(Qt::convertFromPlainText(text));
475 }
476 return text;
477 }
478 }
479 }
480 }
481 return QVariant();
482}
483
484QModelIndex NewModel::parent(const QModelIndex &index) const
485{
486 if (!index.internalPointer()) {
487 return QModelIndex();
488 }
489 const auto entry = static_cast<Entry *>(index.internalPointer());
490 if (entry->mParent && entry->mParent != d->mRoot.get()) {
491 return createIndex(entry->pos(), 0, entry->mParent);
492 }
493 return QModelIndex();
494}
495
496int NewModel::rowCount(const QModelIndex &parent) const
497{
498 if (!parent.isValid()) {
499 return d->mRoot->mChildren.size();
500 } else {
501 if (!parent.internalPointer()) {
502 return 0;
503 }
504 const auto entry = static_cast<Entry *>(parent.internalPointer());
505 return entry->mChildren.size();
506 }
507 return 0;
508}
509
510int NewModel::columnCount(const QModelIndex &parent) const
511{
512 return 1;
513}
diff --git a/framework/src/domain/mime/mimetreeparser/CMakeLists.txt b/framework/src/domain/mime/mimetreeparser/CMakeLists.txt
new file mode 100644
index 00000000..517fb7e5
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/CMakeLists.txt
@@ -0,0 +1,17 @@
1include_directories(.)
2
3set(mimetreeparser_SRCS
4 interface.cpp
5 objecttreesource.cpp
6 stringhtmlwriter.cpp
7)
8
9add_library(mimetreeparser SHARED ${mimetreeparser_SRCS})
10
11qt5_use_modules(mimetreeparser Core Gui)
12target_link_libraries(mimetreeparser KF5::Mime kube_otp)
13
14install(TARGETS mimetreeparser DESTINATION ${LIB_INSTALL_DIR})
15
16add_subdirectory(tests)
17add_subdirectory(otp)
diff --git a/framework/src/domain/mime/mimetreeparser/interface.cpp b/framework/src/domain/mime/mimetreeparser/interface.cpp
new file mode 100644
index 00000000..b8556336
--- /dev/null
+++ b/framework/src/domain/mime/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 <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
153MailMime::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
162QByteArray MailMime::decodedContent() const
163{
164 if (!d->mNode) {
165 return QByteArray();
166 }
167 return d->mNode->decodedContent();
168}
169
170class KeyPrivate
171{
172public:
173 Key *q;
174 GpgME::Key mKey;
175 QByteArray mKeyID;
176};
177
178Key::Key()
179 :d(std::unique_ptr<KeyPrivate>(new KeyPrivate))
180{
181 d->q = this;
182}
183
184
185Key::Key(KeyPrivate *d_ptr)
186 :d(std::unique_ptr<KeyPrivate>(d_ptr))
187{
188 d->q = this;
189}
190
191Key::~Key()
192{
193
194}
195
196QString Key::keyid() const
197{
198 if (!d->mKey.isNull()) {
199 return d->mKey.keyID();
200 }
201
202 return d->mKeyID;
203}
204
205QString 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
215QString Key::email() const
216{
217 if (!d->mKey.isNull()) {
218 return d->mKey.userID(0).email();
219 }
220 return QString();
221}
222
223QString Key::comment() const
224{
225 if (!d->mKey.isNull()) {
226 return d->mKey.userID(0).comment();
227 }
228 return QString();
229}
230
231class SignaturePrivate
232{
233public:
234 Signature *q;
235 GpgME::Signature mSignature;
236 Key::Ptr mKey;
237};
238
239Signature::Signature()
240 :d(std::unique_ptr<SignaturePrivate>(new SignaturePrivate))
241{
242 d->q = this;
243}
244
245
246Signature::Signature(SignaturePrivate *d_ptr)
247 :d(std::unique_ptr<SignaturePrivate>(d_ptr))
248{
249 d->q = this;
250}
251
252Signature::~Signature()
253{
254
255}
256
257QDateTime Signature::creationDateTime() const
258{
259 QDateTime dt;
260 dt.setTime_t(d->mSignature.creationTime());
261 return dt;
262}
263
264QDateTime Signature::expirationDateTime() const
265{
266 QDateTime dt;
267 dt.setTime_t(d->mSignature.expirationTime());
268 return dt;
269}
270
271bool Signature::neverExpires() const
272{
273 return d->mSignature.neverExpires();
274}
275
276Key::Ptr Signature::key() const
277{
278 return d->mKey;
279}
280
281class EncryptionPrivate
282{
283public:
284 Encryption *q;
285 std::vector<Key::Ptr> mRecipients;
286 Encryption::ErrorType mErrorType;
287 QString mErrorString;
288};
289
290Encryption::Encryption(EncryptionPrivate *d_ptr)
291 :d(std::unique_ptr<EncryptionPrivate>(d_ptr))
292{
293 d->q = this;
294}
295
296Encryption::Encryption()
297 :d(std::unique_ptr<EncryptionPrivate>(new EncryptionPrivate))
298{
299 d->q = this;
300 d->mErrorType = Encryption::NoError;
301}
302
303Encryption::~Encryption()
304{
305
306}
307
308std::vector<Key::Ptr> Encryption::recipients() const
309{
310 return d->mRecipients;
311}
312
313QString Encryption::errorString()
314{
315 return d->mErrorString;
316}
317
318Encryption::ErrorType Encryption::errorType()
319{
320 return d->mErrorType;
321}
322
323
324class PartPrivate
325{
326public:
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;
351private:
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
360PartPrivate::PartPrivate(Part* part)
361 : q(part)
362 , mParent(Q_NULLPTR)
363{
364
365}
366
367void PartPrivate::createMailMime(const MimeTreeParser::HtmlMessagePart::Ptr& part)
368{
369 mMailMime = MailMime::Ptr(new MailMime);
370 mMailMime->d->mNode = part->mNode;
371}
372
373void PartPrivate::createMailMime(const MimeTreeParser::AlternativeMessagePart::Ptr& part)
374{
375 mMailMime = MailMime::Ptr(new MailMime);
376 mMailMime->d->mNode = part->mNode;
377}
378
379void PartPrivate::createMailMime(const MimeTreeParser::TextMessagePart::Ptr& part)
380{
381 mMailMime = MailMime::Ptr(new MailMime);
382 mMailMime->d->mNode = part->mNode;
383}
384
385void PartPrivate::createMailMime(const MimeTreeParser::MimeMessagePart::Ptr& part)
386{
387 mMailMime = MailMime::Ptr(new MailMime);
388 mMailMime->d->mNode = part->mNode;
389}
390
391void PartPrivate::createMailMime(const MimeTreeParser::EncryptedMessagePart::Ptr& part)
392{
393 mMailMime = MailMime::Ptr(new MailMime);
394 mMailMime->d->mNode = part->mNode;
395}
396
397void PartPrivate::appendSubPart(Part::Ptr subpart)
398{
399 subpart->d->mParent = q;
400 mSubParts.append(subpart);
401}
402
403Encryption::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
453void PartPrivate::appendEncryption(const MimeTreeParser::EncryptedMessagePart::Ptr& part)
454{
455 mEncryptions.append(createEncryption(part));
456}
457
458void PartPrivate::setEncryptions(const QVector< Encryption::Ptr >& encs)
459{
460 mEncryptions = encs;
461}
462
463QVector<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
504void PartPrivate::appendSignature(const MimeTreeParser::SignedMessagePart::Ptr& part)
505{
506 mSignatures.append(createSignature(part));
507}
508
509
510void PartPrivate::setSignatures(const QVector< Signature::Ptr >& sigs)
511{
512 mSignatures = sigs;
513}
514
515Part *PartPrivate::parent() const
516{
517 return mParent;
518}
519
520QVector< Part::Ptr > PartPrivate::subParts()
521{
522 return mSubParts;
523}
524
525const MailMime::Ptr& PartPrivate::mailMime() const
526{
527 return mMailMime;
528}
529
530const QVector< Encryption::Ptr >& PartPrivate::encryptions() const
531{
532 return mEncryptions;
533}
534
535const QVector< Signature::Ptr >& PartPrivate::signatures() const
536{
537 return mSignatures;
538}
539
540Part::Part()
541 : d(std::unique_ptr<PartPrivate>(new PartPrivate(this)))
542{
543
544}
545
546bool Part::hasSubParts() const
547{
548 return !subParts().isEmpty();
549}
550
551QVector<Part::Ptr> Part::subParts() const
552{
553 return d->subParts();
554}
555
556QByteArray Part::type() const
557{
558 return "Part";
559}
560
561QVector<QByteArray> Part::availableContents() const
562{
563 return QVector<QByteArray>();
564}
565
566QVector<Content::Ptr> Part::content() const
567{
568 return content(availableContents().first());
569}
570
571QVector<Content::Ptr> Part::content(const QByteArray& ct) const
572{
573 return QVector<Content::Ptr>();
574}
575
576QVector<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
586QVector<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
596MailMime::Ptr Part::mailMime() const
597{
598 return d->mailMime();
599}
600
601Part *Part::parent() const
602{
603 return d->parent();
604}
605
606class ContentPrivate
607{
608public:
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
620void ContentPrivate::appendEncryption(const MimeTreeParser::EncryptedMessagePart::Ptr& enc)
621{
622 mEncryptions.append(PartPrivate::createEncryption(enc));
623}
624
625void ContentPrivate::appendSignature(const MimeTreeParser::SignedMessagePart::Ptr& sig)
626{
627 mSignatures.append(PartPrivate::createSignature(sig));
628}
629
630
631Content::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
640Content::Content(ContentPrivate* d_ptr)
641 : d(std::unique_ptr<ContentPrivate>(d_ptr))
642{
643 d->q = this;
644}
645
646Content::~Content()
647{
648}
649
650QVector<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
659QVector<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
668QByteArray Content::content() const
669{
670 return d->mContent;
671}
672
673QByteArray Content::charset() const
674{
675 return d->mCodec;
676}
677
678QString 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
685QString Content::encodedContent(const QByteArray &charset) const
686{
687 QTextCodec *codec = QTextCodec::codecForName(charset);
688 return codec->toUnicode(content());
689}
690
691QByteArray Content::type() const
692{
693 return "Content";
694}
695
696MailMime::Ptr Content::mailMime() const
697{
698 if (d->mMailMime) {
699 return d->mMailMime;
700 } else {
701 return d->mParent->mailMime();
702 }
703}
704
705Part *Content::parent() const
706{
707 return d->mParent;
708}
709
710HtmlContent::HtmlContent(const QByteArray& content, Part* parent)
711 : Content(content, parent)
712{
713
714}
715
716QByteArray HtmlContent::type() const
717{
718 return "HtmlContent";
719}
720
721PlainTextContent::PlainTextContent(const QByteArray& content, Part* parent)
722 : Content(content, parent)
723{
724
725}
726
727PlainTextContent::PlainTextContent(ContentPrivate* d_ptr)
728 : Content(d_ptr)
729{
730
731}
732
733HtmlContent::HtmlContent(ContentPrivate* d_ptr)
734 : Content(d_ptr)
735{
736
737}
738
739
740QByteArray PlainTextContent::type() const
741{
742 return "PlainTextContent";
743}
744
745class AlternativePartPrivate
746{
747public:
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
756private:
757 QMap<QByteArray, QVector<Content::Ptr>> mContent;
758 QVector<QByteArray> mTypes;
759};
760
761void 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
772QVector<QByteArray> AlternativePartPrivate::types() const
773{
774 return mTypes;
775}
776
777QVector<Content::Ptr> AlternativePartPrivate::content(const QByteArray& ct) const
778{
779 return mContent[ct];
780}
781
782AlternativePart::AlternativePart()
783 : d(std::unique_ptr<AlternativePartPrivate>(new AlternativePartPrivate))
784{
785 d->q = this;
786}
787
788AlternativePart::~AlternativePart()
789{
790
791}
792
793QByteArray AlternativePart::type() const
794{
795 return "AlternativePart";
796}
797
798QVector<QByteArray> AlternativePart::availableContents() const
799{
800 return d->types();
801}
802
803QVector<Content::Ptr> AlternativePart::content(const QByteArray& ct) const
804{
805 return d->content(ct);
806}
807
808PartPrivate* AlternativePart::reachParentD() const
809{
810 return Part::d.get();
811}
812
813class SinglePartPrivate
814{
815public:
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
826void 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
855void 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
863void 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
882void 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
890SinglePart::SinglePart()
891 : d(std::unique_ptr<SinglePartPrivate>(new SinglePartPrivate))
892{
893 d->q = this;
894}
895
896SinglePart::~SinglePart()
897{
898
899}
900
901QVector<QByteArray> SinglePart::availableContents() const
902{
903 return QVector<QByteArray>() << d->mType;
904}
905
906QVector< 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
914QByteArray SinglePart::type() const
915{
916 return "SinglePart";
917}
918
919PartPrivate* SinglePart::reachParentD() const
920{
921 return Part::d.get();
922}
923
924ParserPrivate::ParserPrivate(Parser* parser)
925 : q(parser)
926 , mNodeHelper(std::make_shared<MimeTreeParser::NodeHelper>())
927{
928
929}
930
931void 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
954void 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
1013Parser::Parser(const QByteArray& mimeMessage)
1014 :d(std::unique_ptr<ParserPrivate>(new ParserPrivate(this)))
1015{
1016 d->setMessage(mimeMessage);
1017}
1018
1019Parser::~Parser()
1020{
1021}
1022
1023Part::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
1035QVector<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
1085QVector<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
1144QVector<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
1167Part::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}
diff --git a/framework/src/domain/mime/mimetreeparser/interface.h b/framework/src/domain/mime/mimetreeparser/interface.h
new file mode 100644
index 00000000..7c3ea28b
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/interface.h
@@ -0,0 +1,378 @@
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#pragma once
21
22#include <functional>
23#include <memory>
24#include <vector>
25
26#include <QDateTime>
27#include <QUrl>
28#include <QMimeType>
29
30class Part;
31class PartPrivate;
32
33class MailMime;
34class MailMimePrivate;
35
36class AlternativePart;
37class AlternativePartPrivate;
38
39class SinglePart;
40class SinglePartPrivate;
41
42class EncryptionPart;
43class EncryptionPartPrivate;
44
45class EncapsulatedPart;
46class EncapsulatedPartPrivate;
47
48class Content;
49class ContentPrivate;
50
51class CertContent;
52class CertContentPrivate;
53
54class EncryptionError;
55
56class Key;
57class KeyPrivate;
58class Signature;
59class SignaturePrivate;
60class Encryption;
61class EncryptionPrivate;
62
63typedef std::shared_ptr<Signature> SignaturePtr;
64typedef std::shared_ptr<Encryption> EncryptionPtr;
65
66class Parser;
67class ParserPrivate;
68
69/*
70 * A MessagePart that is based on a KMime::Content
71 */
72class MailMime
73{
74public:
75 typedef std::shared_ptr<MailMime> Ptr;
76 /**
77 * Various possible values for the "Content-Disposition" header.
78 */
79 enum Disposition {
80 Invalid, ///< Default, invalid value
81 Inline, ///< inline
82 Attachment ///< attachment
83 };
84
85 MailMime();
86
87 // interessting header parts of a KMime::Content
88 QMimeType mimetype() const;
89 Disposition disposition() const;
90 QUrl label() const;
91 QByteArray cid() const;
92 QByteArray charset() const;
93 QString filename() const;
94
95 // Unique identifier to ecactly this KMime::Content
96 QByteArray link() const;
97
98 QByteArray content() const;
99 //Use default charset
100 QString encodedContent() const;
101
102 // overwrite default charset with given charset
103 QString encodedContent(QByteArray charset) const;
104
105 QByteArray decodedContent() const;
106
107 bool isFirstTextPart() const;
108 bool isFirstPart() const;
109 bool isTopLevelPart() const;
110
111 MailMime::Ptr parent() const;
112
113private:
114 std::unique_ptr<MailMimePrivate> d;
115
116 friend class PartPrivate;
117};
118
119class Content
120{
121public:
122 typedef std::shared_ptr<Content> Ptr;
123 Content(const QByteArray &content, Part *parent);
124 Content(ContentPrivate *d_ptr);
125 virtual ~Content();
126
127 QByteArray content() const;
128
129 QByteArray charset() const;
130
131 //Use default charset
132 QString encodedContent() const;
133
134 // overwrite default charset with given charset
135 QString encodedContent(const QByteArray &charset) const;
136
137 QVector<SignaturePtr> signatures() const;
138 QVector<EncryptionPtr> encryptions() const;
139 MailMime::Ptr mailMime() const;
140 virtual QByteArray type() const;
141 Part* parent() const;
142private:
143 std::unique_ptr<ContentPrivate> d;
144};
145
146class PlainTextContent : public Content
147{
148public:
149 PlainTextContent(const QByteArray &content, Part *parent);
150 PlainTextContent(ContentPrivate *d_ptr);
151 QByteArray type() const Q_DECL_OVERRIDE;
152};
153
154class HtmlContent : public Content
155{
156public:
157 HtmlContent(const QByteArray &content, Part *parent);
158 HtmlContent(ContentPrivate* d_ptr);
159 QByteArray type() const Q_DECL_OVERRIDE;
160};
161
162/*
163 * importing a cert GpgMe::ImportResult
164 * checking a cert (if it is a valid cert)
165 */
166
167class CertContent : public Content
168{
169public:
170 typedef std::shared_ptr<CertContent> Ptr;
171 CertContent(const QByteArray &content, Part *parent);
172
173 QByteArray type() const Q_DECL_OVERRIDE;
174 enum CertType {
175 Pgp,
176 SMime
177 };
178
179 enum CertSubType {
180 Public,
181 Private
182 };
183
184 CertType certType() const;
185 CertSubType certSubType() const;
186 int keyLength() const;
187
188private:
189 std::unique_ptr<CertContentPrivate> d;
190};
191
192class Part
193{
194public:
195 typedef std::shared_ptr<Part> Ptr;
196 Part();
197 virtual QByteArray type() const;
198
199 virtual QVector<QByteArray> availableContents() const;
200 virtual QVector<Content::Ptr> content(const QByteArray& ct) const;
201 QVector<Content::Ptr> content() const;
202
203 bool hasSubParts() const;
204 QVector<Part::Ptr> subParts() const;
205 Part *parent() const;
206
207 QVector<SignaturePtr> signatures() const;
208 QVector<EncryptionPtr> encryptions() const;
209 virtual MailMime::Ptr mailMime() const;
210protected:
211 std::unique_ptr<PartPrivate> d;
212private:
213 friend class ParserPrivate;
214 friend class PartPrivate;
215};
216
217class AlternativePart : public Part
218{
219public:
220 typedef std::shared_ptr<AlternativePart> Ptr;
221
222 AlternativePart();
223 virtual ~AlternativePart();
224
225 QVector<QByteArray> availableContents() const Q_DECL_OVERRIDE;
226 QVector<Content::Ptr> content(const QByteArray& ct) const Q_DECL_OVERRIDE;
227
228 QByteArray type() const Q_DECL_OVERRIDE;
229
230private:
231 PartPrivate *reachParentD() const;
232 std::unique_ptr<AlternativePartPrivate> d;
233
234 friend class ParserPrivate;
235 friend class AlternativePartPrivate;
236};
237
238class SinglePart : public Part
239{
240 public:
241 typedef std::shared_ptr<SinglePart> Ptr;
242
243 SinglePart();
244 virtual ~SinglePart();
245
246 QVector<Content::Ptr> content(const QByteArray& ct) const Q_DECL_OVERRIDE;
247 QVector<QByteArray> availableContents() const Q_DECL_OVERRIDE;
248
249 QByteArray type() const Q_DECL_OVERRIDE;
250private:
251 PartPrivate *reachParentD() const;
252 std::unique_ptr<SinglePartPrivate> d;
253
254 friend class ParserPrivate;
255 friend class SinglePartPrivate;
256};
257
258/*
259 * we want to request complete headers like:
260 * from/to...
261 */
262
263class EncapsulatedPart : public SinglePart
264{
265public:
266 typedef std::shared_ptr<EncapsulatedPart> Ptr;
267 QByteArray type() const Q_DECL_OVERRIDE;
268
269 //template <class T> QByteArray header<T>();
270private:
271 std::unique_ptr<EncapsulatedPartPrivate> d;
272};
273
274class EncryptionError
275{
276public:
277 int errorId() const;
278 QString errorString() const;
279};
280
281class Key
282{
283public:
284 typedef std::shared_ptr<Key> Ptr;
285 Key();
286 Key(KeyPrivate *);
287 ~Key();
288
289 QString keyid() const;
290 QString name() const;
291 QString email() const;
292 QString comment() const;
293 QVector<QString> emails() const;
294 enum KeyTrust {
295 Unknown, Undefined, Never, Marginal, Full, Ultimate
296 };
297 KeyTrust keyTrust() const;
298
299 bool isRevokation() const;
300 bool isInvalid() const;
301 bool isExpired() const;
302
303 std::vector<Key::Ptr> subkeys();
304 Key parentkey() const;
305private:
306 std::unique_ptr<KeyPrivate> d;
307};
308
309class Signature
310{
311public:
312 typedef std::shared_ptr<Signature> Ptr;
313 Signature();
314 Signature(SignaturePrivate *);
315 ~Signature();
316
317 Key::Ptr key() const;
318 QDateTime creationDateTime() const;
319 QDateTime expirationDateTime() const;
320 bool neverExpires() const;
321
322 //template <> StatusObject<SignatureVerificationResult> verify() const;
323 private:
324 std::unique_ptr<SignaturePrivate> d;
325};
326
327/*
328 * Normally the Keys for encryption are subkeys
329 * for clients the parentkeys are "more interessting", because they store the name, email etc.
330 * but a client may also wants show to what subkey the mail is really encrypted, an if this subkey isRevoked or something else
331 */
332class Encryption
333{
334public:
335 enum ErrorType {
336 NoError,
337 PassphraseError,
338 KeyMissing,
339 UnknownError
340 };
341 typedef std::shared_ptr<Encryption> Ptr;
342 Encryption();
343 Encryption(EncryptionPrivate *);
344 ~Encryption();
345 std::vector<Key::Ptr> recipients() const;
346 QString errorString();
347 ErrorType errorType();
348private:
349 std::unique_ptr<EncryptionPrivate> d;
350};
351
352class Parser
353{
354public:
355 typedef std::shared_ptr<Parser> Ptr;
356 Parser(const QByteArray &mimeMessage);
357 ~Parser();
358
359 Part::Ptr getPart(const QUrl &url);
360
361 QVector<Part::Ptr> collect(const Part::Ptr &start, std::function<bool(const Part::Ptr &)> select, std::function<bool(const Content::Ptr &)> filter) const;
362 Part::Ptr find(const Part::Ptr &start, std::function<bool(const Part::Ptr &)> select) const;
363 QVector<Part::Ptr> collectContentParts() const;
364 QVector<Part::Ptr> collectAttachmentParts() const;
365 //template <> QVector<ContentPart::Ptr> collect<ContentPart>() const;
366
367 //template <> static StatusObject<SignatureVerificationResult> verifySignature(const Signature signature) const;
368 //template <> static StatusObject<Part> decrypt(const EncryptedPart part) const;
369
370signals:
371 void partsChanged();
372
373private:
374 std::unique_ptr<ParserPrivate> d;
375
376 friend class InterfaceTest;
377};
378
diff --git a/framework/src/domain/mime/mimetreeparser/interface_p.h b/framework/src/domain/mime/mimetreeparser/interface_p.h
new file mode 100644
index 00000000..8fab221a
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/interface_p.h
@@ -0,0 +1,56 @@
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#pragma once
21
22#include "interface.h"
23
24#include <QSharedPointer>
25#include <QMap>
26
27namespace KMime
28{
29 class Message;
30 typedef QSharedPointer<Message> MessagePtr;
31}
32
33namespace MimeTreeParser
34{
35 class MessagePart;
36 class NodeHelper;
37 typedef QSharedPointer<MessagePart> MessagePartPtr;
38}
39
40class ParserPrivate
41{
42public:
43 ParserPrivate(Parser *parser);
44
45 void setMessage(const QByteArray &mimeMessage);
46 void createTree(const MimeTreeParser::MessagePartPtr& start, const Part::Ptr& tree);
47
48 Part::Ptr mTree;
49 Parser *q;
50
51 MimeTreeParser::MessagePartPtr mPartTree;
52 KMime::MessagePtr mMsg;
53 std::shared_ptr<MimeTreeParser::NodeHelper> mNodeHelper;
54 QString mHtml;
55 QMap<QByteArray, QUrl> mEmbeddedPartMap;
56};
diff --git a/framework/src/domain/mime/mimetreeparser/objecttreesource.cpp b/framework/src/domain/mime/mimetreeparser/objecttreesource.cpp
new file mode 100644
index 00000000..186fdf80
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/objecttreesource.cpp
@@ -0,0 +1,152 @@
1/*
2 Copyright (C) 2009 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net
3 Copyright (c) 2009 Andras Mantia <andras@kdab.net>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18*/
19
20#include "objecttreesource.h"
21
22#include <otp/attachmentstrategy.h>
23#include <otp/bodypartformatterbasefactory.h>
24#include <otp/messagepart.h>
25#include <otp/messagepartrenderer.h>
26
27class ObjectSourcePrivate
28{
29public:
30 ObjectSourcePrivate()
31 : mWriter(0)
32 , mAllowDecryption(true)
33 , mHtmlLoadExternal(true)
34 , mPreferredMode(MimeTreeParser::Util::Html)
35 {
36
37 }
38 MimeTreeParser::HtmlWriter *mWriter;
39 MimeTreeParser::BodyPartFormatterBaseFactory mBodyPartFormatterBaseFactory;
40 bool mAllowDecryption;
41 bool mHtmlLoadExternal;
42 MimeTreeParser::Util::HtmlMode mPreferredMode;
43};
44
45ObjectTreeSource::ObjectTreeSource(MimeTreeParser::HtmlWriter *writer)
46 : MimeTreeParser::Interface::ObjectTreeSource()
47 , d(new ObjectSourcePrivate)
48 {
49 d->mWriter = writer;
50 }
51
52ObjectTreeSource::~ObjectTreeSource()
53{
54 delete d;
55}
56
57void ObjectTreeSource::setAllowDecryption(bool allowDecryption)
58{
59 d->mAllowDecryption = allowDecryption;
60}
61
62MimeTreeParser::HtmlWriter *ObjectTreeSource::htmlWriter()
63{
64 return d->mWriter;
65}
66
67bool ObjectTreeSource::htmlLoadExternal() const
68{
69 return d->mHtmlLoadExternal;
70}
71
72void ObjectTreeSource::setHtmlLoadExternal(bool loadExternal)
73{
74 d->mHtmlLoadExternal = loadExternal;
75}
76
77bool ObjectTreeSource::decryptMessage() const
78{
79 return d->mAllowDecryption;
80}
81
82bool ObjectTreeSource::showSignatureDetails() const
83{
84 return true;
85}
86
87int ObjectTreeSource::levelQuote() const
88{
89 return 1;
90}
91
92const QTextCodec *ObjectTreeSource::overrideCodec()
93{
94 return Q_NULLPTR;
95}
96
97QString ObjectTreeSource::createMessageHeader(KMime::Message *message)
98{
99 return QString();
100}
101
102const MimeTreeParser::AttachmentStrategy *ObjectTreeSource::attachmentStrategy()
103{
104 return MimeTreeParser::AttachmentStrategy::smart();
105}
106
107QObject *ObjectTreeSource::sourceObject()
108{
109 return Q_NULLPTR;
110}
111
112void ObjectTreeSource::setHtmlMode(MimeTreeParser::Util::HtmlMode mode, const QList<MimeTreeParser::Util::HtmlMode> &availableModes)
113{
114 Q_UNUSED(mode);
115 Q_UNUSED(availableModes);
116}
117
118MimeTreeParser::Util::HtmlMode ObjectTreeSource::preferredMode() const
119{
120 return d->mPreferredMode;
121}
122
123bool ObjectTreeSource::autoImportKeys() const
124{
125 return false;
126}
127
128bool ObjectTreeSource::showEmoticons() const
129{
130 return false;
131}
132
133bool ObjectTreeSource::showExpandQuotesMark() const
134{
135 return false;
136}
137
138bool ObjectTreeSource::isPrinting() const
139{
140 return false;
141}
142
143const MimeTreeParser::BodyPartFormatterBaseFactory *ObjectTreeSource::bodyPartFormatterFactory()
144{
145 return &(d->mBodyPartFormatterBaseFactory);
146}
147
148MimeTreeParser::Interface::MessagePartRenderer::Ptr ObjectTreeSource::messagePartTheme(MimeTreeParser::Interface::MessagePart::Ptr msgPart)
149{
150 Q_UNUSED(msgPart);
151 return MimeTreeParser::Interface::MessagePartRenderer::Ptr();
152}
diff --git a/framework/src/domain/mime/mimetreeparser/objecttreesource.h b/framework/src/domain/mime/mimetreeparser/objecttreesource.h
new file mode 100644
index 00000000..2167e06f
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/objecttreesource.h
@@ -0,0 +1,57 @@
1/*
2 Copyright (C) 2009 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net
3 Copyright (c) 2009 Andras Mantia <andras@kdab.net>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18*/
19
20#ifndef MAILVIEWER_OBJECTTREEEMPTYSOURCE_H
21#define MAILVIEWER_OBJECTTREEEMPTYSOURCE_H
22
23#include <otp/objecttreesource.h>
24
25class QString;
26
27class ObjectSourcePrivate;
28class ObjectTreeSource : public MimeTreeParser::Interface::ObjectTreeSource
29{
30public:
31 ObjectTreeSource(MimeTreeParser::HtmlWriter *writer);
32 virtual ~ObjectTreeSource();
33 void setHtmlLoadExternal(bool loadExternal);
34 bool decryptMessage() const Q_DECL_OVERRIDE;
35 bool htmlLoadExternal() const Q_DECL_OVERRIDE;
36 bool showSignatureDetails() const Q_DECL_OVERRIDE;
37 void setHtmlMode(MimeTreeParser::Util::HtmlMode mode, const QList<MimeTreeParser::Util::HtmlMode> &availableModes) Q_DECL_OVERRIDE;
38 MimeTreeParser::Util::HtmlMode preferredMode() const Q_DECL_OVERRIDE;
39 void setAllowDecryption(bool allowDecryption);
40 int levelQuote() const Q_DECL_OVERRIDE;
41 const QTextCodec *overrideCodec() Q_DECL_OVERRIDE;
42 QString createMessageHeader(KMime::Message *message) Q_DECL_OVERRIDE;
43 const MimeTreeParser::AttachmentStrategy *attachmentStrategy() Q_DECL_OVERRIDE;
44 MimeTreeParser::HtmlWriter *htmlWriter() Q_DECL_OVERRIDE;
45 QObject *sourceObject() Q_DECL_OVERRIDE;
46 bool autoImportKeys() const Q_DECL_OVERRIDE;
47 bool showEmoticons() const Q_DECL_OVERRIDE;
48 bool showExpandQuotesMark() const Q_DECL_OVERRIDE;
49 bool isPrinting() const Q_DECL_OVERRIDE;
50 const MimeTreeParser::BodyPartFormatterBaseFactory *bodyPartFormatterFactory() Q_DECL_OVERRIDE;
51 MimeTreeParser::Interface::MessagePartRendererPtr messagePartTheme(MimeTreeParser::Interface::MessagePartPtr msgPart) Q_DECL_OVERRIDE;
52private:
53 ObjectSourcePrivate *const d;
54};
55
56#endif
57
diff --git a/framework/src/domain/mime/mimetreeparser/otp/CMakeLists.txt b/framework/src/domain/mime/mimetreeparser/otp/CMakeLists.txt
new file mode 100644
index 00000000..f480fa21
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/CMakeLists.txt
@@ -0,0 +1,72 @@
1add_definitions( -DQT_NO_CAST_FROM_ASCII )
2add_definitions( -DQT_NO_CAST_TO_ASCII )
3add_definitions("-fvisibility=default")
4
5find_package(Qt5 COMPONENTS REQUIRED Core Gui)
6find_package(KF5Mime "4.87.0" CONFIG REQUIRED)
7find_package(QGpgme CONFIG REQUIRED)
8find_package(KF5Codecs CONFIG REQUIRED)
9find_package(KF5I18n CONFIG REQUIRED)
10
11#add_definitions(-DTRANSLATION_DOMAIN=\"libmimetreeparser\")
12
13# target_include_directories does not handle empty include paths
14include_directories(${GPGME_INCLUDES})
15
16set(libmimetreeparser_SRCS
17 objecttreeparser.cpp
18
19 #Bodyformatter
20 applicationpgpencrypted.cpp
21 applicationpkcs7mime.cpp
22 mailman.cpp
23 multipartalternative.cpp
24 multipartencrypted.cpp
25 multipartmixed.cpp
26 multipartsigned.cpp
27 textplain.cpp
28 texthtml.cpp
29 utils.cpp
30 bodypartformatter_impl.cpp
31
32 #Interfaces
33 bodypartformatter.cpp
34 objecttreesource.cpp
35 bodypart.cpp
36 htmlwriter.cpp
37 messagepartrenderer.cpp
38
39 #bodypartformatter.cpp
40 bodypartformatterbasefactory.cpp
41 cryptohelper.cpp
42 nodehelper.cpp
43 messagepart.cpp
44 partnodebodypart.cpp
45 #Mementos
46 cryptobodypartmemento.cpp
47 decryptverifybodypartmemento.cpp
48 verifydetachedbodypartmemento.cpp
49 verifyopaquebodypartmemento.cpp
50 #Stuff
51 mimetreeparser_debug.cpp
52 qgpgmejobexecutor.cpp
53 util.cpp
54 attachmentstrategy.cpp
55 #HTML Writer
56 queuehtmlwriter.cpp
57 attachmenttemporaryfilesdirs.cpp
58 )
59
60add_library(kube_otp ${libmimetreeparser_SRCS})
61
62target_link_libraries(kube_otp
63 PRIVATE
64 QGpgme
65 KF5::Codecs
66 KF5::I18n
67 KF5::Mime
68 Qt5::Gui
69)
70install(TARGETS kube_otp DESTINATION ${LIB_INSTALL_DIR})
71
72add_subdirectory(autotests)
diff --git a/framework/src/domain/mime/mimetreeparser/otp/applicationpgpencrypted.cpp b/framework/src/domain/mime/mimetreeparser/otp/applicationpgpencrypted.cpp
new file mode 100644
index 00000000..e0f8e30c
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/applicationpgpencrypted.cpp
@@ -0,0 +1,98 @@
1/*
2 Copyright (c) 2016 Sandro Knauß <sknauss@kde.org>
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 "applicationpgpencrypted.h"
21
22#include "utils.h"
23
24#include "objecttreeparser.h"
25#include "messagepart.h"
26
27#include <QGpgME/Protocol>
28
29#include <KMime/Content>
30
31#include "mimetreeparser_debug.h"
32
33using namespace MimeTreeParser;
34
35const ApplicationPGPEncryptedBodyPartFormatter *ApplicationPGPEncryptedBodyPartFormatter::self;
36
37const Interface::BodyPartFormatter *ApplicationPGPEncryptedBodyPartFormatter::create()
38{
39 if (!self) {
40 self = new ApplicationPGPEncryptedBodyPartFormatter();
41 }
42 return self;
43}
44
45Interface::BodyPartFormatter::Result ApplicationPGPEncryptedBodyPartFormatter::format(Interface::BodyPart *part, HtmlWriter *writer) const
46{
47 Q_UNUSED(writer)
48 const auto p = process(*part);
49 const auto mp = static_cast<MessagePart *>(p.data());
50 if (mp) {
51 mp->html(false);
52 return Ok;
53 }
54 return Failed;
55}
56
57Interface::MessagePart::Ptr ApplicationPGPEncryptedBodyPartFormatter::process(Interface::BodyPart &part) const
58{
59 KMime::Content *node(part.content());
60
61 if (node->decodedContent().trimmed() != "Version: 1") {
62 qCWarning(MIMETREEPARSER_LOG) << "Unknown PGP Version String:" << node->decodedContent().trimmed();
63 }
64
65 if (!part.content()->parent()) {
66 return MessagePart::Ptr();
67 }
68
69 KMime::Content *data = findTypeInDirectChilds(part.content()->parent(), "application/octet-stream");
70
71 if (!data) {
72 return MessagePart::Ptr(); //new MimeMessagePart(part.objectTreeParser(), node, false));
73 }
74
75 part.nodeHelper()->setEncryptionState(node, KMMsgFullyEncrypted);
76
77 EncryptedMessagePart::Ptr mp(new EncryptedMessagePart(part.objectTreeParser(),
78 data->decodedText(), QGpgME::openpgp(),
79 part.nodeHelper()->fromAsString(data), node));
80 mp->setIsEncrypted(true);
81 mp->setDecryptMessage(part.source()->decryptMessage());
82 PartMetaData *messagePart(mp->partMetaData());
83 if (!part.source()->decryptMessage()) {
84 part.nodeHelper()->setNodeProcessed(data, false); // Set the data node to done to prevent it from being processed
85 } else if (KMime::Content *newNode = part.nodeHelper()->decryptedNodeForContent(data)) {
86 // if we already have a decrypted node for this encrypted node, don't do the decryption again
87 return MessagePart::Ptr(new MimeMessagePart(part.objectTreeParser(), newNode, part.objectTreeParser()->showOnlyOneMimePart()));
88 } else {
89 mp->startDecryption(data);
90 if (!messagePart->inProgress) {
91 part.nodeHelper()->setNodeProcessed(data, false); // Set the data node to done to prevent it from being processed
92 if (messagePart->isDecryptable && messagePart->isSigned) {
93 part.nodeHelper()->setSignatureState(node, KMMsgFullySigned);
94 }
95 }
96 }
97 return mp;
98}
diff --git a/framework/src/domain/mime/mimetreeparser/otp/applicationpgpencrypted.h b/framework/src/domain/mime/mimetreeparser/otp/applicationpgpencrypted.h
new file mode 100644
index 00000000..f0f4865c
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/applicationpgpencrypted.h
@@ -0,0 +1,41 @@
1/*
2 Copyright (c) 2016 Sandro Knauß <sknauss@kde.org>
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#ifndef __MIMETREEPARSER_BODYFORAMATTER_APPLICATIONPGPENCYPTED_H__
21#define __MIMETREEPARSER_BODYFORAMATTER_APPLICATIONPGPENCYPTED_H__
22
23#include "bodypartformatter.h"
24#include "bodypart.h"
25
26namespace MimeTreeParser
27{
28
29class ApplicationPGPEncryptedBodyPartFormatter : public Interface::BodyPartFormatter
30{
31 static const ApplicationPGPEncryptedBodyPartFormatter *self;
32public:
33 Interface::MessagePart::Ptr process(Interface::BodyPart &part) const Q_DECL_OVERRIDE;
34 Interface::BodyPartFormatter::Result format(Interface::BodyPart *, HtmlWriter *) const Q_DECL_OVERRIDE;
35 using Interface::BodyPartFormatter::format;
36 static const Interface::BodyPartFormatter *create();
37};
38
39}
40
41#endif
diff --git a/framework/src/domain/mime/mimetreeparser/otp/applicationpkcs7mime.cpp b/framework/src/domain/mime/mimetreeparser/otp/applicationpkcs7mime.cpp
new file mode 100644
index 00000000..bcfc0616
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/applicationpkcs7mime.cpp
@@ -0,0 +1,178 @@
1/*
2 Copyright (c) 2016 Sandro Knauß <sknauss@kde.org>
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 "applicationpkcs7mime.h"
21
22#include "utils.h"
23
24#include "attachmentstrategy.h"
25#include "objecttreeparser.h"
26#include "messagepart.h"
27
28#include <QGpgME/Protocol>
29
30#include <KMime/Content>
31
32#include <QTextCodec>
33
34#include "mimetreeparser_debug.h"
35
36using namespace MimeTreeParser;
37
38const ApplicationPkcs7MimeBodyPartFormatter *ApplicationPkcs7MimeBodyPartFormatter::self;
39
40const Interface::BodyPartFormatter *ApplicationPkcs7MimeBodyPartFormatter::create()
41{
42 if (!self) {
43 self = new ApplicationPkcs7MimeBodyPartFormatter();
44 }
45 return self;
46}
47Interface::BodyPartFormatter::Result ApplicationPkcs7MimeBodyPartFormatter::format(Interface::BodyPart *part, HtmlWriter *writer) const
48{
49 Q_UNUSED(writer)
50 const auto p = process(*part);
51 const auto mp = static_cast<MessagePart *>(p.data());
52 if (mp) {
53 mp->html(false);
54 return Ok;
55 }
56 return Failed;
57}
58
59Interface::MessagePart::Ptr ApplicationPkcs7MimeBodyPartFormatter::process(Interface::BodyPart &part) const
60{
61 KMime::Content *node = part.content();
62
63 if (node->head().isEmpty()) {
64 return MessagePart::Ptr();
65 }
66
67 const auto smimeCrypto = QGpgME::smime();
68 if (!smimeCrypto) {
69 return MessagePart::Ptr();
70 }
71
72 const QString smimeType = node->contentType()->parameter(QStringLiteral("smime-type")).toLower();
73
74 if (smimeType == QLatin1String("certs-only")) {
75 part.processResult()->setNeverDisplayInline(true);
76
77 CertMessagePart::Ptr mp(new CertMessagePart(part.objectTreeParser(), node, smimeCrypto, part.source()->autoImportKeys()));
78 return mp;
79 }
80
81 bool isSigned = (smimeType == QLatin1String("signed-data"));
82 bool isEncrypted = (smimeType == QLatin1String("enveloped-data"));
83
84 // Analyze "signTestNode" node to find/verify a signature.
85 // If zero part.objectTreeParser() verification was successfully done after
86 // decrypting via recursion by insertAndParseNewChildNode().
87 KMime::Content *signTestNode = isEncrypted ? nullptr : node;
88
89 // We try decrypting the content
90 // if we either *know* that it is an encrypted message part
91 // or there is neither signed nor encrypted parameter.
92 MessagePart::Ptr mp;
93 if (!isSigned) {
94 if (isEncrypted) {
95 qCDebug(MIMETREEPARSER_LOG) << "pkcs7 mime == S/MIME TYPE: enveloped (encrypted) data";
96 } else {
97 qCDebug(MIMETREEPARSER_LOG) << "pkcs7 mime - type unknown - enveloped (encrypted) data ?";
98 }
99
100 auto _mp = EncryptedMessagePart::Ptr(new EncryptedMessagePart(part.objectTreeParser(),
101 node->decodedText(), smimeCrypto,
102 part.nodeHelper()->fromAsString(node), node));
103 mp = _mp;
104 _mp->setIsEncrypted(true);
105 _mp->setDecryptMessage(part.source()->decryptMessage());
106 PartMetaData *messagePart(_mp->partMetaData());
107 if (!part.source()->decryptMessage()) {
108 isEncrypted = true;
109 signTestNode = nullptr; // PENDING(marc) to be abs. sure, we'd need to have to look at the content
110 } else {
111 _mp->startDecryption();
112 if (messagePart->isDecryptable) {
113 qCDebug(MIMETREEPARSER_LOG) << "pkcs7 mime - encryption found - enveloped (encrypted) data !";
114 isEncrypted = true;
115 part.nodeHelper()->setEncryptionState(node, KMMsgFullyEncrypted);
116 signTestNode = nullptr;
117
118 } else {
119 // decryption failed, which could be because the part was encrypted but
120 // decryption failed, or because we didn't know if it was encrypted, tried,
121 // and failed. If the message was not actually encrypted, we continue
122 // assuming it's signed
123 if (_mp->passphraseError() || (smimeType.isEmpty() && messagePart->isEncrypted)) {
124 isEncrypted = true;
125 signTestNode = nullptr;
126 }
127
128 if (isEncrypted) {
129 qCDebug(MIMETREEPARSER_LOG) << "pkcs7 mime - ERROR: COULD NOT DECRYPT enveloped data !";
130 } else {
131 qCDebug(MIMETREEPARSER_LOG) << "pkcs7 mime - NO encryption found";
132 }
133 }
134 }
135
136 if (isEncrypted) {
137 part.nodeHelper()->setEncryptionState(node, KMMsgFullyEncrypted);
138 }
139 }
140
141 // We now try signature verification if necessarry.
142 if (signTestNode) {
143 if (isSigned) {
144 qCDebug(MIMETREEPARSER_LOG) << "pkcs7 mime == S/MIME TYPE: opaque signed data";
145 } else {
146 qCDebug(MIMETREEPARSER_LOG) << "pkcs7 mime - type unknown - opaque signed data ?";
147 }
148
149 const QTextCodec *aCodec(part.objectTreeParser()->codecFor(signTestNode));
150 const QByteArray signaturetext = signTestNode->decodedContent();
151 auto _mp = SignedMessagePart::Ptr(new SignedMessagePart(part.objectTreeParser(),
152 aCodec->toUnicode(signaturetext), smimeCrypto,
153 part.nodeHelper()->fromAsString(node), signTestNode));
154 mp = _mp;
155 //mp->setDecryptMessage(part.source()->decryptMessage());
156 PartMetaData *messagePart(mp->partMetaData());
157 if (smimeCrypto) {
158 _mp->startVerificationDetached(signaturetext, nullptr, QByteArray());
159 } else {
160 messagePart->auditLogError = GpgME::Error(GPG_ERR_NOT_IMPLEMENTED);
161 }
162
163 if (_mp->isSigned()) {
164 if (!isSigned) {
165 qCDebug(MIMETREEPARSER_LOG) << "pkcs7 mime - signature found - opaque signed data !";
166 isSigned = true;
167 }
168
169 if (signTestNode != node) {
170 part.nodeHelper()->setSignatureState(node, KMMsgFullySigned);
171 }
172 } else {
173 qCDebug(MIMETREEPARSER_LOG) << "pkcs7 mime - NO signature found :-(";
174 }
175 }
176
177 return mp;
178}
diff --git a/framework/src/domain/mime/mimetreeparser/otp/applicationpkcs7mime.h b/framework/src/domain/mime/mimetreeparser/otp/applicationpkcs7mime.h
new file mode 100644
index 00000000..a9a33f7c
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/applicationpkcs7mime.h
@@ -0,0 +1,41 @@
1/*
2 Copyright (c) 2016 Sandro Knauß <sknauss@kde.org>
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#ifndef __MIMETREEPARSER_BODYFORAMATTER_APPLICATIONPKCS7MIME_H__
21#define __MIMETREEPARSER_BODYFORAMATTER_APPLICATIONPKCS7MIME_H__
22
23#include "bodypartformatter.h"
24#include "bodypart.h"
25
26namespace MimeTreeParser
27{
28
29class ApplicationPkcs7MimeBodyPartFormatter : public Interface::BodyPartFormatter
30{
31 static const ApplicationPkcs7MimeBodyPartFormatter *self;
32public:
33 Interface::MessagePart::Ptr process(Interface::BodyPart &part) const Q_DECL_OVERRIDE;
34 Interface::BodyPartFormatter::Result format(Interface::BodyPart *, HtmlWriter *) const Q_DECL_OVERRIDE;
35 using Interface::BodyPartFormatter::format;
36 static const Interface::BodyPartFormatter *create();
37};
38
39}
40
41#endif
diff --git a/framework/src/domain/mime/mimetreeparser/otp/attachmentstrategy.cpp b/framework/src/domain/mime/mimetreeparser/otp/attachmentstrategy.cpp
new file mode 100644
index 00000000..5ea21133
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/attachmentstrategy.cpp
@@ -0,0 +1,343 @@
1/* -*- c++ -*-
2 attachmentstrategy.cpp
3
4 This file is part of KMail, the KDE mail client.
5 Copyright (c) 2003 Marc Mutz <mutz@kde.org>
6 Copyright (C) 2009 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net
7 Copyright (c) 2009 Andras Mantia <andras@kdab.net>
8
9 KMail is free software; you can redistribute it and/or modify it
10 under the terms of the GNU General Public License, version 2, as
11 published by the Free Software Foundation.
12
13 KMail is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21
22 In addition, as a special exception, the copyright holders give
23 permission to link the code of this program with any edition of
24 the Qt library by Trolltech AS, Norway (or with modified versions
25 of Qt that use the same license as Qt), and distribute linked
26 combinations including the two. You must obey the GNU General
27 Public License in all respects for all of the code used other than
28 Qt. If you modify this file, you may extend this exception to
29 your version of the file, but you are not obligated to do so. If
30 you do not wish to do so, delete this exception statement from
31 your version.
32*/
33
34#include "attachmentstrategy.h"
35
36#include "nodehelper.h"
37#include "util.h"
38
39#include <KMime/Content>
40
41#include <QIcon>
42
43#include "mimetreeparser_debug.h"
44
45using namespace MimeTreeParser;
46
47static AttachmentStrategy::Display smartDisplay(KMime::Content *node)
48{
49 const auto cd = node->contentDisposition(false);
50
51 if (cd && cd->disposition() == KMime::Headers::CDinline)
52 // explict "inline" disposition:
53 {
54 return AttachmentStrategy::Inline;
55 }
56 if (cd && cd->disposition() == KMime::Headers::CDattachment)
57 // explicit "attachment" disposition:
58 {
59 return AttachmentStrategy::AsIcon;
60 }
61
62 const auto ct = node->contentType(false);
63 if (ct && ct->isText() && ct->name().trimmed().isEmpty() &&
64 (!cd || cd->filename().trimmed().isEmpty()))
65 // text/* w/o filename parameter:
66 {
67 return AttachmentStrategy::Inline;
68 }
69 return AttachmentStrategy::AsIcon;
70}
71
72//
73// IconicAttachmentStrategy:
74// show everything but the first text/plain body as icons
75//
76
77class IconicAttachmentStrategy : public AttachmentStrategy
78{
79 friend class AttachmentStrategy;
80protected:
81 IconicAttachmentStrategy() : AttachmentStrategy() {}
82 virtual ~IconicAttachmentStrategy() {}
83
84public:
85 const char *name() const Q_DECL_OVERRIDE
86 {
87 return "iconic";
88 }
89
90 bool inlineNestedMessages() const Q_DECL_OVERRIDE
91 {
92 return false;
93 }
94 Display defaultDisplay(KMime::Content *node) const Q_DECL_OVERRIDE
95 {
96 if (node->contentType()->isText() &&
97 node->contentDisposition()->filename().trimmed().isEmpty() &&
98 node->contentType()->name().trimmed().isEmpty())
99 // text/* w/o filename parameter:
100 {
101 return Inline;
102 }
103 return AsIcon;
104 }
105};
106
107//
108// SmartAttachmentStrategy:
109// in addition to Iconic, show all body parts
110// with content-disposition == "inline" and
111// all text parts without a filename or name parameter inline
112//
113
114class SmartAttachmentStrategy : public AttachmentStrategy
115{
116 friend class AttachmentStrategy;
117protected:
118 SmartAttachmentStrategy() : AttachmentStrategy() {}
119 virtual ~SmartAttachmentStrategy() {}
120
121public:
122 const char *name() const Q_DECL_OVERRIDE
123 {
124 return "smart";
125 }
126
127 bool inlineNestedMessages() const Q_DECL_OVERRIDE
128 {
129 return true;
130 }
131 Display defaultDisplay(KMime::Content *node) const Q_DECL_OVERRIDE
132 {
133 return smartDisplay(node);
134 }
135};
136
137//
138// InlinedAttachmentStrategy:
139// show everything possible inline
140//
141
142class InlinedAttachmentStrategy : public AttachmentStrategy
143{
144 friend class AttachmentStrategy;
145protected:
146 InlinedAttachmentStrategy() : AttachmentStrategy() {}
147 virtual ~InlinedAttachmentStrategy() {}
148
149public:
150 const char *name() const Q_DECL_OVERRIDE
151 {
152 return "inlined";
153 }
154
155 bool inlineNestedMessages() const Q_DECL_OVERRIDE
156 {
157 return true;
158 }
159 Display defaultDisplay(KMime::Content *) const Q_DECL_OVERRIDE
160 {
161 return Inline;
162 }
163};
164
165//
166// HiddenAttachmentStrategy
167// show nothing except the first text/plain body part _at all_
168//
169
170class HiddenAttachmentStrategy : public AttachmentStrategy
171{
172 friend class AttachmentStrategy;
173protected:
174 HiddenAttachmentStrategy() : AttachmentStrategy() {}
175 virtual ~HiddenAttachmentStrategy() {}
176
177public:
178 const char *name() const Q_DECL_OVERRIDE
179 {
180 return "hidden";
181 }
182
183 bool inlineNestedMessages() const Q_DECL_OVERRIDE
184 {
185 return false;
186 }
187 Display defaultDisplay(KMime::Content *node) const Q_DECL_OVERRIDE
188 {
189 if (node->contentType()->isText() &&
190 node->contentDisposition()->filename().trimmed().isEmpty() &&
191 node->contentType()->name().trimmed().isEmpty())
192 // text/* w/o filename parameter:
193 {
194 return Inline;
195 }
196 if (!node->parent()) {
197 return Inline;
198 }
199
200 if (node->parent() && node->parent()->contentType()->isMultipart() &&
201 node->parent()->contentType()->subType() == "related") {
202 return Inline;
203 }
204
205 return None;
206 }
207};
208
209class HeaderOnlyAttachmentStrategy : public AttachmentStrategy
210{
211 friend class AttachmentStrategy;
212protected:
213 HeaderOnlyAttachmentStrategy() : AttachmentStrategy() {}
214 virtual ~HeaderOnlyAttachmentStrategy() {}
215
216public:
217 const char *name() const Q_DECL_OVERRIDE
218 {
219 return "headerOnly";
220 }
221
222 bool inlineNestedMessages() const Q_DECL_OVERRIDE
223 {
224 return true;
225 }
226
227 Display defaultDisplay(KMime::Content *node) const Q_DECL_OVERRIDE
228 {
229 if (NodeHelper::isInEncapsulatedMessage(node)) {
230 return smartDisplay(node);
231 }
232
233 if (!Util::labelForContent(node).isEmpty() && QIcon::hasThemeIcon(Util::iconNameForContent(node)) && ! Util::isTypeBlacklisted(node)) {
234 return None;
235 }
236 return smartDisplay(node);
237 }
238
239 bool requiresAttachmentListInHeader() const Q_DECL_OVERRIDE
240 {
241 return true;
242 }
243};
244
245//
246// AttachmentStrategy abstract base:
247//
248
249AttachmentStrategy::AttachmentStrategy()
250{
251
252}
253
254AttachmentStrategy::~AttachmentStrategy()
255{
256
257}
258
259const AttachmentStrategy *AttachmentStrategy::create(Type type)
260{
261 switch (type) {
262 case Iconic: return iconic();
263 case Smart: return smart();
264 case Inlined: return inlined();
265 case Hidden: return hidden();
266 case HeaderOnly: return headerOnly();
267 }
268 qCCritical(MIMETREEPARSER_LOG) << "Unknown attachment startegy ( type =="
269 << (int)type << ") requested!";
270 return nullptr; // make compiler happy
271}
272
273const AttachmentStrategy *AttachmentStrategy::create(const QString &type)
274{
275 const QString lowerType = type.toLower();
276 if (lowerType == QLatin1String("iconic")) {
277 return iconic();
278 }
279 //if ( lowerType == "smart" ) return smart(); // not needed, see below
280 if (lowerType == QLatin1String("inlined")) {
281 return inlined();
282 }
283 if (lowerType == QLatin1String("hidden")) {
284 return hidden();
285 }
286 if (lowerType == QLatin1String("headeronly")) {
287 return headerOnly();
288 }
289 // don't kFatal here, b/c the strings are user-provided
290 // (KConfig), so fail gracefully to the default:
291 return smart();
292}
293
294static const AttachmentStrategy *iconicStrategy = nullptr;
295static const AttachmentStrategy *smartStrategy = nullptr;
296static const AttachmentStrategy *inlinedStrategy = nullptr;
297static const AttachmentStrategy *hiddenStrategy = nullptr;
298static const AttachmentStrategy *headerOnlyStrategy = nullptr;
299
300const AttachmentStrategy *AttachmentStrategy::iconic()
301{
302 if (!iconicStrategy) {
303 iconicStrategy = new IconicAttachmentStrategy();
304 }
305 return iconicStrategy;
306}
307
308const AttachmentStrategy *AttachmentStrategy::smart()
309{
310 if (!smartStrategy) {
311 smartStrategy = new SmartAttachmentStrategy();
312 }
313 return smartStrategy;
314}
315
316const AttachmentStrategy *AttachmentStrategy::inlined()
317{
318 if (!inlinedStrategy) {
319 inlinedStrategy = new InlinedAttachmentStrategy();
320 }
321 return inlinedStrategy;
322}
323
324const AttachmentStrategy *AttachmentStrategy::hidden()
325{
326 if (!hiddenStrategy) {
327 hiddenStrategy = new HiddenAttachmentStrategy();
328 }
329 return hiddenStrategy;
330}
331
332const AttachmentStrategy *AttachmentStrategy::headerOnly()
333{
334 if (!headerOnlyStrategy) {
335 headerOnlyStrategy = new HeaderOnlyAttachmentStrategy();
336 }
337 return headerOnlyStrategy;
338}
339
340bool AttachmentStrategy::requiresAttachmentListInHeader() const
341{
342 return false;
343}
diff --git a/framework/src/domain/mime/mimetreeparser/otp/attachmentstrategy.h b/framework/src/domain/mime/mimetreeparser/otp/attachmentstrategy.h
new file mode 100644
index 00000000..a0b5dc81
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/attachmentstrategy.h
@@ -0,0 +1,86 @@
1/* -*- c++ -*-
2 attachmentstrategy.h
3
4 This file is part of KMail, the KDE mail client.
5 Copyright (c) 2003 Marc Mutz <mutz@kde.org>
6 Copyright (C) 2009 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net
7 Copyright (c) 2009 Andras Mantia <andras@kdab.net>
8
9 KMail is free software; you can redistribute it and/or modify it
10 under the terms of the GNU General Public License, version 2, as
11 published by the Free Software Foundation.
12
13 KMail is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21
22 In addition, as a special exception, the copyright holders give
23 permission to link the code of this program with any edition of
24 the Qt library by Trolltech AS, Norway (or with modified versions
25 of Qt that use the same license as Qt), and distribute linked
26 combinations including the two. You must obey the GNU General
27 Public License in all respects for all of the code used other than
28 Qt. If you modify this file, you may extend this exception to
29 your version of the file, but you are not obligated to do so. If
30 you do not wish to do so, delete this exception statement from
31 your version.
32*/
33
34#ifndef __MIMETREEPARSER_ATTACHMENTSTRATEGY_H__
35#define __MIMETREEPARSER_ATTACHMENTSTRATEGY_H__
36
37class QString;
38namespace KMime
39{
40class Content;
41}
42
43namespace MimeTreeParser
44{
45
46class AttachmentStrategy
47{
48protected:
49 AttachmentStrategy();
50 virtual ~AttachmentStrategy();
51
52public:
53 //
54 // Factory methods:
55 //
56 enum Type { Iconic, Smart, Inlined, Hidden, HeaderOnly };
57
58 static const AttachmentStrategy *create(Type type);
59 static const AttachmentStrategy *create(const QString &type);
60
61 static const AttachmentStrategy *iconic();
62 static const AttachmentStrategy *smart();
63 static const AttachmentStrategy *inlined();
64 static const AttachmentStrategy *hidden();
65 static const AttachmentStrategy *headerOnly();
66
67 //
68 // Navigation methods:
69 //
70
71 virtual const char *name() const = 0;
72
73 //
74 // Bahavioural:
75 //
76
77 enum Display { None, AsIcon, Inline };
78
79 virtual bool inlineNestedMessages() const = 0;
80 virtual Display defaultDisplay(KMime::Content *node) const = 0;
81 virtual bool requiresAttachmentListInHeader() const;
82};
83
84}
85
86#endif // __MIMETREEPARSER_ATTACHMENTSTRATEGY_H__
diff --git a/framework/src/domain/mime/mimetreeparser/otp/attachmenttemporaryfilesdirs.cpp b/framework/src/domain/mime/mimetreeparser/otp/attachmenttemporaryfilesdirs.cpp
new file mode 100644
index 00000000..364bc422
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/attachmenttemporaryfilesdirs.cpp
@@ -0,0 +1,108 @@
1/*
2 Copyright (c) 2013-2017 Montel Laurent <montel@kde.org>
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
21#include "attachmenttemporaryfilesdirs.h"
22
23#include <QDir>
24#include <QFile>
25#include <QTimer>
26
27using namespace MimeTreeParser;
28
29class MimeTreeParser::AttachmentTemporaryFilesDirsPrivate
30{
31public:
32 AttachmentTemporaryFilesDirsPrivate()
33 : mDelayRemoveAll(10000)
34 {
35
36 }
37 QStringList mTempFiles;
38 QStringList mTempDirs;
39 int mDelayRemoveAll;
40};
41
42AttachmentTemporaryFilesDirs::AttachmentTemporaryFilesDirs(QObject *parent)
43 : QObject(parent),
44 d(new AttachmentTemporaryFilesDirsPrivate)
45{
46
47}
48
49AttachmentTemporaryFilesDirs::~AttachmentTemporaryFilesDirs()
50{
51 delete d;
52}
53
54void AttachmentTemporaryFilesDirs::setDelayRemoveAllInMs(int ms)
55{
56 d->mDelayRemoveAll = (ms < 0) ? 0 : ms;
57}
58
59void AttachmentTemporaryFilesDirs::removeTempFiles()
60{
61 QTimer::singleShot(d->mDelayRemoveAll, this, &AttachmentTemporaryFilesDirs::slotRemoveTempFiles);
62}
63
64void AttachmentTemporaryFilesDirs::forceCleanTempFiles()
65{
66 QStringList::ConstIterator end = d->mTempFiles.constEnd();
67 for (QStringList::ConstIterator it = d->mTempFiles.constBegin(); it != end; ++it) {
68 QFile::remove(*it);
69 }
70 d->mTempFiles.clear();
71 end = d->mTempDirs.constEnd();
72 for (QStringList::ConstIterator it = d->mTempDirs.constBegin(); it != end; ++it) {
73 QDir(*it).rmdir(*it);
74 }
75 d->mTempDirs.clear();
76}
77
78void AttachmentTemporaryFilesDirs::slotRemoveTempFiles()
79{
80 forceCleanTempFiles();
81 //Delete it after cleaning
82 deleteLater();
83}
84
85void AttachmentTemporaryFilesDirs::addTempFile(const QString &file)
86{
87 if (!d->mTempFiles.contains(file)) {
88 d->mTempFiles.append(file);
89 }
90}
91
92void AttachmentTemporaryFilesDirs::addTempDir(const QString &dir)
93{
94 if (!d->mTempDirs.contains(dir)) {
95 d->mTempDirs.append(dir);
96 }
97}
98
99QStringList AttachmentTemporaryFilesDirs::temporaryFiles() const
100{
101 return d->mTempFiles;
102}
103
104QStringList AttachmentTemporaryFilesDirs::temporaryDirs() const
105{
106 return d->mTempDirs;
107}
108
diff --git a/framework/src/domain/mime/mimetreeparser/otp/attachmenttemporaryfilesdirs.h b/framework/src/domain/mime/mimetreeparser/otp/attachmenttemporaryfilesdirs.h
new file mode 100644
index 00000000..bf65fcdb
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/attachmenttemporaryfilesdirs.h
@@ -0,0 +1,57 @@
1/*
2 Copyright (c) 2013-2016 Montel Laurent <montel@kde.org>
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
21#ifndef ATTACHMENTTEMPORARYFILESDIRS_H
22#define ATTACHMENTTEMPORARYFILESDIRS_H
23
24#include <QObject>
25#include <QStringList>
26
27namespace MimeTreeParser
28{
29class AttachmentTemporaryFilesDirsPrivate;
30
31class AttachmentTemporaryFilesDirs : public QObject
32{
33 Q_OBJECT
34public:
35 explicit AttachmentTemporaryFilesDirs(QObject *parent = nullptr);
36 ~AttachmentTemporaryFilesDirs();
37
38 void addTempFile(const QString &file);
39 void addTempDir(const QString &dir);
40 QStringList temporaryFiles() const;
41 void removeTempFiles();
42 void forceCleanTempFiles();
43
44 QStringList temporaryDirs() const;
45
46 void setDelayRemoveAllInMs(int ms);
47
48private Q_SLOTS:
49 void slotRemoveTempFiles();
50
51private:
52 AttachmentTemporaryFilesDirsPrivate *const d;
53};
54
55}
56
57#endif // ATTACHMENTTEMPORARYFILESDIRS_H
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/CMakeLists.txt b/framework/src/domain/mime/mimetreeparser/otp/autotests/CMakeLists.txt
new file mode 100644
index 00000000..789ee795
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/CMakeLists.txt
@@ -0,0 +1,46 @@
1include_directories(../)
2include_directories(${CMAKE_CURRENT_BINARY_DIR})
3
4set(AUTOMOC ON)
5
6set(EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR})
7add_definitions( -DMAIL_DATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/data" )
8
9include(${CMAKE_CURRENT_SOURCE_DIR}/kdepim_add_gpg_crypto_test.cmake)
10
11# convenience macro to add qtest unit tests
12macro(add_mimetreeparser_unittest _source)
13 get_filename_component(_name ${_source} NAME_WE)
14 ecm_add_test(${_source} util.cpp setupenv.cpp
15 TEST_NAME ${_name}
16 NAME_PREFIX "mimetreeparser-"
17 LINK_LIBRARIES kube_otp Qt5::Test KF5::Mime Gpgmepp
18 )
19endmacro ()
20
21macro(add_mimetreeparser_class_unittest _source _additionalSource)
22 get_filename_component(_name ${_source} NAME_WE)
23 ecm_add_test(${_source} ${_additionalSource}
24 TEST_NAME ${_name}
25 NAME_PREFIX "mimetreeparser-"
26 LINK_LIBRARIES kube_otp Qt5::Test KF5::Mime Gpgmepp
27 )
28endmacro ()
29
30macro(add_mimetreeparser_crypto_unittest _source)
31 set(_test ${_source} util.cpp)
32 get_filename_component(_name ${_source} NAME_WE)
33 add_executable( ${_name} ${_test} setupenv.cpp)
34 ecm_mark_as_test(mimetreeparser-${_name})
35 target_link_libraries( ${_name}
36 kube_otp
37 Qt5::Test
38 KF5::Mime
39 Gpgmepp
40 )
41 add_gpg_crypto_test(${_name} mimetreeparser-${_name})
42endmacro ()
43
44add_mimetreeparser_crypto_unittest(attachmenttest.cpp)
45add_mimetreeparser_unittest(nodehelpertest.cpp)
46add_mimetreeparser_class_unittest(cryptohelpertest.cpp "../cryptohelper.cpp")
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/attachmenttest.cpp b/framework/src/domain/mime/mimetreeparser/otp/autotests/attachmenttest.cpp
new file mode 100644
index 00000000..44e40a45
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/attachmenttest.cpp
@@ -0,0 +1,68 @@
1/*
2 Copyright (c) 2015 Volker Krause <vkrause@kde.org>
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#include "objecttreeparser.h"
20#include "util.h"
21
22#include "setupenv.h"
23
24#include <qtest.h>
25
26using namespace MimeTreeParser;
27
28class AttachmentTest : public QObject
29{
30 Q_OBJECT
31private Q_SLOTS:
32 void initTestCase();
33 void testEncryptedAttachment_data();
34 void testEncryptedAttachment();
35};
36
37QTEST_MAIN(AttachmentTest)
38
39void AttachmentTest::initTestCase()
40{
41 MimeTreeParser::Test::setupEnv();
42}
43
44void AttachmentTest::testEncryptedAttachment_data()
45{
46 QTest::addColumn<QString>("mbox");
47 QTest::newRow("encrypted") << "openpgp-encrypted-two-attachments.mbox";
48 QTest::newRow("signed") << "openpgp-signed-two-attachments.mbox";
49 QTest::newRow("signed+encrypted") << "openpgp-signed-encrypted-two-attachments.mbox";
50 QTest::newRow("encrypted+partial signed") << "openpgp-encrypted-partially-signed-attachments.mbox";
51}
52
53void AttachmentTest::testEncryptedAttachment()
54{
55 QFETCH(QString, mbox);
56 auto msg = readAndParseMail(mbox);
57 NodeHelper nodeHelper;
58 Test::TestObjectTreeSource testSource(nullptr);
59 testSource.setAllowDecryption(true);
60 ObjectTreeParser otp(&testSource, &nodeHelper);
61 otp.parseObjectTree(msg.data());
62
63 auto attachments = msg->attachments();
64 auto encAtts = nodeHelper.attachmentsOfExtraContents();
65 QCOMPARE(attachments.size() + encAtts.size(), 2);
66}
67
68#include "attachmenttest.moc"
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/cryptohelpertest.cpp b/framework/src/domain/mime/mimetreeparser/otp/autotests/cryptohelpertest.cpp
new file mode 100644
index 00000000..251e657d
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/cryptohelpertest.cpp
@@ -0,0 +1,144 @@
1/* Copyright 2015 Sandro Knauß <knauss@kolabsys.com>
2
3 This program is free software; you can redistribute it and/or
4 modify it under the terms of the GNU General Public License as
5 published by the Free Software Foundation; either version 2 of
6 the License or (at your option) version 3 or any later version
7 accepted by the membership of KDE e.V. (or its successor approved
8 by the membership of KDE e.V.), which shall act as a proxy
9 defined in Section 14 of version 3 of the license.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
18*/
19#include "cryptohelpertest.h"
20
21#include "cryptohelper.h"
22
23#include <QTest>
24
25using namespace MimeTreeParser;
26
27void CryptoHelperTest::testPMFDEmpty()
28{
29 QCOMPARE(prepareMessageForDecryption("").count(), 0);
30}
31
32void CryptoHelperTest::testPMFDWithNoPGPBlock()
33{
34 const QByteArray text = "testblabla";
35 const QList<Block> blocks = prepareMessageForDecryption(text);
36 QCOMPARE(blocks.count(), 1);
37 QCOMPARE(blocks[0].text(), text);
38 QCOMPARE(blocks[0].type(), NoPgpBlock);
39}
40
41void CryptoHelperTest::testPGPBlockType()
42{
43 const QString blockText = QStringLiteral("text");
44 const QString preString = QStringLiteral("before\n");
45 for (int i = 1; i <= PrivateKeyBlock; ++i) {
46 QString name;
47 switch (i) {
48 case PgpMessageBlock:
49 name = QStringLiteral("MESSAGE");
50 break;
51 case MultiPgpMessageBlock:
52 name = QStringLiteral("MESSAGE PART");
53 break;
54 case SignatureBlock:
55 name = QStringLiteral("SIGNATURE");
56 break;
57 case ClearsignedBlock:
58 name = QStringLiteral("SIGNED MESSAGE");
59 break;
60 case PublicKeyBlock:
61 name = QStringLiteral("PUBLIC KEY BLOCK");
62 break;
63 case PrivateKeyBlock:
64 name = QStringLiteral("PRIVATE KEY BLOCK");
65 break;
66 }
67 QString text = QLatin1String("-----BEGIN PGP ") + name + QLatin1String("\n") + blockText;
68 QList<Block> blocks = prepareMessageForDecryption(preString.toLatin1() + text.toLatin1());
69 QCOMPARE(blocks.count(), 1);
70 QCOMPARE(blocks[0].type(), UnknownBlock);
71
72 text += QLatin1String("\n-----END PGP ") + name + QLatin1String("\n");
73 blocks = prepareMessageForDecryption(preString.toLatin1() + text.toLatin1());
74 QCOMPARE(blocks.count(), 2);
75 QCOMPARE(blocks[1].text(), text.toLatin1());
76 QCOMPARE(blocks[1].type(), static_cast<PGPBlockType>(i));
77 }
78}
79
80void CryptoHelperTest::testDeterminePGPBlockType()
81{
82 const QString blockText = QStringLiteral("text");
83 for (int i = 1; i <= PrivateKeyBlock; ++i) {
84 QString name;
85 switch (i) {
86
87 case PgpMessageBlock:
88 name = QStringLiteral("MESSAGE");
89 break;
90 case MultiPgpMessageBlock:
91 name = QStringLiteral("MESSAGE PART");
92 break;
93 case SignatureBlock:
94 name = QStringLiteral("SIGNATURE");
95 break;
96 case ClearsignedBlock:
97 name = QStringLiteral("SIGNED MESSAGE");
98 break;
99 case PublicKeyBlock:
100 name = QStringLiteral("PUBLIC KEY BLOCK");
101 break;
102 case PrivateKeyBlock:
103 name = QStringLiteral("PRIVATE KEY BLOCK");
104 break;
105 }
106 const QString text = QLatin1String("-----BEGIN PGP ") + name + QLatin1String("\n") + blockText + QLatin1String("\n");
107 const Block block = Block(text.toLatin1());
108 QCOMPARE(block.text(), text.toLatin1());
109 QCOMPARE(block.type(), static_cast<PGPBlockType>(i));
110 }
111}
112
113void CryptoHelperTest::testEmbededPGPBlock()
114{
115 const QByteArray text = QByteArray("before\n-----BEGIN PGP MESSAGE-----\ncrypted - you see :)\n-----END PGP MESSAGE-----\nafter");
116 const QList<Block> blocks = prepareMessageForDecryption(text);
117 QCOMPARE(blocks.count(), 3);
118 QCOMPARE(blocks[0].text(), QByteArray("before\n"));
119 QCOMPARE(blocks[1].text(), QByteArray("-----BEGIN PGP MESSAGE-----\ncrypted - you see :)\n-----END PGP MESSAGE-----\n"));
120 QCOMPARE(blocks[2].text(), QByteArray("after"));
121}
122
123void CryptoHelperTest::testClearSignedMessage()
124{
125 const QByteArray text = QByteArray("before\n-----BEGIN PGP SIGNED MESSAGE-----\nsigned content\n-----BEGIN PGP SIGNATURE-----\nfancy signature\n-----END PGP SIGNATURE-----\nafter");
126 const QList<Block> blocks = prepareMessageForDecryption(text);
127 QCOMPARE(blocks.count(), 3);
128 QCOMPARE(blocks[0].text(), QByteArray("before\n"));
129 QCOMPARE(blocks[1].text(), QByteArray("-----BEGIN PGP SIGNED MESSAGE-----\nsigned content\n-----BEGIN PGP SIGNATURE-----\nfancy signature\n-----END PGP SIGNATURE-----\n"));
130 QCOMPARE(blocks[2].text(), QByteArray("after"));
131}
132
133void CryptoHelperTest::testMultipleBlockMessage()
134{
135 const QByteArray text = QByteArray("before\n-----BEGIN PGP SIGNED MESSAGE-----\nsigned content\n-----BEGIN PGP SIGNATURE-----\nfancy signature\n-----END PGP SIGNATURE-----\nafter\n-----BEGIN PGP MESSAGE-----\ncrypted - you see :)\n-----END PGP MESSAGE-----\n");
136 const QList<Block> blocks = prepareMessageForDecryption(text);
137 QCOMPARE(blocks.count(), 4);
138 QCOMPARE(blocks[0].text(), QByteArray("before\n"));
139 QCOMPARE(blocks[1].text(), QByteArray("-----BEGIN PGP SIGNED MESSAGE-----\nsigned content\n-----BEGIN PGP SIGNATURE-----\nfancy signature\n-----END PGP SIGNATURE-----\n"));
140 QCOMPARE(blocks[2].text(), QByteArray("after\n"));
141 QCOMPARE(blocks[3].text(), QByteArray("-----BEGIN PGP MESSAGE-----\ncrypted - you see :)\n-----END PGP MESSAGE-----\n"));
142}
143
144QTEST_APPLESS_MAIN(CryptoHelperTest)
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/cryptohelpertest.h b/framework/src/domain/mime/mimetreeparser/otp/autotests/cryptohelpertest.h
new file mode 100644
index 00000000..71ae086f
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/cryptohelpertest.h
@@ -0,0 +1,42 @@
1/* Copyright 2009 Thomas McGuire <mcguire@kde.org>
2
3 This program is free software; you can redistribute it and/or
4 modify it under the terms of the GNU General Public License as
5 published by the Free Software Foundation; either version 2 of
6 the License or (at your option) version 3 or any later version
7 accepted by the membership of KDE e.V. (or its successor approved
8 by the membership of KDE e.V.), which shall act as a proxy
9 defined in Section 14 of version 3 of the license.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
18*/
19#ifndef CRYPTOHELPERTEST_H
20#define CRYPTOHELPERTEST_H
21
22#include <QObject>
23
24namespace MimeTreeParser
25{
26
27class CryptoHelperTest : public QObject
28{
29 Q_OBJECT
30
31private Q_SLOTS:
32 void testPMFDEmpty();
33 void testPMFDWithNoPGPBlock();
34 void testPGPBlockType();
35 void testDeterminePGPBlockType();
36 void testEmbededPGPBlock();
37 void testClearSignedMessage();
38 void testMultipleBlockMessage();
39};
40
41}
42#endif
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/alternative-notext.mbox b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/alternative-notext.mbox
new file mode 100644
index 00000000..86026437
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/alternative-notext.mbox
@@ -0,0 +1,22 @@
1Return-Path: <konqi@example.org>
2Date: Wed, 8 Jun 2016 20:34:44 -0700
3From: Konqi <konqi@example.org>
4To: konqi@kde.org
5Subject: A random subject with a empty text alternative contenttype
6MIME-Version: 1.0
7Content-Type: multipart/alternative;
8 boundary="----=_Part_12345678_12345678"
9
10
11------=_Part_12345678_12345678
12Content-Transfer-Encoding: 7Bit
13Content-Type: text/html; charset="windows-1252"
14
15<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
16<html><head><meta name="qrichtext" content="1" /><style type="text/css">
17p, li { white-space: pre-wrap; }
18</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;">
19<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; -qt-user-state:0;">Some <span style=" font-weight:600;">HTML</span> text</p></body></html>
20
21
22------=_Part_12345678_12345678--
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/alternative-notext.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/alternative-notext.mbox.html
new file mode 100644
index 00000000..41db4eab
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/alternative-notext.mbox.html
@@ -0,0 +1,17 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <a name="att1"/>
9 <div id="attachmentDiv1">
10 <div style="position: relative">
11 <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; -qt-user-state:0;">Some <span style=" font-weight:600;">HTML</span> text</p>
12 </div>
13 </div>
14 </div>
15 </div>
16 </body>
17</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/alternative-notext.mbox.tree b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/alternative-notext.mbox.tree
new file mode 100644
index 00000000..0de07281
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/alternative-notext.mbox.tree
@@ -0,0 +1,2 @@
1 * MimeTreeParser::MessagePartList
2 * MimeTreeParser::AlternativeMessagePart
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/alternative.mbox b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/alternative.mbox
new file mode 100644
index 00000000..a2c58591
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/alternative.mbox
@@ -0,0 +1,34 @@
1Return-Path: <konqi@example.org>
2Date: Wed, 8 Jun 2016 20:34:44 -0700
3From: Konqi <konqi@example.org>
4To: konqi@kde.org
5Subject: A random subject with alternative contenttype
6MIME-Version: 1.0
7Content-Type: multipart/alternative;
8 boundary="----=_Part_12345678_12345678"
9
10
11------=_Part_12345678_12345678
12Content-Type: text/plain; charset=utf-8
13Content-Transfer-Encoding: quoted-printable
14
15If you can see this text it means that your email client couldn't display o=
16ur newsletter properly.
17Please visit this link to view the newsletter on our website: http://www.go=
18g.com/newsletter/
19
20=2D GOG.com Team
21
22
23------=_Part_12345678_12345678
24Content-Transfer-Encoding: 7Bit
25Content-Type: text/html; charset="windows-1252"
26
27<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
28<html><head><meta name="qrichtext" content="1" /><style type="text/css">
29p, li { white-space: pre-wrap; }
30</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;">
31<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; -qt-user-state:0;">Some <span style=" font-weight:600;">HTML</span> text</p></body></html>
32
33
34------=_Part_12345678_12345678--
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/alternative.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/alternative.mbox.html
new file mode 100644
index 00000000..2fe886f1
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/alternative.mbox.html
@@ -0,0 +1,17 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <a name="att2"/>
9 <div id="attachmentDiv2">
10 <div style="position: relative">
11 <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; -qt-user-state:0;">Some <span style=" font-weight:600;">HTML</span> text</p>
12 </div>
13 </div>
14 </div>
15 </div>
16 </body>
17</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/alternative.mbox.tree b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/alternative.mbox.tree
new file mode 100644
index 00000000..0de07281
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/alternative.mbox.tree
@@ -0,0 +1,2 @@
1 * MimeTreeParser::MessagePartList
2 * MimeTreeParser::AlternativeMessagePart
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/forward-openpgp-signed-encrypted.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/forward-openpgp-signed-encrypted.mbox.html
new file mode 100644
index 00000000..9a81f103
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/forward-openpgp-signed-encrypted.mbox.html
@@ -0,0 +1,84 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <a name="att1"/>
9 <div id="attachmentDiv1">
10 <div class="noquote">
11 <div dir="ltr">bla bla bla</div>
12 </div>
13 </div>
14 <a name="att2"/>
15 <div id="attachmentDiv2">
16 <table cellspacing="1" cellpadding="1" class="rfc822">
17 <tr class="rfc822H">
18 <td dir="ltr">
19 <a href="attachment:2.1?place=body">Encapsulated message</a>
20 </td>
21 </tr>
22 <tr class="rfc822B">
23 <td>
24 <a name="att2.1"/>
25 <div id="attachmentDiv2.1">
26 <table cellspacing="1" cellpadding="1" class="encr">
27 <tr class="encrH">
28 <td dir="ltr">Encrypted message</td>
29 </tr>
30 <tr class="encrB">
31 <td>
32 <div style="position: relative; word-wrap: break-word">
33 <a name="att"/>
34 <div id="attachmentDiv">
35 <table cellspacing="1" cellpadding="1" class="signOkKeyOk">
36 <tr class="signOkKeyOkH">
37 <td dir="ltr">
38 <table cellspacing="0" cellpadding="0" width="100%">
39 <tr>
40 <td rowspan="2">Message was signed by <a href="mailto:test@kolab.org">test@kolab.org</a> (Key ID: <a href="kmail:showCertificate#gpg ### OpenPGP ### 8D9860C58F246DE6">0x8D9860C58F246DE6</a>).<br/>The signature is valid and the key is fully trusted.</td>
41 <td align="right" valign="top" nowrap="nowrap">
42 <a href="kmail:hideSignatureDetails">Hide Details</a>
43 </td>
44 </tr>
45 <tr>
46 <td align="right" valign="bottom" nowrap="nowrap"/>
47 </tr>
48 </table>
49 </td>
50 </tr>
51 <tr class="signOkKeyOkB">
52 <td>
53 <a name="att1"/>
54 <div id="attachmentDiv1">
55 <div class="noquote">
56 <div dir="ltr">encrypted message text</div>
57 </div>
58 </div>
59 </td>
60 </tr>
61 <tr class="signOkKeyOkH">
62 <td dir="ltr">End of signed message</td>
63 </tr>
64 </table>
65 </div>
66 </div>
67 </td>
68 </tr>
69 <tr class="encrH">
70 <td dir="ltr">End of encrypted message</td>
71 </tr>
72 </table>
73 </div>
74 </td>
75 </tr>
76 <tr class="rfc822H">
77 <td dir="ltr">End of encapsulated message</td>
78 </tr>
79 </table>
80 </div>
81 </div>
82 </div>
83 </body>
84</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/openpgp-encoded.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/openpgp-encoded.mbox.html
new file mode 100644
index 00000000..65d5f95e
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/openpgp-encoded.mbox.html
@@ -0,0 +1,38 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <table cellspacing="1" cellpadding="1" class="signWarn">
9 <tr class="signWarnH">
10 <td dir="ltr">
11 <table cellspacing="0" cellpadding="0" width="100%">
12 <tr>
13 <td rowspan="2">Message was signed on 12/21/15 3:46 PM with unknown key <a href="kmail:showCertificate#gpg ### OpenPGP ### E68031D299A6527C">0xE68031D299A6527C</a>.<br/>The validity of the signature cannot be verified.<br/>Status:<i>No public key to verify the signature</i></td>
14 <td align="right" valign="top" nowrap="nowrap">
15 <a href="kmail:hideSignatureDetails">Hide Details</a>
16 </td>
17 </tr>
18 <tr>
19 <td align="right" valign="bottom" nowrap="nowrap"/>
20 </tr>
21 </table>
22 </td>
23 </tr>
24 <tr class="signWarnB">
25 <td>
26 <div class="noquote">
27 <div dir="ltr">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Donec et mollis dolor. Praesent et diam eget libero egestas mattis sit amet vitae augue. Nam tincidunt congue enim, ut porta lorem lacinia consectetur. Donec ut libero sed arcu vehicula ultricies a non tortor. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean ut gravida lorem. Ut turpis felis, pulvinar a semper sed, adipiscing id dolor. Pellentesque auctor nisi id magna consequat sagittis. Curabitur dapibus enim sit amet elit pharetra tincidunt feugiat nisl imperdiet. Ut convallis libero in urna ultrices accumsan. Donec sed odio eros. Donec viverra mi quis quam pulvinar at malesuada arcu rhoncus. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. In rutrum accumsan ultricies. Mauris vitae nisi at sem facilisis semper ac in est.</div>
28 </div>
29 </td>
30 </tr>
31 <tr class="signWarnH">
32 <td dir="ltr">End of signed message</td>
33 </tr>
34 </table>
35 </div>
36 </div>
37 </body>
38</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/openpgp-encrypted+signed.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/openpgp-encrypted+signed.mbox.html
new file mode 100644
index 00000000..96361c30
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/openpgp-encrypted+signed.mbox.html
@@ -0,0 +1,55 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <table cellspacing="1" cellpadding="1" class="encr">
9 <tr class="encrH">
10 <td dir="ltr">Encrypted message</td>
11 </tr>
12 <tr class="encrB">
13 <td>
14 <table cellspacing="1" cellpadding="1" class="signOkKeyOk">
15 <tr class="signOkKeyOkH">
16 <td dir="ltr">
17 <table cellspacing="0" cellpadding="0" width="100%">
18 <tr>
19 <td rowspan="2">Message was signed by <a href="mailto:test@kolab.org">test@kolab.org</a> (Key ID: <a href="kmail:showCertificate#gpg ### OpenPGP ### 8D9860C58F246DE6">0x8D9860C58F246DE6</a>).<br/>The signature is valid and the key is fully trusted.</td>
20 <td align="right" valign="top" nowrap="nowrap">
21 <a href="kmail:hideSignatureDetails">Hide Details</a>
22 </td>
23 </tr>
24 <tr>
25 <td align="right" valign="bottom" nowrap="nowrap"/>
26 </tr>
27 </table>
28 </td>
29 </tr>
30 <tr class="signOkKeyOkB">
31 <td>
32 <div style="position: relative; word-wrap: break-word">
33 <a name="att"/>
34 <div id="attachmentDiv">
35 <div class="noquote">
36 <div dir="ltr">encrypted message text</div>
37 </div>
38 </div>
39 </div>
40 </td>
41 </tr>
42 <tr class="signOkKeyOkH">
43 <td dir="ltr">End of signed message</td>
44 </tr>
45 </table>
46 </td>
47 </tr>
48 <tr class="encrH">
49 <td dir="ltr">End of encrypted message</td>
50 </tr>
51 </table>
52 </div>
53 </div>
54 </body>
55</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/openpgp-encrypted-attachment-and-non-encrypted-attachment.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/openpgp-encrypted-attachment-and-non-encrypted-attachment.mbox.html
new file mode 100644
index 00000000..cc6bf03e
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/openpgp-encrypted-attachment-and-non-encrypted-attachment.mbox.html
@@ -0,0 +1,80 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <a name="att1"/>
9 <div id="attachmentDiv1">
10 <table cellspacing="1" cellpadding="1" class="encr">
11 <tr class="encrH">
12 <td dir="ltr">Encrypted message</td>
13 </tr>
14 <tr class="encrB">
15 <td>
16 <div style="position: relative; word-wrap: break-word">
17 <a name="att"/>
18 <div id="attachmentDiv">
19 <table cellspacing="1" cellpadding="1" class="signOkKeyOk">
20 <tr class="signOkKeyOkH">
21 <td dir="ltr">
22 <table cellspacing="0" cellpadding="0" width="100%">
23 <tr>
24 <td rowspan="2">Message was signed by <a href="mailto:test@kolab.org">test@kolab.org</a> (Key ID: <a href="kmail:showCertificate#gpg ### OpenPGP ### 8D9860C58F246DE6">0x8D9860C58F246DE6</a>).<br/>The signature is valid and the key is fully trusted.</td>
25 <td align="right" valign="top" nowrap="nowrap">
26 <a href="kmail:hideSignatureDetails">Hide Details</a>
27 </td>
28 </tr>
29 <tr>
30 <td align="right" valign="bottom" nowrap="nowrap"/>
31 </tr>
32 </table>
33 </td>
34 </tr>
35 <tr class="signOkKeyOkB">
36 <td>
37 <a name="att1"/>
38 <div id="attachmentDiv1">
39 <a name="att1.1"/>
40 <div id="attachmentDiv1.1">
41 <div class="noquote">
42 <div dir="ltr">test text</div>
43 </div>
44 </div>
45 <a name="att1.2"/>
46 <div id="attachmentDiv1.2">
47 <hr/>
48 <div>
49 <a href="attachment:1:e0:1.2?place=body"><img align="center" height="48" width="48" src="file:text-plain.svg" border="0" style="max-width: 100%" alt=""/>file.txt</a>
50 </div>
51 <div/>
52 </div>
53 </div>
54 </td>
55 </tr>
56 <tr class="signOkKeyOkH">
57 <td dir="ltr">End of signed message</td>
58 </tr>
59 </table>
60 </div>
61 </div>
62 </td>
63 </tr>
64 <tr class="encrH">
65 <td dir="ltr">End of encrypted message</td>
66 </tr>
67 </table>
68 </div>
69 <a name="att2"/>
70 <div id="attachmentDiv2">
71 <hr/>
72 <div>
73 <a href="attachment:2?place=body"><img align="center" height="48" width="48" src="file:image-png.svg" border="0" style="max-width: 100%" alt=""/>image.png</a>
74 </div>
75 <div/>
76 </div>
77 </div>
78 </div>
79 </body>
80</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/openpgp-encrypted-attachment.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/openpgp-encrypted-attachment.mbox.html
new file mode 100644
index 00000000..61bf5d28
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/openpgp-encrypted-attachment.mbox.html
@@ -0,0 +1,69 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <table cellspacing="1" cellpadding="1" class="encr">
9 <tr class="encrH">
10 <td dir="ltr">Encrypted message</td>
11 </tr>
12 <tr class="encrB">
13 <td>
14 <div style="position: relative; word-wrap: break-word">
15 <a name="att"/>
16 <div id="attachmentDiv">
17 <table cellspacing="1" cellpadding="1" class="signOkKeyOk">
18 <tr class="signOkKeyOkH">
19 <td dir="ltr">
20 <table cellspacing="0" cellpadding="0" width="100%">
21 <tr>
22 <td rowspan="2">Message was signed by <a href="mailto:test@kolab.org">test@kolab.org</a> (Key ID: <a href="kmail:showCertificate#gpg ### OpenPGP ### 8D9860C58F246DE6">0x8D9860C58F246DE6</a>).<br/>The signature is valid and the key is fully trusted.</td>
23 <td align="right" valign="top" nowrap="nowrap">
24 <a href="kmail:hideSignatureDetails">Hide Details</a>
25 </td>
26 </tr>
27 <tr>
28 <td align="right" valign="bottom" nowrap="nowrap"/>
29 </tr>
30 </table>
31 </td>
32 </tr>
33 <tr class="signOkKeyOkB">
34 <td>
35 <a name="att1"/>
36 <div id="attachmentDiv1">
37 <a name="att1.1"/>
38 <div id="attachmentDiv1.1">
39 <div class="noquote">
40 <div dir="ltr">test text</div>
41 </div>
42 </div>
43 <a name="att1.2"/>
44 <div id="attachmentDiv1.2">
45 <hr/>
46 <div>
47 <a href="attachment:e0:1.2?place=body"><img align="center" height="48" width="48" src="file:text-plain.svg" border="0" style="max-width: 100%" alt=""/>file.txt</a>
48 </div>
49 <div/>
50 </div>
51 </div>
52 </td>
53 </tr>
54 <tr class="signOkKeyOkH">
55 <td dir="ltr">End of signed message</td>
56 </tr>
57 </table>
58 </div>
59 </div>
60 </td>
61 </tr>
62 <tr class="encrH">
63 <td dir="ltr">End of encrypted message</td>
64 </tr>
65 </table>
66 </div>
67 </div>
68 </body>
69</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/openpgp-encrypted-non-encrypted-attachment.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/openpgp-encrypted-non-encrypted-attachment.mbox.html
new file mode 100644
index 00000000..8d8bde0d
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/openpgp-encrypted-non-encrypted-attachment.mbox.html
@@ -0,0 +1,72 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <a name="att1"/>
9 <div id="attachmentDiv1">
10 <table cellspacing="1" cellpadding="1" class="encr">
11 <tr class="encrH">
12 <td dir="ltr">Encrypted message</td>
13 </tr>
14 <tr class="encrB">
15 <td>
16 <div style="position: relative; word-wrap: break-word">
17 <a name="att"/>
18 <div id="attachmentDiv">
19 <table cellspacing="1" cellpadding="1" class="signOkKeyOk">
20 <tr class="signOkKeyOkH">
21 <td dir="ltr">
22 <table cellspacing="0" cellpadding="0" width="100%">
23 <tr>
24 <td rowspan="2">Message was signed by <a href="mailto:test@kolab.org">test@kolab.org</a> (Key ID: <a href="kmail:showCertificate#gpg ### OpenPGP ### 8D9860C58F246DE6">0x8D9860C58F246DE6</a>).<br/>The signature is valid and the key is fully trusted.</td>
25 <td align="right" valign="top" nowrap="nowrap">
26 <a href="kmail:hideSignatureDetails">Hide Details</a>
27 </td>
28 </tr>
29 <tr>
30 <td align="right" valign="bottom" nowrap="nowrap"/>
31 </tr>
32 </table>
33 </td>
34 </tr>
35 <tr class="signOkKeyOkB">
36 <td>
37 <a name="att1"/>
38 <div id="attachmentDiv1">
39 <a name="att1.1"/>
40 <div id="attachmentDiv1.1">
41 <div class="noquote">
42 <div dir="ltr">test text</div>
43 </div>
44 </div>
45 </div>
46 </td>
47 </tr>
48 <tr class="signOkKeyOkH">
49 <td dir="ltr">End of signed message</td>
50 </tr>
51 </table>
52 </div>
53 </div>
54 </td>
55 </tr>
56 <tr class="encrH">
57 <td dir="ltr">End of encrypted message</td>
58 </tr>
59 </table>
60 </div>
61 <a name="att2"/>
62 <div id="attachmentDiv2">
63 <hr/>
64 <div>
65 <a href="attachment:2?place=body"><img align="center" height="48" width="48" src="file:image-png.svg" border="0" style="max-width: 100%" alt=""/>image.png</a>
66 </div>
67 <div/>
68 </div>
69 </div>
70 </div>
71 </body>
72</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/openpgp-encrypted-partially-signed-attachments.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/openpgp-encrypted-partially-signed-attachments.mbox.html
new file mode 100644
index 00000000..1716b841
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/openpgp-encrypted-partially-signed-attachments.mbox.html
@@ -0,0 +1,102 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <a name="att1"/>
9 <div id="attachmentDiv1">
10 <table cellspacing="1" cellpadding="1" class="encr">
11 <tr class="encrH">
12 <td dir="ltr">Encrypted message</td>
13 </tr>
14 <tr class="encrB">
15 <td>
16 <div style="position: relative; word-wrap: break-word">
17 <a name="att"/>
18 <div id="attachmentDiv">
19 <table cellspacing="1" cellpadding="1" class="signOkKeyOk">
20 <tr class="signOkKeyOkH">
21 <td dir="ltr">
22 <table cellspacing="0" cellpadding="0" width="100%">
23 <tr>
24 <td rowspan="2">Message was signed by <a href="mailto:test@kolab.org">test@kolab.org</a> (Key ID: <a href="kmail:showCertificate#gpg ### OpenPGP ### 8D9860C58F246DE6">0x8D9860C58F246DE6</a>).<br/>The signature is valid and the key is fully trusted.</td>
25 <td align="right" valign="top" nowrap="nowrap">
26 <a href="kmail:hideSignatureDetails">Hide Details</a>
27 </td>
28 </tr>
29 <tr>
30 <td align="right" valign="bottom" nowrap="nowrap"/>
31 </tr>
32 </table>
33 </td>
34 </tr>
35 <tr class="signOkKeyOkB">
36 <td>
37 <a name="att1"/>
38 <div id="attachmentDiv1">
39 <a name="att1.1"/>
40 <div id="attachmentDiv1.1">
41 <div class="noquote">
42 <div dir="ltr">This is the main body.</div>
43 </div>
44 </div>
45 <a name="att1.2"/>
46 <div id="attachmentDiv1.2">
47 <table cellspacing="1" class="textAtm">
48 <tr class="textAtmH">
49 <td dir="ltr">attachment1.txt</td>
50 </tr>
51 <tr class="textAtmB">
52 <td>
53 <div class="noquote">
54 <div dir="ltr">This is a signed attachment.</div>
55 </div>
56 </td>
57 </tr>
58 </table>
59 </div>
60 </div>
61 </td>
62 </tr>
63 <tr class="signOkKeyOkH">
64 <td dir="ltr">End of signed message</td>
65 </tr>
66 </table>
67 </div>
68 </div>
69 </td>
70 </tr>
71 <tr class="encrH">
72 <td dir="ltr">End of encrypted message</td>
73 </tr>
74 </table>
75 </div>
76 <a name="att2"/>
77 <div id="attachmentDiv2">
78 <table cellspacing="1" cellpadding="1" class="encr">
79 <tr class="encrH">
80 <td dir="ltr">Encrypted message</td>
81 </tr>
82 <tr class="encrB">
83 <td>
84 <div style="position: relative; word-wrap: break-word">
85 <a name="att"/>
86 <div id="attachmentDiv">
87 <div class="noquote">
88 <div dir="ltr">This is an unsigned attachment.</div>
89 </div>
90 </div>
91 </div>
92 </td>
93 </tr>
94 <tr class="encrH">
95 <td dir="ltr">End of encrypted message</td>
96 </tr>
97 </table>
98 </div>
99 </div>
100 </div>
101 </body>
102</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/openpgp-inline-charset-encrypted.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/openpgp-inline-charset-encrypted.mbox.html
new file mode 100644
index 00000000..c2fa2fee
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/openpgp-inline-charset-encrypted.mbox.html
@@ -0,0 +1,50 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <table cellspacing="1" cellpadding="1" class="encr">
9 <tr class="encrH">
10 <td dir="ltr">Encrypted message</td>
11 </tr>
12 <tr class="encrB">
13 <td>
14 <table cellspacing="1" cellpadding="1" class="signOkKeyOk">
15 <tr class="signOkKeyOkH">
16 <td dir="ltr">
17 <table cellspacing="0" cellpadding="0" width="100%">
18 <tr>
19 <td rowspan="2">Message was signed by <a href="mailto:test@kolab.org">test@kolab.org</a> (Key ID: <a href="kmail:showCertificate#gpg ### OpenPGP ### 8D9860C58F246DE6">0x8D9860C58F246DE6</a>).<br/>The signature is valid and the key is fully trusted.</td>
20 <td align="right" valign="top" nowrap="nowrap">
21 <a href="kmail:hideSignatureDetails">Hide Details</a>
22 </td>
23 </tr>
24 <tr>
25 <td align="right" valign="bottom" nowrap="nowrap"/>
26 </tr>
27 </table>
28 </td>
29 </tr>
30 <tr class="signOkKeyOkB">
31 <td>
32 <div class="noquote">
33 <div dir="ltr">asdasd asd asd asdf sadf sdaf sadf öäü</div>
34 </div>
35 </td>
36 </tr>
37 <tr class="signOkKeyOkH">
38 <td dir="ltr">End of signed message</td>
39 </tr>
40 </table>
41 </td>
42 </tr>
43 <tr class="encrH">
44 <td dir="ltr">End of encrypted message</td>
45 </tr>
46 </table>
47 </div>
48 </div>
49 </body>
50</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/openpgp-inline-signed.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/openpgp-inline-signed.mbox.html
new file mode 100644
index 00000000..00a9dc5b
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/openpgp-inline-signed.mbox.html
@@ -0,0 +1,38 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <table cellspacing="1" cellpadding="1" class="signOkKeyOk">
9 <tr class="signOkKeyOkH">
10 <td dir="ltr">
11 <table cellspacing="0" cellpadding="0" width="100%">
12 <tr>
13 <td rowspan="2">Message was signed by <a href="mailto:test@kolab.org">test@kolab.org</a> (Key ID: <a href="kmail:showCertificate#gpg ### OpenPGP ### 8D9860C58F246DE6">0x8D9860C58F246DE6</a>).<br/>The signature is valid and the key is fully trusted.</td>
14 <td align="right" valign="top" nowrap="nowrap">
15 <a href="kmail:hideSignatureDetails">Hide Details</a>
16 </td>
17 </tr>
18 <tr>
19 <td align="right" valign="bottom" nowrap="nowrap"/>
20 </tr>
21 </table>
22 </td>
23 </tr>
24 <tr class="signOkKeyOkB">
25 <td>
26 <div class="noquote">
27 <div dir="ltr">ohno öäü</div>
28 </div>
29 </td>
30 </tr>
31 <tr class="signOkKeyOkH">
32 <td dir="ltr">End of signed message</td>
33 </tr>
34 </table>
35 </div>
36 </div>
37 </body>
38</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/openpgp-signed-base64-mailman-footer.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/openpgp-signed-base64-mailman-footer.mbox.html
new file mode 100644
index 00000000..c10cecd8
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/openpgp-signed-base64-mailman-footer.mbox.html
@@ -0,0 +1,81 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <a name="att1"/>
9 <div id="attachmentDiv1">
10 <table cellspacing="1" cellpadding="1" class="signWarn">
11 <tr class="signWarnH">
12 <td dir="ltr">
13 <table cellspacing="0" cellpadding="0" width="100%">
14 <tr>
15 <td rowspan="2">Message was signed on 8/22/15 9:31 AM with unknown key <a href="kmail:showCertificate#gpg ### OpenPGP ### 7F96CCD64D12D247">0x7F96CCD64D12D247</a>.<br/>The validity of the signature cannot be verified.<br/>Status:<i>No public key to verify the signature</i></td>
16 <td align="right" valign="top" nowrap="nowrap">
17 <a href="kmail:hideSignatureDetails">Hide Details</a>
18 </td>
19 </tr>
20 <tr>
21 <td align="right" valign="bottom" nowrap="nowrap"/>
22 </tr>
23 </table>
24 </td>
25 </tr>
26 <tr class="signWarnB">
27 <td>
28 <a name="att1.1"/>
29 <div id="attachmentDiv1.1">
30 <div class="noquote">
31 <div dir="ltr">Hi,</div>
32 <br/>
33 <div dir="ltr">I've talked to Ben, the current Phabricator test setup would actually be&nbsp;</div>
34 <div dir="ltr">usable for "production" use for task/project management for us, without&nbsp;</div>
35 <div dir="ltr">causing the sysadmins unreasonable trouble when migrating to the full&nbsp;</div>
36 <div dir="ltr">production deployment of Phabricator eventually.</div>
37 <br/>
38 <div dir="ltr">Phabricator project layout it orthogonal to repo layout, so we can structure&nbsp;</div>
39 <div dir="ltr">this however we want. Among other teams I see at least the following layouts:</div>
40 <div dir="ltr">- single project for everything</div>
41 <div dir="ltr">- a project per release</div>
42 <div dir="ltr">- a project per component/module (ie. close to the repo layout)</div>
43 <br/>
44 <div dir="ltr">How do we want to structure this?</div>
45 <br/>
46 <div dir="ltr">I would start with a single project to not fragment this too much, as we have&nbsp;</div>
47 <div dir="ltr">a relatively small team actually looking into this, so everyone is looking at&nbsp;</div>
48 <div dir="ltr">most sub-projects anyway. And should we eventually hit scaling limits, we can&nbsp;</div>
49 <div dir="ltr">always expand this I think.</div>
50 <br/>
51 <div dir="ltr">We of course should also talk about what we actually want to put in there. My&nbsp;</div>
52 <div dir="ltr">current motivation is having a place to collect the tasks for getting more of&nbsp;</div>
53 <div dir="ltr">the former pimlibs into KF5, and anything else I run into on the way there&nbsp;</div>
54 <div dir="ltr">that we eventually should clean up/improve.</div>
55 <br/>
56 <div dir="ltr">regards,</div>
57 <div dir="ltr">Volker</div>
58 </div>
59 </div>
60 </td>
61 </tr>
62 <tr class="signWarnH">
63 <td dir="ltr">End of signed message</td>
64 </tr>
65 </table>
66 </div>
67 <a name="att2"/>
68 <div id="attachmentDiv2">
69 <div class="noquote">
70 <div dir="ltr">_______________________________________________</div>
71 <div dir="ltr">KDE PIM mailing list <a href="mailto:kde-pim@kde.org">kde-pim@kde.org</a></div>
72 <div dir="ltr">
73 <a href="https://mail.kde.org/mailman/listinfo/kde-pim">https://mail.kde.org/mailman/listinfo/kde-pim</a>
74 </div>
75 <div dir="ltr">KDE PIM home page at <a href="http://pim.kde.org/">http://pim.kde.org/</a></div>
76 </div>
77 </div>
78 </div>
79 </div>
80 </body>
81</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/openpgp-signed-encrypted-two-attachments.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/openpgp-signed-encrypted-two-attachments.mbox.html
new file mode 100644
index 00000000..2c02a22c
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/openpgp-signed-encrypted-two-attachments.mbox.html
@@ -0,0 +1,91 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <table cellspacing="1" cellpadding="1" class="encr">
9 <tr class="encrH">
10 <td dir="ltr">Encrypted message</td>
11 </tr>
12 <tr class="encrB">
13 <td>
14 <div style="position: relative; word-wrap: break-word">
15 <a name="att"/>
16 <div id="attachmentDiv">
17 <table cellspacing="1" cellpadding="1" class="signOkKeyOk">
18 <tr class="signOkKeyOkH">
19 <td dir="ltr">
20 <table cellspacing="0" cellpadding="0" width="100%">
21 <tr>
22 <td rowspan="2">Message was signed by <a href="mailto:test@kolab.org">test@kolab.org</a> (Key ID: <a href="kmail:showCertificate#gpg ### OpenPGP ### 8D9860C58F246DE6">0x8D9860C58F246DE6</a>).<br/>The signature is valid and the key is fully trusted.</td>
23 <td align="right" valign="top" nowrap="nowrap">
24 <a href="kmail:hideSignatureDetails">Hide Details</a>
25 </td>
26 </tr>
27 <tr>
28 <td align="right" valign="bottom" nowrap="nowrap"/>
29 </tr>
30 </table>
31 </td>
32 </tr>
33 <tr class="signOkKeyOkB">
34 <td>
35 <a name="att1"/>
36 <div id="attachmentDiv1">
37 <a name="att1.1"/>
38 <div id="attachmentDiv1.1">
39 <div class="noquote">
40 <div dir="ltr">this is the main body</div>
41 </div>
42 </div>
43 <a name="att1.2"/>
44 <div id="attachmentDiv1.2">
45 <table cellspacing="1" class="textAtm">
46 <tr class="textAtmH">
47 <td dir="ltr">attachment1.txt</td>
48 </tr>
49 <tr class="textAtmB">
50 <td>
51 <div class="noquote">
52 <div dir="ltr">this is one attachment</div>
53 </div>
54 </td>
55 </tr>
56 </table>
57 </div>
58 <a name="att1.3"/>
59 <div id="attachmentDiv1.3">
60 <table cellspacing="1" class="textAtm">
61 <tr class="textAtmH">
62 <td dir="ltr">attachment2.txt</td>
63 </tr>
64 <tr class="textAtmB">
65 <td>
66 <div class="noquote">
67 <div dir="ltr">this is the second attachment</div>
68 </div>
69 </td>
70 </tr>
71 </table>
72 </div>
73 </div>
74 </td>
75 </tr>
76 <tr class="signOkKeyOkH">
77 <td dir="ltr">End of signed message</td>
78 </tr>
79 </table>
80 </div>
81 </div>
82 </td>
83 </tr>
84 <tr class="encrH">
85 <td dir="ltr">End of encrypted message</td>
86 </tr>
87 </table>
88 </div>
89 </div>
90 </body>
91</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/openpgp-signed-encrypted.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/openpgp-signed-encrypted.mbox.html
new file mode 100644
index 00000000..d3a7a0ce
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/openpgp-signed-encrypted.mbox.html
@@ -0,0 +1,58 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <table cellspacing="1" cellpadding="1" class="encr">
9 <tr class="encrH">
10 <td dir="ltr">Encrypted message</td>
11 </tr>
12 <tr class="encrB">
13 <td>
14 <div style="position: relative; word-wrap: break-word">
15 <a name="att"/>
16 <div id="attachmentDiv">
17 <table cellspacing="1" cellpadding="1" class="signOkKeyOk">
18 <tr class="signOkKeyOkH">
19 <td dir="ltr">
20 <table cellspacing="0" cellpadding="0" width="100%">
21 <tr>
22 <td rowspan="2">Message was signed by <a href="mailto:test@kolab.org">test@kolab.org</a> (Key ID: <a href="kmail:showCertificate#gpg ### OpenPGP ### 8D9860C58F246DE6">0x8D9860C58F246DE6</a>).<br/>The signature is valid and the key is fully trusted.</td>
23 <td align="right" valign="top" nowrap="nowrap">
24 <a href="kmail:hideSignatureDetails">Hide Details</a>
25 </td>
26 </tr>
27 <tr>
28 <td align="right" valign="bottom" nowrap="nowrap"/>
29 </tr>
30 </table>
31 </td>
32 </tr>
33 <tr class="signOkKeyOkB">
34 <td>
35 <a name="att1"/>
36 <div id="attachmentDiv1">
37 <div class="noquote">
38 <div dir="ltr">encrypted message text</div>
39 </div>
40 </div>
41 </td>
42 </tr>
43 <tr class="signOkKeyOkH">
44 <td dir="ltr">End of signed message</td>
45 </tr>
46 </table>
47 </div>
48 </div>
49 </td>
50 </tr>
51 <tr class="encrH">
52 <td dir="ltr">End of encrypted message</td>
53 </tr>
54 </table>
55 </div>
56 </div>
57 </body>
58</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/openpgp-signed-mailinglist+old.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/openpgp-signed-mailinglist+old.mbox.html
new file mode 100644
index 00000000..e7da94db
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/openpgp-signed-mailinglist+old.mbox.html
@@ -0,0 +1,97 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <div style="position: relative; word-wrap: break-word">
9 <a name="att"/>
10 <div id="attachmentDiv">
11 <div class="noquote">
12 <div dir="ltr">Oh man a header :)</div>
13 <br/>
14 </div>
15 </div>
16 </div>
17 <div style="position: relative; word-wrap: break-word">
18 <a name="att"/>
19 <div id="attachmentDiv">
20 <table cellspacing="1" cellpadding="1" class="rfc822">
21 <tr class="rfc822H">
22 <td dir="ltr">
23 <a href="attachment:e1:1?place=body">Encapsulated message</a>
24 </td>
25 </tr>
26 <tr class="rfc822B">
27 <td>
28 <a name="att1"/>
29 <div id="attachmentDiv1">
30 <table cellspacing="1" cellpadding="1" class="signWarn">
31 <tr class="signWarnH">
32 <td dir="ltr">
33 <table cellspacing="0" cellpadding="0" width="100%">
34 <tr>
35 <td rowspan="2">Message was signed on 4/8/13 9:51 AM with unknown key <a href="kmail:showCertificate#gpg ### OpenPGP ### D6B72EB1A7F1DB43">0xD6B72EB1A7F1DB43</a>.<br/>The validity of the signature cannot be verified.<br/>Status:<i>No public key to verify the signature</i></td>
36 <td align="right" valign="top" nowrap="nowrap">
37 <a href="kmail:hideSignatureDetails">Hide Details</a>
38 </td>
39 </tr>
40 <tr>
41 <td align="right" valign="bottom" nowrap="nowrap"/>
42 </tr>
43 </table>
44 </td>
45 </tr>
46 <tr class="signWarnB">
47 <td>
48 <a name="att1.1"/>
49 <div id="attachmentDiv1.1">
50 <div class="noquote">
51 <div dir="ltr">hi..</div>
52 <br/>
53 <div dir="ltr">i noticed a new branch when i pulled kde-workspace today (finally!):&nbsp;</div>
54 <div dir="ltr">activities_optional</div>
55 <br/>
56 <div dir="ltr">the lone commit in it was pushed on april 1, so maybe it's an april fools&nbsp;</div>
57 <div dir="ltr">joke, but if it isn't, it looks like someone is trying to do something that&nbsp;</div>
58 <div dir="ltr">makes no sense (and has no chance of being merged into master). so if this is&nbsp;</div>
59 <div dir="ltr">a "for reals" branch, perhaps the motivation behind it can be shared?</div>
60 <br/>
61 <div dir="ltr">--&nbsp;</div>
62 <div dir="ltr">Aaron J. Seigo</div>
63 </div>
64 </div>
65 </td>
66 </tr>
67 <tr class="signWarnH">
68 <td dir="ltr">End of signed message</td>
69 </tr>
70 </table>
71 </div>
72 </td>
73 </tr>
74 <tr class="rfc822H">
75 <td dir="ltr">End of encapsulated message</td>
76 </tr>
77 </table>
78 </div>
79 </div>
80 <div style="position: relative; word-wrap: break-word">
81 <a name="att"/>
82 <div id="attachmentDiv">
83 <div class="noquote">
84 <div dir="ltr">Plasma-devel mailing list</div>
85 <div dir="ltr">
86 <a href="mailto:Plasma-devel@kde.org">Plasma-devel@kde.org</a>
87 </div>
88 <div dir="ltr">
89 <a href="https://mail.kde.org/mailman/listinfo/plasma-devel">https://mail.kde.org/mailman/listinfo/plasma-devel</a>
90 </div>
91 </div>
92 </div>
93 </div>
94 </div>
95 </div>
96 </body>
97</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/openpgp-signed-mailinglist.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/openpgp-signed-mailinglist.mbox.html
new file mode 100644
index 00000000..f5b20b6d
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/openpgp-signed-mailinglist.mbox.html
@@ -0,0 +1,68 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <a name="att1"/>
9 <div id="attachmentDiv1">
10 <table cellspacing="1" cellpadding="1" class="signWarn">
11 <tr class="signWarnH">
12 <td dir="ltr">
13 <table cellspacing="0" cellpadding="0" width="100%">
14 <tr>
15 <td rowspan="2">Message was signed on 4/8/13 9:51 AM with unknown key <a href="kmail:showCertificate#gpg ### OpenPGP ### D6B72EB1A7F1DB43">0xD6B72EB1A7F1DB43</a>.<br/>The validity of the signature cannot be verified.<br/>Status:<i>No public key to verify the signature</i></td>
16 <td align="right" valign="top" nowrap="nowrap">
17 <a href="kmail:hideSignatureDetails">Hide Details</a>
18 </td>
19 </tr>
20 <tr>
21 <td align="right" valign="bottom" nowrap="nowrap"/>
22 </tr>
23 </table>
24 </td>
25 </tr>
26 <tr class="signWarnB">
27 <td>
28 <a name="att1.1"/>
29 <div id="attachmentDiv1.1">
30 <div class="noquote">
31 <div dir="ltr">hi..</div>
32 <br/>
33 <div dir="ltr">i noticed a new branch when i pulled kde-workspace today (finally!):&nbsp;</div>
34 <div dir="ltr">activities_optional</div>
35 <br/>
36 <div dir="ltr">the lone commit in it was pushed on april 1, so maybe it's an april fools&nbsp;</div>
37 <div dir="ltr">joke, but if it isn't, it looks like someone is trying to do something that&nbsp;</div>
38 <div dir="ltr">makes no sense (and has no chance of being merged into master). so if this is&nbsp;</div>
39 <div dir="ltr">a "for reals" branch, perhaps the motivation behind it can be shared?</div>
40 <br/>
41 <div dir="ltr">--&nbsp;</div>
42 <div dir="ltr">Aaron J. Seigo</div>
43 </div>
44 </div>
45 </td>
46 </tr>
47 <tr class="signWarnH">
48 <td dir="ltr">End of signed message</td>
49 </tr>
50 </table>
51 </div>
52 <a name="att2"/>
53 <div id="attachmentDiv2">
54 <div class="noquote">
55 <div dir="ltr">_______________________________________________</div>
56 <div dir="ltr">Plasma-devel mailing list</div>
57 <div dir="ltr">
58 <a href="mailto:Plasma-devel@kde.org">Plasma-devel@kde.org</a>
59 </div>
60 <div dir="ltr">
61 <a href="https://mail.kde.org/mailman/listinfo/plasma-devel">https://mail.kde.org/mailman/listinfo/plasma-devel</a>
62 </div>
63 </div>
64 </div>
65 </div>
66 </div>
67 </body>
68</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/openpgp-signed-two-attachments.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/openpgp-signed-two-attachments.mbox.html
new file mode 100644
index 00000000..1432aa44
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/openpgp-signed-two-attachments.mbox.html
@@ -0,0 +1,74 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <table cellspacing="1" cellpadding="1" class="signOkKeyOk">
9 <tr class="signOkKeyOkH">
10 <td dir="ltr">
11 <table cellspacing="0" cellpadding="0" width="100%">
12 <tr>
13 <td rowspan="2">Message was signed by <a href="mailto:test@kolab.org">test@kolab.org</a> (Key ID: <a href="kmail:showCertificate#gpg ### OpenPGP ### 8D9860C58F246DE6">0x8D9860C58F246DE6</a>).<br/>The signature is valid and the key is fully trusted.</td>
14 <td align="right" valign="top" nowrap="nowrap">
15 <a href="kmail:hideSignatureDetails">Hide Details</a>
16 </td>
17 </tr>
18 <tr>
19 <td align="right" valign="bottom" nowrap="nowrap"/>
20 </tr>
21 </table>
22 </td>
23 </tr>
24 <tr class="signOkKeyOkB">
25 <td>
26 <a name="att1"/>
27 <div id="attachmentDiv1">
28 <a name="att1.1"/>
29 <div id="attachmentDiv1.1">
30 <div class="noquote">
31 <div dir="ltr">this is the main body text</div>
32 </div>
33 </div>
34 <a name="att1.2"/>
35 <div id="attachmentDiv1.2">
36 <table cellspacing="1" class="textAtm">
37 <tr class="textAtmH">
38 <td dir="ltr">attachment1.txt</td>
39 </tr>
40 <tr class="textAtmB">
41 <td>
42 <div class="noquote">
43 <div dir="ltr">this is attachment one</div>
44 </div>
45 </td>
46 </tr>
47 </table>
48 </div>
49 <a name="att1.3"/>
50 <div id="attachmentDiv1.3">
51 <table cellspacing="1" class="textAtm">
52 <tr class="textAtmH">
53 <td dir="ltr">attachment2.txt</td>
54 </tr>
55 <tr class="textAtmB">
56 <td>
57 <div class="noquote">
58 <div dir="ltr">this is attachment two</div>
59 </div>
60 </td>
61 </tr>
62 </table>
63 </div>
64 </div>
65 </td>
66 </tr>
67 <tr class="signOkKeyOkH">
68 <td dir="ltr">End of signed message</td>
69 </tr>
70 </table>
71 </div>
72 </div>
73 </body>
74</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/signed-forward-openpgp-signed-encrypted.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/signed-forward-openpgp-signed-encrypted.mbox.html
new file mode 100644
index 00000000..b960f318
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/signed-forward-openpgp-signed-encrypted.mbox.html
@@ -0,0 +1,111 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <table cellspacing="1" cellpadding="1" class="signOkKeyOk">
9 <tr class="signOkKeyOkH">
10 <td dir="ltr">
11 <table cellspacing="0" cellpadding="0" width="100%">
12 <tr>
13 <td rowspan="2">Message was signed by <a href="mailto:test@kolab.org">test@kolab.org</a> (Key ID: <a href="kmail:showCertificate#gpg ### OpenPGP ### 8D9860C58F246DE6">0x8D9860C58F246DE6</a>).<br/>The signature is valid and the key is fully trusted.</td>
14 <td align="right" valign="top" nowrap="nowrap">
15 <a href="kmail:hideSignatureDetails">Hide Details</a>
16 </td>
17 </tr>
18 <tr>
19 <td align="right" valign="bottom" nowrap="nowrap"/>
20 </tr>
21 </table>
22 </td>
23 </tr>
24 <tr class="signOkKeyOkB">
25 <td>
26 <a name="att1"/>
27 <div id="attachmentDiv1">
28 <a name="att1.1"/>
29 <div id="attachmentDiv1.1">
30 <div class="noquote">
31 <div dir="ltr">bla bla bla</div>
32 </div>
33 </div>
34 <a name="att1.2"/>
35 <div id="attachmentDiv1.2">
36 <table cellspacing="1" cellpadding="1" class="rfc822">
37 <tr class="rfc822H">
38 <td dir="ltr">
39 <a href="attachment:1.2.1?place=body">Encapsulated message</a>
40 </td>
41 </tr>
42 <tr class="rfc822B">
43 <td>
44 <a name="att1.2.1"/>
45 <div id="attachmentDiv1.2.1">
46 <table cellspacing="1" cellpadding="1" class="encr">
47 <tr class="encrH">
48 <td dir="ltr">Encrypted message</td>
49 </tr>
50 <tr class="encrB">
51 <td>
52 <div style="position: relative; word-wrap: break-word">
53 <a name="att"/>
54 <div id="attachmentDiv">
55 <table cellspacing="1" cellpadding="1" class="signOkKeyOk">
56 <tr class="signOkKeyOkH">
57 <td dir="ltr">
58 <table cellspacing="0" cellpadding="0" width="100%">
59 <tr>
60 <td rowspan="2">Message was signed by <a href="mailto:test@kolab.org">test@kolab.org</a> (Key ID: <a href="kmail:showCertificate#gpg ### OpenPGP ### 8D9860C58F246DE6">0x8D9860C58F246DE6</a>).<br/>The signature is valid and the key is fully trusted.</td>
61 <td align="right" valign="top" nowrap="nowrap">
62 <a href="kmail:hideSignatureDetails">Hide Details</a>
63 </td>
64 </tr>
65 <tr>
66 <td align="right" valign="bottom" nowrap="nowrap"/>
67 </tr>
68 </table>
69 </td>
70 </tr>
71 <tr class="signOkKeyOkB">
72 <td>
73 <a name="att1"/>
74 <div id="attachmentDiv1">
75 <div class="noquote">
76 <div dir="ltr">encrypted message text</div>
77 </div>
78 </div>
79 </td>
80 </tr>
81 <tr class="signOkKeyOkH">
82 <td dir="ltr">End of signed message</td>
83 </tr>
84 </table>
85 </div>
86 </div>
87 </td>
88 </tr>
89 <tr class="encrH">
90 <td dir="ltr">End of encrypted message</td>
91 </tr>
92 </table>
93 </div>
94 </td>
95 </tr>
96 <tr class="rfc822H">
97 <td dir="ltr">End of encapsulated message</td>
98 </tr>
99 </table>
100 </div>
101 </div>
102 </td>
103 </tr>
104 <tr class="signOkKeyOkH">
105 <td dir="ltr">End of signed message</td>
106 </tr>
107 </table>
108 </div>
109 </div>
110 </body>
111</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/smime-opaque-enc+sign.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/smime-opaque-enc+sign.mbox.html
new file mode 100644
index 00000000..d6345a7c
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/smime-opaque-enc+sign.mbox.html
@@ -0,0 +1,60 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <table cellspacing="1" cellpadding="1" class="encr">
9 <tr class="encrH">
10 <td dir="ltr">Encrypted message</td>
11 </tr>
12 <tr class="encrB">
13 <td>
14 <div style="position: relative; word-wrap: break-word">
15 <a name="att"/>
16 <div id="attachmentDiv">
17 <table cellspacing="1" cellpadding="1" class="signOkKeyOk">
18 <tr class="signOkKeyOkH">
19 <td dir="ltr">
20 <table cellspacing="0" cellpadding="0" width="100%">
21 <tr>
22 <td rowspan="2">Message was signed by <a href="mailto:test%40example.com">test@example.com</a> on 1/10/13 3:48 PM with key <a href="kmail:showCertificate#gpgsm ### SMIME ### 4CC658E3212B49DC">0x4CC658E3212B49DC</a><br/>&nbsp;<br/>Status:<i>Good signature.</i><br/>&nbsp;<br/><u>Warning:</u> Sender's mail address is not stored in the <a href="kmail:showCertificate#gpgsm ### SMIME ### 4CC658E3212B49DC">certificate</a> used for signing.<br/>sender: <br/>stored: test@example.com</td>
23 <td align="right" valign="top" nowrap="nowrap">
24 <a href="kmail:hideSignatureDetails">Hide Details</a>
25 </td>
26 </tr>
27 <tr>
28 <td align="right" valign="bottom" nowrap="nowrap"/>
29 </tr>
30 </table>
31 </td>
32 </tr>
33 <tr class="signOkKeyOkB">
34 <td>
35 <div style="position: relative; word-wrap: break-word">
36 <a name="att"/>
37 <div id="attachmentDiv">
38 <div class="noquote">
39 <div dir="ltr">Encrypted and signed mail.</div>
40 </div>
41 </div>
42 </div>
43 </td>
44 </tr>
45 <tr class="signOkKeyOkH">
46 <td dir="ltr">End of signed message</td>
47 </tr>
48 </table>
49 </div>
50 </div>
51 </td>
52 </tr>
53 <tr class="encrH">
54 <td dir="ltr">End of encrypted message</td>
55 </tr>
56 </table>
57 </div>
58 </div>
59 </body>
60</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/smime-opaque-sign.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/smime-opaque-sign.mbox.html
new file mode 100644
index 00000000..248b93ff
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/smime-opaque-sign.mbox.html
@@ -0,0 +1,43 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <table cellspacing="1" cellpadding="1" class="signOkKeyOk">
9 <tr class="signOkKeyOkH">
10 <td dir="ltr">
11 <table cellspacing="0" cellpadding="0" width="100%">
12 <tr>
13 <td rowspan="2">Message was signed by <a href="mailto:test%40example.com">test@example.com</a> on 1/10/13 3:48 PM with key <a href="kmail:showCertificate#gpgsm ### SMIME ### 4CC658E3212B49DC">0x4CC658E3212B49DC</a><br/>&nbsp;<br/>Status:<i>Good signature.</i></td>
14 <td align="right" valign="top" nowrap="nowrap">
15 <a href="kmail:hideSignatureDetails">Hide Details</a>
16 </td>
17 </tr>
18 <tr>
19 <td align="right" valign="bottom" nowrap="nowrap"/>
20 </tr>
21 </table>
22 </td>
23 </tr>
24 <tr class="signOkKeyOkB">
25 <td>
26 <div style="position: relative; word-wrap: break-word">
27 <a name="att"/>
28 <div id="attachmentDiv">
29 <div class="noquote">
30 <div dir="ltr">A simple signed only test.</div>
31 </div>
32 </div>
33 </div>
34 </td>
35 </tr>
36 <tr class="signOkKeyOkH">
37 <td dir="ltr">End of signed message</td>
38 </tr>
39 </table>
40 </div>
41 </div>
42 </body>
43</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/smime-signed-encrypted.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/smime-signed-encrypted.mbox.html
new file mode 100644
index 00000000..6e811632
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/details/smime-signed-encrypted.mbox.html
@@ -0,0 +1,58 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <table cellspacing="1" cellpadding="1" class="encr">
9 <tr class="encrH">
10 <td dir="ltr">Encrypted message</td>
11 </tr>
12 <tr class="encrB">
13 <td>
14 <div style="position: relative; word-wrap: break-word">
15 <a name="att"/>
16 <div id="attachmentDiv">
17 <table cellspacing="1" cellpadding="1" class="signOkKeyOk">
18 <tr class="signOkKeyOkH">
19 <td dir="ltr">
20 <table cellspacing="0" cellpadding="0" width="100%">
21 <tr>
22 <td rowspan="2">Message was signed by <a href="mailto:test%40example.com">test@example.com</a> on 9/8/10 3:51 PM with key <a href="kmail:showCertificate#gpgsm ### SMIME ### 4CC658E3212B49DC">0x4CC658E3212B49DC</a><br/>&nbsp;<br/>Status:<i>Good signature.</i><br/>&nbsp;<br/><u>Warning:</u> Sender's mail address is not stored in the <a href="kmail:showCertificate#gpgsm ### SMIME ### 4CC658E3212B49DC">certificate</a> used for signing.<br/>sender: <br/>stored: test@example.com</td>
23 <td align="right" valign="top" nowrap="nowrap">
24 <a href="kmail:hideSignatureDetails">Hide Details</a>
25 </td>
26 </tr>
27 <tr>
28 <td align="right" valign="bottom" nowrap="nowrap"/>
29 </tr>
30 </table>
31 </td>
32 </tr>
33 <tr class="signOkKeyOkB">
34 <td>
35 <a name="att1"/>
36 <div id="attachmentDiv1">
37 <div class="noquote">
38 <div dir="ltr">encrypted message text</div>
39 </div>
40 </div>
41 </td>
42 </tr>
43 <tr class="signOkKeyOkH">
44 <td dir="ltr">End of signed message</td>
45 </tr>
46 </table>
47 </div>
48 </div>
49 </td>
50 </tr>
51 <tr class="encrH">
52 <td dir="ltr">End of encrypted message</td>
53 </tr>
54 </table>
55 </div>
56 </div>
57 </body>
58</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/encapsulated-with-attachment.mbox b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/encapsulated-with-attachment.mbox
new file mode 100644
index 00000000..885b9d1b
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/encapsulated-with-attachment.mbox
@@ -0,0 +1,53 @@
1From: Thomas McGuire <dontspamme@gmx.net>
2Subject: Fwd: Test with attachment
3Date: Wed, 5 Aug 2009 10:58:27 +0200
4MIME-Version: 1.0
5Content-Type: Multipart/Mixed;
6 boundary="Boundary-00=_zmUeKB+A8hGfCVZ"
7
8
9--Boundary-00=_zmUeKB+A8hGfCVZ
10Content-Type: text/plain;
11 charset="iso-8859-15"
12Content-Transfer-Encoding: 7bit
13Content-Disposition: inline
14
15This is the first encapsulating message.
16
17--Boundary-00=_zmUeKB+A8hGfCVZ
18Content-Type: message/rfc822;
19 name="forwarded message"
20Content-Transfer-Encoding: 7bit
21Content-Description: Thomas McGuire <dontspamme@gmx.net>: Test with attachment
22Content-Disposition: inline
23
24From: Thomas McGuire <dontspamme@gmx.net>
25Subject: Test with attachment
26Date: Wed, 5 Aug 2009 10:57:58 +0200
27MIME-Version: 1.0
28Content-Type: Multipart/Mixed;
29 boundary="Boundary-00=_WmUeKQpGb0DHyx1"
30
31--Boundary-00=_WmUeKQpGb0DHyx1
32Content-Type: text/plain;
33 charset="us-ascii"
34Content-Transfer-Encoding: 7bit
35Content-Disposition: inline
36
37
38
39
40This is the second encapsulated message.
41
42--Boundary-00=_WmUeKQpGb0DHyx1
43Content-Type: text/plain;
44 name="attachment.txt"
45Content-Transfer-Encoding: 7bit
46Content-Disposition: attachment;
47 filename="attachment.txt"
48
49This is an attachment.
50
51--Boundary-00=_WmUeKQpGb0DHyx1--
52
53--Boundary-00=_zmUeKB+A8hGfCVZ--
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/encapsulated-with-attachment.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/encapsulated-with-attachment.mbox.html
new file mode 100644
index 00000000..aa3d1090
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/encapsulated-with-attachment.mbox.html
@@ -0,0 +1,51 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <a name="att1"/>
9 <div id="attachmentDiv1">
10 <div class="noquote">
11 <div dir="ltr">This is the first encapsulating message.</div>
12 </div>
13 </div>
14 <a name="att2"/>
15 <div id="attachmentDiv2">
16 <table cellspacing="1" cellpadding="1" class="rfc822">
17 <tr class="rfc822H">
18 <td dir="ltr">
19 <a href="attachment:2.1?place=body">Encapsulated message</a>
20 </td>
21 </tr>
22 <tr class="rfc822B">
23 <td>
24 <a name="att2.1"/>
25 <div id="attachmentDiv2.1">
26 <a name="att2.1.1"/>
27 <div id="attachmentDiv2.1.1">
28 <div class="noquote">
29 <div dir="ltr">This is the second encapsulated message.</div>
30 </div>
31 </div>
32 <a name="att2.1.2"/>
33 <div id="attachmentDiv2.1.2">
34 <hr/>
35 <div>
36 <a href="attachment:2.1.2?place=body"><img align="center" height="48" width="48" src="file:text-plain.svg" border="0" style="max-width: 100%" alt=""/>attachment.txt</a>
37 </div>
38 <div/>
39 </div>
40 </div>
41 </td>
42 </tr>
43 <tr class="rfc822H">
44 <td dir="ltr">End of encapsulated message</td>
45 </tr>
46 </table>
47 </div>
48 </div>
49 </div>
50 </body>
51</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/encapsulated-with-attachment.mbox.tree b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/encapsulated-with-attachment.mbox.tree
new file mode 100644
index 00000000..050414a2
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/encapsulated-with-attachment.mbox.tree
@@ -0,0 +1,10 @@
1 * MimeTreeParser::MessagePartList
2 * MimeTreeParser::MimeMessagePart
3 * MimeTreeParser::TextMessagePart
4 * MimeTreeParser::MessagePart
5 * MimeTreeParser::EncapsulatedRfc822MessagePart
6 * MimeTreeParser::MimeMessagePart
7 * MimeTreeParser::AttachmentMessagePart
8 * MimeTreeParser::MessagePart
9 * MimeTreeParser::AttachmentMessagePart
10 * MimeTreeParser::MessagePart
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/forward-openpgp-signed-encrypted.mbox b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/forward-openpgp-signed-encrypted.mbox
new file mode 100644
index 00000000..1c166940
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/forward-openpgp-signed-encrypted.mbox
@@ -0,0 +1,69 @@
1From test@kolab.org Wed, 08 Sep 2010 17:52:37 +0200
2From: OpenPGP Test <test@kolab.org>
3Subject: Fwd: OpenPGP signed and encrypted
4Date: Wed, 08 Sep 2010 17:52:37 +0200
5User-Agent: KMail/4.6 pre (Linux/2.6.34-rc2-2-default; KDE/4.5.60; x86_64; ; )
6MIME-Version: 1.0
7Content-Type: multipart/mixed; boundary="nextPart2148910.9CGjQOYhYJ"
8Content-Transfer-Encoding: 7Bit
9
10
11--nextPart2148910.9CGjQOYhYJ
12Content-Transfer-Encoding: 7Bit
13Content-Type: text/plain; charset="us-ascii"
14
15bla bla bla
16--nextPart2148910.9CGjQOYhYJ
17Content-Type: message/rfc822
18Content-Disposition: inline; filename="forwarded message"
19Content-Description: OpenPGP Test <test@kolab.org>: OpenPGP signed and encrypted
20
21From: OpenPGP Test <test@kolab.org>
22To: test@kolab.org
23Subject: OpenPGP signed and encrypted
24Date: Tue, 07 Sep 2010 18:08:44 +0200
25User-Agent: KMail/4.6 pre (Linux/2.6.34-rc2-2-default; KDE/4.5.60; x86_64; ; )
26MIME-Version: 1.0
27Content-Type: multipart/encrypted; boundary="nextPart25203163.0xtB501Z4V"; protocol="application/pgp-encrypted"
28Content-Transfer-Encoding: 7Bit
29
30
31--nextPart25203163.0xtB501Z4V
32Content-Type: application/pgp-encrypted
33Content-Disposition: attachment
34
35Version: 1
36--nextPart25203163.0xtB501Z4V
37Content-Type: application/octet-stream
38Content-Disposition: inline; filename="msg.asc"
39
40-----BEGIN PGP MESSAGE-----
41Version: GnuPG v2.0.15 (GNU/Linux)
42
43hQEMAwzOQ1qnzNo7AQf7BFYWaGiCTGtXY59bSh3LCXNnWZejblYALxIUNXOFEXbm
44y/YA95FmQsy3U5HRCAJV/DY1PEaJz1RTm9bcdIpDC3Ab2YzSwmOwV5fcoUOB2df4
45KjX19Q+2F3JxpPQ0N1gHf4dKfIu19LH+CKeFzUN13aJs5J4A5wlj+NjJikxzmxDS
46kDtNYndynPmo9DJQcsUFw3gpvx5HaHvx1cT4mAB2M5cd2l+vN1jYbaWb0x5Zq41z
47mRNI89aPieC3rcM2289m68fGloNbYvi8mZJu5RrI4Tbi/D7Rjm1y63lHgVV6AN88
48XAzRiedOeF99LoTBulrJdtT8AAgCs8nCetcWpIffdtLpAZiZkzHmYOU7nqGxqpRk
49OVeUTrCn9DW2SMmHjaP4IiKnMvzEycu5F4a72+V1LeMIhMSjTRTq+ZE2PTaqH59z
50QsMn7Nb6GlOICbTptRKNNtyJKO7xXlpT7YtvNKnCyEOkH2XrYH7GvpYCiuQ0/o+7
51SxV436ZejiYIg6DQDXJCoa2DXimGp0C10Jh0HwX0BixpoNtwEjkGRYcX6P/JzkH0
52oBood4Ly+Tiu6iVDisrK3AVGYpIzCrKkE9qULTw4R/jFKR2tcCqGb7Fxtk2LV7Md
533S+DyOKrvKQ5GNwbp9OE97pwk+Lr1JS3UAvj5f6BR+1PVNcC0i0wWkgwDjPh1eGD
54enMQmorE6+N0uHtH2F4fOxo/TbbA3+zhI25kVW3bO03xyUl/cmQZeb52nvfOvtOo
55gSb2j6bPkzljDMPEzrtJjbFtGHJbPfUQYJgZv9OE2EQIqpg6goIw279alBq6GLIX
56pkO+dRmztzjcDyhcLxMuQ4cTizel/0J/bU7U6lvwHSyZVbT4Ev+opG5K70Hbqbwr
57NZcgdWXbSeesxGM/oQaMeSurOevxVl+/zrTVAek61aRRd1baAYqgi2pf2V7y4oK3
58qkdxzmoFpRdNlfrQW65NZWnHOi9rC9XxANIwnVn3kRcDf+t2K4PrFluI157lXM/o
59wX91j88fazysbJlQ6TjsApO9ETiPOFEBqouxCTtCZzlUgyVG8jpIjdHWFnagHeXH
60+lXNdYjxnTWTjTxMOZC9ySMpXkjWdFI1ecxVwu6Ik6RX51rvBJAAXWP75yUjPKJ4
61rRi5oQl/VLl0QznO7lvgMPtUwgDVNWO/r7Kn9B387h9fAJZ/kWFAEDW2yhAzABqO
62rCNKDzBPgfAwCnikCpMoCbOL7SU8BdbzQHD8/Lkv4m0pzliHQ/KkGF710koBzTmF
63N7+wk9pwIuvcrEBQj567
64=GV0c
65-----END PGP MESSAGE-----
66
67--nextPart25203163.0xtB501Z4V--
68
69--nextPart2148910.9CGjQOYhYJ--
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/forward-openpgp-signed-encrypted.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/forward-openpgp-signed-encrypted.mbox.html
new file mode 100644
index 00000000..7632ec39
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/forward-openpgp-signed-encrypted.mbox.html
@@ -0,0 +1,81 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <a name="att1"/>
9 <div id="attachmentDiv1">
10 <div class="noquote">
11 <div dir="ltr">bla bla bla</div>
12 </div>
13 </div>
14 <a name="att2"/>
15 <div id="attachmentDiv2">
16 <table cellspacing="1" cellpadding="1" class="rfc822">
17 <tr class="rfc822H">
18 <td dir="ltr">
19 <a href="attachment:2.1?place=body">Encapsulated message</a>
20 </td>
21 </tr>
22 <tr class="rfc822B">
23 <td>
24 <a name="att2.1"/>
25 <div id="attachmentDiv2.1">
26 <table cellspacing="1" cellpadding="1" class="encr">
27 <tr class="encrH">
28 <td dir="ltr">Encrypted message</td>
29 </tr>
30 <tr class="encrB">
31 <td>
32 <div style="position: relative; word-wrap: break-word">
33 <a name="att"/>
34 <div id="attachmentDiv">
35 <table cellspacing="1" cellpadding="1" class="signOkKeyOk">
36 <tr class="signOkKeyOkH">
37 <td dir="ltr">
38 <table cellspacing="0" cellpadding="0" width="100%">
39 <tr>
40 <td>Signed by <a href="mailto:test@kolab.org">test@kolab.org</a>.</td>
41 <td align="right">
42 <a href="kmail:showSignatureDetails">Show Details</a>
43 </td>
44 </tr>
45 </table>
46 </td>
47 </tr>
48 <tr class="signOkKeyOkB">
49 <td>
50 <a name="att1"/>
51 <div id="attachmentDiv1">
52 <div class="noquote">
53 <div dir="ltr">encrypted message text</div>
54 </div>
55 </div>
56 </td>
57 </tr>
58 <tr class="signOkKeyOkH">
59 <td dir="ltr">End of signed message</td>
60 </tr>
61 </table>
62 </div>
63 </div>
64 </td>
65 </tr>
66 <tr class="encrH">
67 <td dir="ltr">End of encrypted message</td>
68 </tr>
69 </table>
70 </div>
71 </td>
72 </tr>
73 <tr class="rfc822H">
74 <td dir="ltr">End of encapsulated message</td>
75 </tr>
76 </table>
77 </div>
78 </div>
79 </div>
80 </body>
81</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/forward-openpgp-signed-encrypted.mbox.tree b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/forward-openpgp-signed-encrypted.mbox.tree
new file mode 100644
index 00000000..324156db
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/forward-openpgp-signed-encrypted.mbox.tree
@@ -0,0 +1,9 @@
1 * MimeTreeParser::MessagePartList
2 * MimeTreeParser::MimeMessagePart
3 * MimeTreeParser::TextMessagePart
4 * MimeTreeParser::MessagePart
5 * MimeTreeParser::EncapsulatedRfc822MessagePart
6 * MimeTreeParser::EncryptedMessagePart
7 * MimeTreeParser::SignedMessagePart
8 * MimeTreeParser::TextMessagePart
9 * MimeTreeParser::MessagePart
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/headeronly/openpgp-encrypted-attachment-and-non-encrypted-attachment.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/headeronly/openpgp-encrypted-attachment-and-non-encrypted-attachment.mbox.html
new file mode 100644
index 00000000..73c4d2d7
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/headeronly/openpgp-encrypted-attachment-and-non-encrypted-attachment.mbox.html
@@ -0,0 +1,61 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <a name="att1"/>
9 <div id="attachmentDiv1">
10 <table cellspacing="1" cellpadding="1" class="encr">
11 <tr class="encrH">
12 <td dir="ltr">Encrypted message</td>
13 </tr>
14 <tr class="encrB">
15 <td>
16 <div style="position: relative; word-wrap: break-word">
17 <a name="att"/>
18 <div id="attachmentDiv">
19 <table cellspacing="1" cellpadding="1" class="signOkKeyOk">
20 <tr class="signOkKeyOkH">
21 <td dir="ltr">
22 <table cellspacing="0" cellpadding="0" width="100%">
23 <tr>
24 <td>Signed by <a href="mailto:test@kolab.org">test@kolab.org</a>.</td>
25 <td align="right">
26 <a href="kmail:showSignatureDetails">Show Details</a>
27 </td>
28 </tr>
29 </table>
30 </td>
31 </tr>
32 <tr class="signOkKeyOkB">
33 <td>
34 <a name="att1"/>
35 <div id="attachmentDiv1">
36 <a name="att1.1"/>
37 <div id="attachmentDiv1.1">
38 <div class="noquote">
39 <div dir="ltr">test text</div>
40 </div>
41 </div>
42 </div>
43 </td>
44 </tr>
45 <tr class="signOkKeyOkH">
46 <td dir="ltr">End of signed message</td>
47 </tr>
48 </table>
49 </div>
50 </div>
51 </td>
52 </tr>
53 <tr class="encrH">
54 <td dir="ltr">End of encrypted message</td>
55 </tr>
56 </table>
57 </div>
58 </div>
59 </div>
60 </body>
61</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/headeronly/openpgp-encrypted-attachment.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/headeronly/openpgp-encrypted-attachment.mbox.html
new file mode 100644
index 00000000..d5e4550e
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/headeronly/openpgp-encrypted-attachment.mbox.html
@@ -0,0 +1,58 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <table cellspacing="1" cellpadding="1" class="encr">
9 <tr class="encrH">
10 <td dir="ltr">Encrypted message</td>
11 </tr>
12 <tr class="encrB">
13 <td>
14 <div style="position: relative; word-wrap: break-word">
15 <a name="att"/>
16 <div id="attachmentDiv">
17 <table cellspacing="1" cellpadding="1" class="signOkKeyOk">
18 <tr class="signOkKeyOkH">
19 <td dir="ltr">
20 <table cellspacing="0" cellpadding="0" width="100%">
21 <tr>
22 <td>Signed by <a href="mailto:test@kolab.org">test@kolab.org</a>.</td>
23 <td align="right">
24 <a href="kmail:showSignatureDetails">Show Details</a>
25 </td>
26 </tr>
27 </table>
28 </td>
29 </tr>
30 <tr class="signOkKeyOkB">
31 <td>
32 <a name="att1"/>
33 <div id="attachmentDiv1">
34 <a name="att1.1"/>
35 <div id="attachmentDiv1.1">
36 <div class="noquote">
37 <div dir="ltr">test text</div>
38 </div>
39 </div>
40 </div>
41 </td>
42 </tr>
43 <tr class="signOkKeyOkH">
44 <td dir="ltr">End of signed message</td>
45 </tr>
46 </table>
47 </div>
48 </div>
49 </td>
50 </tr>
51 <tr class="encrH">
52 <td dir="ltr">End of encrypted message</td>
53 </tr>
54 </table>
55 </div>
56 </div>
57 </body>
58</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/headeronly/openpgp-encrypted-non-encrypted-attachment.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/headeronly/openpgp-encrypted-non-encrypted-attachment.mbox.html
new file mode 100644
index 00000000..73c4d2d7
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/headeronly/openpgp-encrypted-non-encrypted-attachment.mbox.html
@@ -0,0 +1,61 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <a name="att1"/>
9 <div id="attachmentDiv1">
10 <table cellspacing="1" cellpadding="1" class="encr">
11 <tr class="encrH">
12 <td dir="ltr">Encrypted message</td>
13 </tr>
14 <tr class="encrB">
15 <td>
16 <div style="position: relative; word-wrap: break-word">
17 <a name="att"/>
18 <div id="attachmentDiv">
19 <table cellspacing="1" cellpadding="1" class="signOkKeyOk">
20 <tr class="signOkKeyOkH">
21 <td dir="ltr">
22 <table cellspacing="0" cellpadding="0" width="100%">
23 <tr>
24 <td>Signed by <a href="mailto:test@kolab.org">test@kolab.org</a>.</td>
25 <td align="right">
26 <a href="kmail:showSignatureDetails">Show Details</a>
27 </td>
28 </tr>
29 </table>
30 </td>
31 </tr>
32 <tr class="signOkKeyOkB">
33 <td>
34 <a name="att1"/>
35 <div id="attachmentDiv1">
36 <a name="att1.1"/>
37 <div id="attachmentDiv1.1">
38 <div class="noquote">
39 <div dir="ltr">test text</div>
40 </div>
41 </div>
42 </div>
43 </td>
44 </tr>
45 <tr class="signOkKeyOkH">
46 <td dir="ltr">End of signed message</td>
47 </tr>
48 </table>
49 </div>
50 </div>
51 </td>
52 </tr>
53 <tr class="encrH">
54 <td dir="ltr">End of encrypted message</td>
55 </tr>
56 </table>
57 </div>
58 </div>
59 </div>
60 </body>
61</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/headeronly/openpgp-encrypted-partially-signed-attachments.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/headeronly/openpgp-encrypted-partially-signed-attachments.mbox.html
new file mode 100644
index 00000000..ebad7354
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/headeronly/openpgp-encrypted-partially-signed-attachments.mbox.html
@@ -0,0 +1,84 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <a name="att1"/>
9 <div id="attachmentDiv1">
10 <table cellspacing="1" cellpadding="1" class="encr">
11 <tr class="encrH">
12 <td dir="ltr">Encrypted message</td>
13 </tr>
14 <tr class="encrB">
15 <td>
16 <div style="position: relative; word-wrap: break-word">
17 <a name="att"/>
18 <div id="attachmentDiv">
19 <table cellspacing="1" cellpadding="1" class="signOkKeyOk">
20 <tr class="signOkKeyOkH">
21 <td dir="ltr">
22 <table cellspacing="0" cellpadding="0" width="100%">
23 <tr>
24 <td>Signed by <a href="mailto:test@kolab.org">test@kolab.org</a>.</td>
25 <td align="right">
26 <a href="kmail:showSignatureDetails">Show Details</a>
27 </td>
28 </tr>
29 </table>
30 </td>
31 </tr>
32 <tr class="signOkKeyOkB">
33 <td>
34 <a name="att1"/>
35 <div id="attachmentDiv1">
36 <a name="att1.1"/>
37 <div id="attachmentDiv1.1">
38 <div class="noquote">
39 <div dir="ltr">This is the main body.</div>
40 </div>
41 </div>
42 </div>
43 </td>
44 </tr>
45 <tr class="signOkKeyOkH">
46 <td dir="ltr">End of signed message</td>
47 </tr>
48 </table>
49 </div>
50 </div>
51 </td>
52 </tr>
53 <tr class="encrH">
54 <td dir="ltr">End of encrypted message</td>
55 </tr>
56 </table>
57 </div>
58 <a name="att2"/>
59 <div id="attachmentDiv2">
60 <table cellspacing="1" cellpadding="1" class="encr">
61 <tr class="encrH">
62 <td dir="ltr">Encrypted message</td>
63 </tr>
64 <tr class="encrB">
65 <td>
66 <div style="position: relative; word-wrap: break-word">
67 <a name="att"/>
68 <div id="attachmentDiv">
69 <div class="noquote">
70 <div dir="ltr">This is an unsigned attachment.</div>
71 </div>
72 </div>
73 </div>
74 </td>
75 </tr>
76 <tr class="encrH">
77 <td dir="ltr">End of encrypted message</td>
78 </tr>
79 </table>
80 </div>
81 </div>
82 </div>
83 </body>
84</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/headeronly/openpgp-encrypted-two-attachments.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/headeronly/openpgp-encrypted-two-attachments.mbox.html
new file mode 100644
index 00000000..4cdeaa63
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/headeronly/openpgp-encrypted-two-attachments.mbox.html
@@ -0,0 +1,34 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <table cellspacing="1" cellpadding="1" class="encr">
9 <tr class="encrH">
10 <td dir="ltr">Encrypted message</td>
11 </tr>
12 <tr class="encrB">
13 <td>
14 <div style="position: relative; word-wrap: break-word">
15 <a name="att"/>
16 <div id="attachmentDiv">
17 <a name="att1"/>
18 <div id="attachmentDiv1">
19 <div class="noquote">
20 <div dir="ltr">this is the main body part</div>
21 </div>
22 </div>
23 </div>
24 </div>
25 </td>
26 </tr>
27 <tr class="encrH">
28 <td dir="ltr">End of encrypted message</td>
29 </tr>
30 </table>
31 </div>
32 </div>
33 </body>
34</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/headeronly/openpgp-signed-apple.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/headeronly/openpgp-signed-apple.mbox.html
new file mode 100644
index 00000000..330b02a1
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/headeronly/openpgp-signed-apple.mbox.html
@@ -0,0 +1,50 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <table cellspacing="1" cellpadding="1" class="signWarn">
9 <tr class="signWarnH">
10 <td dir="ltr">
11 <table cellspacing="0" cellpadding="0" width="100%">
12 <tr>
13 <td>Not enough information to check signature validity.</td>
14 <td align="right">
15 <a href="kmail:showSignatureDetails">Show Details</a>
16 </td>
17 </tr>
18 </table>
19 </td>
20 </tr>
21 <tr class="signWarnB">
22 <td>
23 <a name="att1"/>
24 <div id="attachmentDiv1">
25 <a name="att1.2"/>
26 <div id="attachmentDiv1.2">
27 <a name="att1.2.1"/>
28 <div id="attachmentDiv1.2.1">
29 <div style="position: relative">
30 <div class="">pre attachment</div>
31 </div>
32 </div>
33 <a name="att1.2.3"/>
34 <div id="attachmentDiv1.2.3">
35 <div style="position: relative">
36 <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; -qt-user-state:0;">Some <span style=" font-weight:600;">HTML</span> text</p>
37 </div>
38 </div>
39 </div>
40 </div>
41 </td>
42 </tr>
43 <tr class="signWarnH">
44 <td dir="ltr">End of signed message</td>
45 </tr>
46 </table>
47 </div>
48 </div>
49 </body>
50</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/headeronly/openpgp-signed-encrypted-two-attachments.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/headeronly/openpgp-signed-encrypted-two-attachments.mbox.html
new file mode 100644
index 00000000..83b7a66c
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/headeronly/openpgp-signed-encrypted-two-attachments.mbox.html
@@ -0,0 +1,58 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <table cellspacing="1" cellpadding="1" class="encr">
9 <tr class="encrH">
10 <td dir="ltr">Encrypted message</td>
11 </tr>
12 <tr class="encrB">
13 <td>
14 <div style="position: relative; word-wrap: break-word">
15 <a name="att"/>
16 <div id="attachmentDiv">
17 <table cellspacing="1" cellpadding="1" class="signOkKeyOk">
18 <tr class="signOkKeyOkH">
19 <td dir="ltr">
20 <table cellspacing="0" cellpadding="0" width="100%">
21 <tr>
22 <td>Signed by <a href="mailto:test@kolab.org">test@kolab.org</a>.</td>
23 <td align="right">
24 <a href="kmail:showSignatureDetails">Show Details</a>
25 </td>
26 </tr>
27 </table>
28 </td>
29 </tr>
30 <tr class="signOkKeyOkB">
31 <td>
32 <a name="att1"/>
33 <div id="attachmentDiv1">
34 <a name="att1.1"/>
35 <div id="attachmentDiv1.1">
36 <div class="noquote">
37 <div dir="ltr">this is the main body</div>
38 </div>
39 </div>
40 </div>
41 </td>
42 </tr>
43 <tr class="signOkKeyOkH">
44 <td dir="ltr">End of signed message</td>
45 </tr>
46 </table>
47 </div>
48 </div>
49 </td>
50 </tr>
51 <tr class="encrH">
52 <td dir="ltr">End of encrypted message</td>
53 </tr>
54 </table>
55 </div>
56 </div>
57 </body>
58</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/headeronly/openpgp-signed-mailinglist+additional-children.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/headeronly/openpgp-signed-mailinglist+additional-children.mbox.html
new file mode 100644
index 00000000..25b1ad04
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/headeronly/openpgp-signed-mailinglist+additional-children.mbox.html
@@ -0,0 +1,52 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <a name="att1"/>
9 <div id="attachmentDiv1">
10 <a name="att1.1"/>
11 <div id="attachmentDiv1.1">
12 <div class="noquote">
13 <div dir="ltr">hi..</div>
14 <br/>
15 <div dir="ltr">i noticed a new branch when i pulled kde-workspace today (finally!):&nbsp;</div>
16 <div dir="ltr">activities_optional</div>
17 <br/>
18 <div dir="ltr">the lone commit in it was pushed on april 1, so maybe it's an april fools&nbsp;</div>
19 <div dir="ltr">joke, but if it isn't, it looks like someone is trying to do something that&nbsp;</div>
20 <div dir="ltr">makes no sense (and has no chance of being merged into master). so if this is&nbsp;</div>
21 <div dir="ltr">a "for reals" branch, perhaps the motivation behind it can be shared?</div>
22 <br/>
23 <div dir="ltr">--&nbsp;</div>
24 <div dir="ltr">Aaron J. Seigo</div>
25 </div>
26 </div>
27 <a name="att1.2"/>
28 <div id="attachmentDiv1.2">
29 <hr/>
30 <div>
31 <a href="attachment:1.2?place=body"><img align="center" height="48" width="48" src="file:application-pgp-signature.svg" border="0" style="max-width: 100%" alt=""/>signature.asc</a>
32 </div>
33 <div>This is a digitally signed message part.</div>
34 </div>
35 </div>
36 <a name="att2"/>
37 <div id="attachmentDiv2">
38 <div class="noquote">
39 <div dir="ltr">_______________________________________________</div>
40 <div dir="ltr">Plasma-devel mailing list</div>
41 <div dir="ltr">
42 <a href="mailto:Plasma-devel@kde.org">Plasma-devel@kde.org</a>
43 </div>
44 <div dir="ltr">
45 <a href="https://mail.kde.org/mailman/listinfo/plasma-devel">https://mail.kde.org/mailman/listinfo/plasma-devel</a>
46 </div>
47 </div>
48 </div>
49 </div>
50 </div>
51 </body>
52</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/headeronly/openpgp-signed-no-protocol.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/headeronly/openpgp-signed-no-protocol.mbox.html
new file mode 100644
index 00000000..bdac52b6
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/headeronly/openpgp-signed-no-protocol.mbox.html
@@ -0,0 +1,28 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <a name="att1"/>
9 <div id="attachmentDiv1">
10 <div class="noquote">
11 <div dir="ltr">hi..</div>
12 <br/>
13 <div dir="ltr">i noticed a new branch when i pulled kde-workspace today (finally!):&nbsp;</div>
14 <div dir="ltr">activities_optional</div>
15 <br/>
16 <div dir="ltr">the lone commit in it was pushed on april 1, so maybe it's an april fools&nbsp;</div>
17 <div dir="ltr">joke, but if it isn't, it looks like someone is trying to do something that&nbsp;</div>
18 <div dir="ltr">makes no sense (and has no chance of being merged into master). so if this is&nbsp;</div>
19 <div dir="ltr">a "for reals" branch, perhaps the motivation behind it can be shared?</div>
20 <br/>
21 <div dir="ltr">--&nbsp;</div>
22 <div dir="ltr">Aaron J. Seigo</div>
23 </div>
24 </div>
25 </div>
26 </div>
27 </body>
28</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/headeronly/openpgp-signed-two-attachments.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/headeronly/openpgp-signed-two-attachments.mbox.html
new file mode 100644
index 00000000..027097f7
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/headeronly/openpgp-signed-two-attachments.mbox.html
@@ -0,0 +1,41 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <table cellspacing="1" cellpadding="1" class="signOkKeyOk">
9 <tr class="signOkKeyOkH">
10 <td dir="ltr">
11 <table cellspacing="0" cellpadding="0" width="100%">
12 <tr>
13 <td>Signed by <a href="mailto:test@kolab.org">test@kolab.org</a>.</td>
14 <td align="right">
15 <a href="kmail:showSignatureDetails">Show Details</a>
16 </td>
17 </tr>
18 </table>
19 </td>
20 </tr>
21 <tr class="signOkKeyOkB">
22 <td>
23 <a name="att1"/>
24 <div id="attachmentDiv1">
25 <a name="att1.1"/>
26 <div id="attachmentDiv1.1">
27 <div class="noquote">
28 <div dir="ltr">this is the main body text</div>
29 </div>
30 </div>
31 </div>
32 </td>
33 </tr>
34 <tr class="signOkKeyOkH">
35 <td dir="ltr">End of signed message</td>
36 </tr>
37 </table>
38 </div>
39 </div>
40 </body>
41</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/headeronly/smime-signed-apple.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/headeronly/smime-signed-apple.mbox.html
new file mode 100644
index 00000000..9554bb39
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/headeronly/smime-signed-apple.mbox.html
@@ -0,0 +1,50 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <table cellspacing="1" cellpadding="1" class="signErr">
9 <tr class="signErrH">
10 <td dir="ltr">
11 <table cellspacing="0" cellpadding="0" width="100%">
12 <tr>
13 <td>Invalid signature.</td>
14 <td align="right">
15 <a href="kmail:showSignatureDetails">Show Details</a>
16 </td>
17 </tr>
18 </table>
19 </td>
20 </tr>
21 <tr class="signErrB">
22 <td>
23 <a name="att1"/>
24 <div id="attachmentDiv1">
25 <a name="att1.2"/>
26 <div id="attachmentDiv1.2">
27 <a name="att1.2.1"/>
28 <div id="attachmentDiv1.2.1">
29 <div style="position: relative">Olá Konqui,<div class="">Here is the pdf you asked for!</div><div class="">Cheers,</div><div class="">Quaack</div></div>
30 </div>
31 <a name="att1.2.3"/>
32 <div id="attachmentDiv1.2.3">
33 <div style="position: relative">
34 <blockquote type="cite" class="">
35 <div class="">On 20 Jan 2017, at 10:35, Konqui &lt;<a href="mailto:Konqui@kdab.com">Konqui</a></div>
36 </blockquote>
37 </div>
38 </div>
39 </div>
40 </div>
41 </td>
42 </tr>
43 <tr class="signErrH">
44 <td dir="ltr">End of signed message</td>
45 </tr>
46 </table>
47 </div>
48 </div>
49 </body>
50</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/hidden/encapsulated-with-attachment.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/hidden/encapsulated-with-attachment.mbox.html
new file mode 100644
index 00000000..42f5ecd7
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/hidden/encapsulated-with-attachment.mbox.html
@@ -0,0 +1,43 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <a name="att1"/>
9 <div id="attachmentDiv1">
10 <div class="noquote">
11 <div dir="ltr">This is the first encapsulating message.</div>
12 </div>
13 </div>
14 <a name="att2"/>
15 <div id="attachmentDiv2">
16 <table cellspacing="1" cellpadding="1" class="rfc822">
17 <tr class="rfc822H">
18 <td dir="ltr">
19 <a href="attachment:2.1?place=body">Encapsulated message</a>
20 </td>
21 </tr>
22 <tr class="rfc822B">
23 <td>
24 <a name="att2.1"/>
25 <div id="attachmentDiv2.1">
26 <a name="att2.1.1"/>
27 <div id="attachmentDiv2.1.1">
28 <div class="noquote">
29 <div dir="ltr">This is the second encapsulated message.</div>
30 </div>
31 </div>
32 </div>
33 </td>
34 </tr>
35 <tr class="rfc822H">
36 <td dir="ltr">End of encapsulated message</td>
37 </tr>
38 </table>
39 </div>
40 </div>
41 </div>
42 </body>
43</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/hidden/openpgp-encrypted-attachment-and-non-encrypted-attachment.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/hidden/openpgp-encrypted-attachment-and-non-encrypted-attachment.mbox.html
new file mode 100644
index 00000000..73c4d2d7
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/hidden/openpgp-encrypted-attachment-and-non-encrypted-attachment.mbox.html
@@ -0,0 +1,61 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <a name="att1"/>
9 <div id="attachmentDiv1">
10 <table cellspacing="1" cellpadding="1" class="encr">
11 <tr class="encrH">
12 <td dir="ltr">Encrypted message</td>
13 </tr>
14 <tr class="encrB">
15 <td>
16 <div style="position: relative; word-wrap: break-word">
17 <a name="att"/>
18 <div id="attachmentDiv">
19 <table cellspacing="1" cellpadding="1" class="signOkKeyOk">
20 <tr class="signOkKeyOkH">
21 <td dir="ltr">
22 <table cellspacing="0" cellpadding="0" width="100%">
23 <tr>
24 <td>Signed by <a href="mailto:test@kolab.org">test@kolab.org</a>.</td>
25 <td align="right">
26 <a href="kmail:showSignatureDetails">Show Details</a>
27 </td>
28 </tr>
29 </table>
30 </td>
31 </tr>
32 <tr class="signOkKeyOkB">
33 <td>
34 <a name="att1"/>
35 <div id="attachmentDiv1">
36 <a name="att1.1"/>
37 <div id="attachmentDiv1.1">
38 <div class="noquote">
39 <div dir="ltr">test text</div>
40 </div>
41 </div>
42 </div>
43 </td>
44 </tr>
45 <tr class="signOkKeyOkH">
46 <td dir="ltr">End of signed message</td>
47 </tr>
48 </table>
49 </div>
50 </div>
51 </td>
52 </tr>
53 <tr class="encrH">
54 <td dir="ltr">End of encrypted message</td>
55 </tr>
56 </table>
57 </div>
58 </div>
59 </div>
60 </body>
61</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/hidden/openpgp-encrypted-attachment.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/hidden/openpgp-encrypted-attachment.mbox.html
new file mode 100644
index 00000000..d5e4550e
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/hidden/openpgp-encrypted-attachment.mbox.html
@@ -0,0 +1,58 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <table cellspacing="1" cellpadding="1" class="encr">
9 <tr class="encrH">
10 <td dir="ltr">Encrypted message</td>
11 </tr>
12 <tr class="encrB">
13 <td>
14 <div style="position: relative; word-wrap: break-word">
15 <a name="att"/>
16 <div id="attachmentDiv">
17 <table cellspacing="1" cellpadding="1" class="signOkKeyOk">
18 <tr class="signOkKeyOkH">
19 <td dir="ltr">
20 <table cellspacing="0" cellpadding="0" width="100%">
21 <tr>
22 <td>Signed by <a href="mailto:test@kolab.org">test@kolab.org</a>.</td>
23 <td align="right">
24 <a href="kmail:showSignatureDetails">Show Details</a>
25 </td>
26 </tr>
27 </table>
28 </td>
29 </tr>
30 <tr class="signOkKeyOkB">
31 <td>
32 <a name="att1"/>
33 <div id="attachmentDiv1">
34 <a name="att1.1"/>
35 <div id="attachmentDiv1.1">
36 <div class="noquote">
37 <div dir="ltr">test text</div>
38 </div>
39 </div>
40 </div>
41 </td>
42 </tr>
43 <tr class="signOkKeyOkH">
44 <td dir="ltr">End of signed message</td>
45 </tr>
46 </table>
47 </div>
48 </div>
49 </td>
50 </tr>
51 <tr class="encrH">
52 <td dir="ltr">End of encrypted message</td>
53 </tr>
54 </table>
55 </div>
56 </div>
57 </body>
58</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/hidden/openpgp-encrypted-noData.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/hidden/openpgp-encrypted-noData.mbox.html
new file mode 100644
index 00000000..166812fe
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/hidden/openpgp-encrypted-noData.mbox.html
@@ -0,0 +1,10 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv"/>
8 </div>
9 </body>
10</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/hidden/openpgp-encrypted-non-encrypted-attachment.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/hidden/openpgp-encrypted-non-encrypted-attachment.mbox.html
new file mode 100644
index 00000000..73c4d2d7
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/hidden/openpgp-encrypted-non-encrypted-attachment.mbox.html
@@ -0,0 +1,61 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <a name="att1"/>
9 <div id="attachmentDiv1">
10 <table cellspacing="1" cellpadding="1" class="encr">
11 <tr class="encrH">
12 <td dir="ltr">Encrypted message</td>
13 </tr>
14 <tr class="encrB">
15 <td>
16 <div style="position: relative; word-wrap: break-word">
17 <a name="att"/>
18 <div id="attachmentDiv">
19 <table cellspacing="1" cellpadding="1" class="signOkKeyOk">
20 <tr class="signOkKeyOkH">
21 <td dir="ltr">
22 <table cellspacing="0" cellpadding="0" width="100%">
23 <tr>
24 <td>Signed by <a href="mailto:test@kolab.org">test@kolab.org</a>.</td>
25 <td align="right">
26 <a href="kmail:showSignatureDetails">Show Details</a>
27 </td>
28 </tr>
29 </table>
30 </td>
31 </tr>
32 <tr class="signOkKeyOkB">
33 <td>
34 <a name="att1"/>
35 <div id="attachmentDiv1">
36 <a name="att1.1"/>
37 <div id="attachmentDiv1.1">
38 <div class="noquote">
39 <div dir="ltr">test text</div>
40 </div>
41 </div>
42 </div>
43 </td>
44 </tr>
45 <tr class="signOkKeyOkH">
46 <td dir="ltr">End of signed message</td>
47 </tr>
48 </table>
49 </div>
50 </div>
51 </td>
52 </tr>
53 <tr class="encrH">
54 <td dir="ltr">End of encrypted message</td>
55 </tr>
56 </table>
57 </div>
58 </div>
59 </div>
60 </body>
61</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/hidden/openpgp-encrypted-partially-signed-attachments.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/hidden/openpgp-encrypted-partially-signed-attachments.mbox.html
new file mode 100644
index 00000000..ebad7354
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/hidden/openpgp-encrypted-partially-signed-attachments.mbox.html
@@ -0,0 +1,84 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <a name="att1"/>
9 <div id="attachmentDiv1">
10 <table cellspacing="1" cellpadding="1" class="encr">
11 <tr class="encrH">
12 <td dir="ltr">Encrypted message</td>
13 </tr>
14 <tr class="encrB">
15 <td>
16 <div style="position: relative; word-wrap: break-word">
17 <a name="att"/>
18 <div id="attachmentDiv">
19 <table cellspacing="1" cellpadding="1" class="signOkKeyOk">
20 <tr class="signOkKeyOkH">
21 <td dir="ltr">
22 <table cellspacing="0" cellpadding="0" width="100%">
23 <tr>
24 <td>Signed by <a href="mailto:test@kolab.org">test@kolab.org</a>.</td>
25 <td align="right">
26 <a href="kmail:showSignatureDetails">Show Details</a>
27 </td>
28 </tr>
29 </table>
30 </td>
31 </tr>
32 <tr class="signOkKeyOkB">
33 <td>
34 <a name="att1"/>
35 <div id="attachmentDiv1">
36 <a name="att1.1"/>
37 <div id="attachmentDiv1.1">
38 <div class="noquote">
39 <div dir="ltr">This is the main body.</div>
40 </div>
41 </div>
42 </div>
43 </td>
44 </tr>
45 <tr class="signOkKeyOkH">
46 <td dir="ltr">End of signed message</td>
47 </tr>
48 </table>
49 </div>
50 </div>
51 </td>
52 </tr>
53 <tr class="encrH">
54 <td dir="ltr">End of encrypted message</td>
55 </tr>
56 </table>
57 </div>
58 <a name="att2"/>
59 <div id="attachmentDiv2">
60 <table cellspacing="1" cellpadding="1" class="encr">
61 <tr class="encrH">
62 <td dir="ltr">Encrypted message</td>
63 </tr>
64 <tr class="encrB">
65 <td>
66 <div style="position: relative; word-wrap: break-word">
67 <a name="att"/>
68 <div id="attachmentDiv">
69 <div class="noquote">
70 <div dir="ltr">This is an unsigned attachment.</div>
71 </div>
72 </div>
73 </div>
74 </td>
75 </tr>
76 <tr class="encrH">
77 <td dir="ltr">End of encrypted message</td>
78 </tr>
79 </table>
80 </div>
81 </div>
82 </div>
83 </body>
84</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/hidden/openpgp-encrypted-two-attachments.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/hidden/openpgp-encrypted-two-attachments.mbox.html
new file mode 100644
index 00000000..4cdeaa63
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/hidden/openpgp-encrypted-two-attachments.mbox.html
@@ -0,0 +1,34 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <table cellspacing="1" cellpadding="1" class="encr">
9 <tr class="encrH">
10 <td dir="ltr">Encrypted message</td>
11 </tr>
12 <tr class="encrB">
13 <td>
14 <div style="position: relative; word-wrap: break-word">
15 <a name="att"/>
16 <div id="attachmentDiv">
17 <a name="att1"/>
18 <div id="attachmentDiv1">
19 <div class="noquote">
20 <div dir="ltr">this is the main body part</div>
21 </div>
22 </div>
23 </div>
24 </div>
25 </td>
26 </tr>
27 <tr class="encrH">
28 <td dir="ltr">End of encrypted message</td>
29 </tr>
30 </table>
31 </div>
32 </div>
33 </body>
34</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/hidden/openpgp-signed-apple.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/hidden/openpgp-signed-apple.mbox.html
new file mode 100644
index 00000000..330b02a1
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/hidden/openpgp-signed-apple.mbox.html
@@ -0,0 +1,50 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <table cellspacing="1" cellpadding="1" class="signWarn">
9 <tr class="signWarnH">
10 <td dir="ltr">
11 <table cellspacing="0" cellpadding="0" width="100%">
12 <tr>
13 <td>Not enough information to check signature validity.</td>
14 <td align="right">
15 <a href="kmail:showSignatureDetails">Show Details</a>
16 </td>
17 </tr>
18 </table>
19 </td>
20 </tr>
21 <tr class="signWarnB">
22 <td>
23 <a name="att1"/>
24 <div id="attachmentDiv1">
25 <a name="att1.2"/>
26 <div id="attachmentDiv1.2">
27 <a name="att1.2.1"/>
28 <div id="attachmentDiv1.2.1">
29 <div style="position: relative">
30 <div class="">pre attachment</div>
31 </div>
32 </div>
33 <a name="att1.2.3"/>
34 <div id="attachmentDiv1.2.3">
35 <div style="position: relative">
36 <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; -qt-user-state:0;">Some <span style=" font-weight:600;">HTML</span> text</p>
37 </div>
38 </div>
39 </div>
40 </div>
41 </td>
42 </tr>
43 <tr class="signWarnH">
44 <td dir="ltr">End of signed message</td>
45 </tr>
46 </table>
47 </div>
48 </div>
49 </body>
50</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/hidden/openpgp-signed-encrypted-two-attachments.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/hidden/openpgp-signed-encrypted-two-attachments.mbox.html
new file mode 100644
index 00000000..83b7a66c
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/hidden/openpgp-signed-encrypted-two-attachments.mbox.html
@@ -0,0 +1,58 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <table cellspacing="1" cellpadding="1" class="encr">
9 <tr class="encrH">
10 <td dir="ltr">Encrypted message</td>
11 </tr>
12 <tr class="encrB">
13 <td>
14 <div style="position: relative; word-wrap: break-word">
15 <a name="att"/>
16 <div id="attachmentDiv">
17 <table cellspacing="1" cellpadding="1" class="signOkKeyOk">
18 <tr class="signOkKeyOkH">
19 <td dir="ltr">
20 <table cellspacing="0" cellpadding="0" width="100%">
21 <tr>
22 <td>Signed by <a href="mailto:test@kolab.org">test@kolab.org</a>.</td>
23 <td align="right">
24 <a href="kmail:showSignatureDetails">Show Details</a>
25 </td>
26 </tr>
27 </table>
28 </td>
29 </tr>
30 <tr class="signOkKeyOkB">
31 <td>
32 <a name="att1"/>
33 <div id="attachmentDiv1">
34 <a name="att1.1"/>
35 <div id="attachmentDiv1.1">
36 <div class="noquote">
37 <div dir="ltr">this is the main body</div>
38 </div>
39 </div>
40 </div>
41 </td>
42 </tr>
43 <tr class="signOkKeyOkH">
44 <td dir="ltr">End of signed message</td>
45 </tr>
46 </table>
47 </div>
48 </div>
49 </td>
50 </tr>
51 <tr class="encrH">
52 <td dir="ltr">End of encrypted message</td>
53 </tr>
54 </table>
55 </div>
56 </div>
57 </body>
58</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/hidden/openpgp-signed-mailinglist+additional-children.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/hidden/openpgp-signed-mailinglist+additional-children.mbox.html
new file mode 100644
index 00000000..1f0da385
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/hidden/openpgp-signed-mailinglist+additional-children.mbox.html
@@ -0,0 +1,44 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <a name="att1"/>
9 <div id="attachmentDiv1">
10 <a name="att1.1"/>
11 <div id="attachmentDiv1.1">
12 <div class="noquote">
13 <div dir="ltr">hi..</div>
14 <br/>
15 <div dir="ltr">i noticed a new branch when i pulled kde-workspace today (finally!):&nbsp;</div>
16 <div dir="ltr">activities_optional</div>
17 <br/>
18 <div dir="ltr">the lone commit in it was pushed on april 1, so maybe it's an april fools&nbsp;</div>
19 <div dir="ltr">joke, but if it isn't, it looks like someone is trying to do something that&nbsp;</div>
20 <div dir="ltr">makes no sense (and has no chance of being merged into master). so if this is&nbsp;</div>
21 <div dir="ltr">a "for reals" branch, perhaps the motivation behind it can be shared?</div>
22 <br/>
23 <div dir="ltr">--&nbsp;</div>
24 <div dir="ltr">Aaron J. Seigo</div>
25 </div>
26 </div>
27 </div>
28 <a name="att2"/>
29 <div id="attachmentDiv2">
30 <div class="noquote">
31 <div dir="ltr">_______________________________________________</div>
32 <div dir="ltr">Plasma-devel mailing list</div>
33 <div dir="ltr">
34 <a href="mailto:Plasma-devel@kde.org">Plasma-devel@kde.org</a>
35 </div>
36 <div dir="ltr">
37 <a href="https://mail.kde.org/mailman/listinfo/plasma-devel">https://mail.kde.org/mailman/listinfo/plasma-devel</a>
38 </div>
39 </div>
40 </div>
41 </div>
42 </div>
43 </body>
44</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/hidden/openpgp-signed-no-protocol.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/hidden/openpgp-signed-no-protocol.mbox.html
new file mode 100644
index 00000000..bdac52b6
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/hidden/openpgp-signed-no-protocol.mbox.html
@@ -0,0 +1,28 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <a name="att1"/>
9 <div id="attachmentDiv1">
10 <div class="noquote">
11 <div dir="ltr">hi..</div>
12 <br/>
13 <div dir="ltr">i noticed a new branch when i pulled kde-workspace today (finally!):&nbsp;</div>
14 <div dir="ltr">activities_optional</div>
15 <br/>
16 <div dir="ltr">the lone commit in it was pushed on april 1, so maybe it's an april fools&nbsp;</div>
17 <div dir="ltr">joke, but if it isn't, it looks like someone is trying to do something that&nbsp;</div>
18 <div dir="ltr">makes no sense (and has no chance of being merged into master). so if this is&nbsp;</div>
19 <div dir="ltr">a "for reals" branch, perhaps the motivation behind it can be shared?</div>
20 <br/>
21 <div dir="ltr">--&nbsp;</div>
22 <div dir="ltr">Aaron J. Seigo</div>
23 </div>
24 </div>
25 </div>
26 </div>
27 </body>
28</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/hidden/openpgp-signed-two-attachments.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/hidden/openpgp-signed-two-attachments.mbox.html
new file mode 100644
index 00000000..027097f7
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/hidden/openpgp-signed-two-attachments.mbox.html
@@ -0,0 +1,41 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <table cellspacing="1" cellpadding="1" class="signOkKeyOk">
9 <tr class="signOkKeyOkH">
10 <td dir="ltr">
11 <table cellspacing="0" cellpadding="0" width="100%">
12 <tr>
13 <td>Signed by <a href="mailto:test@kolab.org">test@kolab.org</a>.</td>
14 <td align="right">
15 <a href="kmail:showSignatureDetails">Show Details</a>
16 </td>
17 </tr>
18 </table>
19 </td>
20 </tr>
21 <tr class="signOkKeyOkB">
22 <td>
23 <a name="att1"/>
24 <div id="attachmentDiv1">
25 <a name="att1.1"/>
26 <div id="attachmentDiv1.1">
27 <div class="noquote">
28 <div dir="ltr">this is the main body text</div>
29 </div>
30 </div>
31 </div>
32 </td>
33 </tr>
34 <tr class="signOkKeyOkH">
35 <td dir="ltr">End of signed message</td>
36 </tr>
37 </table>
38 </div>
39 </div>
40 </body>
41</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/hidden/smime-signed-apple.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/hidden/smime-signed-apple.mbox.html
new file mode 100644
index 00000000..9554bb39
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/hidden/smime-signed-apple.mbox.html
@@ -0,0 +1,50 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <table cellspacing="1" cellpadding="1" class="signErr">
9 <tr class="signErrH">
10 <td dir="ltr">
11 <table cellspacing="0" cellpadding="0" width="100%">
12 <tr>
13 <td>Invalid signature.</td>
14 <td align="right">
15 <a href="kmail:showSignatureDetails">Show Details</a>
16 </td>
17 </tr>
18 </table>
19 </td>
20 </tr>
21 <tr class="signErrB">
22 <td>
23 <a name="att1"/>
24 <div id="attachmentDiv1">
25 <a name="att1.2"/>
26 <div id="attachmentDiv1.2">
27 <a name="att1.2.1"/>
28 <div id="attachmentDiv1.2.1">
29 <div style="position: relative">Olá Konqui,<div class="">Here is the pdf you asked for!</div><div class="">Cheers,</div><div class="">Quaack</div></div>
30 </div>
31 <a name="att1.2.3"/>
32 <div id="attachmentDiv1.2.3">
33 <div style="position: relative">
34 <blockquote type="cite" class="">
35 <div class="">On 20 Jan 2017, at 10:35, Konqui &lt;<a href="mailto:Konqui@kdab.com">Konqui</a></div>
36 </blockquote>
37 </div>
38 </div>
39 </div>
40 </div>
41 </td>
42 </tr>
43 <tr class="signErrH">
44 <td dir="ltr">End of signed message</td>
45 </tr>
46 </table>
47 </div>
48 </div>
49 </body>
50</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/html.mbox b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/html.mbox
new file mode 100644
index 00000000..eebd4283
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/html.mbox
@@ -0,0 +1,31 @@
1From foo@example.com Thu, 26 May 2011 01:16:54 +0100
2From: Thomas McGuire <foo@example.com>
3Subject: HTML test
4Date: Thu, 26 May 2011 01:16:54 +0100
5Message-ID: <1501334.pROlBb7MZF@herrwackelpudding.localhost>
6X-KMail-Transport: GMX
7X-KMail-Fcc: 28
8X-KMail-Drafts: 7
9X-KMail-Templates: 9
10User-Agent: KMail/4.6 beta5 (Linux/2.6.34.7-0.7-desktop; KDE/4.6.41; x86_64; git-0269848; 2011-04-19)
11MIME-Version: 1.0
12Content-Type: multipart/alternative; boundary="nextPart8606278.tpV19BTJKu"
13Content-Transfer-Encoding: 7Bit
14
15
16--nextPart8606278.tpV19BTJKu
17Content-Transfer-Encoding: 7Bit
18Content-Type: text/plain; charset="windows-1252"
19
20Some HTML text
21--nextPart8606278.tpV19BTJKu
22Content-Transfer-Encoding: 7Bit
23Content-Type: text/html; charset="windows-1252"
24
25<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
26<html><head><meta name="qrichtext" content="1" /><style type="text/css">
27p, li { white-space: pre-wrap; }
28</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;">
29<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; -qt-user-state:0;">Some <span style=" font-weight:600;">HTML</span> text</p></body></html>
30--nextPart8606278.tpV19BTJKu--
31
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/html.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/html.mbox.html
new file mode 100644
index 00000000..2fe886f1
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/html.mbox.html
@@ -0,0 +1,17 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <a name="att2"/>
9 <div id="attachmentDiv2">
10 <div style="position: relative">
11 <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; -qt-user-state:0;">Some <span style=" font-weight:600;">HTML</span> text</p>
12 </div>
13 </div>
14 </div>
15 </div>
16 </body>
17</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/html.mbox.tree b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/html.mbox.tree
new file mode 100644
index 00000000..0de07281
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/html.mbox.tree
@@ -0,0 +1,2 @@
1 * MimeTreeParser::MessagePartList
2 * MimeTreeParser::AlternativeMessagePart
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/htmlonly.mbox b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/htmlonly.mbox
new file mode 100644
index 00000000..e45b1c4d
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/htmlonly.mbox
@@ -0,0 +1,21 @@
1From foo@example.com Thu, 26 May 2011 01:16:54 +0100
2From: Thomas McGuire <foo@example.com>
3Subject: HTML test
4Date: Thu, 26 May 2011 01:16:54 +0100
5Message-ID: <1501334.pROlBb7MZF@herrwackelpudding.localhost>
6X-KMail-Transport: GMX
7X-KMail-Fcc: 28
8X-KMail-Drafts: 7
9X-KMail-Templates: 9
10User-Agent: KMail/4.6 beta5 (Linux/2.6.34.7-0.7-desktop; KDE/4.6.41; x86_64; git-0269848; 2011-04-19)
11MIME-Version: 1.0
12Content-Type: text/html
13Content-Transfer-Encoding: 7Bit
14
15<html>
16<head>
17</head>
18<body>
19<b>SOME</b> HTML text.
20</body>
21</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/htmlonly.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/htmlonly.mbox.html
new file mode 100644
index 00000000..1143f2cf
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/htmlonly.mbox.html
@@ -0,0 +1,12 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <div style="position: relative"><b>SOME</b> HTML text.</div>
9 </div>
10 </div>
11 </body>
12</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/htmlonly.mbox.tree b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/htmlonly.mbox.tree
new file mode 100644
index 00000000..a4c3191b
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/htmlonly.mbox.tree
@@ -0,0 +1,2 @@
1 * MimeTreeParser::MessagePartList
2 * MimeTreeParser::HtmlMessagePart
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/htmlonlyexternal.mbox b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/htmlonlyexternal.mbox
new file mode 100644
index 00000000..4eb3e2c3
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/htmlonlyexternal.mbox
@@ -0,0 +1,21 @@
1From foo@example.com Thu, 26 May 2011 01:16:54 +0100
2From: Thomas McGuire <foo@example.com>
3Subject: HTML test
4Date: Thu, 26 May 2011 01:16:54 +0100
5Message-ID: <1501334.pROlBb7MZF@herrwackelpudding.localhost>
6X-KMail-Transport: GMX
7X-KMail-Fcc: 28
8X-KMail-Drafts: 7
9X-KMail-Templates: 9
10User-Agent: KMail/4.6 beta5 (Linux/2.6.34.7-0.7-desktop; KDE/4.6.41; x86_64; git-0269848; 2011-04-19)
11MIME-Version: 1.0
12Content-Type: text/html
13Content-Transfer-Encoding: 7Bit
14
15<html>
16<head>
17</head>
18<body>
19<b>SOME</b> HTML text. <img src="http://example.org/test.img" >
20</body>
21</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/iconic/openpgp-encrypted-partially-signed-attachments.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/iconic/openpgp-encrypted-partially-signed-attachments.mbox.html
new file mode 100644
index 00000000..b6f734c2
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/iconic/openpgp-encrypted-partially-signed-attachments.mbox.html
@@ -0,0 +1,92 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <a name="att1"/>
9 <div id="attachmentDiv1">
10 <table cellspacing="1" cellpadding="1" class="encr">
11 <tr class="encrH">
12 <td dir="ltr">Encrypted message</td>
13 </tr>
14 <tr class="encrB">
15 <td>
16 <div style="position: relative; word-wrap: break-word">
17 <a name="att"/>
18 <div id="attachmentDiv">
19 <table cellspacing="1" cellpadding="1" class="signOkKeyOk">
20 <tr class="signOkKeyOkH">
21 <td dir="ltr">
22 <table cellspacing="0" cellpadding="0" width="100%">
23 <tr>
24 <td>Signed by <a href="mailto:test@kolab.org">test@kolab.org</a>.</td>
25 <td align="right">
26 <a href="kmail:showSignatureDetails">Show Details</a>
27 </td>
28 </tr>
29 </table>
30 </td>
31 </tr>
32 <tr class="signOkKeyOkB">
33 <td>
34 <a name="att1"/>
35 <div id="attachmentDiv1">
36 <a name="att1.1"/>
37 <div id="attachmentDiv1.1">
38 <div class="noquote">
39 <div dir="ltr">This is the main body.</div>
40 </div>
41 </div>
42 <a name="att1.2"/>
43 <div id="attachmentDiv1.2">
44 <hr/>
45 <div>
46 <a href="attachment:1:e0:1.2?place=body"><img align="center" height="48" width="48" src="file:text-plain.svg" border="0" style="max-width: 100%" alt=""/>attachment1.txt</a>
47 </div>
48 <div/>
49 </div>
50 </div>
51 </td>
52 </tr>
53 <tr class="signOkKeyOkH">
54 <td dir="ltr">End of signed message</td>
55 </tr>
56 </table>
57 </div>
58 </div>
59 </td>
60 </tr>
61 <tr class="encrH">
62 <td dir="ltr">End of encrypted message</td>
63 </tr>
64 </table>
65 </div>
66 <a name="att2"/>
67 <div id="attachmentDiv2">
68 <table cellspacing="1" cellpadding="1" class="encr">
69 <tr class="encrH">
70 <td dir="ltr">Encrypted message</td>
71 </tr>
72 <tr class="encrB">
73 <td>
74 <div style="position: relative; word-wrap: break-word">
75 <a name="att"/>
76 <div id="attachmentDiv">
77 <div class="noquote">
78 <div dir="ltr">This is an unsigned attachment.</div>
79 </div>
80 </div>
81 </div>
82 </td>
83 </tr>
84 <tr class="encrH">
85 <td dir="ltr">End of encrypted message</td>
86 </tr>
87 </table>
88 </div>
89 </div>
90 </div>
91 </body>
92</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/iconic/openpgp-encrypted-two-attachments.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/iconic/openpgp-encrypted-two-attachments.mbox.html
new file mode 100644
index 00000000..7f0b7abd
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/iconic/openpgp-encrypted-two-attachments.mbox.html
@@ -0,0 +1,50 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <table cellspacing="1" cellpadding="1" class="encr">
9 <tr class="encrH">
10 <td dir="ltr">Encrypted message</td>
11 </tr>
12 <tr class="encrB">
13 <td>
14 <div style="position: relative; word-wrap: break-word">
15 <a name="att"/>
16 <div id="attachmentDiv">
17 <a name="att1"/>
18 <div id="attachmentDiv1">
19 <div class="noquote">
20 <div dir="ltr">this is the main body part</div>
21 </div>
22 </div>
23 <a name="att2"/>
24 <div id="attachmentDiv2">
25 <hr/>
26 <div>
27 <a href="attachment:e0:2?place=body"><img align="center" height="48" width="48" src="file:text-plain.svg" border="0" style="max-width: 100%" alt=""/>attachment1.txt</a>
28 </div>
29 <div/>
30 </div>
31 <a name="att3"/>
32 <div id="attachmentDiv3">
33 <hr/>
34 <div>
35 <a href="attachment:e0:3?place=body"><img align="center" height="48" width="48" src="file:text-plain.svg" border="0" style="max-width: 100%" alt=""/>attachment2.txt</a>
36 </div>
37 <div/>
38 </div>
39 </div>
40 </div>
41 </td>
42 </tr>
43 <tr class="encrH">
44 <td dir="ltr">End of encrypted message</td>
45 </tr>
46 </table>
47 </div>
48 </div>
49 </body>
50</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/iconic/openpgp-signed-encrypted-two-attachments.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/iconic/openpgp-signed-encrypted-two-attachments.mbox.html
new file mode 100644
index 00000000..8d6b5814
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/iconic/openpgp-signed-encrypted-two-attachments.mbox.html
@@ -0,0 +1,74 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <table cellspacing="1" cellpadding="1" class="encr">
9 <tr class="encrH">
10 <td dir="ltr">Encrypted message</td>
11 </tr>
12 <tr class="encrB">
13 <td>
14 <div style="position: relative; word-wrap: break-word">
15 <a name="att"/>
16 <div id="attachmentDiv">
17 <table cellspacing="1" cellpadding="1" class="signOkKeyOk">
18 <tr class="signOkKeyOkH">
19 <td dir="ltr">
20 <table cellspacing="0" cellpadding="0" width="100%">
21 <tr>
22 <td>Signed by <a href="mailto:test@kolab.org">test@kolab.org</a>.</td>
23 <td align="right">
24 <a href="kmail:showSignatureDetails">Show Details</a>
25 </td>
26 </tr>
27 </table>
28 </td>
29 </tr>
30 <tr class="signOkKeyOkB">
31 <td>
32 <a name="att1"/>
33 <div id="attachmentDiv1">
34 <a name="att1.1"/>
35 <div id="attachmentDiv1.1">
36 <div class="noquote">
37 <div dir="ltr">this is the main body</div>
38 </div>
39 </div>
40 <a name="att1.2"/>
41 <div id="attachmentDiv1.2">
42 <hr/>
43 <div>
44 <a href="attachment:e0:1.2?place=body"><img align="center" height="48" width="48" src="file:text-plain.svg" border="0" style="max-width: 100%" alt=""/>attachment1.txt</a>
45 </div>
46 <div/>
47 </div>
48 <a name="att1.3"/>
49 <div id="attachmentDiv1.3">
50 <hr/>
51 <div>
52 <a href="attachment:e0:1.3?place=body"><img align="center" height="48" width="48" src="file:text-plain.svg" border="0" style="max-width: 100%" alt=""/>attachment2.txt</a>
53 </div>
54 <div/>
55 </div>
56 </div>
57 </td>
58 </tr>
59 <tr class="signOkKeyOkH">
60 <td dir="ltr">End of signed message</td>
61 </tr>
62 </table>
63 </div>
64 </div>
65 </td>
66 </tr>
67 <tr class="encrH">
68 <td dir="ltr">End of encrypted message</td>
69 </tr>
70 </table>
71 </div>
72 </div>
73 </body>
74</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/iconic/openpgp-signed-two-attachments.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/iconic/openpgp-signed-two-attachments.mbox.html
new file mode 100644
index 00000000..73dbc5f4
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/iconic/openpgp-signed-two-attachments.mbox.html
@@ -0,0 +1,57 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <table cellspacing="1" cellpadding="1" class="signOkKeyOk">
9 <tr class="signOkKeyOkH">
10 <td dir="ltr">
11 <table cellspacing="0" cellpadding="0" width="100%">
12 <tr>
13 <td>Signed by <a href="mailto:test@kolab.org">test@kolab.org</a>.</td>
14 <td align="right">
15 <a href="kmail:showSignatureDetails">Show Details</a>
16 </td>
17 </tr>
18 </table>
19 </td>
20 </tr>
21 <tr class="signOkKeyOkB">
22 <td>
23 <a name="att1"/>
24 <div id="attachmentDiv1">
25 <a name="att1.1"/>
26 <div id="attachmentDiv1.1">
27 <div class="noquote">
28 <div dir="ltr">this is the main body text</div>
29 </div>
30 </div>
31 <a name="att1.2"/>
32 <div id="attachmentDiv1.2">
33 <hr/>
34 <div>
35 <a href="attachment:1.2?place=body"><img align="center" height="48" width="48" src="file:text-plain.svg" border="0" style="max-width: 100%" alt=""/>attachment1.txt</a>
36 </div>
37 <div/>
38 </div>
39 <a name="att1.3"/>
40 <div id="attachmentDiv1.3">
41 <hr/>
42 <div>
43 <a href="attachment:1.3?place=body"><img align="center" height="48" width="48" src="file:text-plain.svg" border="0" style="max-width: 100%" alt=""/>attachment2.txt</a>
44 </div>
45 <div/>
46 </div>
47 </div>
48 </td>
49 </tr>
50 <tr class="signOkKeyOkH">
51 <td dir="ltr">End of signed message</td>
52 </tr>
53 </table>
54 </div>
55 </div>
56 </body>
57</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlined/encapsulated-with-attachment.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlined/encapsulated-with-attachment.mbox.html
new file mode 100644
index 00000000..26d3dd60
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlined/encapsulated-with-attachment.mbox.html
@@ -0,0 +1,58 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <a name="att1"/>
9 <div id="attachmentDiv1">
10 <div class="noquote">
11 <div dir="ltr">This is the first encapsulating message.</div>
12 </div>
13 </div>
14 <a name="att2"/>
15 <div id="attachmentDiv2">
16 <table cellspacing="1" cellpadding="1" class="rfc822">
17 <tr class="rfc822H">
18 <td dir="ltr">
19 <a href="attachment:2.1?place=body">Encapsulated message</a>
20 </td>
21 </tr>
22 <tr class="rfc822B">
23 <td>
24 <a name="att2.1"/>
25 <div id="attachmentDiv2.1">
26 <a name="att2.1.1"/>
27 <div id="attachmentDiv2.1.1">
28 <div class="noquote">
29 <div dir="ltr">This is the second encapsulated message.</div>
30 </div>
31 </div>
32 <a name="att2.1.2"/>
33 <div id="attachmentDiv2.1.2">
34 <table cellspacing="1" class="textAtm">
35 <tr class="textAtmH">
36 <td dir="ltr">attachment.txt</td>
37 </tr>
38 <tr class="textAtmB">
39 <td>
40 <div class="noquote">
41 <div dir="ltr">This is an attachment.</div>
42 </div>
43 </td>
44 </tr>
45 </table>
46 </div>
47 </div>
48 </td>
49 </tr>
50 <tr class="rfc822H">
51 <td dir="ltr">End of encapsulated message</td>
52 </tr>
53 </table>
54 </div>
55 </div>
56 </div>
57 </body>
58</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlined/openpgp-encrypted-attachment-and-non-encrypted-attachment.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlined/openpgp-encrypted-attachment-and-non-encrypted-attachment.mbox.html
new file mode 100644
index 00000000..49e503f1
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlined/openpgp-encrypted-attachment-and-non-encrypted-attachment.mbox.html
@@ -0,0 +1,90 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <a name="att1"/>
9 <div id="attachmentDiv1">
10 <table cellspacing="1" cellpadding="1" class="encr">
11 <tr class="encrH">
12 <td dir="ltr">Encrypted message</td>
13 </tr>
14 <tr class="encrB">
15 <td>
16 <div style="position: relative; word-wrap: break-word">
17 <a name="att"/>
18 <div id="attachmentDiv">
19 <table cellspacing="1" cellpadding="1" class="signOkKeyOk">
20 <tr class="signOkKeyOkH">
21 <td dir="ltr">
22 <table cellspacing="0" cellpadding="0" width="100%">
23 <tr>
24 <td>Signed by <a href="mailto:test@kolab.org">test@kolab.org</a>.</td>
25 <td align="right">
26 <a href="kmail:showSignatureDetails">Show Details</a>
27 </td>
28 </tr>
29 </table>
30 </td>
31 </tr>
32 <tr class="signOkKeyOkB">
33 <td>
34 <a name="att1"/>
35 <div id="attachmentDiv1">
36 <a name="att1.1"/>
37 <div id="attachmentDiv1.1">
38 <div class="noquote">
39 <div dir="ltr">test text</div>
40 </div>
41 </div>
42 <a name="att1.2"/>
43 <div id="attachmentDiv1.2">
44 <table cellspacing="1" class="textAtm">
45 <tr class="textAtmH">
46 <td dir="ltr">file.txt</td>
47 </tr>
48 <tr class="textAtmB">
49 <td>
50 <div class="noquote">
51 <div dir="ltr">some plain ascii text...</div>
52 <br/>
53 </div>
54 </td>
55 </tr>
56 </table>
57 </div>
58 </div>
59 </td>
60 </tr>
61 <tr class="signOkKeyOkH">
62 <td dir="ltr">End of signed message</td>
63 </tr>
64 </table>
65 </div>
66 </div>
67 </td>
68 </tr>
69 <tr class="encrH">
70 <td dir="ltr">End of encrypted message</td>
71 </tr>
72 </table>
73 </div>
74 <a name="att2"/>
75 <div id="attachmentDiv2">
76 <hr/>
77 <div>
78 <a href="attachment:2?place=body">
79 <img align="center" src="file:image.png" border="0" style="max-width: 100%"/>
80 </a>
81 </div>
82 <div>
83 <a href="attachment:2?place=body">image.png</a>
84 </div>
85 <div/>
86 </div>
87 </div>
88 </div>
89 </body>
90</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlined/openpgp-encrypted-attachment.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlined/openpgp-encrypted-attachment.mbox.html
new file mode 100644
index 00000000..67897491
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlined/openpgp-encrypted-attachment.mbox.html
@@ -0,0 +1,74 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <table cellspacing="1" cellpadding="1" class="encr">
9 <tr class="encrH">
10 <td dir="ltr">Encrypted message</td>
11 </tr>
12 <tr class="encrB">
13 <td>
14 <div style="position: relative; word-wrap: break-word">
15 <a name="att"/>
16 <div id="attachmentDiv">
17 <table cellspacing="1" cellpadding="1" class="signOkKeyOk">
18 <tr class="signOkKeyOkH">
19 <td dir="ltr">
20 <table cellspacing="0" cellpadding="0" width="100%">
21 <tr>
22 <td>Signed by <a href="mailto:test@kolab.org">test@kolab.org</a>.</td>
23 <td align="right">
24 <a href="kmail:showSignatureDetails">Show Details</a>
25 </td>
26 </tr>
27 </table>
28 </td>
29 </tr>
30 <tr class="signOkKeyOkB">
31 <td>
32 <a name="att1"/>
33 <div id="attachmentDiv1">
34 <a name="att1.1"/>
35 <div id="attachmentDiv1.1">
36 <div class="noquote">
37 <div dir="ltr">test text</div>
38 </div>
39 </div>
40 <a name="att1.2"/>
41 <div id="attachmentDiv1.2">
42 <table cellspacing="1" class="textAtm">
43 <tr class="textAtmH">
44 <td dir="ltr">file.txt</td>
45 </tr>
46 <tr class="textAtmB">
47 <td>
48 <div class="noquote">
49 <div dir="ltr">some plain ascii text...</div>
50 <br/>
51 </div>
52 </td>
53 </tr>
54 </table>
55 </div>
56 </div>
57 </td>
58 </tr>
59 <tr class="signOkKeyOkH">
60 <td dir="ltr">End of signed message</td>
61 </tr>
62 </table>
63 </div>
64 </div>
65 </td>
66 </tr>
67 <tr class="encrH">
68 <td dir="ltr">End of encrypted message</td>
69 </tr>
70 </table>
71 </div>
72 </div>
73 </body>
74</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlined/openpgp-encrypted-non-encrypted-attachment.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlined/openpgp-encrypted-non-encrypted-attachment.mbox.html
new file mode 100644
index 00000000..6091ee5a
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlined/openpgp-encrypted-non-encrypted-attachment.mbox.html
@@ -0,0 +1,74 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <a name="att1"/>
9 <div id="attachmentDiv1">
10 <table cellspacing="1" cellpadding="1" class="encr">
11 <tr class="encrH">
12 <td dir="ltr">Encrypted message</td>
13 </tr>
14 <tr class="encrB">
15 <td>
16 <div style="position: relative; word-wrap: break-word">
17 <a name="att"/>
18 <div id="attachmentDiv">
19 <table cellspacing="1" cellpadding="1" class="signOkKeyOk">
20 <tr class="signOkKeyOkH">
21 <td dir="ltr">
22 <table cellspacing="0" cellpadding="0" width="100%">
23 <tr>
24 <td>Signed by <a href="mailto:test@kolab.org">test@kolab.org</a>.</td>
25 <td align="right">
26 <a href="kmail:showSignatureDetails">Show Details</a>
27 </td>
28 </tr>
29 </table>
30 </td>
31 </tr>
32 <tr class="signOkKeyOkB">
33 <td>
34 <a name="att1"/>
35 <div id="attachmentDiv1">
36 <a name="att1.1"/>
37 <div id="attachmentDiv1.1">
38 <div class="noquote">
39 <div dir="ltr">test text</div>
40 </div>
41 </div>
42 </div>
43 </td>
44 </tr>
45 <tr class="signOkKeyOkH">
46 <td dir="ltr">End of signed message</td>
47 </tr>
48 </table>
49 </div>
50 </div>
51 </td>
52 </tr>
53 <tr class="encrH">
54 <td dir="ltr">End of encrypted message</td>
55 </tr>
56 </table>
57 </div>
58 <a name="att2"/>
59 <div id="attachmentDiv2">
60 <hr/>
61 <div>
62 <a href="attachment:2?place=body">
63 <img align="center" src="file:image.png" border="0" style="max-width: 100%"/>
64 </a>
65 </div>
66 <div>
67 <a href="attachment:2?place=body">image.png</a>
68 </div>
69 <div/>
70 </div>
71 </div>
72 </div>
73 </body>
74</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlined/openpgp-signed-apple.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlined/openpgp-signed-apple.mbox.html
new file mode 100644
index 00000000..092d5e1c
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlined/openpgp-signed-apple.mbox.html
@@ -0,0 +1,63 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <table cellspacing="1" cellpadding="1" class="signWarn">
9 <tr class="signWarnH">
10 <td dir="ltr">
11 <table cellspacing="0" cellpadding="0" width="100%">
12 <tr>
13 <td>Not enough information to check signature validity.</td>
14 <td align="right">
15 <a href="kmail:showSignatureDetails">Show Details</a>
16 </td>
17 </tr>
18 </table>
19 </td>
20 </tr>
21 <tr class="signWarnB">
22 <td>
23 <a name="att1"/>
24 <div id="attachmentDiv1">
25 <a name="att1.2"/>
26 <div id="attachmentDiv1.2">
27 <a name="att1.2.1"/>
28 <div id="attachmentDiv1.2.1">
29 <div style="position: relative">
30 <div class="">pre attachment</div>
31 </div>
32 </div>
33 <a name="att1.2.2"/>
34 <div id="attachmentDiv1.2.2">
35 <hr/>
36 <div>
37 <a href="attachment:1.2.2?place=body">
38 <img align="center" src="file:image.png" border="0" style="max-width: 100%"/>
39 </a>
40 </div>
41 <div>
42 <a href="attachment:1.2.2?place=body">image.png</a>
43 </div>
44 <div/>
45 </div>
46 <a name="att1.2.3"/>
47 <div id="attachmentDiv1.2.3">
48 <div style="position: relative">
49 <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; -qt-user-state:0;">Some <span style=" font-weight:600;">HTML</span> text</p>
50 </div>
51 </div>
52 </div>
53 </div>
54 </td>
55 </tr>
56 <tr class="signWarnH">
57 <td dir="ltr">End of signed message</td>
58 </tr>
59 </table>
60 </div>
61 </div>
62 </body>
63</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlined/openpgp-signed-mailinglist+additional-children.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlined/openpgp-signed-mailinglist+additional-children.mbox.html
new file mode 100644
index 00000000..cc49cbcc
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlined/openpgp-signed-mailinglist+additional-children.mbox.html
@@ -0,0 +1,67 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <a name="att1"/>
9 <div id="attachmentDiv1">
10 <a name="att1.1"/>
11 <div id="attachmentDiv1.1">
12 <div class="noquote">
13 <div dir="ltr">hi..</div>
14 <br/>
15 <div dir="ltr">i noticed a new branch when i pulled kde-workspace today (finally!):&nbsp;</div>
16 <div dir="ltr">activities_optional</div>
17 <br/>
18 <div dir="ltr">the lone commit in it was pushed on april 1, so maybe it's an april fools&nbsp;</div>
19 <div dir="ltr">joke, but if it isn't, it looks like someone is trying to do something that&nbsp;</div>
20 <div dir="ltr">makes no sense (and has no chance of being merged into master). so if this is&nbsp;</div>
21 <div dir="ltr">a "for reals" branch, perhaps the motivation behind it can be shared?</div>
22 <br/>
23 <div dir="ltr">--&nbsp;</div>
24 <div dir="ltr">Aaron J. Seigo</div>
25 </div>
26 </div>
27 <a name="att1.2"/>
28 <div id="attachmentDiv1.2">
29 <hr/>
30 <div>
31 <a href="attachment:1.2?place=body"><img align="center" height="48" width="48" src="file:application-pgp-signature.svg" border="0" style="max-width: 100%" alt=""/>signature.asc</a>
32 </div>
33 <div>This is a digitally signed message part.</div>
34 </div>
35 <a name="att1.3"/>
36 <div id="attachmentDiv1.3">
37 <table cellspacing="1" class="textAtm">
38 <tr class="textAtmH">
39 <td dir="ltr">broken.attachment</td>
40 </tr>
41 <tr class="textAtmB">
42 <td>
43 <div class="noquote">
44 <div dir="ltr">Let's break a signed message - This messageblock should not be here :D</div>
45 </div>
46 </td>
47 </tr>
48 </table>
49 </div>
50 </div>
51 <a name="att2"/>
52 <div id="attachmentDiv2">
53 <div class="noquote">
54 <div dir="ltr">_______________________________________________</div>
55 <div dir="ltr">Plasma-devel mailing list</div>
56 <div dir="ltr">
57 <a href="mailto:Plasma-devel@kde.org">Plasma-devel@kde.org</a>
58 </div>
59 <div dir="ltr">
60 <a href="https://mail.kde.org/mailman/listinfo/plasma-devel">https://mail.kde.org/mailman/listinfo/plasma-devel</a>
61 </div>
62 </div>
63 </div>
64 </div>
65 </div>
66 </body>
67</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlined/smime-signed-apple.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlined/smime-signed-apple.mbox.html
new file mode 100644
index 00000000..d3f3eeb3
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlined/smime-signed-apple.mbox.html
@@ -0,0 +1,63 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <table cellspacing="1" cellpadding="1" class="signErr">
9 <tr class="signErrH">
10 <td dir="ltr">
11 <table cellspacing="0" cellpadding="0" width="100%">
12 <tr>
13 <td>Invalid signature.</td>
14 <td align="right">
15 <a href="kmail:showSignatureDetails">Show Details</a>
16 </td>
17 </tr>
18 </table>
19 </td>
20 </tr>
21 <tr class="signErrB">
22 <td>
23 <a name="att1"/>
24 <div id="attachmentDiv1">
25 <a name="att1.2"/>
26 <div id="attachmentDiv1.2">
27 <a name="att1.2.1"/>
28 <div id="attachmentDiv1.2.1">
29 <div style="position: relative">Olá Konqui,<div class="">Here is the pdf you asked for!</div><div class="">Cheers,</div><div class="">Quaack</div></div>
30 </div>
31 <a name="att1.2.2"/>
32 <div id="attachmentDiv1.2.2">
33 <hr/>
34 <div>
35 <a href="attachment:1.2.2?place=body">
36 <img align="center" src="file:image.png" border="0" style="max-width: 100%"/>
37 </a>
38 </div>
39 <div>
40 <a href="attachment:1.2.2?place=body">image.png</a>
41 </div>
42 <div/>
43 </div>
44 <a name="att1.2.3"/>
45 <div id="attachmentDiv1.2.3">
46 <div style="position: relative">
47 <blockquote type="cite" class="">
48 <div class="">On 20 Jan 2017, at 10:35, Konqui &lt;<a href="mailto:Konqui@kdab.com">Konqui</a></div>
49 </blockquote>
50 </div>
51 </div>
52 </div>
53 </div>
54 </td>
55 </tr>
56 <tr class="signErrH">
57 <td dir="ltr">End of signed message</td>
58 </tr>
59 </table>
60 </div>
61 </div>
62 </body>
63</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlinepgpencrypted-appendix.mbox b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlinepgpencrypted-appendix.mbox
new file mode 100644
index 00000000..c05a7e69
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlinepgpencrypted-appendix.mbox
@@ -0,0 +1,33 @@
1From test@kolab.org Wed, 25 May 2011 23:49:40 +0100
2From: OpenPGP Test <test@kolab.org>
3To: test@kolab.org
4Subject: inlinepgpencrypted
5Date: Wed, 25 May 2011 23:49:40 +0100
6Message-ID: <1786696.yKXrOjjflF@herrwackelpudding.localhost>
7X-KMail-Transport: GMX
8X-KMail-Fcc: 28
9X-KMail-Drafts: 7
10X-KMail-Templates: 9
11User-Agent: KMail/4.6 beta5 (Linux/2.6.34.7-0.7-desktop; KDE/4.6.41; x86_64; git-0269848; 2011-04-19)
12MIME-Version: 1.0
13Content-Transfer-Encoding: 7Bit
14Content-Type: text/plain; charset="us-ascii"
15
16-----BEGIN PGP MESSAGE-----
17Version: GnuPG v2.0.15 (GNU/Linux)
18
19hQEMAwzOQ1qnzNo7AQf/a3aNTLpQBfcUr+4AKsZQLj4h6z7e7a5AaCW8AG0wrbxN
20kBYB7E5jdZh45DX/99gvoZslthWryUCX2kKZ3LtIllxKVjqNuK5hSt+SAuKkwiMR
21Xcbf1KFKENKupgGSO9B2NJRbjoExdJ+fC3mGXnO3dT7xJJAo3oLE8Nivu+Bj1peY
22E1wCf+vcTwVHFrA7SV8eMRb9Z9wBXmU8Q8e9ekJ7ZsRX3tMeBs6jvscVvfMf6DYY
23N14snZBZuGNKT9a3DPny7IC1S0lHcaam34ogWwMi3FxPGJt/Lg52kARlkF5TDhcP
24N6H0EB/iqDRjOOUoEVm8um5XOSR1FpEiAdD0DON3y9JPATnrYq7sgYZz3BVImYY+
25N/jV8fEiN0a34pcOq8NQedMuOsJHNBS5MtbQH/kJLq0MXBpXekGlHo4MKw0trISc
26Rw3pW6/BFfhPJLni29g9tw==
27=fRFW
28-----END PGP MESSAGE-----
29
30_______________________________________________
31test mailing list
32test@lists.kde.org
33http://kde.org
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlinepgpencrypted-appendix.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlinepgpencrypted-appendix.mbox.html
new file mode 100644
index 00000000..8af2b1c6
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlinepgpencrypted-appendix.mbox.html
@@ -0,0 +1,36 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <table cellspacing="1" cellpadding="1" class="encr">
9 <tr class="encrH">
10 <td dir="ltr">Encrypted message</td>
11 </tr>
12 <tr class="encrB">
13 <td>
14 <div class="noquote">
15 <div dir="ltr">some random text</div>
16 </div>
17 </td>
18 </tr>
19 <tr class="encrH">
20 <td dir="ltr">End of encrypted message</td>
21 </tr>
22 </table>
23 <div class="noquote">
24 <div dir="ltr">_______________________________________________</div>
25 <div dir="ltr">test mailing list</div>
26 <div dir="ltr">
27 <a href="mailto:test@lists.kde.org">test@lists.kde.org</a>
28 </div>
29 <div dir="ltr">
30 <a href="http://kde.org">http://kde.org</a>
31 </div>
32 </div>
33 </div>
34 </div>
35 </body>
36</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlinepgpencrypted-appendix.mbox.tree b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlinepgpencrypted-appendix.mbox.tree
new file mode 100644
index 00000000..018f5c33
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlinepgpencrypted-appendix.mbox.tree
@@ -0,0 +1,4 @@
1 * MimeTreeParser::MessagePartList
2 * MimeTreeParser::TextMessagePart
3 * MimeTreeParser::EncryptedMessagePart
4 * MimeTreeParser::MessagePart
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlinepgpencrypted-error.mbox b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlinepgpencrypted-error.mbox
new file mode 100644
index 00000000..529b4d3b
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlinepgpencrypted-error.mbox
@@ -0,0 +1,55 @@
1From test@kolab.org Wed, 25 May 2011 23:49:40 +0100
2From: OpenPGP Test <test@kolab.org>
3To: test@kolab.org
4Subject: inlinepgpencrypted - no seckey
5Date: Wed, 25 May 2011 23:49:40 +0100
6Message-ID: <1786696.yKXrOjjflF@herrwackelpudding.localhost>
7X-KMail-Transport: GMX
8X-KMail-Fcc: 28
9X-KMail-Drafts: 7
10X-KMail-Templates: 9
11User-Agent: KMail/4.6 beta5 (Linux/2.6.34.7-0.7-desktop; KDE/4.6.41; x86_64; git-0269848; 2011-04-19)
12MIME-Version: 1.0
13Content-Transfer-Encoding: 7Bit
14Content-Type: text/plain; charset="us-ascii"
15
16-----BEGIN PGP MESSAGE-----
17Version: GnuPG v2
18
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
533Qu241eOBdb6
54=J3lb
55-----END PGP MESSAGE-----
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlinepgpencrypted-error.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlinepgpencrypted-error.mbox.html
new file mode 100644
index 00000000..a4427e01
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlinepgpencrypted-error.mbox.html
@@ -0,0 +1,24 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <table cellspacing="1" cellpadding="1" class="encr">
9 <tr class="encrH">
10 <td dir="ltr">Encrypted message (decryption not possible)<br/>Reason: Crypto plug-in "OpenPGP" could not decrypt the data.<br/>Error: Decryption failed</td>
11 </tr>
12 <tr class="encrB">
13 <td>
14 <div style="font-size:x-large; text-align:center; padding:20pt;">No secret key found to encrypt the message. It is encrypted for following keys:<br/><a href="kmail:showCertificate#gpg ### OpenPGP ### 553D4262DA4BADF2">0x553D4262DA4BADF2</a><br/><a href="kmail:showCertificate#gpg ### OpenPGP ### F6E6A3D126C630E0">0xF6E6A3D126C630E0</a></div>
15 </td>
16 </tr>
17 <tr class="encrH">
18 <td dir="ltr">End of encrypted message</td>
19 </tr>
20 </table>
21 </div>
22 </div>
23 </body>
24</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlinepgpencrypted-error.mbox.tree b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlinepgpencrypted-error.mbox.tree
new file mode 100644
index 00000000..6680b8bc
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlinepgpencrypted-error.mbox.tree
@@ -0,0 +1,3 @@
1 * MimeTreeParser::MessagePartList
2 * MimeTreeParser::TextMessagePart
3 * MimeTreeParser::EncryptedMessagePart
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlinepgpencrypted.mbox b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlinepgpencrypted.mbox
new file mode 100644
index 00000000..b581602c
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlinepgpencrypted.mbox
@@ -0,0 +1,29 @@
1From test@kolab.org Wed, 25 May 2011 23:49:40 +0100
2From: OpenPGP Test <test@kolab.org>
3To: test@kolab.org
4Subject: inlinepgpencrypted
5Date: Wed, 25 May 2011 23:49:40 +0100
6Message-ID: <1786696.yKXrOjjflF@herrwackelpudding.localhost>
7X-KMail-Transport: GMX
8X-KMail-Fcc: 28
9X-KMail-Drafts: 7
10X-KMail-Templates: 9
11User-Agent: KMail/4.6 beta5 (Linux/2.6.34.7-0.7-desktop; KDE/4.6.41; x86_64; git-0269848; 2011-04-19)
12MIME-Version: 1.0
13Content-Transfer-Encoding: 7Bit
14Content-Type: text/plain; charset="us-ascii"
15
16-----BEGIN PGP MESSAGE-----
17Version: GnuPG v2.0.15 (GNU/Linux)
18
19hQEMAwzOQ1qnzNo7AQf/a3aNTLpQBfcUr+4AKsZQLj4h6z7e7a5AaCW8AG0wrbxN
20kBYB7E5jdZh45DX/99gvoZslthWryUCX2kKZ3LtIllxKVjqNuK5hSt+SAuKkwiMR
21Xcbf1KFKENKupgGSO9B2NJRbjoExdJ+fC3mGXnO3dT7xJJAo3oLE8Nivu+Bj1peY
22E1wCf+vcTwVHFrA7SV8eMRb9Z9wBXmU8Q8e9ekJ7ZsRX3tMeBs6jvscVvfMf6DYY
23N14snZBZuGNKT9a3DPny7IC1S0lHcaam34ogWwMi3FxPGJt/Lg52kARlkF5TDhcP
24N6H0EB/iqDRjOOUoEVm8um5XOSR1FpEiAdD0DON3y9JPATnrYq7sgYZz3BVImYY+
25N/jV8fEiN0a34pcOq8NQedMuOsJHNBS5MtbQH/kJLq0MXBpXekGlHo4MKw0trISc
26Rw3pW6/BFfhPJLni29g9tw==
27=fRFW
28-----END PGP MESSAGE-----
29
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlinepgpencrypted.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlinepgpencrypted.mbox.html
new file mode 100644
index 00000000..1f695bdf
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlinepgpencrypted.mbox.html
@@ -0,0 +1,26 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <table cellspacing="1" cellpadding="1" class="encr">
9 <tr class="encrH">
10 <td dir="ltr">Encrypted message</td>
11 </tr>
12 <tr class="encrB">
13 <td>
14 <div class="noquote">
15 <div dir="ltr">some random text</div>
16 </div>
17 </td>
18 </tr>
19 <tr class="encrH">
20 <td dir="ltr">End of encrypted message</td>
21 </tr>
22 </table>
23 </div>
24 </div>
25 </body>
26</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlinepgpencrypted.mbox.inProgress.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlinepgpencrypted.mbox.inProgress.html
new file mode 100644
index 00000000..e5eb55d0
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlinepgpencrypted.mbox.inProgress.html
@@ -0,0 +1,24 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <table cellspacing="1" cellpadding="1" class="encr">
9 <tr class="encrH">
10 <td dir="ltr">Please wait while the message is being decrypted...</td>
11 </tr>
12 <tr class="encrB">
13 <td>
14 <div style="font-size:x-large; text-align:center; padding:20pt;"/>
15 </td>
16 </tr>
17 <tr class="encrH">
18 <td dir="ltr">End of encrypted message</td>
19 </tr>
20 </table>
21 </div>
22 </div>
23 </body>
24</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlinepgpencrypted.mbox.tree b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlinepgpencrypted.mbox.tree
new file mode 100644
index 00000000..6680b8bc
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/inlinepgpencrypted.mbox.tree
@@ -0,0 +1,3 @@
1 * MimeTreeParser::MessagePartList
2 * MimeTreeParser::TextMessagePart
3 * MimeTreeParser::EncryptedMessagePart
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/mailheader.css b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/mailheader.css
new file mode 100644
index 00000000..10181957
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/mailheader.css
@@ -0,0 +1,512 @@
1div.header {
2 margin-bottom: 10pt ! important;
3}
4
5table.textAtm {
6 margin-top: 10pt ! important;
7 margin-bottom: 10pt ! important;
8}
9
10tr.textAtmH,
11tr.textAtmB,
12tr.rfc822B {
13 font-weight: normal ! important;
14}
15
16tr.signInProgressH,
17tr.rfc822H,
18tr.encrH,
19tr.signOkKeyOkH,
20tr.signOkKeyBadH,
21tr.signWarnH,
22tr.signErrH {
23 font-weight: bold ! important;
24}
25
26tr.textAtmH td,
27tr.textAtmB td {
28 padding: 3px ! important;
29}
30
31table.rfc822 {
32 width: 100% ! important;
33 border: solid 1px black ! important;
34 margin-top: 10pt ! important;
35 margin-bottom: 10pt ! important;
36}
37
38table.textAtm,
39table.encr,
40table.signWarn,
41table.signErr,
42table.signOkKeyBad,
43table.signOkKeyOk,
44table.signInProgress,
45div.fancy.header table {
46 width: 100% ! important;
47 border-width: 0px ! important;
48 line-height: normal;
49}
50
51div.htmlWarn {
52 margin: 0px 5% ! important;
53 padding: 10px ! important;
54 text-align: left ! important;
55 line-height: normal;
56}
57
58div.fancy.header > div {
59 font-weight: bold ! important;
60 padding: 4px ! important;
61 line-height: normal;
62}
63
64div.fancy.header table {
65 padding: 2px ! important;
66 text-align: left ! important;
67 border-collapse: separate ! important;
68}
69
70div.fancy.header table th {
71 font-family: "Sans Serif" ! important;
72 font-size: 0px ! important;
73
74 padding: 0px ! important;
75 white-space: nowrap ! important;
76 border-spacing: 0px ! important;
77 text-align: left ! important;
78 vertical-align: top ! important;
79 background-color: #d6d2d0 ! important;
80 color: #221f1e ! important;
81 border: 1px ! important;
82}
83
84div.fancy.header table td {
85 font-family: "Sans Serif" ! important;
86 font-size: 0px ! important;
87
88 padding: 0px ! important;
89 border-spacing: 0px ! important;
90 text-align: left ! important;
91 vertical-align: top ! important;
92 width: 100% ! important;
93 background-color: #d6d2d0 ! important;
94 color: #221f1e ! important;
95 border: 1px ! important;
96}
97
98div.fancy.header table a:hover {
99 background-color: transparent ! important;
100}
101
102span.pimsmileytext {
103 position: absolute;
104 top: 0px;
105 left: 0px;
106 visibility: hidden;
107}
108
109img.pimsmileyimg {
110}
111
112div.quotelevelmark {
113 position: absolute;
114 margin-left:-10px;
115}
116
117@media screen {
118
119body {
120 font-family: "Sans Serif" ! important;
121 font-size: 0px ! important;
122 color: #1f1c1b ! important;
123 background-color: #ffffff ! important;
124}
125
126a {
127 color: #0057ae ! important;
128 text-decoration: none ! important;
129}
130
131a.white {
132 color: white ! important;
133}
134
135a.black {
136 color: black ! important;
137}
138
139table.textAtm { background-color: #1f1c1b ! important; }
140
141tr.textAtmH {
142 background-color: #ffffff ! important;
143 font-family: "Sans Serif" ! important;
144 font-size: 0px ! important;
145}
146
147tr.textAtmB {
148 background-color: #ffffff ! important;
149}
150
151table.signInProgress,
152table.rfc822 {
153 background-color: #ffffff ! important;
154}
155
156tr.signInProgressH,
157tr.rfc822H {
158 font-family: "Sans Serif" ! important;
159 font-size: 0px ! important;
160}
161
162table.encr {
163 background-color: #0069cc ! important;
164}
165
166tr.encrH {
167 background-color: #0080ff ! important;
168 color: #ffffff ! important;
169 font-family: "Sans Serif" ! important;
170 font-size: 0px ! important;
171}
172
173tr.encrB { background-color: #e0f0ff ! important; }
174
175table.signOkKeyOk {
176 background-color: #33cc33 ! important;
177}
178
179tr.signOkKeyOkH {
180 background-color: #40ff40 ! important;
181 color: #27ae60 ! important;
182 font-family: "Sans Serif" ! important;
183 font-size: 0px ! important;
184}
185
186tr.signOkKeyOkB { background-color: #e8ffe8 ! important; }
187
188table.signOkKeyBad {
189 background-color: #cccc33 ! important;
190}
191
192tr.signOkKeyBadH {
193 background-color: #ffff40 ! important;
194 color: #f67400 ! important;
195 font-family: "Sans Serif" ! important;
196 font-size: 0px ! important;
197}
198
199tr.signOkKeyBadB { background-color: #ffffe8 ! important; }
200
201table.signWarn {
202 background-color: #cccc33 ! important;
203}
204
205tr.signWarnH {
206 background-color: #ffff40 ! important;
207 color: #f67400 ! important;
208 font-family: "Sans Serif" ! important;
209 font-size: 0px ! important;
210}
211
212tr.signWarnB { background-color: #ffffe8 ! important; }
213
214table.signErr {
215 background-color: #cc0000 ! important;
216}
217
218tr.signErrH {
219 background-color: #ff0000 ! important;
220 color: #da4453 ! important;
221 font-family: "Sans Serif" ! important;
222 font-size: 0px ! important;
223}
224
225tr.signErrB { background-color: #ffe0e0 ! important; }
226
227div.htmlWarn {
228 border: 2px solid #ff4040 ! important;
229 line-height: normal;
230}
231
232div.header {
233 font-family: "Sans Serif" ! important;
234 font-size: 0px ! important;
235}
236
237div.fancy.header > div {
238 background-color: #43ace8 ! important;
239 color: #ffffff ! important;
240 border: solid #221f1e 1px ! important;
241 line-height: normal;
242}
243
244div.fancy.header > div a[href] { color: #ffffff ! important; }
245
246div.fancy.header > div a[href]:hover { text-decoration: underline ! important; }
247
248div.fancy.header > div.spamheader {
249 background-color: #cdcdcd ! important;
250 border-top: 0px ! important;
251 padding: 3px ! important;
252 color: black ! important;
253 font-weight: bold ! important;
254 font-size: smaller ! important;
255}
256
257div.fancy.header > table.outer {
258 background-color: #d6d2d0 ! important;
259 color: #221f1e ! important;
260 border-bottom: solid #221f1e 1px ! important;
261 border-left: solid #221f1e 1px ! important;
262 border-right: solid #221f1e 1px ! important;
263}
264
265div.senderpic{
266 padding: 0px ! important;
267 font-size:0.8em ! important;
268 border:1px solid #b3aba7 ! important;
269 background-color:#d6d2d0 ! important;
270}
271
272div.senderstatus{
273 text-align:center ! important;
274}
275
276div.quotelevel1 {
277 color: #008000 ! important;
278 font-style: italic ! important;
279}
280
281div.quotelevel2 {
282 color: #007000 ! important;
283 font-style: italic ! important;
284}
285
286div.quotelevel3 {
287 color: #006000 ! important;
288 font-style: italic ! important;
289}
290
291div.deepquotelevel1 {
292 color: #008000 ! important;
293 font-style: italic ! important;
294}
295
296div.deepquotelevel2 {
297 color: #007000 ! important;
298 font-style: italic ! important;
299}
300
301div.deepquotelevel3 {
302 color: #006000 ! important;
303 font-style: italic ! important;
304}
305
306blockquote {
307 margin: 4pt 0 4pt 0;
308 padding: 0 0 0 1em;
309 border-left: 2px solid #008000;
310 unicode-bidi: -webkit-plaintext
311}
312
313blockquote blockquote {
314 margin: 4pt 0 4pt 0;
315 padding: 0 0 0 1em;
316 border-left: 2px solid #007000;
317 unicode-bidi: -webkit-plaintext
318}
319
320blockquote blockquote blockquote {
321 margin: 4pt 0 4pt 0;
322 padding: 0 0 0 1em;
323 border-left: 2px solid #006000;
324 unicode-bidi: -webkit-plaintext
325}
326
327blockquote blockquote blockquote blockquote {
328 margin: 4pt 0 4pt 0;
329 padding: 0 0 0 1em;
330 border-left: 2px solid #008000;
331 unicode-bidi: -webkit-plaintext
332}
333
334blockquote blockquote blockquote blockquote blockquote {
335 margin: 4pt 0 4pt 0;
336 padding: 0 0 0 1em;
337 border-left: 2px solid #007000;
338 unicode-bidi: -webkit-plaintext
339}
340
341blockquote blockquote blockquote blockquote blockquote blockquote {
342 margin: 4pt 0 4pt 0;
343 padding: 0 0 0 1em;
344 border-left: 2px solid #006000;
345 unicode-bidi: -webkit-plaintext
346}
347
348blockquote blockquote blockquote blockquote blockquote blockquote blockquote {
349 margin: 4pt 0 4pt 0;
350 padding: 0 0 0 1em;
351 border-left: 2px solid #008000;
352 unicode-bidi: -webkit-plaintext
353}
354
355blockquote blockquote blockquote blockquote blockquote blockquote blockquote blockquote {
356 margin: 4pt 0 4pt 0;
357 padding: 0 0 0 1em;
358 border-left: 2px solid #007000;
359 unicode-bidi: -webkit-plaintext
360}
361
362blockquote blockquote blockquote blockquote blockquote blockquote blockquote blockquote blockquote {
363 margin: 4pt 0 4pt 0;
364 padding: 0 0 0 1em;
365 border-left: 2px solid #006000;
366 unicode-bidi: -webkit-plaintext
367}
368
369.quotemarks{
370 color:transparent;
371 font-size:0px;
372}
373
374}
375@media print {
376
377body {
378 font-family: "Sans Serif" ! important;
379 font-size: 9pt ! important;
380 color: #000000 ! important;
381 background-color: #ffffff ! important
382}
383
384tr.textAtmH,
385tr.signInProgressH,
386tr.rfc822H,
387tr.encrH,
388tr.signOkKeyOkH,
389tr.signOkKeyBadH,
390tr.signWarnH,
391tr.signErrH,
392div.header {
393 font-family: "Sans Serif" ! important;
394 font-size: 9pt ! important;
395}
396
397div.fancy.header > div {
398 background-color: #d6d2d0 ! important;
399 color: #221f1e ! important;
400 padding: 4px ! important;
401 border: solid #221f1e 1px ! important;
402 line-height: normal;
403}
404
405div.fancy.header > div a[href] { color: #221f1e ! important; }
406
407div.fancy.header > table.outer{
408 background-color: #d6d2d0 ! important;
409 color: #221f1e ! important;
410 border-bottom: solid #221f1e 1px ! important;
411 border-left: solid #221f1e 1px ! important;
412 border-right: solid #221f1e 1px ! important;
413}
414
415div.spamheader {
416 display:none ! important;
417}
418
419div.htmlWarn {
420 border: 2px solid #ffffff ! important;
421 line-height: normal;
422}
423
424div.senderpic{
425 font-size:0.8em ! important;
426 border:1px solid black ! important;
427 background-color:#d6d2d0 ! important;
428}
429
430div.senderstatus{
431 text-align:center ! important;
432}
433
434div.noprint {
435 display:none ! important;
436}
437
438blockquote {
439 margin: 4pt 0 4pt 0;
440 padding: 0 0 0 1em;
441 border-left: 2px solid #008000;
442 unicode-bidi: -webkit-plaintext
443}
444
445blockquote blockquote {
446 margin: 4pt 0 4pt 0;
447 padding: 0 0 0 1em;
448 border-left: 2px solid #007000;
449 unicode-bidi: -webkit-plaintext
450}
451
452blockquote blockquote blockquote {
453 margin: 4pt 0 4pt 0;
454 padding: 0 0 0 1em;
455 border-left: 2px solid #006000;
456 unicode-bidi: -webkit-plaintext
457}
458
459blockquote blockquote blockquote blockquote {
460 margin: 4pt 0 4pt 0;
461 padding: 0 0 0 1em;
462 border-left: 2px solid #008000;
463 unicode-bidi: -webkit-plaintext
464}
465
466blockquote blockquote blockquote blockquote blockquote {
467 margin: 4pt 0 4pt 0;
468 padding: 0 0 0 1em;
469 border-left: 2px solid #007000;
470 unicode-bidi: -webkit-plaintext
471}
472
473blockquote blockquote blockquote blockquote blockquote blockquote {
474 margin: 4pt 0 4pt 0;
475 padding: 0 0 0 1em;
476 border-left: 2px solid #006000;
477 unicode-bidi: -webkit-plaintext
478}
479
480blockquote blockquote blockquote blockquote blockquote blockquote blockquote {
481 margin: 4pt 0 4pt 0;
482 padding: 0 0 0 1em;
483 border-left: 2px solid #008000;
484 unicode-bidi: -webkit-plaintext
485}
486
487blockquote blockquote blockquote blockquote blockquote blockquote blockquote blockquote {
488 margin: 4pt 0 4pt 0;
489 padding: 0 0 0 1em;
490 border-left: 2px solid #007000;
491 unicode-bidi: -webkit-plaintext
492}
493
494blockquote blockquote blockquote blockquote blockquote blockquote blockquote blockquote blockquote {
495 margin: 4pt 0 4pt 0;
496 padding: 0 0 0 1em;
497 border-left: 2px solid #006000;
498 unicode-bidi: -webkit-plaintext
499}
500
501.quotemarks{
502 color:transparent;
503 font-size:0px;
504}
505
506.quotemarksemptyline{
507 color:transparent;
508 font-size:0px;
509 line-height: 12pt;
510}
511
512}
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/no-content-type.mbox b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/no-content-type.mbox
new file mode 100644
index 00000000..ad050d8c
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/no-content-type.mbox
@@ -0,0 +1,7 @@
1From: hans@example.com
2To: karl@example.com
3Subject: Simple Mail Without Content-Type Header
4Date: Sat, 15 May 2010 10:52:24 +0200
5MIME-Version: 1.0
6
7asdfasdf \ No newline at end of file
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/no-content-type.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/no-content-type.mbox.html
new file mode 100644
index 00000000..9e3eb752
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/no-content-type.mbox.html
@@ -0,0 +1,14 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <div class="noquote">
9 <div dir="ltr">asdfasdf</div>
10 </div>
11 </div>
12 </div>
13 </body>
14</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/no-content-type.mbox.tree b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/no-content-type.mbox.tree
new file mode 100644
index 00000000..c003ec97
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/no-content-type.mbox.tree
@@ -0,0 +1,3 @@
1 * MimeTreeParser::MessagePartList
2 * MimeTreeParser::TextMessagePart
3 * MimeTreeParser::MessagePart
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encoded.mbox b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encoded.mbox
new file mode 100644
index 00000000..168c8dc4
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encoded.mbox
@@ -0,0 +1,33 @@
1Content-Type: text/plain; charset="us-ascii"
2Content-Transfer-Encoding: 7bit
3
4-----BEGIN PGP MESSAGE-----
5Version: GnuPG v2
6
7owGdk31MlVUcx5G3jClvI1JH7GiOil0uYJATBW8FK6HkDtBJI93heQ6Xk8/L9Tnn
8XExALDfJQaJutl4UzUoCIxDwos0XTLktqJaJkJE5QAU3yMKpkRqdc+GC/dsfz/bs
9ec7v+/v+Pr/v2TnbxytgxvUt8T99cCirZEa9zw/5fopuIHX1xsjjr4gXgO2EqUDW
10+WdAMAVQRdQEJF0jSKKIMgNAGdsxkbBmA0jB1AxSdQ1JAAIZQxUo/BgjZpCN5Kn6
11SVEVMgPzX69CJCENEqFqYwgo2MYUCKAENjCu4MAOZBgQCNH/9NUoAoVIkw1kTLfl
126qquKJhMeDYDqwERQRoVf9yOkI2/KTgfGbp4J5R3ViGlvGTKoANTiABk3I4ZrORF
13FGsSlhmXmTSJNKyaAKPArhsUAjc1oECOAcOHbXp8samehJOAhsSAAxViSUzKFGpg
14CSPCoWm6BiiXFNb/9waeRxqCmuhpM6ADy5P+zGAVBbyAHwYF/CQxATtTHFiDXIP7
15Uu3IEPZMDytieYokUhQOEpENTLCRuEegYYLFERXatMm5+dIoINCGBVIzeJEZMB8L
16mzK043xG3OimUQvHwF4IDUT5kqc5FyBmw1yJd1AAFtZkjKh7BN7GAd07nkSK+ayG
175gEpOEoSUwnUPPQFc13GfOGGTjwfPcFSMQ8aF3OnbRoIDxJUEGGQ43PvyyjUNYm5
18R+L+dQnzGg1SXeCw8wDTiemoG4YIIH/s0OCjY5E/VRfsTLyESO61GVgW++c1qlBd
19oQGDUYNre9xPB0NcEnFZJoPppi4o83gU8MxxFCK9Ewvk94bz4Lk2B2z3jvD1mhHg
205e/nLW60V8CjwZ7LTpKCxjNIzhNRXcER/6RVXUzaGdocqGY3vd+5LO7+qSV3pdFP
21+s6tv3Jp8EZg+V9qza9zKhf4b3xn4MfVSYOW+qaihPsHrc3O5hpzXCV77sROqxWw
22vbfaOrpClvTszr8+8pVc/ELc0aeOm+856kqfDYNZmUXt33+Y1nLT4nvH0tomz47f
23tfzP+dlpQW8vRj3v+qcXyXpef3Hvldy2gfECZ3LMGdMomlsR8fQm42zMcLh3eW1N
24Xszwqt5dLXimuVk6vb+/etNN8xgeuhoWeb79myzXg9jUv11xbQ8OR9/wnXP7QGzj
25kWN+v+RpX58qa2dbTr2VeMn/aPJrOZ3ZYS0dW7s6dhcu/3lzT1DdhS8zGgIrynIH
26tjkf21tmXtZ8pOjj/a7xsahsEGKxLG4h+St9Oj9ace1kn1+vdWl06+nEvhPlOWnh
275OVrw1nzTv6+pjo3K9Vr3YFZeZmu6K1jA2OW7M96Bs85ChL23ck95Cx5/bfuHeX3
28wktCXYN1/fvWpxxIfyk2/pm0N4bCRyrfS8z13VPNMr6rckbuP1YS2rp0z27X/FEy
29FPHm4OHHv7iXsuaPg5dpg6XWN9B/SG/81Bq8tv5WwMjFRVUZId6Rn+sL68uudls3
30z+vMvLwwrdw5t+Pb5ISuppDUVkvmme4nTahhwYX7setmpdxt29ZeO5MFPFK6Pep2
31+nlHdfFa78ZFpSMV/Q1n/wU=
32=zzr4
33-----END PGP MESSAGE-----
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encoded.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encoded.mbox.html
new file mode 100644
index 00000000..585bb4f0
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encoded.mbox.html
@@ -0,0 +1,35 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <table cellspacing="1" cellpadding="1" class="signWarn">
9 <tr class="signWarnH">
10 <td dir="ltr">
11 <table cellspacing="0" cellpadding="0" width="100%">
12 <tr>
13 <td>Not enough information to check signature validity.</td>
14 <td align="right">
15 <a href="kmail:showSignatureDetails">Show Details</a>
16 </td>
17 </tr>
18 </table>
19 </td>
20 </tr>
21 <tr class="signWarnB">
22 <td>
23 <div class="noquote">
24 <div dir="ltr">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Donec et mollis dolor. Praesent et diam eget libero egestas mattis sit amet vitae augue. Nam tincidunt congue enim, ut porta lorem lacinia consectetur. Donec ut libero sed arcu vehicula ultricies a non tortor. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean ut gravida lorem. Ut turpis felis, pulvinar a semper sed, adipiscing id dolor. Pellentesque auctor nisi id magna consequat sagittis. Curabitur dapibus enim sit amet elit pharetra tincidunt feugiat nisl imperdiet. Ut convallis libero in urna ultrices accumsan. Donec sed odio eros. Donec viverra mi quis quam pulvinar at malesuada arcu rhoncus. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. In rutrum accumsan ultricies. Mauris vitae nisi at sem facilisis semper ac in est.</div>
25 </div>
26 </td>
27 </tr>
28 <tr class="signWarnH">
29 <td dir="ltr">End of signed message</td>
30 </tr>
31 </table>
32 </div>
33 </div>
34 </body>
35</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encoded.mbox.tree b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encoded.mbox.tree
new file mode 100644
index 00000000..ea8223fd
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encoded.mbox.tree
@@ -0,0 +1,4 @@
1 * MimeTreeParser::MessagePartList
2 * MimeTreeParser::TextMessagePart
3 * MimeTreeParser::EncryptedMessagePart
4 * MimeTreeParser::SignedMessagePart
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted+signed.mbox b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted+signed.mbox
new file mode 100644
index 00000000..fbe5ce7f
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted+signed.mbox
@@ -0,0 +1,46 @@
1From test@kolab.org Wed, 08 Sep 2010 17:02:52 +0200
2From: OpenPGP Test <test@kolab.org>
3To: test@kolab.org
4Subject: OpenPGP encrypted
5Date: Wed, 08 Sep 2010 17:02:52 +0200
6User-Agent: KMail/4.6 pre (Linux/2.6.34-rc2-2-default; KDE/4.5.60; x86_64; ; )
7MIME-Version: 1.0
8Content-Type: multipart/encrypted; boundary="nextPart1357031.ppLHckZtsp"; protocol="application/pgp-encrypted"
9Content-Transfer-Encoding: 7Bit
10
11
12--nextPart1357031.ppLHckZtsp
13Content-Type: application/pgp-encrypted
14Content-Disposition: attachment
15
16Version: 1
17--nextPart1357031.ppLHckZtsp
18Content-Type: application/octet-stream
19Content-Disposition: inline; filename="msg.asc"
20
21-----BEGIN PGP MESSAGE-----
22Version: GnuPG v2
23
24hQEMAwzOQ1qnzNo7AQgAkQI925mOOybpzcGcjUE7jmfMVoUvJW6Br9Zx9UEtko5H
25fdmnrrUhFDyhBdwcg2E6AChipNcJjZhdX17lNAO0kI2IoPJAEkX9lyhjoiVEH/M8
26xmJEKFRejYzefx0S8esKyqqtfAmMKfsA5HmKRY8iDmQnI5d/FKhkcqLTJYo7fQyL
27rEEycdr5PU5OJbMtE5+8+kbmG8PywjiCCad68FXakXIEFyWX1A99W/0ScWtqrqDB
28kuQSdxJs4aAZWopxGKxDobt/qVyG6W6+PUnLx3eg80KytcWNxLJRV7WEJMj4OYCU
29JdHrh4J1DTTRbuRmqx9de3fBDFHNNZpJP43UJYJWtoSMAxiZbYU0+6KtAQQAnRxD
30XNwzBSmPk9NG3HYf/RleTAS8/fMp3D973Y3VF5JV72ljWqZO/1o/3RIpD0pl5nSE
31nI0wPyncmPvAgQl5EAKTdYonKLuMCTbQ4eT7vkOdfA/HUxgaoE8ppH/OYXDDiqz/
322GlNCCHcaQcVWwkHbWWgyhd/VvB7Mt9PVqPgTsHSwQ4BZf/JDwWe3MDAg21Raryf
33dN4ZmWUzd3osDIRyy8H2hZL9vgZ3r0auIP6DMyYrPf3yTj9ApZeBiIpgBvqMwvR/
348si6r1JaXr48KRCxAkn9fIXe2jMwQE2bk5tYDrTXY868V3DA5iKq1dgXUSn0tzG3
350x9pjaOFkwxm0wtlpwnhZREJ9/ieIY7hy6p7CDpq4ZGRh8jQta4tWrjxN0wly+Dh
36a9TjzHzgTh9BTh7vjcDLitOQFL5NcCQtK717FQ5Z1DRnkHDVLPoyPnW+5sllOVr9
37UcqNXwrPbrtElFjHULL5Y1pem1+AE6nCAtlvCAc7cil6QDsU508sOXW7BhTWATIU
38tGHg/nIC0qVs24LR3MyYrwvfXL2JOkN/IK/g+0bbs6DMYVYaEOjcjqtidxWuLsE5
39aFEwlUFzcTcSauW+AOKwH+YFihRnlh9+4taaZxGZeutb12D5u8eEk+3nyVcsdogY
40k/nT6ElN8i/Z2vgcxL6ABvB+g/bZmp/eCJphDNYc00lP8NMC4EEJvTZL438ThJ3j
41wz0Qo4PlV0/OMXYxP2iZup2/rCe+pkmc9Gyz3Mkal4/9bBtIgP9cWBR7JZLMrK50
42KO4+NtrZYEY4JXRJlAo=
43=1lYB
44-----END PGP MESSAGE-----
45
46--nextPart1357031.ppLHckZtsp--
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted+signed.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted+signed.mbox.html
new file mode 100644
index 00000000..54c58b85
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted+signed.mbox.html
@@ -0,0 +1,52 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <table cellspacing="1" cellpadding="1" class="encr">
9 <tr class="encrH">
10 <td dir="ltr">Encrypted message</td>
11 </tr>
12 <tr class="encrB">
13 <td>
14 <table cellspacing="1" cellpadding="1" class="signOkKeyOk">
15 <tr class="signOkKeyOkH">
16 <td dir="ltr">
17 <table cellspacing="0" cellpadding="0" width="100%">
18 <tr>
19 <td>Signed by <a href="mailto:test@kolab.org">test@kolab.org</a>.</td>
20 <td align="right">
21 <a href="kmail:showSignatureDetails">Show Details</a>
22 </td>
23 </tr>
24 </table>
25 </td>
26 </tr>
27 <tr class="signOkKeyOkB">
28 <td>
29 <div style="position: relative; word-wrap: break-word">
30 <a name="att"/>
31 <div id="attachmentDiv">
32 <div class="noquote">
33 <div dir="ltr">encrypted message text</div>
34 </div>
35 </div>
36 </div>
37 </td>
38 </tr>
39 <tr class="signOkKeyOkH">
40 <td dir="ltr">End of signed message</td>
41 </tr>
42 </table>
43 </td>
44 </tr>
45 <tr class="encrH">
46 <td dir="ltr">End of encrypted message</td>
47 </tr>
48 </table>
49 </div>
50 </div>
51 </body>
52</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted+signed.mbox.tree b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted+signed.mbox.tree
new file mode 100644
index 00000000..7d5bbeb7
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted+signed.mbox.tree
@@ -0,0 +1,5 @@
1 * MimeTreeParser::MessagePartList
2 * MimeTreeParser::EncryptedMessagePart
3 * MimeTreeParser::SignedMessagePart
4 * MimeTreeParser::TextMessagePart
5 * MimeTreeParser::MessagePart
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-applemail.mbox b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-applemail.mbox
new file mode 100644
index 00000000..f5d083ff
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-applemail.mbox
@@ -0,0 +1,52 @@
1Received: from konqi
2From: "Konqui" <konqi@example.org>
3To: "Bird" <bird@example.org>
4Subject: gpg problem 1/2
5Date: Tue, 22 Mar 2016 17:09:18 +0100
6Message-ID: <123456@example.org>
7Accept-Language: de-DE, en-US
8Content-Language: en-US
9Content-Type: multipart/mixed;
10 boundary="_003_55514CDCA78D430384C5F0810DF10C7Adsmpgde_"
11MIME-Version: 1.0
12
13
14--_003_55514CDCA78D430384C5F0810DF10C7Adsmpgde_
15Content-Type: text/plain; charset="us-ascii"
16
17
18--_003_55514CDCA78D430384C5F0810DF10C7Adsmpgde_
19Content-Type: application/pgp-encrypted;
20 name="PGPMIME Versions Identification"
21Content-Description: PGP/MIME Versions Identification
22Content-Disposition: attachment; filename="PGPMIME Versions Identification";
23 size=77; creation-date="Tue, 22 Mar 2016 16:09:18 GMT";
24 modification-date="Tue, 22 Mar 2016 16:09:18 GMT"
25Content-ID: <D82BB3DF89947646AD6C4C24C8C54BAC@example.org>
26Content-Transfer-Encoding: base64
27
28VmVyc2lvbjogMQ0NCg==
29
30--_003_55514CDCA78D430384C5F0810DF10C7Adsmpgde_
31Content-Type: application/octet-stream; name="encrypted.asc"
32Content-Description: OpenPGP encrypted message.asc
33Content-Disposition: attachment; filename="encrypted.asc"; size=872;
34 creation-date="Tue, 22 Mar 2016 16:09:18 GMT";
35 modification-date="Tue, 22 Mar 2016 16:09:18 GMT"
36Content-ID: <58A712E65AB1824AB726904A6449178F@example.org>
37Content-Transfer-Encoding: base64
38
39LS0tLS1CRUdJTiBQR1AgTUVTU0FHRS0tLS0tClZlcnNpb246IEdudVBHIHYxCkNvbW1lbnQ6IFVz
40aW5nIEdudVBHIHdpdGggSWNlZG92ZSAtIGh0dHA6Ly93d3cuZW5pZ21haWwubmV0LwoKaEl3REdK
41bHRoVFQ3b3EwQkEvOU50TExYYmlJSlZTNnBPeW53RWVTem5yUUs3a1lWbGE4Uk00My8vSkVDQ2tH
42SgphekVhU0J6bmFiQnY2ZXBhRm1RdFZITE1YbENiWm5NbVc5bG95cVBCZk1vQW1zNmtLS0JkRy9q
43cWh1czg5aVhFCitzZVhuZ0MyMzNWYS9nWk1iMkR4T3FJb2tWTmZqOXRwUjd4UTh3Uy9qSFREaUxO
44YzFHT1FDN2t1NDJ6MmJOTEEKSVFGUkQvcWJCRno4OWhVNHdQNGNZb0F5c09uRURvakZyc3JuQ2lk
45VEhKT0pybmRNNlBQVXRIL2pRQ3lmci9FRwoydFNwSndZS3ZtVDZseTN5cWFHTEJ0UlBJeGl2K2RN
46ZSs3eXcwdDQwcWJqdnZhVEdhdkVyRUJKRUtYNWVXYlROCi9zamFqSHBVSHFzNlNJaU1oZUg5ZHIr
47V2Z6Rk9OdFZiUEVnR1JtT0VSaGxnVGwvbkxvODZBWnBqSnJvSUdLSkoKdFRIQ2NvUUdBV0crTjd3
48ckNFMVJ4UjBra01zNG5Sb3pqMFRMdTZaeVhNcytIMDYzTWV3VFBOeE5BaVFUMU5iaQp1ZEtXbWZM
49Qmx4bjA2cCtKRHpVS3hqOFBGd09iZGJ4VHZBQ3piQXZCWTFhSE1RPT0KPW1MbDMKLS0tLS1FTkQg
50UEdQIE1FU1NBR0UtLS0tLQoK
51
52--_003_55514CDCA78D430384C5F0810DF10C7Adsmpgde_--
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-applemail.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-applemail.mbox.html
new file mode 100644
index 00000000..ca8d7fbb
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-applemail.mbox.html
@@ -0,0 +1,39 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <a name="att1"/>
9 <div id="attachmentDiv1"/>
10 <a name="att2"/>
11 <div id="attachmentDiv2">
12 <table cellspacing="1" cellpadding="1" class="encr">
13 <tr class="encrH">
14 <td dir="ltr">Encrypted message</td>
15 </tr>
16 <tr class="encrB">
17 <td>
18 <div style="position: relative; word-wrap: break-word">
19 <a name="att"/>
20 <div id="attachmentDiv">
21 <a name="att1"/>
22 <div id="attachmentDiv1">
23 <div class="noquote">
24 <div dir="ltr">test</div>
25 </div>
26 </div>
27 </div>
28 </div>
29 </td>
30 </tr>
31 <tr class="encrH">
32 <td dir="ltr">End of encrypted message</td>
33 </tr>
34 </table>
35 </div>
36 </div>
37 </div>
38 </body>
39</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-applemail.mbox.tree b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-applemail.mbox.tree
new file mode 100644
index 00000000..8ef3df2d
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-applemail.mbox.tree
@@ -0,0 +1,7 @@
1 * MimeTreeParser::MessagePartList
2 * MimeTreeParser::MimeMessagePart
3 * MimeTreeParser::TextMessagePart
4 * MimeTreeParser::EncryptedMessagePart
5 * MimeTreeParser::MimeMessagePart
6 * MimeTreeParser::TextMessagePart
7 * MimeTreeParser::MessagePart
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-attachment-and-non-encrypted-attachment.mbox b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-attachment-and-non-encrypted-attachment.mbox
new file mode 100644
index 00000000..2d9726ea
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-attachment-and-non-encrypted-attachment.mbox
@@ -0,0 +1,115 @@
1From test@kolab.org Fri May 01 15:12:47 2015
2From: testkey <test@kolab.org>
3To: you@you.com
4Subject: enc & non enc attachment
5Date: Fri, 01 May 2015 17:12:47 +0200
6Message-ID: <13897561.XENKdJMSlR@tabin.local>
7X-KMail-Identity: 1197256126
8User-Agent: KMail/4.13.0.1 (Linux/3.19.1-towo.1-siduction-amd64; KDE/4.14.2; x86_64; git-cd33034; 2015-04-11)
9MIME-Version: 1.0
10Content-Type: multipart/mixed; boundary="nextPart1939768.sIoLGH0PD8"
11Content-Transfer-Encoding: 7Bit
12
13This is a multi-part message in MIME format.
14
15--nextPart1939768.sIoLGH0PD8
16Content-Type: multipart/encrypted; boundary="nextPart2814166.CHKktCGlQ3"; protocol="application/pgp-encrypted"
17
18
19--nextPart2814166.CHKktCGlQ3
20Content-Type: application/pgp-encrypted
21Content-Disposition: attachment
22Content-Transfer-Encoding: 7Bit
23
24Version: 1
25--nextPart2814166.CHKktCGlQ3
26Content-Type: application/octet-stream
27Content-Disposition: inline; filename="msg.asc"
28Content-Transfer-Encoding: 7Bit
29
30-----BEGIN PGP MESSAGE-----
31Version: GnuPG v2
32
33hIwDGJlthTT7oq0BA/9cXFQ6mN9Vxnc2B9M10odS3/6z1tsIY9oJdsiOjpfxqapX
34P7nOzR/jNWdFQanXoG1SjAcY2FeZEN0c3SkxEM6R5QVF1vMh/Xsni1clI+peZyVT
35Z4OSU74YCfYLg+cgDnPCF3kyNPVe6Z1pnfWOCZNCG3rpApw6UVLN63ScWC6eQIUB
36DAMMzkNap8zaOwEIANKHn1svvj+hBOIZYf8R+q2Bw7cd4xEChiJ7uQLnD98j0Fh1
3785v7/8JbZx6rEDDenPp1mCciDodb0aCmi0XLuzJz2ANGTVflfq+ZA+v1pwLksWCs
380YcHLEjOJzjr3KKmvu6wqnun5J2yV69K3OW3qTTGhNvcYZulqQ617pPa48+sFCgh
39nM8TMAD0ElVEwmMtrS3AWoJz52Af+R3YzpAnX8NzV317/JG+b6e2ksl3tR7TWp1q
402FOqC1sXAxuv+DIz4GgRfaK1+xYr2ckkg+H/3HJqa5LmJ7rGCyv+Epfp9u+OvdBG
41PBvuCtO3tm0crmnttMw57Gy35BKutRf/8MpBj/nS6QFX0t7XOLeL4Me7/a2H20wz
42HZsuRGDXMCh0lL0FYCBAwdbbYvvy0gz/5iaNvoADtaIu+VtbFNrTUN0SwuL+AIFS
43+WIiaSbFt4Ng3t9YmqL6pqB7fjxI10S+PK0s7ABqe4pgbzUWWt1yzBcxfk8l/47Q
44JrlvcE7HuDOhNOHfZIgUP2Dbeu+pVvHIJbmLsNWpl4s+nHhoxc9HrVhYG/MTZtQ3
45kkUWviegO6mwEZjQvgBxjWib7090sCxkO847b8A93mfQNHnuy2ZEEJ+9xyk7nIWs
464RsiNR8pYc/SMvdocyAvQMH/qSvmn/IFJ+jHhtT8UJlXJ0bHvXTHjHMqBp6fP69z
47Jh1ERadWQdMaTkzQ+asl+kl/x3p6RZP8MEVbZIl/3pcV+xiFCYcFu2TETKMtbW+b
48NYOlrltFxFDvyu3WeNNp0g9k0nFpD/T1OXHRBRcbUDWE4QF6NWTm6NO9wy2UYHCi
497QTSecBWgMaw7cUdwvnW6chIVoov1pm69BI9D0PoV76zCI7KzpiDsTFxdilKwbQf
50K/PDnv9Adx3ERh0/F8llBHrj2UGsRs4aHSEBDBJIHDCp8+lqtsRcINQBKEU3qIjt
51wf5vizdaVIgQnsD2z8QmBQ7QCCipI0ur6GKl+YWDDOSDLDUs9dK4A6xo/4Q0bsnI
52rH63ti5HslGq6uArfFkewH2MWff/8Li3uGEqzpK5NhP5UpbArelK+QaQQP5SdsmW
53XFwUqDS4QTCKNJXw/5SQMl8UE10l2Xaav3TkiOYTcBcvPNDovYgnMyRff/tTeFa8
5483STkvpGtkULkCntp22fydv5rg6DZ7eJrYfC2oZXdM87hHhUALUO6Y/VtVmNdNYw
55F3Uim4PDuLIKt+mFqRtFqnWm+5X/AslC31qLkjH+Fbb83TY+mC9gbIn7CZGJRCjn
56zzzMX2h15V/VHzNUgx9V/h28T0/z25FxoozZiJxpmhOtqoxMHp+y6nXXfMoIAD1D
57963Pc7u1HS0ny54A7bqc6KKd4W9IF7HkXn3SoBwCyn0IOPoKQTDD8mW3lbBI6+h9
58vP+MAQpfD8s+3VZ9r7OKYCVmUv47ViTRlf428Co6WT7rTHjGM09tqz826fTOXA==
59=6Eu9
60-----END PGP MESSAGE-----
61
62--nextPart2814166.CHKktCGlQ3--
63
64--nextPart1939768.sIoLGH0PD8
65Content-Disposition: attachment; filename="image.png"
66Content-Transfer-Encoding: base64
67Content-Type: image/png; name="image.png"
68
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
112o/0d59+Lh3Y1HHcAAAAASUVORK5CYII=
113
114--nextPart1939768.sIoLGH0PD8--
115
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-attachment-and-non-encrypted-attachment.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-attachment-and-non-encrypted-attachment.mbox.html
new file mode 100644
index 00000000..092a3440
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-attachment-and-non-encrypted-attachment.mbox.html
@@ -0,0 +1,77 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <a name="att1"/>
9 <div id="attachmentDiv1">
10 <table cellspacing="1" cellpadding="1" class="encr">
11 <tr class="encrH">
12 <td dir="ltr">Encrypted message</td>
13 </tr>
14 <tr class="encrB">
15 <td>
16 <div style="position: relative; word-wrap: break-word">
17 <a name="att"/>
18 <div id="attachmentDiv">
19 <table cellspacing="1" cellpadding="1" class="signOkKeyOk">
20 <tr class="signOkKeyOkH">
21 <td dir="ltr">
22 <table cellspacing="0" cellpadding="0" width="100%">
23 <tr>
24 <td>Signed by <a href="mailto:test@kolab.org">test@kolab.org</a>.</td>
25 <td align="right">
26 <a href="kmail:showSignatureDetails">Show Details</a>
27 </td>
28 </tr>
29 </table>
30 </td>
31 </tr>
32 <tr class="signOkKeyOkB">
33 <td>
34 <a name="att1"/>
35 <div id="attachmentDiv1">
36 <a name="att1.1"/>
37 <div id="attachmentDiv1.1">
38 <div class="noquote">
39 <div dir="ltr">test text</div>
40 </div>
41 </div>
42 <a name="att1.2"/>
43 <div id="attachmentDiv1.2">
44 <hr/>
45 <div>
46 <a href="attachment:1:e0:1.2?place=body"><img align="center" height="48" width="48" src="file:text-plain.svg" border="0" style="max-width: 100%" alt=""/>file.txt</a>
47 </div>
48 <div/>
49 </div>
50 </div>
51 </td>
52 </tr>
53 <tr class="signOkKeyOkH">
54 <td dir="ltr">End of signed message</td>
55 </tr>
56 </table>
57 </div>
58 </div>
59 </td>
60 </tr>
61 <tr class="encrH">
62 <td dir="ltr">End of encrypted message</td>
63 </tr>
64 </table>
65 </div>
66 <a name="att2"/>
67 <div id="attachmentDiv2">
68 <hr/>
69 <div>
70 <a href="attachment:2?place=body"><img align="center" height="48" width="48" src="file:image-png.svg" border="0" style="max-width: 100%" alt=""/>image.png</a>
71 </div>
72 <div/>
73 </div>
74 </div>
75 </div>
76 </body>
77</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-attachment-and-non-encrypted-attachment.mbox.tree b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-attachment-and-non-encrypted-attachment.mbox.tree
new file mode 100644
index 00000000..473f0b10
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-attachment-and-non-encrypted-attachment.mbox.tree
@@ -0,0 +1,11 @@
1 * MimeTreeParser::MessagePartList
2 * MimeTreeParser::MimeMessagePart
3 * MimeTreeParser::EncryptedMessagePart
4 * MimeTreeParser::SignedMessagePart
5 * MimeTreeParser::MimeMessagePart
6 * MimeTreeParser::TextMessagePart
7 * MimeTreeParser::MessagePart
8 * MimeTreeParser::AttachmentMessagePart
9 * MimeTreeParser::MessagePart
10 * MimeTreeParser::AttachmentMessagePart
11 * MimeTreeParser::MessagePart
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-attachment.mbox b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-attachment.mbox
new file mode 100644
index 00000000..4204fb0b
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-attachment.mbox
@@ -0,0 +1,57 @@
1From test@kolab.org Fri May 01 15:13:18 2015
2From: testkey <test@kolab.org>
3To: you@you.com
4Subject: enc attachment
5Date: Fri, 01 May 2015 17:13:18 +0200
6Message-ID: <2401407.XhOc2XYoOt@tabin.local>
7X-KMail-Identity: 1197256126
8User-Agent: KMail/4.13.0.1 (Linux/3.19.1-towo.1-siduction-amd64; KDE/4.14.2; x86_64; git-cd33034; 2015-04-11)
9MIME-Version: 1.0
10Content-Type: multipart/encrypted; boundary="nextPart4793536.cjk9hKXLQ5"; protocol="application/pgp-encrypted"
11
12
13--nextPart4793536.cjk9hKXLQ5
14Content-Type: application/pgp-encrypted
15Content-Disposition: attachment
16Content-Transfer-Encoding: 7Bit
17
18Version: 1
19--nextPart4793536.cjk9hKXLQ5
20Content-Type: application/octet-stream
21Content-Disposition: inline; filename="msg.asc"
22Content-Transfer-Encoding: 7Bit
23
24-----BEGIN PGP MESSAGE-----
25Version: GnuPG v2
26
27hIwDGJlthTT7oq0BBACLMnR5Mln6JGCccvqZCgM3qUkFWZ7a33b7Nl2g5lSOLX6q
28dlGOr/0jAuqdkRwCDTom3hsrH2vf1kARTSST+5cewngS2CgBTAwwc8JymEagTkKf
29VK/tTnM6G7puMVkwuafpuRggyJAUYvjktgUKOUaXtuxX71g0NagAFQGqlvGuq4UB
30DAMMzkNap8zaOwEH+wWSs5xdV1x37T11EnUvkhoOMu+uId2U7NEx2JdQ/FGJZdti
31mtqfM9GKTtQlGVcn3ISH7Wmrw3x0bsOLuW7oxkU4xIS3tVvibxaaWdmponN5TUai
32Dr4LCTEmG7+jLNopF6V4zPmpQ9YxMwQOm8ITml+auNOg9EtHxavwr3Xd1hOYA92N
331bkOiHzmb9hQtUy1GfRRk91tRqtRPoaamczLxWV9yROFDRNuSSbZ8oBU/K4YgSTL
34D+/FhCt6MxV0DQzp+UCSL7ZsMx+ldPnZK44Udd17+U3xQDDUffo6cSg6FAF425Rh
35v3ZQP0j7LtSIwDh2Rxc+Is4DuSmfZksL5nLPH5nS6QGJnsVEqVcZgQPktl1Zaeil
36x/6WaWruuJm92G2fd9x2/giTLZnk918BVi/n00xR/n4bnSQmmFhXVqAVjGlG6Tr9
37dxej8dSiFdxO8ZjFe5tguQw76xlCu/9MxmSXTP7Mfvm4jqdcjUOINwHOzR/h2T62
38ZlrmqoxMHm4RN0PQ334tSzQXD4gcoUHL+xq62ATt7/jx0p0pIXPmPVUFopCk8k1E
39m2ErPLnyfGLd4LNZRL03oP0jCjX6Q/LFWLTjCIdU6+aM6nT26CZy98yZV0SRGyhu
40qYxN0aVW+RatmDRWWtouOJllQouQ7ZaqmjHLgyOj32/oT8cYUWWdFswSsnMhJjxb
41r6iajUeAZgiN+zqwgf6j1Z8/mMvb+yirP+Rn9ME1fq3XSYHlnIOxKNBa+St8DdaP
42/ZvrkwNTpVp1GmaZLBXdqdeLmflJ4U/X7wphZGR3sgjOwj0oYotX1Zb8OrtlP5WC
43VXhhrt40e7Ilt2B0x7Xe9DWKByDCqrQUhwxwTS3miiKH9G1VEcHko70O98RjKJQ3
44I4VW6e/Gqv2zAMiiOu1MBTeGfPQItca4bUrVzhKjm27twtZD4wLSQDTQECxrCWHC
45BFAdzvsIry0FIXoO6Vh16Hojq+YZ8tpmknGfpg0pnuRvAdDWU+0uuECXDE0UZFWC
462g3Bs2Dn2BYYyrI6X92swHz8qP3JvdxN0dpsYMkMdHN4yWXJogNSfXzy2udf0A4P
47NNZMOonhlwH+DBRfcWS0A/j0/fdDCDzR5Ca5dbX7lL4EscbBeoCP1JJyVoOp6DUc
48ICuHJGGrnpNdG9DMa97tqpyGRHTAwI3lJXPKTDEHN9v9XobIyndFgi/tcPLZ7QWz
499mN94NKLmeYWjrMiRbNQk8BYXR9K17SHb4BkIMdBxRsJjgMEC8qniUH64Nnf8/x4
50yaRCuUo0bkHDE3AqCzZE1R0he66dDkfOIz+mLwcpG8jZWjFm7sXAflBe3jvIc0lm
51NyWQ1WnMkP83fWm/+YqrLLf+tTQtievRPeS1Dd/7v9yqUWEmQ0pUOj3MNf9Ej2KI
52vu5ap7fHIevcBn42BPwQgSnp4YmXEY0ir5Ccwogusnt7QliNSRmkN6Jap4AF
53=AVJ4
54-----END PGP MESSAGE-----
55
56--nextPart4793536.cjk9hKXLQ5--
57
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-attachment.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-attachment.mbox.html
new file mode 100644
index 00000000..2b266b02
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-attachment.mbox.html
@@ -0,0 +1,66 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <table cellspacing="1" cellpadding="1" class="encr">
9 <tr class="encrH">
10 <td dir="ltr">Encrypted message</td>
11 </tr>
12 <tr class="encrB">
13 <td>
14 <div style="position: relative; word-wrap: break-word">
15 <a name="att"/>
16 <div id="attachmentDiv">
17 <table cellspacing="1" cellpadding="1" class="signOkKeyOk">
18 <tr class="signOkKeyOkH">
19 <td dir="ltr">
20 <table cellspacing="0" cellpadding="0" width="100%">
21 <tr>
22 <td>Signed by <a href="mailto:test@kolab.org">test@kolab.org</a>.</td>
23 <td align="right">
24 <a href="kmail:showSignatureDetails">Show Details</a>
25 </td>
26 </tr>
27 </table>
28 </td>
29 </tr>
30 <tr class="signOkKeyOkB">
31 <td>
32 <a name="att1"/>
33 <div id="attachmentDiv1">
34 <a name="att1.1"/>
35 <div id="attachmentDiv1.1">
36 <div class="noquote">
37 <div dir="ltr">test text</div>
38 </div>
39 </div>
40 <a name="att1.2"/>
41 <div id="attachmentDiv1.2">
42 <hr/>
43 <div>
44 <a href="attachment:e0:1.2?place=body"><img align="center" height="48" width="48" src="file:text-plain.svg" border="0" style="max-width: 100%" alt=""/>file.txt</a>
45 </div>
46 <div/>
47 </div>
48 </div>
49 </td>
50 </tr>
51 <tr class="signOkKeyOkH">
52 <td dir="ltr">End of signed message</td>
53 </tr>
54 </table>
55 </div>
56 </div>
57 </td>
58 </tr>
59 <tr class="encrH">
60 <td dir="ltr">End of encrypted message</td>
61 </tr>
62 </table>
63 </div>
64 </div>
65 </body>
66</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-attachment.mbox.tree b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-attachment.mbox.tree
new file mode 100644
index 00000000..f433fd45
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-attachment.mbox.tree
@@ -0,0 +1,8 @@
1 * MimeTreeParser::MessagePartList
2 * MimeTreeParser::EncryptedMessagePart
3 * MimeTreeParser::SignedMessagePart
4 * MimeTreeParser::MimeMessagePart
5 * MimeTreeParser::TextMessagePart
6 * MimeTreeParser::MessagePart
7 * MimeTreeParser::AttachmentMessagePart
8 * MimeTreeParser::MessagePart
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-enigmail1.6.mbox b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-enigmail1.6.mbox
new file mode 100644
index 00000000..9afd17e3
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-enigmail1.6.mbox
@@ -0,0 +1,48 @@
1From you@you.com Sat, 29 Mar 2014 15:04:21 +0100
2FCC: imap://hefee%40netzguerilla.net@mail.netzguerilla.net/Sent
3X-Identity-Key: id1
4X-Account-Key: account4
5Message-ID: <5336D2E5.6010602@you.com>
6Date: Sat, 29 Mar 2014 15:04:21 +0100
7From: you <you@you.com>
8X-Mozilla-Draft-Info: internal/draft; vcard=0; receipt=0; DSN=0; uuencode=0
9User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:24.0) Gecko/20100101 Icedove/24.4.0
10MIME-Version: 1.0
11To: test@kolab.com
12Subject: test
13X-Enigmail-Version: 1.6
14X-Enigmail-Draft-Status: 515
15X-Enigmail-Draft-Status: 739
16Content-Type: multipart/encrypted;
17 protocol="application/pgp-encrypted";
18 boundary="23VWJ4jAoB40SD17lh6TOBXK3fQSEGNu5"
19
20This is an OpenPGP/MIME encrypted message (RFC 4880 and 3156)
21--23VWJ4jAoB40SD17lh6TOBXK3fQSEGNu5
22Content-Type: application/pgp-encrypted
23Content-Description: PGP/MIME version identification
24
25Version: 1
26
27--23VWJ4jAoB40SD17lh6TOBXK3fQSEGNu5
28Content-Type: application/octet-stream; name="encrypted.asc"
29Content-Description: OpenPGP encrypted message
30Content-Disposition: inline; filename="encrypted.asc"
31
32-----BEGIN PGP MESSAGE-----
33Version: GnuPG v1
34Comment: Using GnuPG with Icedove - http://www.enigmail.net/
35
36hIwDGJlthTT7oq0BA/9NtLLXbiIJVS6pOynwEeSznrQK7kYVla8RM43//JECCkGJ
37azEaSBznabBv6epaFmQtVHLMXlCbZnMmW9loyqPBfMoAms6kKKBdG/jqhus89iXE
38+seXngC233Va/gZMb2DxOqIokVNfj9tpR7xQ8wS/jHTDiLNc1GOQC7ku42z2bNLA
39IQFRD/qbBFz89hU4wP4cYoAysOnEDojFrsrnCidTHJOJrndM6PPUtH/jQCyfr/EG
402tSpJwYKvmT6ly3yqaGLBtRPIxiv+dMe+7yw0t40qbjvvaTGavErEBJEKX5eWbTN
41/sjajHpUHqs6SIiMheH9dr+WfzFONtVbPEgGRmOERhlgTl/nLo86AZpjJroIGKJJ
42tTHCcoQGAWG+N7wrCE1RxR0kkMs4nRozj0TLu6ZyXMs+H063MewTPNxNAiQT1Nbi
43udKWmfLBlxn06p+JDzUKxj8PFwObdbxTvACzbAvBY1aHMQ==
44=mLl3
45-----END PGP MESSAGE-----
46
47--23VWJ4jAoB40SD17lh6TOBXK3fQSEGNu5--
48
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-enigmail1.6.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-enigmail1.6.mbox.html
new file mode 100644
index 00000000..09d904bb
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-enigmail1.6.mbox.html
@@ -0,0 +1,34 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <table cellspacing="1" cellpadding="1" class="encr">
9 <tr class="encrH">
10 <td dir="ltr">Encrypted message</td>
11 </tr>
12 <tr class="encrB">
13 <td>
14 <div style="position: relative; word-wrap: break-word">
15 <a name="att"/>
16 <div id="attachmentDiv">
17 <a name="att1"/>
18 <div id="attachmentDiv1">
19 <div class="noquote">
20 <div dir="ltr">test</div>
21 </div>
22 </div>
23 </div>
24 </div>
25 </td>
26 </tr>
27 <tr class="encrH">
28 <td dir="ltr">End of encrypted message</td>
29 </tr>
30 </table>
31 </div>
32 </div>
33 </body>
34</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-enigmail1.6.mbox.tree b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-enigmail1.6.mbox.tree
new file mode 100644
index 00000000..009ba99a
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-enigmail1.6.mbox.tree
@@ -0,0 +1,5 @@
1 * MimeTreeParser::MessagePartList
2 * MimeTreeParser::EncryptedMessagePart
3 * MimeTreeParser::MimeMessagePart
4 * MimeTreeParser::TextMessagePart
5 * MimeTreeParser::MessagePart
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-noData.mbox b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-noData.mbox
new file mode 100644
index 00000000..c4f14226
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-noData.mbox
@@ -0,0 +1,17 @@
1From test@kolab.org Wed, 08 Sep 2010 17:02:52 +0200
2From: OpenPGP Test <test@kolab.org>
3To: test@kolab.org
4Subject: OpenPGP encrypted
5Date: Wed, 08 Sep 2010 17:02:52 +0200
6User-Agent: KMail/4.6 pre (Linux/2.6.34-rc2-2-default; KDE/4.5.60; x86_64; ; )
7MIME-Version: 1.0
8Content-Type: multipart/encrypted; boundary="nextPart1357031.ppLHckZtsp"; protocol="application/pgp-encrypted"
9Content-Transfer-Encoding: 7Bit
10
11--nextPart1357031.ppLHckZtsp
12Content-Type: application/pgp-encrypted
13Content-Disposition: attachment
14
15Version: 1
16
17--nextPart1357031.ppLHckZtsp--
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-noData.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-noData.mbox.html
new file mode 100644
index 00000000..52196784
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-noData.mbox.html
@@ -0,0 +1,19 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <a name="att1"/>
9 <div id="attachmentDiv1">
10 <hr/>
11 <div>
12 <a href="attachment:1?place=body"><img align="center" height="48" width="48" src="file:application-pgp-encrypted.svg" border="0" style="max-width: 100%" alt=""/>Unnamed</a>
13 </div>
14 <div/>
15 </div>
16 </div>
17 </div>
18 </body>
19</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-noData.mbox.tree b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-noData.mbox.tree
new file mode 100644
index 00000000..79a20c8d
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-noData.mbox.tree
@@ -0,0 +1,4 @@
1 * MimeTreeParser::MessagePartList
2 * MimeTreeParser::MimeMessagePart
3 * MimeTreeParser::AttachmentMessagePart
4 * MimeTreeParser::MessagePart
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-non-encrypted-attachment.mbox b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-non-encrypted-attachment.mbox
new file mode 100644
index 00000000..2957bf3c
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-non-encrypted-attachment.mbox
@@ -0,0 +1,114 @@
1From test@kolab.org Fri May 01 15:13:51 2015
2From: testkey <test@kolab.org>
3To: you@you.com
4Subject: non enc attachment
5Date: Fri, 01 May 2015 17:13:51 +0200
6Message-ID: <20157069.RcaNBhWVXx@tabin.local>
7X-KMail-Identity: 1197256126
8User-Agent: KMail/4.13.0.1 (Linux/3.19.1-towo.1-siduction-amd64; KDE/4.14.2; x86_64; git-cd33034; 2015-04-11)
9MIME-Version: 1.0
10Content-Type: multipart/mixed; boundary="nextPart1612969.Xqz3IcFXZ3"
11Content-Transfer-Encoding: 7Bit
12
13This is a multi-part message in MIME format.
14
15--nextPart1612969.Xqz3IcFXZ3
16Content-Type: multipart/encrypted; boundary="nextPart2213427.hvfAsaxZ1O"; protocol="application/pgp-encrypted"
17
18
19--nextPart2213427.hvfAsaxZ1O
20Content-Type: application/pgp-encrypted
21Content-Disposition: attachment
22Content-Transfer-Encoding: 7Bit
23
24Version: 1
25--nextPart2213427.hvfAsaxZ1O
26Content-Type: application/octet-stream
27Content-Disposition: inline; filename="msg.asc"
28Content-Transfer-Encoding: 7Bit
29
30-----BEGIN PGP MESSAGE-----
31Version: GnuPG v2
32
33hIwDGJlthTT7oq0BBACdvp3PFrRx6vxZhxt06LqyaO7+SWBbVUr7GOa3QaYCCBm+
34/KTUZEfhPuGVcsVpJKZbPsUKVhYfvYpDAsZu3TehmKflobWAV/cCIK2BkQB744pd
35oaCtTj7pmCO05Zt5Uo/tXbrgceuW+/huwn2DO2fk4MUBsluH3fMbvccZJnR3yoUB
36DAMMzkNap8zaOwEIAOTKI8Bh3NhfuJdWDsjv+UT6E4kf/zg3D95mJ+3gK8kHFXdd
37YyEKaMsYx64kSwm1KcjsJ4gWykJlB34YDDfcIrnbgO2QRss9GhgOcUtLr0KNcY+0
38OJ4sbNmI8I3XssMb4rHtDrcXc0ODd0v/C/Lw2VfUdN+bBY4EetG096OPiZ4R41kF
39Rj95nhO8tpoJx9VltegCdZI2AxtykOYvUaLFyYqCoKMmQwqGjdaUv1CeMnxUIPsn
40A/x6TQ0AjpZ5IfMqO7QmOv3ACFo5/Ur93NauOK6szg/zdih9OxUL0Qid3nZQQwqA
41J9ZChTcUgdjQo8EjpWLy0oXR9uLSHcLwRo3OdRzS6QEz7Dus6hmpEal3zTZFj2NR
42mK38tpp+eWhzSAFAx8tyImz8SU2N2o7xQHlEdlWYGeIQg0embMnmMv6WJVWhYahb
43x5OTfHCGwHFsLhZDmtaNhAVMlYdqxKXoR45H9cmGNPpU5kN5Ckjr6I+I51cfgAOE
441vF54jYHXd1btfGrwAyKWBfFVQFPWjuIUdsEl8zDjNTmcCCbA77tEGohSqDi+WWL
45LyYw5g/uwwZ+5OzeRdjAJ+9xtJ+WXhnDUffG8GgGgZWBQD+S2Ix4lZ2NcAeyLPt/
46cXeDHkPUXj7cjm0hl7SvBKzR6X4EEWO/hh19mxngd4+e19q6Qm4K2QMfTwQQyNED
47whBkRXhcXcRxWlb/ICwFDIgxKLJko5HVTknBJDllNdm6l2C4Y9/IY2imqXni8xZX
48zQfAkDOBlfouUrHbPuBCRdCLmp7BgbanCaJDvgYGBvXEgjMKpV3bezTfm4Ugwiyk
49dJfMu/4XftZy7XqoFuDheNXuQ5JobpvVDaiTQ0EWHpHeX3TZ0xa8i1FO6ANLakUp
50aVFWzYl91bxbNHIcPh278neyi+LNWWT6TvkCwcbUYUfuIySOwfSUJAHDIJOx50Ha
51If0fNQDtF4o9mDFwXDwVf8e2zx5NjheqgYX/qIWjFE2cCCkUM4UkNrkYBOiwnh9F
52RJz4M6wKPhZT34MBlTKgwDbypDp+XYnM2HunzIDXCAigYsD/AuLNeFwsIPiXWJ47
53oXamEJxyqS9G0t/iy7+1mbNiFct7pvHt6QUKZ9aXSXj3MkQuWjjVNvMz6KocTSyL
54AeWQNYuvA5NkyZOA5+VU1ma3wn7IAXYkgw+OMvcz9VNevXNg956ZnGbb47Fqppp+
55MjK2ptM1UzIEnHxXtq3KFrXG1AzzhyFnKIjsiDpFTlMGTT9pRGnK8zyYXYPEgpwW
56874A5auLFOvhwPSNMhMai+XSoE3P+zZDqhXMYU8c49O+SeOtISg3Iy5tXuIPlIps
577k0KA60hPulMYBQLtZ/yDO/gXhkeWaw=
58=EJZ6
59-----END PGP MESSAGE-----
60
61--nextPart2213427.hvfAsaxZ1O--
62
63--nextPart1612969.Xqz3IcFXZ3
64Content-Disposition: attachment; filename="image.png"
65Content-Transfer-Encoding: base64
66Content-Type: image/png; name="image.png"
67
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
111o/0d59+Lh3Y1HHcAAAAASUVORK5CYII=
112
113--nextPart1612969.Xqz3IcFXZ3--
114
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-non-encrypted-attachment.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-non-encrypted-attachment.mbox.html
new file mode 100644
index 00000000..e20a9568
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-non-encrypted-attachment.mbox.html
@@ -0,0 +1,69 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <a name="att1"/>
9 <div id="attachmentDiv1">
10 <table cellspacing="1" cellpadding="1" class="encr">
11 <tr class="encrH">
12 <td dir="ltr">Encrypted message</td>
13 </tr>
14 <tr class="encrB">
15 <td>
16 <div style="position: relative; word-wrap: break-word">
17 <a name="att"/>
18 <div id="attachmentDiv">
19 <table cellspacing="1" cellpadding="1" class="signOkKeyOk">
20 <tr class="signOkKeyOkH">
21 <td dir="ltr">
22 <table cellspacing="0" cellpadding="0" width="100%">
23 <tr>
24 <td>Signed by <a href="mailto:test@kolab.org">test@kolab.org</a>.</td>
25 <td align="right">
26 <a href="kmail:showSignatureDetails">Show Details</a>
27 </td>
28 </tr>
29 </table>
30 </td>
31 </tr>
32 <tr class="signOkKeyOkB">
33 <td>
34 <a name="att1"/>
35 <div id="attachmentDiv1">
36 <a name="att1.1"/>
37 <div id="attachmentDiv1.1">
38 <div class="noquote">
39 <div dir="ltr">test text</div>
40 </div>
41 </div>
42 </div>
43 </td>
44 </tr>
45 <tr class="signOkKeyOkH">
46 <td dir="ltr">End of signed message</td>
47 </tr>
48 </table>
49 </div>
50 </div>
51 </td>
52 </tr>
53 <tr class="encrH">
54 <td dir="ltr">End of encrypted message</td>
55 </tr>
56 </table>
57 </div>
58 <a name="att2"/>
59 <div id="attachmentDiv2">
60 <hr/>
61 <div>
62 <a href="attachment:2?place=body"><img align="center" height="48" width="48" src="file:image-png.svg" border="0" style="max-width: 100%" alt=""/>image.png</a>
63 </div>
64 <div/>
65 </div>
66 </div>
67 </div>
68 </body>
69</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-non-encrypted-attachment.mbox.tree b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-non-encrypted-attachment.mbox.tree
new file mode 100644
index 00000000..c2a6ad01
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-non-encrypted-attachment.mbox.tree
@@ -0,0 +1,9 @@
1 * MimeTreeParser::MessagePartList
2 * MimeTreeParser::MimeMessagePart
3 * MimeTreeParser::EncryptedMessagePart
4 * MimeTreeParser::SignedMessagePart
5 * MimeTreeParser::MimeMessagePart
6 * MimeTreeParser::TextMessagePart
7 * MimeTreeParser::MessagePart
8 * MimeTreeParser::AttachmentMessagePart
9 * MimeTreeParser::MessagePart
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-partially-signed-attachments.mbox b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-partially-signed-attachments.mbox
new file mode 100644
index 00000000..222b5936
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-partially-signed-attachments.mbox
@@ -0,0 +1,91 @@
1From: firstname.lastname@example.com
2To: test@kolab.org
3Subject: OpenPGP encrypted one signed and one unsigned attachment
4Date: Sun, 30 Aug 2015 12:08:40 +0200
5Message-ID: <1737262.ESByPkoaL9@vkpc5>
6X-KMail-Identity: 402312391
7X-KMail-Dictionary: en_US
8User-Agent: KMail/5.0.42 pre (Linux/3.16.6-2-desktop; KDE/5.14.0; x86_64; ; )
9MIME-Version: 1.0
10Content-Type: multipart/mixed; boundary="nextPart2760349.k2GQmrcl5a"
11Content-Transfer-Encoding: 7Bit
12
13This is a multi-part message in MIME format.
14
15--nextPart2760349.k2GQmrcl5a
16Content-Type: multipart/encrypted; boundary="nextPart2260160.XvshVD34ka"; protocol="application/pgp-encrypted"
17
18--nextPart2260160.XvshVD34ka
19Content-Type: application/pgp-encrypted
20Content-Disposition: attachment
21Content-Transfer-Encoding: 7Bit
22
23Version: 1
24--nextPart2260160.XvshVD34ka
25Content-Type: application/octet-stream
26Content-Disposition: inline; filename="msg.asc"
27Content-Transfer-Encoding: 7Bit
28
29-----BEGIN PGP MESSAGE-----
30Version: GnuPG v2
31
32hQEMAwzOQ1qnzNo7AQgAooa+Peu1t5aiX4WvssEnRlapkr62/49/Dbz5assvshdr
33o9zOC+89Qr5P/ea+tC1MO7ek/DfyL9C4EzOMp/r08GeInLqWMc9qLZO//YaT2JUp
340IZFbpUQ4C2zjXMRuy/PMrltHuCfsvhvHX1YAHp/xjRsKpQoE3mSDzz5sc8/Hj9n
352eoKoCEW1rgt2qHtV4DD6lvUPo42LMZclJ9GqfoD7WLoEH9ebRLhXr3D00j6xBfy
36//nooAhzQSW8b7GZth4Qc7DKAZMhuEly/kp07nhuPd1fMJDEAB/zh9EYlgnn3STb
37MCdtFs6kMC24uA2eDCH330Bn9OzzWnsVU9ae4XiKnNLpAeactSFRKKvGytBQaloq
38Gvn1+D0Xmw6OsSmRJ84DzKqG4E6bzE+XhMEMnLlqDvZDsOtU/sCGvMK0MM3y5B7M
39ONqWLN3ng3zrGPec5gqfvnWgKpANUrbJkzS8LNjv4hwoKh0tFpghWQiefG0Z9Hw1
40UaYbFwvaFgXcm72oBkynDCleWjQ2vnDE4P38PldqZbAW/Pw1q6Yq8m9MhS1VpbI5
41WBjuRQhgQvMG0LY0gR/3Qor5tX9ASllWnPfWYVuOiSOAe5Hsp7BmELXkWftHii7k
42YW0Qim7jleDaY1MGfFr0vrO/PiYxGTb+4IUyUgEBYEpxa9ozUoeftF6JbHPuEZI0
43ENX8aIVJ9FnpssrR5HlpXieF12ec9ZFeV7mAwcucJ3RXuDcQHQTHgEbfnzTsaEpL
44Hxs+6euOCJXhKOYVrsAlB4IxK0OQm4XHiZ7WBp5Jp7rlSHltdxFpSnHIfqngyCA4
45L+c/gsUEVbNI++iOOhOKVT47XIf+s/xa1Y4XghGbHIA3/pQphYo4U5dTeLoX0OWI
4664tPxliQTKuJ+NAv9Km2bDgvlvn83jsc94THi5u+fIdDEGp7pgoQNXR69Lkz1bsA
47Hh9xqKzVloHu4SsT3OGX+bfTO3jUh8ieU/4tflZMrJ9RkEfE08PzTnopT08FppFx
48QWpHv/fwyJf6Cw1IevM5pVLAShg0sbCPF/xsYTjSyWNib1wFtxkmv5MToCFwkxi5
49b1yT23BNlV5RV6wcjmrum7p2fMwPjbt8X6z+pgUtkD7zImBWeYddMjgSjOFuFEjF
50gpfVoV3OIvMPZMe2jqWsjnjujHJr2z5IZn3A7WI0b4SIDP0sGwsTBiogKqcBNWpn
51O4MKUq9JwC0K/MY7yS1MCLoHfwU18z19Es/flaAgwtXk8IWIcjkkumAwNl+y8Q+I
52+8AFGdiXTKld9QVwCKnMS3QivHPuFNL8rfcWKsr1nOhOEhaO+zD94eOOiCbwiXGr
53E6WsewNCVQUN4bxAXl2vRi+9WRctLy7bsuIL5dgUz0CMYkQ+dDmrBflD0nyC
54=TGNA
55-----END PGP MESSAGE-----
56
57--nextPart2260160.XvshVD34ka--
58
59--nextPart2760349.k2GQmrcl5a
60Content-Type: multipart/encrypted; boundary="nextPart22530242.iRl14SoP0Q"; protocol="application/pgp-encrypted"
61
62--nextPart22530242.iRl14SoP0Q
63Content-Type: application/pgp-encrypted
64Content-Disposition: attachment
65Content-Transfer-Encoding: 7Bit
66
67Version: 1
68--nextPart22530242.iRl14SoP0Q
69Content-Type: application/octet-stream
70Content-Disposition: inline; filename="msg.asc"
71Content-Transfer-Encoding: 7Bit
72
73-----BEGIN PGP MESSAGE-----
74Version: GnuPG v2
75
76hQEMAwzOQ1qnzNo7AQgAtA94bBRwgpo64zcyh+4dzt0Pr2pmNjiS4ZX+a/xzYCmD
77oS1a26s/LVZH+aJYC13l1kEu+6YjKn+ruQvMfhihOxglHBLjmUO17TPFC05AReSn
78amMqPbgS6mOwhlBJHrBa/SVwkxmbMaNBUJ/KxRXFtTW/V4pPWImRvI9mnmpo8fHy
79ZLvVAI3hGe7vPG5Vbdi5/Iu/JzqwlglVsP82gYpLlx7HhWGF4gmTGc6YBwFtzEvS
80eqFtKRDqN60bo4HnNLOEnMaWlYPTpt3QibLWWIbtpA1Gb0Q/1NvDnn3Lyj8H+0WV
818H6Ks9/cvAuoAMOad6y4gDJb+K/AS7Ha+08/3lMYG9LAAgGe8Qocxg1W3ha1o0Ph
82YSfV2ooebsLiHjwspjYpsX5ijnRj6uNGp5Dt28EIo1ANF+oDiSKEwAMGPBtcnqaO
83FWXy39dP3VXE73nsO+dyTidaATFBlYg+IpPTDSFTgsk7XDV973EpdXvOkBEp/vBv
84EZknuZFOkS0v5QHk9Y/hhlSLACTIEWQpBiFGOwUVSZFXLEY5zQNTbQdRbz3ZYcE9
85mIFzD9Ujw6tIJIOFazhwr9SxxyeaAygWeg4ifmmdhAsmFYum
86=WsAv
87-----END PGP MESSAGE-----
88
89--nextPart22530242.iRl14SoP0Q--
90
91--nextPart2760349.k2GQmrcl5a--
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-partially-signed-attachments.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-partially-signed-attachments.mbox.html
new file mode 100644
index 00000000..c0b9d79e
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-partially-signed-attachments.mbox.html
@@ -0,0 +1,99 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <a name="att1"/>
9 <div id="attachmentDiv1">
10 <table cellspacing="1" cellpadding="1" class="encr">
11 <tr class="encrH">
12 <td dir="ltr">Encrypted message</td>
13 </tr>
14 <tr class="encrB">
15 <td>
16 <div style="position: relative; word-wrap: break-word">
17 <a name="att"/>
18 <div id="attachmentDiv">
19 <table cellspacing="1" cellpadding="1" class="signOkKeyOk">
20 <tr class="signOkKeyOkH">
21 <td dir="ltr">
22 <table cellspacing="0" cellpadding="0" width="100%">
23 <tr>
24 <td>Signed by <a href="mailto:test@kolab.org">test@kolab.org</a>.</td>
25 <td align="right">
26 <a href="kmail:showSignatureDetails">Show Details</a>
27 </td>
28 </tr>
29 </table>
30 </td>
31 </tr>
32 <tr class="signOkKeyOkB">
33 <td>
34 <a name="att1"/>
35 <div id="attachmentDiv1">
36 <a name="att1.1"/>
37 <div id="attachmentDiv1.1">
38 <div class="noquote">
39 <div dir="ltr">This is the main body.</div>
40 </div>
41 </div>
42 <a name="att1.2"/>
43 <div id="attachmentDiv1.2">
44 <table cellspacing="1" class="textAtm">
45 <tr class="textAtmH">
46 <td dir="ltr">attachment1.txt</td>
47 </tr>
48 <tr class="textAtmB">
49 <td>
50 <div class="noquote">
51 <div dir="ltr">This is a signed attachment.</div>
52 </div>
53 </td>
54 </tr>
55 </table>
56 </div>
57 </div>
58 </td>
59 </tr>
60 <tr class="signOkKeyOkH">
61 <td dir="ltr">End of signed message</td>
62 </tr>
63 </table>
64 </div>
65 </div>
66 </td>
67 </tr>
68 <tr class="encrH">
69 <td dir="ltr">End of encrypted message</td>
70 </tr>
71 </table>
72 </div>
73 <a name="att2"/>
74 <div id="attachmentDiv2">
75 <table cellspacing="1" cellpadding="1" class="encr">
76 <tr class="encrH">
77 <td dir="ltr">Encrypted message</td>
78 </tr>
79 <tr class="encrB">
80 <td>
81 <div style="position: relative; word-wrap: break-word">
82 <a name="att"/>
83 <div id="attachmentDiv">
84 <div class="noquote">
85 <div dir="ltr">This is an unsigned attachment.</div>
86 </div>
87 </div>
88 </div>
89 </td>
90 </tr>
91 <tr class="encrH">
92 <td dir="ltr">End of encrypted message</td>
93 </tr>
94 </table>
95 </div>
96 </div>
97 </div>
98 </body>
99</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-partially-signed-attachments.mbox.tree b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-partially-signed-attachments.mbox.tree
new file mode 100644
index 00000000..5cb83749
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-partially-signed-attachments.mbox.tree
@@ -0,0 +1,12 @@
1 * MimeTreeParser::MessagePartList
2 * MimeTreeParser::MimeMessagePart
3 * MimeTreeParser::EncryptedMessagePart
4 * MimeTreeParser::SignedMessagePart
5 * MimeTreeParser::MimeMessagePart
6 * MimeTreeParser::TextMessagePart
7 * MimeTreeParser::MessagePart
8 * MimeTreeParser::AttachmentMessagePart
9 * MimeTreeParser::MessagePart
10 * MimeTreeParser::EncryptedMessagePart
11 * MimeTreeParser::TextMessagePart
12 * MimeTreeParser::MessagePart
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-two-attachments.mbox b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-two-attachments.mbox
new file mode 100644
index 00000000..c53e0916
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-two-attachments.mbox
@@ -0,0 +1,42 @@
1From: firstname.lastname@example.com
2To: test@kolab.org
3Subject: OpenPGP encrypted with 2 text attachments
4Date: Sun, 30 Aug 2015 12:05:17 +0200
5Message-ID: <1505824.VT0nqpAGu0@vkpc5>
6X-KMail-Identity: 402312391
7X-KMail-Dictionary: en_US
8User-Agent: KMail/5.0.42 pre (Linux/3.16.6-2-desktop; KDE/5.14.0; x86_64; ; )
9MIME-Version: 1.0
10Content-Type: multipart/encrypted; boundary="nextPart3335835.KxmPgziKxd"; protocol="application/pgp-encrypted"
11
12--nextPart3335835.KxmPgziKxd
13Content-Type: application/pgp-encrypted
14Content-Disposition: attachment
15Content-Transfer-Encoding: 7Bit
16
17Version: 1
18--nextPart3335835.KxmPgziKxd
19Content-Type: application/octet-stream
20Content-Disposition: inline; filename="msg.asc"
21Content-Transfer-Encoding: 7Bit
22
23-----BEGIN PGP MESSAGE-----
24Version: GnuPG v2
25
26hQEMAwzOQ1qnzNo7AQgA6tTJs017mI+xuhjcSTr3F5X/rJghq0oaXbjk1K0W33p+
27jsEyPTbuUIvI2a+5xHJ3BV+gnnO0Xosz57tGTF/eVAySnGiMse5cu2RQR9b/9EkC
28uzt3tIChyub3GUODG3yzXqkhSiwIImvedWgnJYTJ7eeBkWdizVT0b0byiG2d7Hjq
29hzYzyPJRwuoE36hryDsFycAhPfSsQAirxDJXk4HTsfBCmz7tzJhtt4wKc7z5m8fq
30y+jddnDADq5+tykJS6zemJOzgU3AOQyVJbmsx2vTV1CJdpKPHvTTgghpAaSuVbg0
31tR9BX1nPnA/bgX/V7C+3PwuCvB0ZKmv5d8kaGaTwO9LAhQGaWTfhG1cyy+MJhXdR
32rH63PMkZh/lrvj7qJYofI5iVoe1CiMaX44BiwKVclGf7bEFdzc2NSRvvTSzisN3T
33nSzydEttuEY5jGagQQNT1l1l4I8HAtUgwGtkKZVTAUL6iKHYAqzB77sRs33UJy4k
34ZSIWFnSY8l+HLG+MYKsYCGsvJHkxEHnMiS1EZcmpUFhxOGQpiF2rJ4qnL2jbFWbA
359N1O5N1N/DJ/YKjwgy/jVVj6AOCrBZrxvKKt2mtG/wVX0F/KSKiEd8mgrLIx1udw
36tibiDAJmDxUk8K0lAdOHBrzBChvysiT/QxCJFcSY6FE99Rral+BWjeyAIQQWvc2B
37cEceZCtzjCOrwvoJwl2uEX+51nmMp+z1EoeyyhmUZZ4y65yOg4P6KGXGcLmIjSbH
38IhsSls1jRkSrypf/wcEd6o7KZdeYbfA=
39=Sud3
40-----END PGP MESSAGE-----
41
42--nextPart3335835.KxmPgziKxd--
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-two-attachments.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-two-attachments.mbox.html
new file mode 100644
index 00000000..cca4c455
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-two-attachments.mbox.html
@@ -0,0 +1,64 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <table cellspacing="1" cellpadding="1" class="encr">
9 <tr class="encrH">
10 <td dir="ltr">Encrypted message</td>
11 </tr>
12 <tr class="encrB">
13 <td>
14 <div style="position: relative; word-wrap: break-word">
15 <a name="att"/>
16 <div id="attachmentDiv">
17 <a name="att1"/>
18 <div id="attachmentDiv1">
19 <div class="noquote">
20 <div dir="ltr">this is the main body part</div>
21 </div>
22 </div>
23 <a name="att2"/>
24 <div id="attachmentDiv2">
25 <table cellspacing="1" class="textAtm">
26 <tr class="textAtmH">
27 <td dir="ltr">attachment1.txt</td>
28 </tr>
29 <tr class="textAtmB">
30 <td>
31 <div class="noquote">
32 <div dir="ltr">this is the first attachment</div>
33 </div>
34 </td>
35 </tr>
36 </table>
37 </div>
38 <a name="att3"/>
39 <div id="attachmentDiv3">
40 <table cellspacing="1" class="textAtm">
41 <tr class="textAtmH">
42 <td dir="ltr">attachment2.txt</td>
43 </tr>
44 <tr class="textAtmB">
45 <td>
46 <div class="noquote">
47 <div dir="ltr">this is the second attachment</div>
48 </div>
49 </td>
50 </tr>
51 </table>
52 </div>
53 </div>
54 </div>
55 </td>
56 </tr>
57 <tr class="encrH">
58 <td dir="ltr">End of encrypted message</td>
59 </tr>
60 </table>
61 </div>
62 </div>
63 </body>
64</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-two-attachments.mbox.tree b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-two-attachments.mbox.tree
new file mode 100644
index 00000000..71a67b58
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted-two-attachments.mbox.tree
@@ -0,0 +1,9 @@
1 * MimeTreeParser::MessagePartList
2 * MimeTreeParser::EncryptedMessagePart
3 * MimeTreeParser::MimeMessagePart
4 * MimeTreeParser::TextMessagePart
5 * MimeTreeParser::MessagePart
6 * MimeTreeParser::AttachmentMessagePart
7 * MimeTreeParser::MessagePart
8 * MimeTreeParser::AttachmentMessagePart
9 * MimeTreeParser::MessagePart
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted.mbox b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted.mbox
new file mode 100644
index 00000000..5102fa78
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted.mbox
@@ -0,0 +1,36 @@
1From test@kolab.org Wed, 08 Sep 2010 17:02:52 +0200
2From: OpenPGP Test <test@kolab.org>
3To: test@kolab.org
4Subject: OpenPGP encrypted
5Date: Wed, 08 Sep 2010 17:02:52 +0200
6User-Agent: KMail/4.6 pre (Linux/2.6.34-rc2-2-default; KDE/4.5.60; x86_64; ; )
7MIME-Version: 1.0
8Content-Type: multipart/encrypted; boundary="nextPart1357031.ppLHckZtsp"; protocol="application/pgp-encrypted"
9Content-Transfer-Encoding: 7Bit
10
11
12--nextPart1357031.ppLHckZtsp
13Content-Type: application/pgp-encrypted
14Content-Disposition: attachment
15
16Version: 1
17--nextPart1357031.ppLHckZtsp
18Content-Type: application/octet-stream
19Content-Disposition: inline; filename="msg.asc"
20
21-----BEGIN PGP MESSAGE-----
22Version: GnuPG v2.0.15 (GNU/Linux)
23
24hQEMAwzOQ1qnzNo7AQgAtWfDWWI2JUGuptpackiIxpWViEEpGAeruETubiIPwxNb
25DNmXrMDhbm/zIbPntIGWJDgUMfABZCUgmlJLWhsceDTt+tXnWGha2VYrN2/WsF6/
26Pqs/TavTvMIJQHDaIH5yDDCaMoq/mGSbcu7go2H8Sw7aBEYlM8jGlqc1HziXnZ1q
273vDiA+4qWfvbNoSRo1kb9Pcq997yg6WqZXH2hJ7cp+hIQ4uTP1/+qgYHMvfPlzQk
28XcDguGbIer88ELhuR5622unGBAB4dqp+5w6n9c6rrCH81qhV4W0nqSEvj1tBj78S
29ZTi6VBAo5eS0e3iOJqMpwUZz6hQUpJw2wnNRGvLgI9KZAag0HkgPdMeANowg7vpE
30L4nU7B0ybhswA2Y7QT/wwCDZu9N1JGeBmy0dgy4sA38Ki27rn2/lIaP0j14JycwM
31RTJ1uwI+ZuQiwXlyYtdFZJWe8nraWARch0oKqhaR7aSsxGWo63eiGEQhkQCBFBb3
32Vg0nNCZRBauEqIESEW5EV2zrJqdfNYcz+f9IP125dnQEKgLZ6FxTt3+v
33=mhNl
34-----END PGP MESSAGE-----
35
36--nextPart1357031.ppLHckZtsp--
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted.mbox.html
new file mode 100644
index 00000000..ba0976cd
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted.mbox.html
@@ -0,0 +1,31 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <table cellspacing="1" cellpadding="1" class="encr">
9 <tr class="encrH">
10 <td dir="ltr">Encrypted message</td>
11 </tr>
12 <tr class="encrB">
13 <td>
14 <div style="position: relative; word-wrap: break-word">
15 <a name="att"/>
16 <div id="attachmentDiv">
17 <div class="noquote">
18 <div dir="ltr">encrypted message text</div>
19 </div>
20 </div>
21 </div>
22 </td>
23 </tr>
24 <tr class="encrH">
25 <td dir="ltr">End of encrypted message</td>
26 </tr>
27 </table>
28 </div>
29 </div>
30 </body>
31</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted.mbox.inProgress.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted.mbox.inProgress.html
new file mode 100644
index 00000000..e5eb55d0
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted.mbox.inProgress.html
@@ -0,0 +1,24 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <table cellspacing="1" cellpadding="1" class="encr">
9 <tr class="encrH">
10 <td dir="ltr">Please wait while the message is being decrypted...</td>
11 </tr>
12 <tr class="encrB">
13 <td>
14 <div style="font-size:x-large; text-align:center; padding:20pt;"/>
15 </td>
16 </tr>
17 <tr class="encrH">
18 <td dir="ltr">End of encrypted message</td>
19 </tr>
20 </table>
21 </div>
22 </div>
23 </body>
24</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted.mbox.tree b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted.mbox.tree
new file mode 100644
index 00000000..82f705c2
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-encrypted.mbox.tree
@@ -0,0 +1,4 @@
1 * MimeTreeParser::MessagePartList
2 * MimeTreeParser::EncryptedMessagePart
3 * MimeTreeParser::TextMessagePart
4 * MimeTreeParser::MessagePart
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-inline-charset-encrypted.mbox b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-inline-charset-encrypted.mbox
new file mode 100644
index 00000000..8bd06910
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-inline-charset-encrypted.mbox
@@ -0,0 +1,40 @@
1From test@example.com Thu, 17 Oct 2013 02:13:03 +0200
2Return-Path: <test@example.com>
3Delivered-To: you@you.com
4Received: from localhost (localhost [127.0.0.1])
5 by test@example.com (Postfix) with ESMTP id B30D8120030
6 for <you@you.com>; Thu, 17 Oct 2013 02:13:05 +0200 (CEST)
7From: test <test@example.com>
8To: you@you.com
9Subject: charset
10Date: Thu, 17 Oct 2013 02:13:03 +0200
11Message-ID: <4081645.yGjUJ4o4Se@example.local>
12User-Agent: KMail/4.12 pre (Linux/3.11-4.towo-siduction-amd64; KDE/4.11.2; x86_64; git-f7f14e3; 2013-10-15)
13MIME-Version: 1.0
14Content-Transfer-Encoding: 7Bit
15Content-Type: text/plain; charset="ISO-8859-15"
16
17-----BEGIN PGP MESSAGE-----
18Version: GnuPG v2.0.22 (GNU/Linux)
19
20hIwDGJlthTT7oq0BBACbaRZudMigMTetPZNRgkfEXv4QQowR1jborw0dcgKKqMQ1
216o67NkpxvmXKGJTfTVCLBX3nk6FKYo6NwlPCyU7X9X0DDk8hvaBdR9wGfrdm5YWX
22GKOzcqJY1EypiMsspXeZvjzEW7O8I956c3vBb/2pM3xqYEK1kh8+d9bVH+cjf4UB
23DAMMzkNap8zaOwEH/1rPShyYL8meJN+/GGgS8+Nf1BW5pSHdAPCg0dnX4QCLEx7u
24GkBU6N4JGYayaCBofibOLacQPhYZdnR5Xb/Pvrx03GrzyzyDp0WyeI9nGNfkani7
25sCRWbzlMPsEvGEvJVnMLNRSk4xhPIWumL4APkw+Mgi6mf+Br8z0RhfnGwyMA53Mr
26pG9VQKlq3v7/aaN40pMjAsxiytcHS515jXrb3Ko4pWbTlAr/eytOEfkLRJgSOpQT
27BY7lWs+UQJqiG8Yn65vS9LMDNJgX9EOGx77Z4u9wvv4ZieOxzgbHGg5kYCoae7ba
28hxZeNjYKscH+E6epbOxM/wlTdr4UTiiW9dMsH0zSwMUB891gToeXq+LDGEPTKVSX
29tsJm4HS/kISJBwrCI4EUqWZML6xQ427NkZGmF2z/sD3kmL66GjspIKnb4zHmXacp
3084n2KrI9s7p6AnKnQjsxvB/4/lpXPCIY5GH7KjySEJiMsHECzeN1dJSL6keykBsx
31DtmYDA+dhZ6UWbwzx/78+mjNREhyp/UiSAmLzlJh89OH/xelAPvKcIosYwz4cY9N
32wjralTmL+Y0aHKeZJOeqPLaXADcPFiZrCNPCH65Ey5GEtDpjLpEbjVbykPV9+YkK
337JKW6bwMraOl5zmAoR77PWMo3IoYb9q4GuqDr1V2ZGlb7eMH1gj1nfgfVintKC1X
343jFfy7aK6LIQDVKEwbi0SxVXTKStuliVUy5oX4woDOxmTEotJf1QlKZpn5oF20UP
35tumYrp0SPoP8Bo4EVRVaLupduI5cYce1q/kFj9Iho/wk56MoG9PxMMfsH7oKg3AA
36CqQ6/kM4oJNdN5xIf1EH5HeaNFkDy1jlLznnhwVAZKPo/9ffpg==
37=bPqu
38-----END PGP MESSAGE-----
39
40
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-inline-charset-encrypted.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-inline-charset-encrypted.mbox.html
new file mode 100644
index 00000000..344dc237
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-inline-charset-encrypted.mbox.html
@@ -0,0 +1,47 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <table cellspacing="1" cellpadding="1" class="encr">
9 <tr class="encrH">
10 <td dir="ltr">Encrypted message</td>
11 </tr>
12 <tr class="encrB">
13 <td>
14 <table cellspacing="1" cellpadding="1" class="signOkKeyOk">
15 <tr class="signOkKeyOkH">
16 <td dir="ltr">
17 <table cellspacing="0" cellpadding="0" width="100%">
18 <tr>
19 <td>Signed by <a href="mailto:test@kolab.org">test@kolab.org</a>.</td>
20 <td align="right">
21 <a href="kmail:showSignatureDetails">Show Details</a>
22 </td>
23 </tr>
24 </table>
25 </td>
26 </tr>
27 <tr class="signOkKeyOkB">
28 <td>
29 <div class="noquote">
30 <div dir="ltr">asdasd asd asd asdf sadf sdaf sadf öäü</div>
31 </div>
32 </td>
33 </tr>
34 <tr class="signOkKeyOkH">
35 <td dir="ltr">End of signed message</td>
36 </tr>
37 </table>
38 </td>
39 </tr>
40 <tr class="encrH">
41 <td dir="ltr">End of encrypted message</td>
42 </tr>
43 </table>
44 </div>
45 </div>
46 </body>
47</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-inline-charset-encrypted.mbox.inProgress.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-inline-charset-encrypted.mbox.inProgress.html
new file mode 100644
index 00000000..e5eb55d0
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-inline-charset-encrypted.mbox.inProgress.html
@@ -0,0 +1,24 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <table cellspacing="1" cellpadding="1" class="encr">
9 <tr class="encrH">
10 <td dir="ltr">Please wait while the message is being decrypted...</td>
11 </tr>
12 <tr class="encrB">
13 <td>
14 <div style="font-size:x-large; text-align:center; padding:20pt;"/>
15 </td>
16 </tr>
17 <tr class="encrH">
18 <td dir="ltr">End of encrypted message</td>
19 </tr>
20 </table>
21 </div>
22 </div>
23 </body>
24</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-inline-charset-encrypted.mbox.tree b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-inline-charset-encrypted.mbox.tree
new file mode 100644
index 00000000..ea8223fd
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-inline-charset-encrypted.mbox.tree
@@ -0,0 +1,4 @@
1 * MimeTreeParser::MessagePartList
2 * MimeTreeParser::TextMessagePart
3 * MimeTreeParser::EncryptedMessagePart
4 * MimeTreeParser::SignedMessagePart
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-inline-signed-broken.mbox b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-inline-signed-broken.mbox
new file mode 100644
index 00000000..fc0d2df9
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-inline-signed-broken.mbox
@@ -0,0 +1,26 @@
1From: test <test@kolab.org>
2To: you@you.de
3Subject: test
4Date: Tue, 25 Aug 2015 16:47:10 +0200
5Message-ID: <1662097.O9NVKTC5pT@11b508545ba2>
6X-KMail-Identity: 1428848833
7User-Agent: KMail/4.13.0.3 (Linux/4.1.0-rc5-siduction-amd64; KDE/4.14.3; x86_64; git-7c86098; 2015-08-23)
8MIME-Version: 1.0
9Content-Transfer-Encoding: quoted-printable
10Content-Type: text/plain; charset="iso-8859-1"
11
12-----BEGIN PGP SIGNED MESSAGE-----
13Hash: SHA256
14
15ohno break it =F6=E4=FC
16-----BEGIN PGP SIGNATURE-----
17Version: GnuPG v2
18
19iQEcBAEBCAAGBQJV3H/vAAoJEI2YYMWPJG3mEZQH/2mbCDa60risTUsomEecasc7
20kIc8Ch+OjZwlEQWKEiFbpLCMVjMwf0oGFcpc/dqnIyIqeVvF6Em+v7iqKuyAaihu
217ZxxC816tDDI7UIpmyWu39McqGB/2hoA/q+QAMgBiaIuMwYJK9Aw08hXzoCds6O7
22Uor2Y6kMSwEiRnTSYvQHdoaZY3F9SFTLPgjvwfSu7scvp7xvH7bAVIqGGfkLjXpP
23OFkDhEqUI7ORwD5cvvzEu57XmbGB7Nj5LRCGcTq6IlaGeN6Pw5+hOdd6MQ4iISwy
24870msP9NvktURnfXYC3fYnJaK/eUln7LYCBl/k04Z/3Um6dMYyQGh63oGv/2qxQ=3D
25=3D4ctb
26-----END PGP SIGNATURE-----
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-inline-signed.mbox b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-inline-signed.mbox
new file mode 100644
index 00000000..6099a51f
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-inline-signed.mbox
@@ -0,0 +1,26 @@
1From: test <test@kolab.org>
2To: you@you.de
3Subject: test
4Date: Tue, 25 Aug 2015 16:47:10 +0200
5Message-ID: <1662097.O9NVKTC5pT@11b508545ba2>
6X-KMail-Identity: 1428848833
7User-Agent: KMail/4.13.0.3 (Linux/4.1.0-rc5-siduction-amd64; KDE/4.14.3; x86_64; git-7c86098; 2015-08-23)
8MIME-Version: 1.0
9Content-Transfer-Encoding: quoted-printable
10Content-Type: text/plain; charset="iso-8859-1"
11
12-----BEGIN PGP SIGNED MESSAGE-----
13Hash: SHA256
14
15ohno =F6=E4=FC
16-----BEGIN PGP SIGNATURE-----
17Version: GnuPG v2
18
19iQEcBAEBCAAGBQJV3H/vAAoJEI2YYMWPJG3mEZQH/2mbCDa60risTUsomEecasc7
20kIc8Ch+OjZwlEQWKEiFbpLCMVjMwf0oGFcpc/dqnIyIqeVvF6Em+v7iqKuyAaihu
217ZxxC816tDDI7UIpmyWu39McqGB/2hoA/q+QAMgBiaIuMwYJK9Aw08hXzoCds6O7
22Uor2Y6kMSwEiRnTSYvQHdoaZY3F9SFTLPgjvwfSu7scvp7xvH7bAVIqGGfkLjXpP
23OFkDhEqUI7ORwD5cvvzEu57XmbGB7Nj5LRCGcTq6IlaGeN6Pw5+hOdd6MQ4iISwy
24870msP9NvktURnfXYC3fYnJaK/eUln7LYCBl/k04Z/3Um6dMYyQGh63oGv/2qxQ=3D
25=3D4ctb
26-----END PGP SIGNATURE-----
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-inline-signed.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-inline-signed.mbox.html
new file mode 100644
index 00000000..d32d0235
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-inline-signed.mbox.html
@@ -0,0 +1,35 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <table cellspacing="1" cellpadding="1" class="signOkKeyOk">
9 <tr class="signOkKeyOkH">
10 <td dir="ltr">
11 <table cellspacing="0" cellpadding="0" width="100%">
12 <tr>
13 <td>Signed by <a href="mailto:test@kolab.org">test@kolab.org</a>.</td>
14 <td align="right">
15 <a href="kmail:showSignatureDetails">Show Details</a>
16 </td>
17 </tr>
18 </table>
19 </td>
20 </tr>
21 <tr class="signOkKeyOkB">
22 <td>
23 <div class="noquote">
24 <div dir="ltr">ohno öäü</div>
25 </div>
26 </td>
27 </tr>
28 <tr class="signOkKeyOkH">
29 <td dir="ltr">End of signed message</td>
30 </tr>
31 </table>
32 </div>
33 </div>
34 </body>
35</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-inline-signed.mbox.inProgress.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-inline-signed.mbox.inProgress.html
new file mode 100644
index 00000000..45a999d3
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-inline-signed.mbox.inProgress.html
@@ -0,0 +1,22 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <table cellspacing="1" cellpadding="1" class="signInProgress">
9 <tr class="signInProgressH">
10 <td dir="ltr">Please wait while the signature is being verified...</td>
11 </tr>
12 <tr class="signInProgressB">
13 <td/>
14 </tr>
15 <tr class="signInProgressH">
16 <td dir="ltr">End of signed message</td>
17 </tr>
18 </table>
19 </div>
20 </div>
21 </body>
22</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-inline-signed.mbox.tree b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-inline-signed.mbox.tree
new file mode 100644
index 00000000..23e99880
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-inline-signed.mbox.tree
@@ -0,0 +1,3 @@
1 * MimeTreeParser::MessagePartList
2 * MimeTreeParser::TextMessagePart
3 * MimeTreeParser::SignedMessagePart
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-inline-wrong-charset-encrypted.mbox b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-inline-wrong-charset-encrypted.mbox
new file mode 100644
index 00000000..5ecfc612
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-inline-wrong-charset-encrypted.mbox
@@ -0,0 +1,34 @@
1From t.glaser@tarent.de Mon Aug 18 10:59:01 2014
2Return-Path: <t.glaser@tarent.de>
3Received: from tgwrk.ig42.org (tgwrk.ig42.org.
4 [2a01:238:4200:4342:321e:80ff:fe12:4223]) by mx.google.com with ESMTPSA id
5 pe6sm40660135wjb.38.2014.08.18.01.59.01 for <t.glaser@tarent.de>
6 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Mon, 18
7 Aug 2014 01:59:01 -0700 (PDT)
8Date: Mon, 18 Aug 2014 10:59:00 +0200 (CEST)
9From: Thorsten Glaser <t.glaser@tarent.de>
10X-X-Sender: tglase@tglase.lan.tarent.de
11To: Thorsten Glaser <t.glaser@tarent.de>
12Subject: Test for Mozilla bug#1054187
13Message-ID: <alpine.DEB.2.11.1408181058220.30583@tglase.lan.tarent.de>
14User-Agent: Alpine 2.11 (DEB 23 2013-08-11)
15MIME-Version: 1.0
16Content-Type: TEXT/PLAIN; charset=US-ASCII
17
18-----BEGIN PGP MESSAGE-----
19Version: GnuPG v2
20
21hIwDTFSKP3rBSXcBBAClcHW9/6kw8i+XkMes47vcmGBCjIC0UysqkVYyNqT2Y6tb
22s7pdfZFQPVWbdYoxP0WLzGgNFEWttlojWJmaTNiDVLOP22hFuJL3LUxesC1cWE+6
23foCkENDI2YnkAw4o5HTrmHoBlG7N/Nzzu3+1kfUVANSoAhgWd5WJDsXyvPMoD9LA
247gHZZmq4bK5OwTHvAvdUOstCGd6Wqj5zkVXT59WOfYxYLcrGZ8I62lBS1/90TlJe
25iEolBoaufZT7K2YW7k/+DPYgRIzvWISTccPWpcS7OOyifvK4zOFJeGsVq/DowP52
26Zt1xQj1En5CVUT/MkpvS1rB4BfSuhJETZdtUGveUe0HhcAzbnbIJULdK0p1XAo4O
27q84vmOVD0BtKJVy/+rIW7h4aOr8C66HNDKLiUzWtdEaG97GQwhpQZ05JNsulG9tV
28wyP6UWmDMY/5YuRoVHOYx8NXORHX4E5P151Tr5Fted9TpXI/gOTHHyPK5AiiDG6U
29ja4fgkO6hYnjHxqqooxfGj+pg5atynnbMTALfWoXxmqyKrIB+SKqnsw+sCL3ro2x
30j54EGZZ9wM6AYDQ48lJV5beWgQ55r28HxlhPNl/driNkMZHUazAdsubWo5NqJPXl
31HrHQ4lv53ZUohbpVvdkmsldWhA5me7yRhQHytTQMMfadmSiYZVsy53siQ/5gEhUv
32DQ4ggpUjf8twqR+5TLue5/r/fRXkGfKr5U1w4qcMcFcGwEIbwE+qtIDY0Cw/+xU=
33=Ecl0
34-----END PGP MESSAGE-----
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-inline-wrong-charset-encrypted.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-inline-wrong-charset-encrypted.mbox.html
new file mode 100644
index 00000000..3ed4e0fb
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-inline-wrong-charset-encrypted.mbox.html
@@ -0,0 +1,47 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <table cellspacing="1" cellpadding="1" class="encr">
9 <tr class="encrH">
10 <td dir="ltr">Encrypted message</td>
11 </tr>
12 <tr class="encrB">
13 <td>
14 <table cellspacing="1" cellpadding="1" class="signOkKeyOk">
15 <tr class="signOkKeyOkH">
16 <td dir="ltr">
17 <table cellspacing="0" cellpadding="0" width="100%">
18 <tr>
19 <td>Signed by <a href="mailto:test@kolab.org">test@kolab.org</a>.</td>
20 <td align="right">
21 <a href="kmail:showSignatureDetails">Show Details</a>
22 </td>
23 </tr>
24 </table>
25 </td>
26 </tr>
27 <tr class="signOkKeyOkB">
28 <td>
29 <div class="noquote">
30 <div dir="ltr">This is a utf-8 message you see - öäüß@ł€¶ŧ←↓→øþ</div>
31 </div>
32 </td>
33 </tr>
34 <tr class="signOkKeyOkH">
35 <td dir="ltr">End of signed message</td>
36 </tr>
37 </table>
38 </td>
39 </tr>
40 <tr class="encrH">
41 <td dir="ltr">End of encrypted message</td>
42 </tr>
43 </table>
44 </div>
45 </div>
46 </body>
47</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-inline-wrong-charset-encrypted.mbox.tree b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-inline-wrong-charset-encrypted.mbox.tree
new file mode 100644
index 00000000..ea8223fd
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-inline-wrong-charset-encrypted.mbox.tree
@@ -0,0 +1,4 @@
1 * MimeTreeParser::MessagePartList
2 * MimeTreeParser::TextMessagePart
3 * MimeTreeParser::EncryptedMessagePart
4 * MimeTreeParser::SignedMessagePart
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-apple.mbox b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-apple.mbox
new file mode 100644
index 00000000..ba85df86
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-apple.mbox
@@ -0,0 +1,129 @@
1Return-Path: <sender@example.org>
2Sender: sender@example.org
3From: Quonk <sender@example.org>
4X-Pgp-Agent: GPGMail
5Content-Type: multipart/signed; boundary="Apple-Mail=_12345678-1234-1234-1234-12345678"; protocol="application/pgp-signature"; micalg=pgp-sha512
6Subject: PDF
7Date: Mon, 16 Jan 2017 15:14:51 +0100
8Message-Id: <199E2891-3080-42B6-ABCD-1230B78EBABC@example.org>
9To: Konqi <konqui@example.org>
10Mime-Version: 1.0 (Mac OS X Mail 9.3 \(3124\))
11
12
13--Apple-Mail=_12345678-1234-1234-1234-12345678
14Content-Type: multipart/alternative;
15 boundary="Apple-Mail=_23456789-1234-1234-1234-12345678"
16
17
18--Apple-Mail=_23456789-1234-1234-1234-12345678
19Content-Transfer-Encoding: quoted-printable
20Content-Type: text/plain;
21 charset=utf-8
22
23
24
25=E2=80=A6
26Quonk
27Klar=C3=A4lvdalens Datakonsult AB, a KDAB Group company
28Sweden (HQ) +46-563-540090, Germany +49-30-521325470
29KDAB - The Qt, C++ and OpenGL Experts | www.kdab.com
30
31
32--Apple-Mail=_23456789-1234-1234-1234-12345678
33Content-Type: multipart/mixed;
34 boundary="Apple-Mail=_34567890-1234-1234-1234-12345678"
35
36
37--Apple-Mail=_34567890-1234-1234-1234-12345678
38Content-Transfer-Encoding: 7bit
39Content-Type: text/html;
40 charset=us-ascii
41
42<html><head><meta http-equiv="Content-Type" content="text/html charset=us-ascii"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><div class="">pre attachment</div></body></html>
43--Apple-Mail=_34567890-1234-1234-1234-12345678
44Content-Disposition: attachment; filename="image.png"
45Content-Transfer-Encoding: base64
46Content-Type: image/png; name="image.png"
47
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
91o/0d59+Lh3Y1HHcAAAAASUVORK5CYII=
92--Apple-Mail=_34567890-1234-1234-1234-12345678
93Content-Transfer-Encoding: quoted-printable
94Content-Type: text/html;
95 charset=utf-8
96
97<html><head><meta name="qrichtext" content="1" /><style type="text/css">
98p, li { white-space: pre-wrap; }
99</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;">
100<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; -qt-user-state:0;">Some <span style=" font-weight:600;">HTML</span> text</p>
101</body></html>
102
103--Apple-Mail=_34567890-1234-1234-1234-12345678--
104
105--Apple-Mail=_23456789-1234-1234-1234-12345678--
106
107--Apple-Mail=_12345678-1234-1234-1234-12345678
108Content-Transfer-Encoding: 7bit
109Content-Disposition: attachment;
110 filename=signature.asc
111Content-Type: application/pgp-signature;
112 name=signature.asc
113Content-Description: Message signed with OpenPGP using GPGMail
114
115-----BEGIN PGP SIGNATURE-----
116
117iQEzBAEBCAAdFiEEG6Mjkys/qoJhMseejZhgxY8kbeYFAlh/rcwACgkQjZhgxY8k
118beYaoQf+Miuj4cnVumYXMopVMHJs6AK6D+uKO4jXHl/XUK3TOg17kFUZDEN/9JFd
119SCN9oD5emzpBl4GSmYBbjvLvXTHTLHviVD6In35+wgMlQL+xfAv91Dx56QslCQMo
120UhDYGgFPiEAfCY2UozQD/R3KWOHFB9bNdtOM0hdT84D35W2PZhzTlz2q3hpq3bYw
121lNhFVebqURh9OEAZglB3Q9oDE13PJDtRLflKquC5ZU8N4Bj23TCOgxv4FzSyyAn/
122XalEKdwYrkZ8p4rRtd0YvAVevDUC4pQNGTgfsXgldoPEGUBXsdlczLPEj2sjLvNu
123HX1GMDrZL/+7DZsURYV5DjhsqWzExQ==
124=q0F6
125-----END PGP SIGNATURE-----
126
127--Apple-Mail=_12345678-1234-1234-1234-12345678--
128
129
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-apple.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-apple.mbox.html
new file mode 100644
index 00000000..234eaae4
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-apple.mbox.html
@@ -0,0 +1,58 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <table cellspacing="1" cellpadding="1" class="signWarn">
9 <tr class="signWarnH">
10 <td dir="ltr">
11 <table cellspacing="0" cellpadding="0" width="100%">
12 <tr>
13 <td>Not enough information to check signature validity.</td>
14 <td align="right">
15 <a href="kmail:showSignatureDetails">Show Details</a>
16 </td>
17 </tr>
18 </table>
19 </td>
20 </tr>
21 <tr class="signWarnB">
22 <td>
23 <a name="att1"/>
24 <div id="attachmentDiv1">
25 <a name="att1.2"/>
26 <div id="attachmentDiv1.2">
27 <a name="att1.2.1"/>
28 <div id="attachmentDiv1.2.1">
29 <div style="position: relative">
30 <div class="">pre attachment</div>
31 </div>
32 </div>
33 <a name="att1.2.2"/>
34 <div id="attachmentDiv1.2.2">
35 <hr/>
36 <div>
37 <a href="attachment:1.2.2?place=body"><img align="center" height="48" width="48" src="file:image-png.svg" border="0" style="max-width: 100%" alt=""/>image.png</a>
38 </div>
39 <div/>
40 </div>
41 <a name="att1.2.3"/>
42 <div id="attachmentDiv1.2.3">
43 <div style="position: relative">
44 <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; -qt-user-state:0;">Some <span style=" font-weight:600;">HTML</span> text</p>
45 </div>
46 </div>
47 </div>
48 </div>
49 </td>
50 </tr>
51 <tr class="signWarnH">
52 <td dir="ltr">End of signed message</td>
53 </tr>
54 </table>
55 </div>
56 </div>
57 </body>
58</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-apple.mbox.inProgress.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-apple.mbox.inProgress.html
new file mode 100644
index 00000000..b5236fe4
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-apple.mbox.inProgress.html
@@ -0,0 +1,49 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <table cellspacing="1" cellpadding="1" class="signInProgress">
9 <tr class="signInProgressH">
10 <td dir="ltr">Please wait while the signature is being verified...</td>
11 </tr>
12 <tr class="signInProgressB">
13 <td>
14 <a name="att1"/>
15 <div id="attachmentDiv1">
16 <a name="att1.2"/>
17 <div id="attachmentDiv1.2">
18 <a name="att1.2.1"/>
19 <div id="attachmentDiv1.2.1">
20 <div style="position: relative">
21 <div class="">pre attachment</div>
22 </div>
23 </div>
24 <a name="att1.2.2"/>
25 <div id="attachmentDiv1.2.2">
26 <hr/>
27 <div>
28 <a href="attachment:1.2.2?place=body"><img align="center" height="48" width="48" src="file:image-png.svg" border="0" style="max-width: 100%" alt=""/>image.png</a>
29 </div>
30 <div/>
31 </div>
32 <a name="att1.2.3"/>
33 <div id="attachmentDiv1.2.3">
34 <div style="position: relative">
35 <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; -qt-user-state:0;">Some <span style=" font-weight:600;">HTML</span> text</p>
36 </div>
37 </div>
38 </div>
39 </div>
40 </td>
41 </tr>
42 <tr class="signInProgressH">
43 <td dir="ltr">End of signed message</td>
44 </tr>
45 </table>
46 </div>
47 </div>
48 </body>
49</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-apple.mbox.tree b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-apple.mbox.tree
new file mode 100644
index 00000000..3ade4efe
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-apple.mbox.tree
@@ -0,0 +1,3 @@
1 * MimeTreeParser::MessagePartList
2 * MimeTreeParser::SignedMessagePart
3 * MimeTreeParser::AlternativeMessagePart
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-base64-mailman-footer.mbox b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-base64-mailman-footer.mbox
new file mode 100644
index 00000000..70bf4ef8
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-base64-mailman-footer.mbox
@@ -0,0 +1,117 @@
1Return-Path: <kde-pim-bounces@kde.org>
2X-Sieve: CMU Sieve 2.3
3X-Virus-Scanned: amavisd-new at site
4Authentication-Results: linux.site (amavisd-new); dkim=pass (1024-bit key)
5 header.d=kde.org
6Received: from postbox.kde.org (localhost.localdomain [127.0.0.1])
7 by postbox.kde.org (Postfix) with ESMTP id 867B8BF274;
8 Sat, 22 Aug 2015 09:32:21 +0000 (UTC)
9DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=kde.org; s=default;
10 t=1440235945; bh=WhGhdxvdvRs04JdzjAkPcBVPmx7putlUE3ka9dvMIoc=;
11 h=From:To:Date:Subject:Reply-To:List-Id:List-Unsubscribe:List-Post:
12 List-Help:List-Subscribe:From;
13 b=mvxeMMGebkZKq7hekRypkPvt6S8lidA/8vQ3AC5Kft8HDmj8lDUpvOo0VXwCF0OG+
14 iAOPKxYtxclf8PgYvgK8NIzr56CwcdlNm3/PpoSe20P3I1DGFpDDMFtW5tOD05SSHz
15 5L6PCQyb+KFW1GrXgcm+eHshzJh3U8nHcyd8Vw2E=
16X-Original-To: kde-pim@kde.org
17Delivered-To: kde-pim@localhost.kde.org
18X-Virus-Scanned: amavisd-new at site
19From: Volker Krause <vkrause@kde.org>
20To: KDEPIM <kde-pim@kde.org>
21Date: Sat, 22 Aug 2015 11:31:38 +0200
22Message-ID: <11737387.KAAPH2KlE3@vkpc5>
23Organization: KDE
24User-Agent: KMail/4.14.3 (Linux/3.16.6-2-desktop; KDE/4.14.7; x86_64;
25 git-c97b13e; 2014-12-30)
26MIME-Version: 1.0
27Subject: [Kde-pim] Phabricator Project Setup
28X-BeenThere: kde-pim@kde.org
29X-Mailman-Version: 2.1.16
30Precedence: list
31Reply-To: KDE PIM <kde-pim@kde.org>
32List-Id: KDE PIM <kde-pim.kde.org>
33List-Unsubscribe: <https://mail.kde.org/mailman/options/kde-pim>,
34 <mailto:kde-pim-request@kde.org?subject=unsubscribe>
35List-Post: <mailto:kde-pim@kde.org>
36List-Help: <mailto:kde-pim-request@kde.org?subject=help>
37List-Subscribe: <https://mail.kde.org/mailman/listinfo/kde-pim>,
38 <mailto:kde-pim-request@kde.org?subject=subscribe>
39Content-Type: multipart/mixed; boundary="===============1910646461178264940=="
40Errors-To: kde-pim-bounces@kde.org
41Sender: "kde-pim" <kde-pim-bounces@kde.org>
42
43
44--===============1910646461178264940==
45Content-Type: multipart/signed; boundary="nextPart2440608.7aDuJBW7cK"; micalg="pgp-sha1"; protocol="application/pgp-signature"
46
47--nextPart2440608.7aDuJBW7cK
48Content-Transfer-Encoding: quoted-printable
49Content-Type: text/plain; charset="us-ascii"
50
51Hi,
52
53I've talked to Ben, the current Phabricator test setup would actually b=
54e=20
55usable for "production" use for task/project management for us, without=
56=20
57causing the sysadmins unreasonable trouble when migrating to the full=20=
58
59production deployment of Phabricator eventually.
60
61Phabricator project layout it orthogonal to repo layout, so we can stru=
62cture=20
63this however we want. Among other teams I see at least the following la=
64youts:
65- single project for everything
66- a project per release
67- a project per component/module (ie. close to the repo layout)
68
69How do we want to structure this?
70
71I would start with a single project to not fragment this too much, as w=
72e have=20
73a relatively small team actually looking into this, so everyone is look=
74ing at=20
75most sub-projects anyway. And should we eventually hit scaling limits, =
76we can=20
77always expand this I think.
78
79We of course should also talk about what we actually want to put in the=
80re. My=20
81current motivation is having a place to collect the tasks for getting m=
82ore of=20
83the former pimlibs into KF5, and anything else I run into on the way th=
84ere=20
85that we eventually should clean up/improve.
86
87regards,
88Volker
89
90--nextPart2440608.7aDuJBW7cK
91Content-Type: application/pgp-signature; name="signature.asc"
92Content-Description: This is a digitally signed message part.
93Content-Transfer-Encoding: 7Bit
94
95-----BEGIN PGP SIGNATURE-----
96Version: GnuPG v2
97
98iD8DBQBV2EF9f5bM1k0S0kcRAk9cAJ4vHEh9JkT3Jy3EfxII7nP9HPmxrQCgjeLF
99eYXCyN9NRAyC6CHeNnWZN10=
100=Y8W4
101-----END PGP SIGNATURE-----
102
103--nextPart2440608.7aDuJBW7cK--
104
105
106--===============1910646461178264940==
107Content-Type: text/plain; charset="utf-8"
108MIME-Version: 1.0
109Content-Transfer-Encoding: base64
110Content-Disposition: inline
111
112X19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX18KS0RFIFBJTSBt
113YWlsaW5nIGxpc3Qga2RlLXBpbUBrZGUub3JnCmh0dHBzOi8vbWFpbC5rZGUub3JnL21haWxtYW4v
114bGlzdGluZm8va2RlLXBpbQpLREUgUElNIGhvbWUgcGFnZSBhdCBodHRwOi8vcGltLmtkZS5vcmcv
115
116--===============1910646461178264940==--
117
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-base64-mailman-footer.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-base64-mailman-footer.mbox.html
new file mode 100644
index 00000000..50eddaa6
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-base64-mailman-footer.mbox.html
@@ -0,0 +1,78 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <a name="att1"/>
9 <div id="attachmentDiv1">
10 <table cellspacing="1" cellpadding="1" class="signWarn">
11 <tr class="signWarnH">
12 <td dir="ltr">
13 <table cellspacing="0" cellpadding="0" width="100%">
14 <tr>
15 <td>Not enough information to check signature validity.</td>
16 <td align="right">
17 <a href="kmail:showSignatureDetails">Show Details</a>
18 </td>
19 </tr>
20 </table>
21 </td>
22 </tr>
23 <tr class="signWarnB">
24 <td>
25 <a name="att1.1"/>
26 <div id="attachmentDiv1.1">
27 <div class="noquote">
28 <div dir="ltr">Hi,</div>
29 <br/>
30 <div dir="ltr">I've talked to Ben, the current Phabricator test setup would actually be&nbsp;</div>
31 <div dir="ltr">usable for "production" use for task/project management for us, without&nbsp;</div>
32 <div dir="ltr">causing the sysadmins unreasonable trouble when migrating to the full&nbsp;</div>
33 <div dir="ltr">production deployment of Phabricator eventually.</div>
34 <br/>
35 <div dir="ltr">Phabricator project layout it orthogonal to repo layout, so we can structure&nbsp;</div>
36 <div dir="ltr">this however we want. Among other teams I see at least the following layouts:</div>
37 <div dir="ltr">- single project for everything</div>
38 <div dir="ltr">- a project per release</div>
39 <div dir="ltr">- a project per component/module (ie. close to the repo layout)</div>
40 <br/>
41 <div dir="ltr">How do we want to structure this?</div>
42 <br/>
43 <div dir="ltr">I would start with a single project to not fragment this too much, as we have&nbsp;</div>
44 <div dir="ltr">a relatively small team actually looking into this, so everyone is looking at&nbsp;</div>
45 <div dir="ltr">most sub-projects anyway. And should we eventually hit scaling limits, we can&nbsp;</div>
46 <div dir="ltr">always expand this I think.</div>
47 <br/>
48 <div dir="ltr">We of course should also talk about what we actually want to put in there. My&nbsp;</div>
49 <div dir="ltr">current motivation is having a place to collect the tasks for getting more of&nbsp;</div>
50 <div dir="ltr">the former pimlibs into KF5, and anything else I run into on the way there&nbsp;</div>
51 <div dir="ltr">that we eventually should clean up/improve.</div>
52 <br/>
53 <div dir="ltr">regards,</div>
54 <div dir="ltr">Volker</div>
55 </div>
56 </div>
57 </td>
58 </tr>
59 <tr class="signWarnH">
60 <td dir="ltr">End of signed message</td>
61 </tr>
62 </table>
63 </div>
64 <a name="att2"/>
65 <div id="attachmentDiv2">
66 <div class="noquote">
67 <div dir="ltr">_______________________________________________</div>
68 <div dir="ltr">KDE PIM mailing list <a href="mailto:kde-pim@kde.org">kde-pim@kde.org</a></div>
69 <div dir="ltr">
70 <a href="https://mail.kde.org/mailman/listinfo/kde-pim">https://mail.kde.org/mailman/listinfo/kde-pim</a>
71 </div>
72 <div dir="ltr">KDE PIM home page at <a href="http://pim.kde.org/">http://pim.kde.org/</a></div>
73 </div>
74 </div>
75 </div>
76 </div>
77 </body>
78</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-base64-mailman-footer.mbox.tree b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-base64-mailman-footer.mbox.tree
new file mode 100644
index 00000000..2753978c
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-base64-mailman-footer.mbox.tree
@@ -0,0 +1,7 @@
1 * MimeTreeParser::MessagePartList
2 * MimeTreeParser::MimeMessagePart
3 * MimeTreeParser::SignedMessagePart
4 * MimeTreeParser::TextMessagePart
5 * MimeTreeParser::MessagePart
6 * MimeTreeParser::AttachmentMessagePart
7 * MimeTreeParser::MessagePart
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-encrypted-two-attachments.mbox b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-encrypted-two-attachments.mbox
new file mode 100644
index 00000000..7939af83
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-encrypted-two-attachments.mbox
@@ -0,0 +1,52 @@
1From: firstname.lastname@example.com
2To: test@kolab.org
3Subject: OpenPGP signed+encrypted with 2 text attachments
4Date: Sun, 30 Aug 2015 12:01:20 +0200
5Message-ID: <4368981.7YjI8cQ7Br@vkpc5>
6X-KMail-Identity: 402312391
7X-KMail-Dictionary: en_US
8User-Agent: KMail/5.0.42 pre (Linux/3.16.6-2-desktop; KDE/5.14.0; x86_64; ; )
9MIME-Version: 1.0
10Content-Type: multipart/encrypted; boundary="nextPart3246504.5GAivIUY6Q"; protocol="application/pgp-encrypted"
11
12--nextPart3246504.5GAivIUY6Q
13Content-Type: application/pgp-encrypted
14Content-Disposition: attachment
15Content-Transfer-Encoding: 7Bit
16
17Version: 1
18--nextPart3246504.5GAivIUY6Q
19Content-Type: application/octet-stream
20Content-Disposition: inline; filename="msg.asc"
21Content-Transfer-Encoding: 7Bit
22
23-----BEGIN PGP MESSAGE-----
24Version: GnuPG v2
25
26hQEMAwzOQ1qnzNo7AQf9Ge9nFjtqLOKSQNhobS+0iCB2GUdLP7LCIWu6gBo9pWa+
279wCNLxwmhqWOYv37RAk6v5VXjCYUX3/7UF7e7epSqo7YjS7VsUOow0gszQjJqocK
28Gd1T1oyNknza6oaRGgVeWPOZVAPb+Gj+3yS8VZa33Aq2ay7F7eI8dvRUN7Z3TuAh
29BOVV+itwHHzanfNG8RoCvokcE1vkANfvI3u7R4Q93U8Q+Qmjh1L5ypPe37N5BtAF
30UCPCiD9XySHjm5PyXx8ImrJDeUgFs1YhYox4B6NKsCcmm7R8NdYZYGNo1kzR4yKV
31FzMu1NUU/bwtvrpRXLe4dBL1pEkO2PpuMYDUR9+WVNLpAafTDbeIHPi/Z8v48seQ
32JxscRehfOB3DG1xrvQTMFJc3UJEBqNMkM9gOxLKOQcCcZp79FMsfWB7EjjlPR1Oh
33gyA5NR+4HxNw75Q5FpZ7qziWvIrb1Kzwfbsb9Dimx+MmiNMX9kUEPqkPo4pspwZ9
34pLCfjYUnikcnYyQ0b2ojsjQmLotYlz8mK0GH9L40zfFb1+oYfuu4Y9FPzHdPzNjx
35aFRY8cJQy1CNkCITsz53kni5rk3zVsapq0+NeBDEBYoUqX815fo0W+HVF7/j/uhT
36lPkJhRnJZPwOr5XgzPk3Yk9GlSRLJiqKF4/G8ya/nKyiNIebKM7DTcldWCmZM95B
37BIftaRN4hvVBhl0ElFnZg0xLP1AePFuuplRQTDuW8gpaNKrxwXiF3d4XJdVmjh/p
38YmnieIhbogUHFXugc3g9rE8c3oHA8b514ajSHUm9DXc0cXqw/DrsxXZtKXb+IDpF
39uv9AiM7bSU7I0h/AlaAL5uU0mL58XhkXXFQtaTbMS+u4Rv/Ie1IsnlWR4QSc4m7x
4091rfC1fIf/U43wwwnR+UjIRyr2vWcgTTpwnsZFDD8eSoJ8WqinazJRlMud6Sv+L6
41gI2wiCYyEYHrFEHy0WuS2nUSMNl5AWm31zB+erfKSLZr4EIFBIy4dJWZKwYhi46Q
42pDbw1Svf7xVdHix+5UkkYy3AY70ipf5bxA7FTJ1geJa86VKShDkqVpU6EtT+YQIJ
437geWCyskT0DTaPp6qc8QpjajmRYssDcjiTke5WiqLQAjm8BIuny0fNm6kNC8KMS7
44eGmaBI2nB92bgrqlAW+LhvW95YB0dfO8beg3jKk8s6OJ4gicGFEFp6hXFfEsEZiv
45gi7Q2QCVFvCV11884H8rtZYmMRFGmuVUvm6xh/z1xicmfSy0YUowgkA3jpi7o913
46fqmYOHAwzCxv8Zp7xBf9hLT8DxMXdxqYUnJ+FaEMRcFkJ1MAFBpQ9uDbbqAz5bd5
47F3d6o0JSleOOTDlNH7wpN15HYtaCx9v3mXLN9FY4Y1g4mE8wdU0JZn7sFEmgmAkV
48/vj9khHS6eB01GPiCA6sy/u2tSdCQQ==
49=1GHi
50-----END PGP MESSAGE-----
51
52--nextPart3246504.5GAivIUY6Q--
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-encrypted-two-attachments.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-encrypted-two-attachments.mbox.html
new file mode 100644
index 00000000..53856e74
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-encrypted-two-attachments.mbox.html
@@ -0,0 +1,88 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <table cellspacing="1" cellpadding="1" class="encr">
9 <tr class="encrH">
10 <td dir="ltr">Encrypted message</td>
11 </tr>
12 <tr class="encrB">
13 <td>
14 <div style="position: relative; word-wrap: break-word">
15 <a name="att"/>
16 <div id="attachmentDiv">
17 <table cellspacing="1" cellpadding="1" class="signOkKeyOk">
18 <tr class="signOkKeyOkH">
19 <td dir="ltr">
20 <table cellspacing="0" cellpadding="0" width="100%">
21 <tr>
22 <td>Signed by <a href="mailto:test@kolab.org">test@kolab.org</a>.</td>
23 <td align="right">
24 <a href="kmail:showSignatureDetails">Show Details</a>
25 </td>
26 </tr>
27 </table>
28 </td>
29 </tr>
30 <tr class="signOkKeyOkB">
31 <td>
32 <a name="att1"/>
33 <div id="attachmentDiv1">
34 <a name="att1.1"/>
35 <div id="attachmentDiv1.1">
36 <div class="noquote">
37 <div dir="ltr">this is the main body</div>
38 </div>
39 </div>
40 <a name="att1.2"/>
41 <div id="attachmentDiv1.2">
42 <table cellspacing="1" class="textAtm">
43 <tr class="textAtmH">
44 <td dir="ltr">attachment1.txt</td>
45 </tr>
46 <tr class="textAtmB">
47 <td>
48 <div class="noquote">
49 <div dir="ltr">this is one attachment</div>
50 </div>
51 </td>
52 </tr>
53 </table>
54 </div>
55 <a name="att1.3"/>
56 <div id="attachmentDiv1.3">
57 <table cellspacing="1" class="textAtm">
58 <tr class="textAtmH">
59 <td dir="ltr">attachment2.txt</td>
60 </tr>
61 <tr class="textAtmB">
62 <td>
63 <div class="noquote">
64 <div dir="ltr">this is the second attachment</div>
65 </div>
66 </td>
67 </tr>
68 </table>
69 </div>
70 </div>
71 </td>
72 </tr>
73 <tr class="signOkKeyOkH">
74 <td dir="ltr">End of signed message</td>
75 </tr>
76 </table>
77 </div>
78 </div>
79 </td>
80 </tr>
81 <tr class="encrH">
82 <td dir="ltr">End of encrypted message</td>
83 </tr>
84 </table>
85 </div>
86 </div>
87 </body>
88</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-encrypted-two-attachments.mbox.tree b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-encrypted-two-attachments.mbox.tree
new file mode 100644
index 00000000..6705a4d7
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-encrypted-two-attachments.mbox.tree
@@ -0,0 +1,10 @@
1 * MimeTreeParser::MessagePartList
2 * MimeTreeParser::EncryptedMessagePart
3 * MimeTreeParser::SignedMessagePart
4 * MimeTreeParser::MimeMessagePart
5 * MimeTreeParser::TextMessagePart
6 * MimeTreeParser::MessagePart
7 * MimeTreeParser::AttachmentMessagePart
8 * MimeTreeParser::MessagePart
9 * MimeTreeParser::AttachmentMessagePart
10 * MimeTreeParser::MessagePart
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-encrypted.mbox b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-encrypted.mbox
new file mode 100644
index 00000000..6d723d52
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-encrypted.mbox
@@ -0,0 +1,47 @@
1From: OpenPGP Test <test@kolab.org>
2To: test@kolab.org
3Subject: OpenPGP signed and encrypted
4Date: Tue, 07 Sep 2010 18:08:44 +0200
5User-Agent: KMail/4.6 pre (Linux/2.6.34-rc2-2-default; KDE/4.5.60; x86_64; ; )
6MIME-Version: 1.0
7Content-Type: multipart/encrypted; boundary="nextPart25203163.0xtB501Z4V"; protocol="application/pgp-encrypted"
8Content-Transfer-Encoding: 7Bit
9
10
11--nextPart25203163.0xtB501Z4V
12Content-Type: application/pgp-encrypted
13Content-Disposition: attachment
14
15Version: 1
16--nextPart25203163.0xtB501Z4V
17Content-Type: application/octet-stream
18Content-Disposition: inline; filename="msg.asc"
19
20-----BEGIN PGP MESSAGE-----
21Version: GnuPG v2.0.15 (GNU/Linux)
22
23hQEMAwzOQ1qnzNo7AQf7BFYWaGiCTGtXY59bSh3LCXNnWZejblYALxIUNXOFEXbm
24y/YA95FmQsy3U5HRCAJV/DY1PEaJz1RTm9bcdIpDC3Ab2YzSwmOwV5fcoUOB2df4
25KjX19Q+2F3JxpPQ0N1gHf4dKfIu19LH+CKeFzUN13aJs5J4A5wlj+NjJikxzmxDS
26kDtNYndynPmo9DJQcsUFw3gpvx5HaHvx1cT4mAB2M5cd2l+vN1jYbaWb0x5Zq41z
27mRNI89aPieC3rcM2289m68fGloNbYvi8mZJu5RrI4Tbi/D7Rjm1y63lHgVV6AN88
28XAzRiedOeF99LoTBulrJdtT8AAgCs8nCetcWpIffdtLpAZiZkzHmYOU7nqGxqpRk
29OVeUTrCn9DW2SMmHjaP4IiKnMvzEycu5F4a72+V1LeMIhMSjTRTq+ZE2PTaqH59z
30QsMn7Nb6GlOICbTptRKNNtyJKO7xXlpT7YtvNKnCyEOkH2XrYH7GvpYCiuQ0/o+7
31SxV436ZejiYIg6DQDXJCoa2DXimGp0C10Jh0HwX0BixpoNtwEjkGRYcX6P/JzkH0
32oBood4Ly+Tiu6iVDisrK3AVGYpIzCrKkE9qULTw4R/jFKR2tcCqGb7Fxtk2LV7Md
333S+DyOKrvKQ5GNwbp9OE97pwk+Lr1JS3UAvj5f6BR+1PVNcC0i0wWkgwDjPh1eGD
34enMQmorE6+N0uHtH2F4fOxo/TbbA3+zhI25kVW3bO03xyUl/cmQZeb52nvfOvtOo
35gSb2j6bPkzljDMPEzrtJjbFtGHJbPfUQYJgZv9OE2EQIqpg6goIw279alBq6GLIX
36pkO+dRmztzjcDyhcLxMuQ4cTizel/0J/bU7U6lvwHSyZVbT4Ev+opG5K70Hbqbwr
37NZcgdWXbSeesxGM/oQaMeSurOevxVl+/zrTVAek61aRRd1baAYqgi2pf2V7y4oK3
38qkdxzmoFpRdNlfrQW65NZWnHOi9rC9XxANIwnVn3kRcDf+t2K4PrFluI157lXM/o
39wX91j88fazysbJlQ6TjsApO9ETiPOFEBqouxCTtCZzlUgyVG8jpIjdHWFnagHeXH
40+lXNdYjxnTWTjTxMOZC9ySMpXkjWdFI1ecxVwu6Ik6RX51rvBJAAXWP75yUjPKJ4
41rRi5oQl/VLl0QznO7lvgMPtUwgDVNWO/r7Kn9B387h9fAJZ/kWFAEDW2yhAzABqO
42rCNKDzBPgfAwCnikCpMoCbOL7SU8BdbzQHD8/Lkv4m0pzliHQ/KkGF710koBzTmF
43N7+wk9pwIuvcrEBQj567
44=GV0c
45-----END PGP MESSAGE-----
46
47--nextPart25203163.0xtB501Z4V--
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-encrypted.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-encrypted.mbox.html
new file mode 100644
index 00000000..86a964b8
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-encrypted.mbox.html
@@ -0,0 +1,55 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <table cellspacing="1" cellpadding="1" class="encr">
9 <tr class="encrH">
10 <td dir="ltr">Encrypted message</td>
11 </tr>
12 <tr class="encrB">
13 <td>
14 <div style="position: relative; word-wrap: break-word">
15 <a name="att"/>
16 <div id="attachmentDiv">
17 <table cellspacing="1" cellpadding="1" class="signOkKeyOk">
18 <tr class="signOkKeyOkH">
19 <td dir="ltr">
20 <table cellspacing="0" cellpadding="0" width="100%">
21 <tr>
22 <td>Signed by <a href="mailto:test@kolab.org">test@kolab.org</a>.</td>
23 <td align="right">
24 <a href="kmail:showSignatureDetails">Show Details</a>
25 </td>
26 </tr>
27 </table>
28 </td>
29 </tr>
30 <tr class="signOkKeyOkB">
31 <td>
32 <a name="att1"/>
33 <div id="attachmentDiv1">
34 <div class="noquote">
35 <div dir="ltr">encrypted message text</div>
36 </div>
37 </div>
38 </td>
39 </tr>
40 <tr class="signOkKeyOkH">
41 <td dir="ltr">End of signed message</td>
42 </tr>
43 </table>
44 </div>
45 </div>
46 </td>
47 </tr>
48 <tr class="encrH">
49 <td dir="ltr">End of encrypted message</td>
50 </tr>
51 </table>
52 </div>
53 </div>
54 </body>
55</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-encrypted.mbox.tree b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-encrypted.mbox.tree
new file mode 100644
index 00000000..7d5bbeb7
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-encrypted.mbox.tree
@@ -0,0 +1,5 @@
1 * MimeTreeParser::MessagePartList
2 * MimeTreeParser::EncryptedMessagePart
3 * MimeTreeParser::SignedMessagePart
4 * MimeTreeParser::TextMessagePart
5 * MimeTreeParser::MessagePart
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-mailinglist+additional-children.mbox b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-mailinglist+additional-children.mbox
new file mode 100644
index 00000000..dbca8d45
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-mailinglist+additional-children.mbox
@@ -0,0 +1,126 @@
1Return-Path: <plasma-devel-bounces@kde.org>
2Delivered-To: einar@heavensinferno.net
3Received: from localhost (localhost.localdomain [127.0.0.1])
4 by akihabara.dennogumi.org (Postfix) with ESMTP id 15AB75CD846
5 for <einar@heavensinferno.net>; Mon, 8 Apr 2013 12:15:03 +0200 (CEST)
6Authentication-Results: akihabara.dennogumi.org; dkim=pass
7 (1024-bit key; insecure key) header.i=@kde.org header.b=vQ0NnJ9g;
8 dkim-adsp=pass
9X-Virus-Scanned: Debian amavisd-new at akihabara.dennogumi.org
10X-Spam-Flag: NO
11X-Spam-Score: -3.818
12X-Spam-Level:
13X-Spam-Status: No, score=-3.818 required=5 tests=[BAYES_50=0.8,
14 RCVD_IN_DNSWL_MED=-2.3, RP_MATCHES_RCVD=-2.328, T_DKIM_INVALID=0.01]
15 autolearn=unavailable
16Received: from akihabara.dennogumi.org ([127.0.0.1])
17 by localhost (akihabara.dennogumi.org [127.0.0.1]) (amavisd-new, port 10024)
18 with ESMTP id RMAq-XNJ040f for <einar@heavensinferno.net>;
19 Mon, 8 Apr 2013 12:14:44 +0200 (CEST)
20Received: from postbox.kde.org (postbox.kde.org [46.4.96.248])
21 by akihabara.dennogumi.org (Postfix) with ESMTP id 321675CD845
22 for <einar@heavensinferno.net>; Mon, 8 Apr 2013 12:14:44 +0200 (CEST)
23Authentication-Results: akihabara.dennogumi.org; dkim=pass
24 (1024-bit key; insecure key) header.i=@kde.org header.b=vQ0NnJ9g;
25 dkim-adsp=pass
26Received: from postbox.kde.org (localhost [IPv6:::1])
27 by postbox.kde.org (Postfix) with ESMTP id 9F5E1B37F95;
28 Mon, 8 Apr 2013 10:13:32 +0000 (UTC)
29DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=kde.org; s=default;
30 t=1365416012; bh=ZJtmtbDLoGFwSyJUINdTk4UpuX+xzxcjGp7LSPrKNUs=;
31 h=From:To:Subject:Date:Message-ID:MIME-Version:Reply-To:List-Id:
32 List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe:
33 Content-Type:Sender; b=vQ0NnJ9gjeyPLhPbQx6o9UxmILkS9KrhaKG6luAS/GR
34 6iR3HKXR3HE0BCkTMD5xmKL5ztFMGcU5e79fz0ch0sd2pnZ0y1WVw7KjCxsv/YtO9HM
35 OplAHmhwRI5zH8KKQbyvdPULvssI/ISdViAXmHw04hNPsBjsIGkTPgvNbPFuk=
36X-Original-To: plasma-devel@kde.org
37Delivered-To: plasma-devel@localhost.kde.org
38Received: from mail.bddf.ca (unknown [64.141.113.219])
39 by postbox.kde.org (Postfix) with ESMTP id 782C6B37BE6
40 for <plasma-devel@kde.org>; Mon, 8 Apr 2013 09:51:17 +0000 (UTC)
41Received: from freedom.localnet (242.13.24.31.ftth.as8758.net [31.24.13.242])
42 (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits))
43 (No client certificate requested)
44 by mail.bddf.ca (Postfix) with ESMTPSA id 0CB712DC040
45 for <plasma-devel@kde.org>; Mon, 8 Apr 2013 03:51:16 -0600 (MDT)
46From: "Aaron J. Seigo" <aseigo@kde.org>
47To: plasma-devel@kde.org
48Subject: activities_optional branch in kde-workspace
49Date: Mon, 08 Apr 2013 11:51:11 +0200
50Message-ID: <4143483.eqrJjo7JEn@freedom>
51User-Agent: KMail/4.11 pre (Linux/3.6.3-1-desktop; KDE/4.10.60; i686;
52 git-da50be0; 2013-03-12)
53MIME-Version: 1.0
54X-Scanned-By: MIMEDefang 2.71 on 46.4.96.248
55X-BeenThere: plasma-devel@kde.org
56X-Mailman-Version: 2.1.14
57Precedence: list
58Reply-To: plasma-devel@kde.org
59List-Id: <plasma-devel.kde.org>
60List-Unsubscribe: <https://mail.kde.org/mailman/options/plasma-devel>,
61 <mailto:plasma-devel-request@kde.org?subject=unsubscribe>
62List-Archive: <http://mail.kde.org/pipermail/plasma-devel>
63List-Post: <mailto:plasma-devel@kde.org>
64List-Help: <mailto:plasma-devel-request@kde.org?subject=help>
65List-Subscribe: <https://mail.kde.org/mailman/listinfo/plasma-devel>,
66 <mailto:plasma-devel-request@kde.org?subject=subscribe>
67Content-Type: multipart/mixed; boundary="===============6664737512143839854=="
68Errors-To: plasma-devel-bounces@kde.org
69Sender: plasma-devel-bounces@kde.org
70
71
72--===============6664737512143839854==
73Content-Type: multipart/signed; boundary="nextPart1996263.NlFDv9GTkA"; micalg="pgp-sha1"; protocol="application/pgp-signature"
74
75
76--nextPart1996263.NlFDv9GTkA
77Content-Transfer-Encoding: 7Bit
78Content-Type: text/plain; charset="us-ascii"
79
80hi..
81
82i noticed a new branch when i pulled kde-workspace today (finally!):
83activities_optional
84
85the lone commit in it was pushed on april 1, so maybe it's an april fools
86joke, but if it isn't, it looks like someone is trying to do something that
87makes no sense (and has no chance of being merged into master). so if this is
88a "for reals" branch, perhaps the motivation behind it can be shared?
89
90--
91Aaron J. Seigo
92--nextPart1996263.NlFDv9GTkA
93Content-Type: application/pgp-signature; name="signature.asc"
94Content-Description: This is a digitally signed message part.
95Content-Transfer-Encoding: 7Bit
96
97-----BEGIN PGP SIGNATURE-----
98Version: GnuPG v2.0.19 (GNU/Linux)
99
100iEUEABECAAYFAlFikxAACgkQ1rcusafx20MHbwCfeXOgTDwtR81XJwAdcQB40Lt7
101t2IAmJpIZxdU+SSruySeEfbQs3VXq/8=
102=BQPF
103-----END PGP SIGNATURE-----
104--nextPart1996263.NlFDv9GTkA
105Content-Type: text/plain; name="broken.attachment"
106Content-Transfer-Encoding: 7Bit
107
108Let's break a signed message - This messageblock should not be here :D
109
110--nextPart1996263.NlFDv9GTkA--
111
112
113--===============6664737512143839854==
114Content-Type: text/plain; charset="us-ascii"
115MIME-Version: 1.0
116Content-Transfer-Encoding: 7bit
117Content-Disposition: inline
118
119_______________________________________________
120Plasma-devel mailing list
121Plasma-devel@kde.org
122https://mail.kde.org/mailman/listinfo/plasma-devel
123
124--===============6664737512143839854==--
125
126
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-mailinglist+additional-children.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-mailinglist+additional-children.mbox.html
new file mode 100644
index 00000000..a95252e4
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-mailinglist+additional-children.mbox.html
@@ -0,0 +1,60 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <a name="att1"/>
9 <div id="attachmentDiv1">
10 <a name="att1.1"/>
11 <div id="attachmentDiv1.1">
12 <div class="noquote">
13 <div dir="ltr">hi..</div>
14 <br/>
15 <div dir="ltr">i noticed a new branch when i pulled kde-workspace today (finally!):&nbsp;</div>
16 <div dir="ltr">activities_optional</div>
17 <br/>
18 <div dir="ltr">the lone commit in it was pushed on april 1, so maybe it's an april fools&nbsp;</div>
19 <div dir="ltr">joke, but if it isn't, it looks like someone is trying to do something that&nbsp;</div>
20 <div dir="ltr">makes no sense (and has no chance of being merged into master). so if this is&nbsp;</div>
21 <div dir="ltr">a "for reals" branch, perhaps the motivation behind it can be shared?</div>
22 <br/>
23 <div dir="ltr">--&nbsp;</div>
24 <div dir="ltr">Aaron J. Seigo</div>
25 </div>
26 </div>
27 <a name="att1.2"/>
28 <div id="attachmentDiv1.2">
29 <hr/>
30 <div>
31 <a href="attachment:1.2?place=body"><img align="center" height="48" width="48" src="file:application-pgp-signature.svg" border="0" style="max-width: 100%" alt=""/>signature.asc</a>
32 </div>
33 <div>This is a digitally signed message part.</div>
34 </div>
35 <a name="att1.3"/>
36 <div id="attachmentDiv1.3">
37 <hr/>
38 <div>
39 <a href="attachment:1.3?place=body"><img align="center" height="48" width="48" src="file:text-plain.svg" border="0" style="max-width: 100%" alt=""/>broken.attachment</a>
40 </div>
41 <div/>
42 </div>
43 </div>
44 <a name="att2"/>
45 <div id="attachmentDiv2">
46 <div class="noquote">
47 <div dir="ltr">_______________________________________________</div>
48 <div dir="ltr">Plasma-devel mailing list</div>
49 <div dir="ltr">
50 <a href="mailto:Plasma-devel@kde.org">Plasma-devel@kde.org</a>
51 </div>
52 <div dir="ltr">
53 <a href="https://mail.kde.org/mailman/listinfo/plasma-devel">https://mail.kde.org/mailman/listinfo/plasma-devel</a>
54 </div>
55 </div>
56 </div>
57 </div>
58 </div>
59 </body>
60</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-mailinglist+additional-children.mbox.tree b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-mailinglist+additional-children.mbox.tree
new file mode 100644
index 00000000..906e6274
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-mailinglist+additional-children.mbox.tree
@@ -0,0 +1,10 @@
1 * MimeTreeParser::MessagePartList
2 * MimeTreeParser::MimeMessagePart
3 * MimeTreeParser::MimeMessagePart
4 * MimeTreeParser::TextMessagePart
5 * MimeTreeParser::MessagePart
6 * MimeTreeParser::AttachmentMessagePart
7 * MimeTreeParser::AttachmentMessagePart
8 * MimeTreeParser::MessagePart
9 * MimeTreeParser::AttachmentMessagePart
10 * MimeTreeParser::MessagePart
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-mailinglist+old.mbox b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-mailinglist+old.mbox
new file mode 100644
index 00000000..362dff3e
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-mailinglist+old.mbox
@@ -0,0 +1,67 @@
1Return-Path: <plasma-devel-bounces@kde.org>
2Delivered-To: einar@heavensinferno.net
3From: "Aaron J. Seigo" <aseigo@kde.org>
4To: plasma-devel@kde.org
5Subject: activities_optional branch in kde-workspace
6Date: Mon, 08 Apr 2013 11:51:11 +0200
7Message-ID: <4143483.eqrJjo7JEn@freedom>
8User-Agent: KMail/4.11 pre (Linux/3.6.3-1-desktop; KDE/4.10.60; i686;
9 git-da50be0; 2013-03-12)
10X-Mailman-Version: 2.1.14
11Precedence: list
12Reply-To: plasma-devel@kde.org
13List-Id: <plasma-devel.kde.org>
14List-Unsubscribe: <https://mail.kde.org/mailman/options/plasma-devel>,
15 <mailto:plasma-devel-request@kde.org?subject=unsubscribe>
16List-Archive: <http://mail.kde.org/pipermail/plasma-devel>
17List-Post: <mailto:plasma-devel@kde.org>
18List-Help: <mailto:plasma-devel-request@kde.org?subject=help>
19List-Subscribe: <https://mail.kde.org/mailman/listinfo/plasma-devel>,
20 <mailto:plasma-devel-request@kde.org?subject=subscribe>
21Errors-To: plasma-devel-bounces@kde.org
22Sender: plasma-devel-bounces@kde.org
23
24Oh man a header :)
25
26--__--__--
27
28Message:
29MIME-Version: 1.0
30Content-Type: multipart/signed; boundary="nextPart1996263.NlFDv9GTkA"; micalg="pgp-sha1"; protocol="application/pgp-signature"
31
32
33--nextPart1996263.NlFDv9GTkA
34Content-Transfer-Encoding: 7Bit
35Content-Type: text/plain; charset="us-ascii"
36
37hi..
38
39i noticed a new branch when i pulled kde-workspace today (finally!):
40activities_optional
41
42the lone commit in it was pushed on april 1, so maybe it's an april fools
43joke, but if it isn't, it looks like someone is trying to do something that
44makes no sense (and has no chance of being merged into master). so if this is
45a "for reals" branch, perhaps the motivation behind it can be shared?
46
47--
48Aaron J. Seigo
49--nextPart1996263.NlFDv9GTkA
50Content-Type: application/pgp-signature; name="signature.asc"
51Content-Description: This is a digitally signed message part.
52Content-Transfer-Encoding: 7Bit
53
54-----BEGIN PGP SIGNATURE-----
55Version: GnuPG v2.0.19 (GNU/Linux)
56
57iEUEABECAAYFAlFikxAACgkQ1rcusafx20MHbwCfeXOgTDwtR81XJwAdcQB40Lt7
58t2IAmJpIZxdU+SSruySeEfbQs3VXq/8=
59=BQPF
60-----END PGP SIGNATURE-----
61
62--__--__--
63
64_______________________________________________
65Plasma-devel mailing list
66Plasma-devel@kde.org
67https://mail.kde.org/mailman/listinfo/plasma-devel \ No newline at end of file
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-mailinglist+old.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-mailinglist+old.mbox.html
new file mode 100644
index 00000000..59c6d690
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-mailinglist+old.mbox.html
@@ -0,0 +1,94 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <div style="position: relative; word-wrap: break-word">
9 <a name="att"/>
10 <div id="attachmentDiv">
11 <div class="noquote">
12 <div dir="ltr">Oh man a header :)</div>
13 <br/>
14 </div>
15 </div>
16 </div>
17 <div style="position: relative; word-wrap: break-word">
18 <a name="att"/>
19 <div id="attachmentDiv">
20 <table cellspacing="1" cellpadding="1" class="rfc822">
21 <tr class="rfc822H">
22 <td dir="ltr">
23 <a href="attachment:e1:1?place=body">Encapsulated message</a>
24 </td>
25 </tr>
26 <tr class="rfc822B">
27 <td>
28 <a name="att1"/>
29 <div id="attachmentDiv1">
30 <table cellspacing="1" cellpadding="1" class="signWarn">
31 <tr class="signWarnH">
32 <td dir="ltr">
33 <table cellspacing="0" cellpadding="0" width="100%">
34 <tr>
35 <td>Not enough information to check signature validity.</td>
36 <td align="right">
37 <a href="kmail:showSignatureDetails">Show Details</a>
38 </td>
39 </tr>
40 </table>
41 </td>
42 </tr>
43 <tr class="signWarnB">
44 <td>
45 <a name="att1.1"/>
46 <div id="attachmentDiv1.1">
47 <div class="noquote">
48 <div dir="ltr">hi..</div>
49 <br/>
50 <div dir="ltr">i noticed a new branch when i pulled kde-workspace today (finally!):&nbsp;</div>
51 <div dir="ltr">activities_optional</div>
52 <br/>
53 <div dir="ltr">the lone commit in it was pushed on april 1, so maybe it's an april fools&nbsp;</div>
54 <div dir="ltr">joke, but if it isn't, it looks like someone is trying to do something that&nbsp;</div>
55 <div dir="ltr">makes no sense (and has no chance of being merged into master). so if this is&nbsp;</div>
56 <div dir="ltr">a "for reals" branch, perhaps the motivation behind it can be shared?</div>
57 <br/>
58 <div dir="ltr">--&nbsp;</div>
59 <div dir="ltr">Aaron J. Seigo</div>
60 </div>
61 </div>
62 </td>
63 </tr>
64 <tr class="signWarnH">
65 <td dir="ltr">End of signed message</td>
66 </tr>
67 </table>
68 </div>
69 </td>
70 </tr>
71 <tr class="rfc822H">
72 <td dir="ltr">End of encapsulated message</td>
73 </tr>
74 </table>
75 </div>
76 </div>
77 <div style="position: relative; word-wrap: break-word">
78 <a name="att"/>
79 <div id="attachmentDiv">
80 <div class="noquote">
81 <div dir="ltr">Plasma-devel mailing list</div>
82 <div dir="ltr">
83 <a href="mailto:Plasma-devel@kde.org">Plasma-devel@kde.org</a>
84 </div>
85 <div dir="ltr">
86 <a href="https://mail.kde.org/mailman/listinfo/plasma-devel">https://mail.kde.org/mailman/listinfo/plasma-devel</a>
87 </div>
88 </div>
89 </div>
90 </div>
91 </div>
92 </div>
93 </body>
94</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-mailinglist+old.mbox.tree b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-mailinglist+old.mbox.tree
new file mode 100644
index 00000000..c8310bc1
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-mailinglist+old.mbox.tree
@@ -0,0 +1,13 @@
1 * MimeTreeParser::MessagePartList
2 * MimeTreeParser::MessagePartList
3 * MimeTreeParser::MimeMessagePart
4 * MimeTreeParser::TextMessagePart
5 * MimeTreeParser::MessagePart
6 * MimeTreeParser::MimeMessagePart
7 * MimeTreeParser::EncapsulatedRfc822MessagePart
8 * MimeTreeParser::SignedMessagePart
9 * MimeTreeParser::TextMessagePart
10 * MimeTreeParser::MessagePart
11 * MimeTreeParser::MimeMessagePart
12 * MimeTreeParser::TextMessagePart
13 * MimeTreeParser::MessagePart
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-mailinglist.mbox b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-mailinglist.mbox
new file mode 100644
index 00000000..8adb9f4c
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-mailinglist.mbox
@@ -0,0 +1,121 @@
1Return-Path: <plasma-devel-bounces@kde.org>
2Delivered-To: einar@heavensinferno.net
3Received: from localhost (localhost.localdomain [127.0.0.1])
4 by akihabara.dennogumi.org (Postfix) with ESMTP id 15AB75CD846
5 for <einar@heavensinferno.net>; Mon, 8 Apr 2013 12:15:03 +0200 (CEST)
6Authentication-Results: akihabara.dennogumi.org; dkim=pass
7 (1024-bit key; insecure key) header.i=@kde.org header.b=vQ0NnJ9g;
8 dkim-adsp=pass
9X-Virus-Scanned: Debian amavisd-new at akihabara.dennogumi.org
10X-Spam-Flag: NO
11X-Spam-Score: -3.818
12X-Spam-Level:
13X-Spam-Status: No, score=-3.818 required=5 tests=[BAYES_50=0.8,
14 RCVD_IN_DNSWL_MED=-2.3, RP_MATCHES_RCVD=-2.328, T_DKIM_INVALID=0.01]
15 autolearn=unavailable
16Received: from akihabara.dennogumi.org ([127.0.0.1])
17 by localhost (akihabara.dennogumi.org [127.0.0.1]) (amavisd-new, port 10024)
18 with ESMTP id RMAq-XNJ040f for <einar@heavensinferno.net>;
19 Mon, 8 Apr 2013 12:14:44 +0200 (CEST)
20Received: from postbox.kde.org (postbox.kde.org [46.4.96.248])
21 by akihabara.dennogumi.org (Postfix) with ESMTP id 321675CD845
22 for <einar@heavensinferno.net>; Mon, 8 Apr 2013 12:14:44 +0200 (CEST)
23Authentication-Results: akihabara.dennogumi.org; dkim=pass
24 (1024-bit key; insecure key) header.i=@kde.org header.b=vQ0NnJ9g;
25 dkim-adsp=pass
26Received: from postbox.kde.org (localhost [IPv6:::1])
27 by postbox.kde.org (Postfix) with ESMTP id 9F5E1B37F95;
28 Mon, 8 Apr 2013 10:13:32 +0000 (UTC)
29DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=kde.org; s=default;
30 t=1365416012; bh=ZJtmtbDLoGFwSyJUINdTk4UpuX+xzxcjGp7LSPrKNUs=;
31 h=From:To:Subject:Date:Message-ID:MIME-Version:Reply-To:List-Id:
32 List-Unsubscribe:List-Archive:List-Post:List-Help:List-Subscribe:
33 Content-Type:Sender; b=vQ0NnJ9gjeyPLhPbQx6o9UxmILkS9KrhaKG6luAS/GR
34 6iR3HKXR3HE0BCkTMD5xmKL5ztFMGcU5e79fz0ch0sd2pnZ0y1WVw7KjCxsv/YtO9HM
35 OplAHmhwRI5zH8KKQbyvdPULvssI/ISdViAXmHw04hNPsBjsIGkTPgvNbPFuk=
36X-Original-To: plasma-devel@kde.org
37Delivered-To: plasma-devel@localhost.kde.org
38Received: from mail.bddf.ca (unknown [64.141.113.219])
39 by postbox.kde.org (Postfix) with ESMTP id 782C6B37BE6
40 for <plasma-devel@kde.org>; Mon, 8 Apr 2013 09:51:17 +0000 (UTC)
41Received: from freedom.localnet (242.13.24.31.ftth.as8758.net [31.24.13.242])
42 (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits))
43 (No client certificate requested)
44 by mail.bddf.ca (Postfix) with ESMTPSA id 0CB712DC040
45 for <plasma-devel@kde.org>; Mon, 8 Apr 2013 03:51:16 -0600 (MDT)
46From: "Aaron J. Seigo" <aseigo@kde.org>
47To: plasma-devel@kde.org
48Subject: activities_optional branch in kde-workspace
49Date: Mon, 08 Apr 2013 11:51:11 +0200
50Message-ID: <4143483.eqrJjo7JEn@freedom>
51User-Agent: KMail/4.11 pre (Linux/3.6.3-1-desktop; KDE/4.10.60; i686;
52 git-da50be0; 2013-03-12)
53MIME-Version: 1.0
54X-Scanned-By: MIMEDefang 2.71 on 46.4.96.248
55X-BeenThere: plasma-devel@kde.org
56X-Mailman-Version: 2.1.14
57Precedence: list
58Reply-To: plasma-devel@kde.org
59List-Id: <plasma-devel.kde.org>
60List-Unsubscribe: <https://mail.kde.org/mailman/options/plasma-devel>,
61 <mailto:plasma-devel-request@kde.org?subject=unsubscribe>
62List-Archive: <http://mail.kde.org/pipermail/plasma-devel>
63List-Post: <mailto:plasma-devel@kde.org>
64List-Help: <mailto:plasma-devel-request@kde.org?subject=help>
65List-Subscribe: <https://mail.kde.org/mailman/listinfo/plasma-devel>,
66 <mailto:plasma-devel-request@kde.org?subject=subscribe>
67Content-Type: multipart/mixed; boundary="===============6664737512143839854=="
68Errors-To: plasma-devel-bounces@kde.org
69Sender: plasma-devel-bounces@kde.org
70
71
72--===============6664737512143839854==
73Content-Type: multipart/signed; boundary="nextPart1996263.NlFDv9GTkA"; micalg="pgp-sha1"; protocol="application/pgp-signature"
74
75
76--nextPart1996263.NlFDv9GTkA
77Content-Transfer-Encoding: 7Bit
78Content-Type: text/plain; charset="us-ascii"
79
80hi..
81
82i noticed a new branch when i pulled kde-workspace today (finally!):
83activities_optional
84
85the lone commit in it was pushed on april 1, so maybe it's an april fools
86joke, but if it isn't, it looks like someone is trying to do something that
87makes no sense (and has no chance of being merged into master). so if this is
88a "for reals" branch, perhaps the motivation behind it can be shared?
89
90--
91Aaron J. Seigo
92--nextPart1996263.NlFDv9GTkA
93Content-Type: application/pgp-signature; name="signature.asc"
94Content-Description: This is a digitally signed message part.
95Content-Transfer-Encoding: 7Bit
96
97-----BEGIN PGP SIGNATURE-----
98Version: GnuPG v2.0.19 (GNU/Linux)
99
100iEUEABECAAYFAlFikxAACgkQ1rcusafx20MHbwCfeXOgTDwtR81XJwAdcQB40Lt7
101t2IAmJpIZxdU+SSruySeEfbQs3VXq/8=
102=BQPF
103-----END PGP SIGNATURE-----
104
105--nextPart1996263.NlFDv9GTkA--
106
107
108--===============6664737512143839854==
109Content-Type: text/plain; charset="us-ascii"
110MIME-Version: 1.0
111Content-Transfer-Encoding: 7bit
112Content-Disposition: inline
113
114_______________________________________________
115Plasma-devel mailing list
116Plasma-devel@kde.org
117https://mail.kde.org/mailman/listinfo/plasma-devel
118
119--===============6664737512143839854==--
120
121
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-mailinglist.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-mailinglist.mbox.html
new file mode 100644
index 00000000..7acb6fbf
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-mailinglist.mbox.html
@@ -0,0 +1,65 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <a name="att1"/>
9 <div id="attachmentDiv1">
10 <table cellspacing="1" cellpadding="1" class="signWarn">
11 <tr class="signWarnH">
12 <td dir="ltr">
13 <table cellspacing="0" cellpadding="0" width="100%">
14 <tr>
15 <td>Not enough information to check signature validity.</td>
16 <td align="right">
17 <a href="kmail:showSignatureDetails">Show Details</a>
18 </td>
19 </tr>
20 </table>
21 </td>
22 </tr>
23 <tr class="signWarnB">
24 <td>
25 <a name="att1.1"/>
26 <div id="attachmentDiv1.1">
27 <div class="noquote">
28 <div dir="ltr">hi..</div>
29 <br/>
30 <div dir="ltr">i noticed a new branch when i pulled kde-workspace today (finally!):&nbsp;</div>
31 <div dir="ltr">activities_optional</div>
32 <br/>
33 <div dir="ltr">the lone commit in it was pushed on april 1, so maybe it's an april fools&nbsp;</div>
34 <div dir="ltr">joke, but if it isn't, it looks like someone is trying to do something that&nbsp;</div>
35 <div dir="ltr">makes no sense (and has no chance of being merged into master). so if this is&nbsp;</div>
36 <div dir="ltr">a "for reals" branch, perhaps the motivation behind it can be shared?</div>
37 <br/>
38 <div dir="ltr">--&nbsp;</div>
39 <div dir="ltr">Aaron J. Seigo</div>
40 </div>
41 </div>
42 </td>
43 </tr>
44 <tr class="signWarnH">
45 <td dir="ltr">End of signed message</td>
46 </tr>
47 </table>
48 </div>
49 <a name="att2"/>
50 <div id="attachmentDiv2">
51 <div class="noquote">
52 <div dir="ltr">_______________________________________________</div>
53 <div dir="ltr">Plasma-devel mailing list</div>
54 <div dir="ltr">
55 <a href="mailto:Plasma-devel@kde.org">Plasma-devel@kde.org</a>
56 </div>
57 <div dir="ltr">
58 <a href="https://mail.kde.org/mailman/listinfo/plasma-devel">https://mail.kde.org/mailman/listinfo/plasma-devel</a>
59 </div>
60 </div>
61 </div>
62 </div>
63 </div>
64 </body>
65</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-mailinglist.mbox.inProgress.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-mailinglist.mbox.inProgress.html
new file mode 100644
index 00000000..77c6b29b
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-mailinglist.mbox.inProgress.html
@@ -0,0 +1,56 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <a name="att1"/>
9 <div id="attachmentDiv1">
10 <table cellspacing="1" cellpadding="1" class="signInProgress">
11 <tr class="signInProgressH">
12 <td dir="ltr">Please wait while the signature is being verified...</td>
13 </tr>
14 <tr class="signInProgressB">
15 <td>
16 <a name="att1.1"/>
17 <div id="attachmentDiv1.1">
18 <div class="noquote">
19 <div dir="ltr">hi..</div>
20 <br/>
21 <div dir="ltr">i noticed a new branch when i pulled kde-workspace today (finally!):&nbsp;</div>
22 <div dir="ltr">activities_optional</div>
23 <br/>
24 <div dir="ltr">the lone commit in it was pushed on april 1, so maybe it's an april fools&nbsp;</div>
25 <div dir="ltr">joke, but if it isn't, it looks like someone is trying to do something that&nbsp;</div>
26 <div dir="ltr">makes no sense (and has no chance of being merged into master). so if this is&nbsp;</div>
27 <div dir="ltr">a "for reals" branch, perhaps the motivation behind it can be shared?</div>
28 <br/>
29 <div dir="ltr">--&nbsp;</div>
30 <div dir="ltr">Aaron J. Seigo</div>
31 </div>
32 </div>
33 </td>
34 </tr>
35 <tr class="signInProgressH">
36 <td dir="ltr">End of signed message</td>
37 </tr>
38 </table>
39 </div>
40 <a name="att2"/>
41 <div id="attachmentDiv2">
42 <div class="noquote">
43 <div dir="ltr">_______________________________________________</div>
44 <div dir="ltr">Plasma-devel mailing list</div>
45 <div dir="ltr">
46 <a href="mailto:Plasma-devel@kde.org">Plasma-devel@kde.org</a>
47 </div>
48 <div dir="ltr">
49 <a href="https://mail.kde.org/mailman/listinfo/plasma-devel">https://mail.kde.org/mailman/listinfo/plasma-devel</a>
50 </div>
51 </div>
52 </div>
53 </div>
54 </div>
55 </body>
56</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-mailinglist.mbox.tree b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-mailinglist.mbox.tree
new file mode 100644
index 00000000..2753978c
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-mailinglist.mbox.tree
@@ -0,0 +1,7 @@
1 * MimeTreeParser::MessagePartList
2 * MimeTreeParser::MimeMessagePart
3 * MimeTreeParser::SignedMessagePart
4 * MimeTreeParser::TextMessagePart
5 * MimeTreeParser::MessagePart
6 * MimeTreeParser::AttachmentMessagePart
7 * MimeTreeParser::MessagePart
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-no-protocol.mbox b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-no-protocol.mbox
new file mode 100644
index 00000000..e50879b9
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-no-protocol.mbox
@@ -0,0 +1,35 @@
1Return-Path: <plasma-devel-bounces@kde.org>
2Delivered-To: einar@heavensinferno.net
3Content-Type: multipart/signed; boundary="nextPart1996263.NlFDv9GTkA"; micalg="pgp-sha1"; protocol="application/broken-signature"
4
5
6--nextPart1996263.NlFDv9GTkA
7Content-Transfer-Encoding: 7Bit
8Content-Type: text/plain; charset="us-ascii"
9
10hi..
11
12i noticed a new branch when i pulled kde-workspace today (finally!):
13activities_optional
14
15the lone commit in it was pushed on april 1, so maybe it's an april fools
16joke, but if it isn't, it looks like someone is trying to do something that
17makes no sense (and has no chance of being merged into master). so if this is
18a "for reals" branch, perhaps the motivation behind it can be shared?
19
20--
21Aaron J. Seigo
22--nextPart1996263.NlFDv9GTkA
23Content-Type: application/broken-signature; name="signature.asc"
24Content-Description: This is a digitally signed message part.
25Content-Transfer-Encoding: 7Bit
26
27-----BEGIN PGP SIGNATURE-----
28Version: GnuPG v2.0.19 (GNU/Linux)
29
30iEUEABECAAYFAlFikxAACgkQ1rcusafx20MHbwCfeXOgTDwtR81XJwAdcQB40Lt7
31t2IAmJpIZxdU+SSruySeEfbQs3VXq/8=
32=BQPF
33-----END PGP SIGNATURE-----
34
35--nextPart1996263.NlFDv9GTkA--
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-no-protocol.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-no-protocol.mbox.html
new file mode 100644
index 00000000..4a5f337b
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-no-protocol.mbox.html
@@ -0,0 +1,36 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <a name="att1"/>
9 <div id="attachmentDiv1">
10 <div class="noquote">
11 <div dir="ltr">hi..</div>
12 <br/>
13 <div dir="ltr">i noticed a new branch when i pulled kde-workspace today (finally!):&nbsp;</div>
14 <div dir="ltr">activities_optional</div>
15 <br/>
16 <div dir="ltr">the lone commit in it was pushed on april 1, so maybe it's an april fools&nbsp;</div>
17 <div dir="ltr">joke, but if it isn't, it looks like someone is trying to do something that&nbsp;</div>
18 <div dir="ltr">makes no sense (and has no chance of being merged into master). so if this is&nbsp;</div>
19 <div dir="ltr">a "for reals" branch, perhaps the motivation behind it can be shared?</div>
20 <br/>
21 <div dir="ltr">--&nbsp;</div>
22 <div dir="ltr">Aaron J. Seigo</div>
23 </div>
24 </div>
25 <a name="att2"/>
26 <div id="attachmentDiv2">
27 <hr/>
28 <div>
29 <a href="attachment:2?place=body"><img align="center" height="48" width="48" src="file:unknown.svg" border="0" style="max-width: 100%" alt=""/>signature.asc</a>
30 </div>
31 <div>This is a digitally signed message part.</div>
32 </div>
33 </div>
34 </div>
35 </body>
36</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-no-protocol.mbox.tree b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-no-protocol.mbox.tree
new file mode 100644
index 00000000..d824a11b
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-no-protocol.mbox.tree
@@ -0,0 +1,5 @@
1 * MimeTreeParser::MessagePartList
2 * MimeTreeParser::MimeMessagePart
3 * MimeTreeParser::TextMessagePart
4 * MimeTreeParser::MessagePart
5 * MimeTreeParser::AttachmentMessagePart
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-two-attachments.mbox b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-two-attachments.mbox
new file mode 100644
index 00000000..462f62a3
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-two-attachments.mbox
@@ -0,0 +1,54 @@
1From: firstname.lastname@example.com
2To: test@kolab.org
3Subject: OpenPGP signed with 2 text attachments
4Date: Sun, 30 Aug 2015 12:02:56 +0200
5Message-ID: <2033829.IGepAdxqt9@vkpc5>
6X-KMail-Identity: 402312391
7X-KMail-Dictionary: en_US
8User-Agent: KMail/5.0.42 pre (Linux/3.16.6-2-desktop; KDE/5.14.0; x86_64; ; )
9MIME-Version: 1.0
10Content-Type: multipart/signed; boundary="nextPart3682207.KLrp2sxpbf"; micalg="pgp-sha1"; protocol="application/pgp-signature"
11
12--nextPart3682207.KLrp2sxpbf
13Content-Type: multipart/mixed; boundary="nextPart2397422.QDHKUNdbyg"
14Content-Transfer-Encoding: 7Bit
15
16This is a multi-part message in MIME format.
17
18--nextPart2397422.QDHKUNdbyg
19Content-Transfer-Encoding: 7Bit
20Content-Type: text/plain; charset="us-ascii"
21
22this is the main body text
23--nextPart2397422.QDHKUNdbyg
24Content-Disposition: inline; filename="attachment1.txt"
25Content-Transfer-Encoding: 7Bit
26Content-Type: text/plain; charset="utf-8"; name="attachment1.txt"
27
28this is attachment one
29--nextPart2397422.QDHKUNdbyg
30Content-Disposition: inline; filename="attachment2.txt"
31Content-Transfer-Encoding: 7Bit
32Content-Type: text/plain; charset="utf-8"; name="attachment2.txt"
33
34this is attachment two
35--nextPart2397422.QDHKUNdbyg--
36
37--nextPart3682207.KLrp2sxpbf
38Content-Type: application/pgp-signature; name="signature.asc"
39Content-Description: This is a digitally signed message part.
40Content-Transfer-Encoding: 7Bit
41
42-----BEGIN PGP SIGNATURE-----
43Version: GnuPG v2
44
45iQEVAwUAVeLU0I2YYMWPJG3mAQL/fgf+LXmO7bKafdd4g5OOVHHyXRprVmX/6hBq
46mZoor29KLIHkvAH9OJi4qBy/ZKwqqKLfttLzHb2UaAfl5kn4f0ckmnwUhU7u32Sm
47JZ0Q50SxrRVFRyvTvPG22ho9IwQUO1YSZrL4wO9v8ZBQ3vkfpmAiUQVxPQMINc8L
48i68xQEm4y1Dtoc+DTUkoIMeOOPnEl6PTMPrwn906K0r30hI4788fEGRn6uOXb+vD
49G/ISlXu+JHIxxf/J5/jVjKNbra+trrfSPzB3piJIjBLEPO5FvLx8SgQFJcJHt/kw
50ps8D5YULj/MVMLlsPtXDdZmbOi/G9pN0tr05MKcXsO5Ywe7n2BhASw==
51=2Nzb
52-----END PGP SIGNATURE-----
53
54--nextPart3682207.KLrp2sxpbf--
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-two-attachments.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-two-attachments.mbox.html
new file mode 100644
index 00000000..06718641
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-two-attachments.mbox.html
@@ -0,0 +1,71 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <table cellspacing="1" cellpadding="1" class="signOkKeyOk">
9 <tr class="signOkKeyOkH">
10 <td dir="ltr">
11 <table cellspacing="0" cellpadding="0" width="100%">
12 <tr>
13 <td>Signed by <a href="mailto:test@kolab.org">test@kolab.org</a>.</td>
14 <td align="right">
15 <a href="kmail:showSignatureDetails">Show Details</a>
16 </td>
17 </tr>
18 </table>
19 </td>
20 </tr>
21 <tr class="signOkKeyOkB">
22 <td>
23 <a name="att1"/>
24 <div id="attachmentDiv1">
25 <a name="att1.1"/>
26 <div id="attachmentDiv1.1">
27 <div class="noquote">
28 <div dir="ltr">this is the main body text</div>
29 </div>
30 </div>
31 <a name="att1.2"/>
32 <div id="attachmentDiv1.2">
33 <table cellspacing="1" class="textAtm">
34 <tr class="textAtmH">
35 <td dir="ltr">attachment1.txt</td>
36 </tr>
37 <tr class="textAtmB">
38 <td>
39 <div class="noquote">
40 <div dir="ltr">this is attachment one</div>
41 </div>
42 </td>
43 </tr>
44 </table>
45 </div>
46 <a name="att1.3"/>
47 <div id="attachmentDiv1.3">
48 <table cellspacing="1" class="textAtm">
49 <tr class="textAtmH">
50 <td dir="ltr">attachment2.txt</td>
51 </tr>
52 <tr class="textAtmB">
53 <td>
54 <div class="noquote">
55 <div dir="ltr">this is attachment two</div>
56 </div>
57 </td>
58 </tr>
59 </table>
60 </div>
61 </div>
62 </td>
63 </tr>
64 <tr class="signOkKeyOkH">
65 <td dir="ltr">End of signed message</td>
66 </tr>
67 </table>
68 </div>
69 </div>
70 </body>
71</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-two-attachments.mbox.tree b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-two-attachments.mbox.tree
new file mode 100644
index 00000000..7133f4be
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/openpgp-signed-two-attachments.mbox.tree
@@ -0,0 +1,9 @@
1 * MimeTreeParser::MessagePartList
2 * MimeTreeParser::SignedMessagePart
3 * MimeTreeParser::MimeMessagePart
4 * MimeTreeParser::TextMessagePart
5 * MimeTreeParser::MessagePart
6 * MimeTreeParser::AttachmentMessagePart
7 * MimeTreeParser::MessagePart
8 * MimeTreeParser::AttachmentMessagePart
9 * MimeTreeParser::MessagePart
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/signed-forward-openpgp-signed-encrypted.mbox b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/signed-forward-openpgp-signed-encrypted.mbox
new file mode 100644
index 00000000..9b715161
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/signed-forward-openpgp-signed-encrypted.mbox
@@ -0,0 +1,92 @@
1From test@kolab.org Wed, 08 Sep 2010 17:53:29 +0200
2From: OpenPGP Test <test@kolab.org>
3Subject: Signed Fwd: OpenPGP signed and encrypted
4Date: Wed, 08 Sep 2010 17:53:29 +0200
5User-Agent: KMail/4.6 pre (Linux/2.6.34-rc2-2-default; KDE/4.5.60; x86_64; ; )
6MIME-Version: 1.0
7Content-Type: multipart/signed; boundary="nextPart4350242.cT7m6ulPOV"; micalg="pgp-sha1"; protocol="application/pgp-signature"
8Content-Transfer-Encoding: 7Bit
9
10
11--nextPart4350242.cT7m6ulPOV
12Content-Type: multipart/mixed; boundary="nextPart1512490.WQBKYaOrt8"
13Content-Transfer-Encoding: 7Bit
14
15
16--nextPart1512490.WQBKYaOrt8
17Content-Transfer-Encoding: 7Bit
18Content-Type: text/plain; charset="us-ascii"
19
20bla bla bla
21--nextPart1512490.WQBKYaOrt8
22Content-Type: message/rfc822
23Content-Disposition: inline; filename="forwarded message"
24Content-Description: OpenPGP Test <test@kolab.org>: OpenPGP signed and encrypted
25
26From: OpenPGP Test <test@kolab.org>
27To: test@kolab.org
28Subject: OpenPGP signed and encrypted
29Date: Tue, 07 Sep 2010 18:08:44 +0200
30User-Agent: KMail/4.6 pre (Linux/2.6.34-rc2-2-default; KDE/4.5.60; x86_64; ; )
31MIME-Version: 1.0
32Content-Type: multipart/encrypted; boundary="nextPart25203163.0xtB501Z4V"; protocol="application/pgp-encrypted"
33Content-Transfer-Encoding: 7Bit
34
35
36--nextPart25203163.0xtB501Z4V
37Content-Type: application/pgp-encrypted
38Content-Disposition: attachment
39
40Version: 1
41--nextPart25203163.0xtB501Z4V
42Content-Type: application/octet-stream
43Content-Disposition: inline; filename="msg.asc"
44
45-----BEGIN PGP MESSAGE-----
46Version: GnuPG v2.0.15 (GNU/Linux)
47
48hQEMAwzOQ1qnzNo7AQf7BFYWaGiCTGtXY59bSh3LCXNnWZejblYALxIUNXOFEXbm
49y/YA95FmQsy3U5HRCAJV/DY1PEaJz1RTm9bcdIpDC3Ab2YzSwmOwV5fcoUOB2df4
50KjX19Q+2F3JxpPQ0N1gHf4dKfIu19LH+CKeFzUN13aJs5J4A5wlj+NjJikxzmxDS
51kDtNYndynPmo9DJQcsUFw3gpvx5HaHvx1cT4mAB2M5cd2l+vN1jYbaWb0x5Zq41z
52mRNI89aPieC3rcM2289m68fGloNbYvi8mZJu5RrI4Tbi/D7Rjm1y63lHgVV6AN88
53XAzRiedOeF99LoTBulrJdtT8AAgCs8nCetcWpIffdtLpAZiZkzHmYOU7nqGxqpRk
54OVeUTrCn9DW2SMmHjaP4IiKnMvzEycu5F4a72+V1LeMIhMSjTRTq+ZE2PTaqH59z
55QsMn7Nb6GlOICbTptRKNNtyJKO7xXlpT7YtvNKnCyEOkH2XrYH7GvpYCiuQ0/o+7
56SxV436ZejiYIg6DQDXJCoa2DXimGp0C10Jh0HwX0BixpoNtwEjkGRYcX6P/JzkH0
57oBood4Ly+Tiu6iVDisrK3AVGYpIzCrKkE9qULTw4R/jFKR2tcCqGb7Fxtk2LV7Md
583S+DyOKrvKQ5GNwbp9OE97pwk+Lr1JS3UAvj5f6BR+1PVNcC0i0wWkgwDjPh1eGD
59enMQmorE6+N0uHtH2F4fOxo/TbbA3+zhI25kVW3bO03xyUl/cmQZeb52nvfOvtOo
60gSb2j6bPkzljDMPEzrtJjbFtGHJbPfUQYJgZv9OE2EQIqpg6goIw279alBq6GLIX
61pkO+dRmztzjcDyhcLxMuQ4cTizel/0J/bU7U6lvwHSyZVbT4Ev+opG5K70Hbqbwr
62NZcgdWXbSeesxGM/oQaMeSurOevxVl+/zrTVAek61aRRd1baAYqgi2pf2V7y4oK3
63qkdxzmoFpRdNlfrQW65NZWnHOi9rC9XxANIwnVn3kRcDf+t2K4PrFluI157lXM/o
64wX91j88fazysbJlQ6TjsApO9ETiPOFEBqouxCTtCZzlUgyVG8jpIjdHWFnagHeXH
65+lXNdYjxnTWTjTxMOZC9ySMpXkjWdFI1ecxVwu6Ik6RX51rvBJAAXWP75yUjPKJ4
66rRi5oQl/VLl0QznO7lvgMPtUwgDVNWO/r7Kn9B387h9fAJZ/kWFAEDW2yhAzABqO
67rCNKDzBPgfAwCnikCpMoCbOL7SU8BdbzQHD8/Lkv4m0pzliHQ/KkGF710koBzTmF
68N7+wk9pwIuvcrEBQj567
69=GV0c
70-----END PGP MESSAGE-----
71
72--nextPart25203163.0xtB501Z4V--
73
74--nextPart1512490.WQBKYaOrt8--
75
76--nextPart4350242.cT7m6ulPOV
77Content-Type: application/pgp-signature; name="signature.asc"
78Content-Description: This is a digitally signed message part.
79
80-----BEGIN PGP SIGNATURE-----
81Version: GnuPG v2.0.15 (GNU/Linux)
82
83iQEcBAABAgAGBQJMh7F5AAoJEI2YYMWPJG3mOB0IALeHfwg8u7wK0tDKtKqxQSqC
842Bbk4pTLuLw/VniQNauDG+kc1eUc5RJk/R31aB1ysiQCV5Q8ucI8c9vCDDMbd+s4
85t2bZUEzMpXrw/aFiHgYGXFAY+tpqZqDGNVRNHWsPYJKtx8cci9E5DLnBJcVXVqib
863iiHlr9AQOok3PUmpPk1a61q2L0kk8wqRenC0yZXNw5qFn5WW/hFeCOfYB+t+s5N
87IuE6ihFCJIlvGborrvl6VgPJTCyUQ3XgI90vS6ABN8TFlCNr3grXOWUePc2a20or
88xFgh38cnSR64WJajU5K1nUL9/RgfIcs+PvyHuJaRf/iUFkj1jiMEuaSi9jVFco0=
89=bArV
90-----END PGP SIGNATURE-----
91
92--nextPart4350242.cT7m6ulPOV--
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/signed-forward-openpgp-signed-encrypted.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/signed-forward-openpgp-signed-encrypted.mbox.html
new file mode 100644
index 00000000..b91772b7
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/signed-forward-openpgp-signed-encrypted.mbox.html
@@ -0,0 +1,105 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <table cellspacing="1" cellpadding="1" class="signOkKeyOk">
9 <tr class="signOkKeyOkH">
10 <td dir="ltr">
11 <table cellspacing="0" cellpadding="0" width="100%">
12 <tr>
13 <td>Signed by <a href="mailto:test@kolab.org">test@kolab.org</a>.</td>
14 <td align="right">
15 <a href="kmail:showSignatureDetails">Show Details</a>
16 </td>
17 </tr>
18 </table>
19 </td>
20 </tr>
21 <tr class="signOkKeyOkB">
22 <td>
23 <a name="att1"/>
24 <div id="attachmentDiv1">
25 <a name="att1.1"/>
26 <div id="attachmentDiv1.1">
27 <div class="noquote">
28 <div dir="ltr">bla bla bla</div>
29 </div>
30 </div>
31 <a name="att1.2"/>
32 <div id="attachmentDiv1.2">
33 <table cellspacing="1" cellpadding="1" class="rfc822">
34 <tr class="rfc822H">
35 <td dir="ltr">
36 <a href="attachment:1.2.1?place=body">Encapsulated message</a>
37 </td>
38 </tr>
39 <tr class="rfc822B">
40 <td>
41 <a name="att1.2.1"/>
42 <div id="attachmentDiv1.2.1">
43 <table cellspacing="1" cellpadding="1" class="encr">
44 <tr class="encrH">
45 <td dir="ltr">Encrypted message</td>
46 </tr>
47 <tr class="encrB">
48 <td>
49 <div style="position: relative; word-wrap: break-word">
50 <a name="att"/>
51 <div id="attachmentDiv">
52 <table cellspacing="1" cellpadding="1" class="signOkKeyOk">
53 <tr class="signOkKeyOkH">
54 <td dir="ltr">
55 <table cellspacing="0" cellpadding="0" width="100%">
56 <tr>
57 <td>Signed by <a href="mailto:test@kolab.org">test@kolab.org</a>.</td>
58 <td align="right">
59 <a href="kmail:showSignatureDetails">Show Details</a>
60 </td>
61 </tr>
62 </table>
63 </td>
64 </tr>
65 <tr class="signOkKeyOkB">
66 <td>
67 <a name="att1"/>
68 <div id="attachmentDiv1">
69 <div class="noquote">
70 <div dir="ltr">encrypted message text</div>
71 </div>
72 </div>
73 </td>
74 </tr>
75 <tr class="signOkKeyOkH">
76 <td dir="ltr">End of signed message</td>
77 </tr>
78 </table>
79 </div>
80 </div>
81 </td>
82 </tr>
83 <tr class="encrH">
84 <td dir="ltr">End of encrypted message</td>
85 </tr>
86 </table>
87 </div>
88 </td>
89 </tr>
90 <tr class="rfc822H">
91 <td dir="ltr">End of encapsulated message</td>
92 </tr>
93 </table>
94 </div>
95 </div>
96 </td>
97 </tr>
98 <tr class="signOkKeyOkH">
99 <td dir="ltr">End of signed message</td>
100 </tr>
101 </table>
102 </div>
103 </div>
104 </body>
105</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/signed-forward-openpgp-signed-encrypted.mbox.tree b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/signed-forward-openpgp-signed-encrypted.mbox.tree
new file mode 100644
index 00000000..818f894c
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/signed-forward-openpgp-signed-encrypted.mbox.tree
@@ -0,0 +1,10 @@
1 * MimeTreeParser::MessagePartList
2 * MimeTreeParser::SignedMessagePart
3 * MimeTreeParser::MimeMessagePart
4 * MimeTreeParser::TextMessagePart
5 * MimeTreeParser::MessagePart
6 * MimeTreeParser::EncapsulatedRfc822MessagePart
7 * MimeTreeParser::EncryptedMessagePart
8 * MimeTreeParser::SignedMessagePart
9 * MimeTreeParser::TextMessagePart
10 * MimeTreeParser::MessagePart
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-cert.mbox b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-cert.mbox
new file mode 100644
index 00000000..b3c8a19e
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-cert.mbox
@@ -0,0 +1,24 @@
1From test@example.com Sat, 13 Apr 2013 01:54:30 +0200
2From: test <test@example.com>
3To: you@you.com
4Subject: test
5Date: Sat, 13 Apr 2013 01:54:30 +0200
6Message-ID: <1576646.QQxzHWx8dA@tabin>
7X-KMail-Identity: 505942601
8User-Agent: KMail/4.10.2 (Linux/3.9.0-rc4-experimental-amd64; KDE/4.10.60; x86_64; git-fc9b82c; 2013-04-11)
9MIME-Version: 1.0
10Content-Type: application/pkcs7-mime; name="smime.crt"; smime-type="certs-only"
11Content-Transfer-Encoding: base64
12Content-Disposition: attachment; filename="smime.crt"
13
14asdfasdfasdfasdfasdfasdfasdfasdf
15asdfasdfadsfsadfasdf
16asdfasdf
17sadfas
18dfasdf
19sadf
20sadf
21adsf
22adsf
23asdf
24
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-cert.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-cert.mbox.html
new file mode 100644
index 00000000..1b86390f
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-cert.mbox.html
@@ -0,0 +1,10 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">Sorry, certificate could not be imported.<br/>Reason: BER error</div>
8 </div>
9 </body>
10</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-cert.mbox.tree b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-cert.mbox.tree
new file mode 100644
index 00000000..c34c2eca
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-cert.mbox.tree
@@ -0,0 +1,2 @@
1 * MimeTreeParser::MessagePartList
2 * MimeTreeParser::CertMessagePart
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-encrypted-octet-stream.mbox b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-encrypted-octet-stream.mbox
new file mode 100644
index 00000000..887fe358
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-encrypted-octet-stream.mbox
@@ -0,0 +1,23 @@
1From test@example.com Sat, 13 Apr 2013 01:54:30 +0200
2From: test <test@example.com>
3To: you@you.com
4Subject: test
5Date: Sat, 13 Apr 2013 01:54:30 +0200
6Message-ID: <1576646.QQxzHWx8dA@tabin>
7X-KMail-Identity: 505942601
8User-Agent: KMail/4.10.2 (Linux/3.9.0-rc4-experimental-amd64; KDE/4.10.60; x86_64; git-fc9b82c; 2013-04-11)
9MIME-Version: 1.0
10Content-Type: application/octet-stream;
11 name="smime.p7m"
12Content-Transfer-Encoding: base64
13Content-Disposition: attachment; filename="smime.p7m"
14
15MIAGCSqGSIb3DQEHA6CAMIACAQAxgfwwgfkCAQAwYjBVMQswCQYDVQQGEwJVUzENMAsGA1UECgwE
16S0RBQjEWMBQGA1UEAwwNdW5pdHRlc3QgY2VydDEfMB0GCSqGSIb3DQEJARYQdGVzdEBleGFtcGxl
17LmNvbQIJANNFIDoYY4XJMA0GCSqGSIb3DQEBAQUABIGAJwmmaOeidXUHSQGOf2OBIsPYafVqdORe
18y54pEXbXiAfSVUWgI4a9CsiWwcDX8vlaX9ZLLr+L2VmOfr6Yc5214yxzausZVvnUFjy6LUXotuEX
19tSar4EW7XI9DjaZc1l985naMsTx9JUa5GyQ9J6PGqhosAKpKMGgKkFAHaOwE1/IwgAYJKoZIhvcN
20AQcBMBQGCCqGSIb3DQMHBAieDfmz3WGbN6CABHgEpsLrNn0PAZTDUfNomDypvSCl5bQH+9cKm80m
21upMV2r8RBiXS7OaP4SpCxq18afDTTPatvboHIoEX92taTbq8soiAgEs6raSGtEYZNvFL0IYqm7MA
22o5HCOmjiEcInyPf14lL3HnPk10FaP3hh58qTHUh4LPYtL7UECOZELYnUfUVhAAAAAAAAAAAAAA==
23
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-encrypted-octet-stream.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-encrypted-octet-stream.mbox.html
new file mode 100644
index 00000000..6b08c47e
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-encrypted-octet-stream.mbox.html
@@ -0,0 +1,31 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <table cellspacing="1" cellpadding="1" class="encr">
9 <tr class="encrH">
10 <td dir="ltr">Encrypted message</td>
11 </tr>
12 <tr class="encrB">
13 <td>
14 <div style="position: relative; word-wrap: break-word">
15 <a name="att"/>
16 <div id="attachmentDiv">
17 <div class="noquote">
18 <div dir="ltr">The quick brown fox jumped over the lazy dog.</div>
19 </div>
20 </div>
21 </div>
22 </td>
23 </tr>
24 <tr class="encrH">
25 <td dir="ltr">End of encrypted message</td>
26 </tr>
27 </table>
28 </div>
29 </div>
30 </body>
31</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-encrypted-octet-stream.mbox.tree b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-encrypted-octet-stream.mbox.tree
new file mode 100644
index 00000000..82f705c2
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-encrypted-octet-stream.mbox.tree
@@ -0,0 +1,4 @@
1 * MimeTreeParser::MessagePartList
2 * MimeTreeParser::EncryptedMessagePart
3 * MimeTreeParser::TextMessagePart
4 * MimeTreeParser::MessagePart
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-encrypted.mbox b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-encrypted.mbox
new file mode 100644
index 00000000..6b6d6a0d
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-encrypted.mbox
@@ -0,0 +1,22 @@
1From test@example.com Sat, 13 Apr 2013 01:54:30 +0200
2From: test <test@example.com>
3To: you@you.com
4Subject: test
5Date: Sat, 13 Apr 2013 01:54:30 +0200
6Message-ID: <1576646.QQxzHWx8dA@tabin>
7X-KMail-Identity: 505942601
8User-Agent: KMail/4.10.2 (Linux/3.9.0-rc4-experimental-amd64; KDE/4.10.60; x86_64; git-fc9b82c; 2013-04-11)
9MIME-Version: 1.0
10Content-Type: application/pkcs7-mime; name="smime.p7m"; smime-type="enveloped-data"
11Content-Transfer-Encoding: base64
12Content-Disposition: attachment; filename="smime.p7m"
13
14MIAGCSqGSIb3DQEHA6CAMIACAQAxgfwwgfkCAQAwYjBVMQswCQYDVQQGEwJVUzENMAsGA1UECgwE
15S0RBQjEWMBQGA1UEAwwNdW5pdHRlc3QgY2VydDEfMB0GCSqGSIb3DQEJARYQdGVzdEBleGFtcGxl
16LmNvbQIJANNFIDoYY4XJMA0GCSqGSIb3DQEBAQUABIGAJwmmaOeidXUHSQGOf2OBIsPYafVqdORe
17y54pEXbXiAfSVUWgI4a9CsiWwcDX8vlaX9ZLLr+L2VmOfr6Yc5214yxzausZVvnUFjy6LUXotuEX
18tSar4EW7XI9DjaZc1l985naMsTx9JUa5GyQ9J6PGqhosAKpKMGgKkFAHaOwE1/IwgAYJKoZIhvcN
19AQcBMBQGCCqGSIb3DQMHBAieDfmz3WGbN6CABHgEpsLrNn0PAZTDUfNomDypvSCl5bQH+9cKm80m
20upMV2r8RBiXS7OaP4SpCxq18afDTTPatvboHIoEX92taTbq8soiAgEs6raSGtEYZNvFL0IYqm7MA
21o5HCOmjiEcInyPf14lL3HnPk10FaP3hh58qTHUh4LPYtL7UECOZELYnUfUVhAAAAAAAAAAAAAA==
22
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-encrypted.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-encrypted.mbox.html
new file mode 100644
index 00000000..6b08c47e
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-encrypted.mbox.html
@@ -0,0 +1,31 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <table cellspacing="1" cellpadding="1" class="encr">
9 <tr class="encrH">
10 <td dir="ltr">Encrypted message</td>
11 </tr>
12 <tr class="encrB">
13 <td>
14 <div style="position: relative; word-wrap: break-word">
15 <a name="att"/>
16 <div id="attachmentDiv">
17 <div class="noquote">
18 <div dir="ltr">The quick brown fox jumped over the lazy dog.</div>
19 </div>
20 </div>
21 </div>
22 </td>
23 </tr>
24 <tr class="encrH">
25 <td dir="ltr">End of encrypted message</td>
26 </tr>
27 </table>
28 </div>
29 </div>
30 </body>
31</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-encrypted.mbox.inProgress.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-encrypted.mbox.inProgress.html
new file mode 100644
index 00000000..e5eb55d0
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-encrypted.mbox.inProgress.html
@@ -0,0 +1,24 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <table cellspacing="1" cellpadding="1" class="encr">
9 <tr class="encrH">
10 <td dir="ltr">Please wait while the message is being decrypted...</td>
11 </tr>
12 <tr class="encrB">
13 <td>
14 <div style="font-size:x-large; text-align:center; padding:20pt;"/>
15 </td>
16 </tr>
17 <tr class="encrH">
18 <td dir="ltr">End of encrypted message</td>
19 </tr>
20 </table>
21 </div>
22 </div>
23 </body>
24</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-encrypted.mbox.tree b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-encrypted.mbox.tree
new file mode 100644
index 00000000..82f705c2
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-encrypted.mbox.tree
@@ -0,0 +1,4 @@
1 * MimeTreeParser::MessagePartList
2 * MimeTreeParser::EncryptedMessagePart
3 * MimeTreeParser::TextMessagePart
4 * MimeTreeParser::MessagePart
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-opaque-enc+sign.mbox b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-opaque-enc+sign.mbox
new file mode 100644
index 00000000..be75c1e2
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-opaque-enc+sign.mbox
@@ -0,0 +1,37 @@
1From test@example.com Fri Sep 11 10:18:48 2015
2From: test <test@example.com>
3To: you@you.com
4Subject: enc+sign
5Date: Fri, 11 Sep 2015 12:18:48 +0200
6Message-ID: <49743203.WFa6qKaG4S@tabin.local>
7X-KMail-Identity: 792434561
8User-Agent: KMail/4.13.0.1 (Linux/4.1.5-towo.1-siduction-amd64; KDE/4.14.2; x86_64; ; )
9MIME-Version: 1.0
10Content-Type: application/pkcs7-mime; name="smime.p7m"; smime-type="enveloped-data"
11Content-Transfer-Encoding: base64
12Content-Disposition: attachment; filename="smime.p7m"
13
14MIAGCSqGSIb3DQEHA6CAMIACAQAxgfwwgfkCAQAwYjBVMQswCQYDVQQGEwJVUzENMAsGA1UECgwE
15S0RBQjEWMBQGA1UEAwwNdW5pdHRlc3QgY2VydDEfMB0GCSqGSIb3DQEJARYQdGVzdEBleGFtcGxl
16LmNvbQIJANNFIDoYY4XJMA0GCSqGSIb3DQEBAQUABIGAkttyRl8OyZkRGfs3CMfzYchrG4rRMfbE
17WIkAFIXf64yAzPZfo6cCn0Il/6q4793FeKUrsJUvzP21KBLp4u1t5qLL5iPAmAzBdLg0teoEzLZe
18CEZHinM+WSMdz2wEV8lgAt8x/3yhXmDMB09FEapUaBCK5NhbLAFkpI2mFg66zxcwgAYJKoZIhvcN
19AQcBMB0GCWCGSAFlAwQBAgQQ6DSjjkXJNm5cXQNek9eozqCABIIDsI7lufqw58g/uVM2gymkldmb
20BOgdimxshJkYznbbG5sfQPNM9ROhXKRUOc8hfzgmZ9dOZvtAyzepROMHrcL1mFFx7c1vgyT85fai
21PLEqRs8FRoztXJ0I3q57r+sPW4BtYJp9WCBSerdLSrBFK1zvKpVQtSCYbbDCc3462KnSsBrE4XTf
22BiiLWkpok4fNIdqYG1aWPLgRbp7wwLiXcq5RxYCail1tlyAty6dWBrYE1+ABZoqnKUqNqbghxJUd
23X9t3EziUnzw7c0Hq03sJEShzbXI9BxOTs8ZZ+1Ktx3rdh6RhZZ7XfJ7XIuN0yYhusBeOgC8AuILN
24lYojgmXMin52VPFmz9siI8jnKaqsr1uUqVfMLNc+mLhZEjuOu99eAHwdJUS95BKWm9J8DBe/lpz3
25s09Oz2oOoiQx0WxKmQZ4GXW/UI9OwykfNLqWfmDrbMbGW4Mvq615ucHZixFdp2vf3kU72wfk8hFK
26EIU/1Ov85glDj96ELdXErXn02BNvVBQIsRx3DbBGEgj9tz+WHbCR6RjEK4eV5lhInZplFGmYr4Uu
279ALS+MRGTYZnALNPrWu6b1aWprnlJCyKZeeyqUzpQVPoWOh7CfdvBxvQoil4Z9Neb9O0AGCZ0axc
28zZ4yYcS/LpHkLgYPC5BphNtpyciQh6ZFeexi8rsZuHRu+YUKnuM+DQyUxtUvDYhiX7CT49MMXIwt
29bdyA0IAbIXXb/Us9GGX11gAfz2EFI3QBDHtsyciEgCIlA32OiiJF0T5CnQsku2yu6c3TWC4k+feL
30jjTEhm/KPUL6SkksarFeEncJlVt7impW9FlHyBpISwlQF4RAxDYTRX475VTNu7wn3PQx376m8iBN
31K20MjdfIM162jcQh9IWnqTZ0nH/gT2kQPYe7GqjFi6XmU3bwdzW9SXR0G9A8juIXaaNR7aXcsB53
32/W4WHPcdJBwRELa0dT1/bPg8z3EV2vM7Flc/Q5ugTWHxk8GHNEPpotArpLq1sEAZu78mSCqzzEDA
33TwzEpj9LZYIv4rDYYLCAxUR9YGhiJ5Qm21YnklkE++4zfpsl/KJcwtNsp7SWdzeVuPoUYBNhsgp/
34PZgudzQoOhxcF4ChHMVSNk1f1tlvXZppeXwv9OwpSszz0zZUr46ievpkQwL0Sa1XAfKwAt7bFJwp
35i95ae7p58ZdizJVsz0QUhFDxIDL76qiPEYXC7nIf4ZPQ36dzLAfZ6OwWGffluxHpE6oqUoM3l320
367yQT66xfRKLGl5Cr1Q8UBBABC6aR2dt6RVbsxbR3mB0PAAAAAAAAAAAAAA==
37
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-opaque-enc+sign.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-opaque-enc+sign.mbox.html
new file mode 100644
index 00000000..7f60c04f
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-opaque-enc+sign.mbox.html
@@ -0,0 +1,57 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <table cellspacing="1" cellpadding="1" class="encr">
9 <tr class="encrH">
10 <td dir="ltr">Encrypted message</td>
11 </tr>
12 <tr class="encrB">
13 <td>
14 <div style="position: relative; word-wrap: break-word">
15 <a name="att"/>
16 <div id="attachmentDiv">
17 <table cellspacing="1" cellpadding="1" class="signOkKeyOk">
18 <tr class="signOkKeyOkH">
19 <td dir="ltr">
20 <table cellspacing="0" cellpadding="0" width="100%">
21 <tr>
22 <td>Signed by <a href="mailto:test@example.com">test@example.com</a>.</td>
23 <td align="right">
24 <a href="kmail:showSignatureDetails">Show Details</a>
25 </td>
26 </tr>
27 </table>
28 </td>
29 </tr>
30 <tr class="signOkKeyOkB">
31 <td>
32 <div style="position: relative; word-wrap: break-word">
33 <a name="att"/>
34 <div id="attachmentDiv">
35 <div class="noquote">
36 <div dir="ltr">Encrypted and signed mail.</div>
37 </div>
38 </div>
39 </div>
40 </td>
41 </tr>
42 <tr class="signOkKeyOkH">
43 <td dir="ltr">End of signed message</td>
44 </tr>
45 </table>
46 </div>
47 </div>
48 </td>
49 </tr>
50 <tr class="encrH">
51 <td dir="ltr">End of encrypted message</td>
52 </tr>
53 </table>
54 </div>
55 </div>
56 </body>
57</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-opaque-enc+sign.mbox.tree b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-opaque-enc+sign.mbox.tree
new file mode 100644
index 00000000..7d5bbeb7
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-opaque-enc+sign.mbox.tree
@@ -0,0 +1,5 @@
1 * MimeTreeParser::MessagePartList
2 * MimeTreeParser::EncryptedMessagePart
3 * MimeTreeParser::SignedMessagePart
4 * MimeTreeParser::TextMessagePart
5 * MimeTreeParser::MessagePart
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-opaque-sign.mbox b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-opaque-sign.mbox
new file mode 100644
index 00000000..6e1739ac
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-opaque-sign.mbox
@@ -0,0 +1,25 @@
1From test@example.com Fri Sep 11 10:16:06 2015
2From: test <test@example.com>
3To: you@you.com
4Subject: sign only
5Date: Fri, 11 Sep 2015 12:16:06 +0200
6Message-ID: <3182420.pXWeMPZlAJ@tabin.local>
7X-KMail-Identity: 792434561
8User-Agent: KMail/4.13.0.1 (Linux/4.1.5-towo.1-siduction-amd64; KDE/4.14.2; x86_64; ; )
9MIME-Version: 1.0
10Content-Type: application/pkcs7-mime; name="smime.p7m"; smime-type="signed-data"
11Content-Transfer-Encoding: base64
12Content-Disposition: attachment; filename="smime.p7m"
13
14MIAGCSqGSIb3DQEHAqCAMIACAQExCzAJBgUrDgMCGgUAMIAGCSqGSIb3DQEHAaCAJIAEZUNvbnRl
15bnQtVHJhbnNmZXItRW5jb2Rpbmc6IDdCaXQKQ29udGVudC1UeXBlOiB0ZXh0L3BsYWluOyBjaGFy
16c2V0PSJ1dGYtOCIKCkEgc2ltcGxlIHNpZ25lZCBvbmx5IHRlc3QuAAAAAAAAMYIBkjCCAY4CAQEw
17YjBVMQswCQYDVQQGEwJVUzENMAsGA1UECgwES0RBQjEWMBQGA1UEAwwNdW5pdHRlc3QgY2VydDEf
18MB0GCSqGSIb3DQEJARYQdGVzdEBleGFtcGxlLmNvbQIJANNFIDoYY4XJMAkGBSsOAwIaBQCggYcw
19GAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMTMwMTEwMTU0ODEyWjAj
20BgkqhkiG9w0BCQQxFgQUvJ5zI7oyv5fNx1H1wabIa6atsdcwKAYJKoZIhvcNAQkPMRswGTALBglg
21hkgBZQMEAQIwCgYIKoZIhvcNAwcwDQYJKoZIhvcNAQEBBQAEgYAHFCw88FPy1n2lu5ql5sD2J4Yi
222/N9gUQvNQF5F/kd48HncdihLPZRs7eEX7IzDZNeylTmyp2WIiGEwQrIHbxtqU32NRouc09Zv4bu
23iUwUoz1SM2s7qipikwayQMD3d5zWNhszNLBsw8z48uXAzjZAejBCfPP0/w3z7DZDJC2R2QAAAAAA
24AA==
25
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-opaque-sign.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-opaque-sign.mbox.html
new file mode 100644
index 00000000..6088bc72
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-opaque-sign.mbox.html
@@ -0,0 +1,40 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <table cellspacing="1" cellpadding="1" class="signOkKeyOk">
9 <tr class="signOkKeyOkH">
10 <td dir="ltr">
11 <table cellspacing="0" cellpadding="0" width="100%">
12 <tr>
13 <td>Signed by <a href="mailto:test@example.com">test@example.com</a>.</td>
14 <td align="right">
15 <a href="kmail:showSignatureDetails">Show Details</a>
16 </td>
17 </tr>
18 </table>
19 </td>
20 </tr>
21 <tr class="signOkKeyOkB">
22 <td>
23 <div style="position: relative; word-wrap: break-word">
24 <a name="att"/>
25 <div id="attachmentDiv">
26 <div class="noquote">
27 <div dir="ltr">A simple signed only test.</div>
28 </div>
29 </div>
30 </div>
31 </td>
32 </tr>
33 <tr class="signOkKeyOkH">
34 <td dir="ltr">End of signed message</td>
35 </tr>
36 </table>
37 </div>
38 </div>
39 </body>
40</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-opaque-sign.mbox.inProgress.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-opaque-sign.mbox.inProgress.html
new file mode 100644
index 00000000..45a999d3
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-opaque-sign.mbox.inProgress.html
@@ -0,0 +1,22 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <table cellspacing="1" cellpadding="1" class="signInProgress">
9 <tr class="signInProgressH">
10 <td dir="ltr">Please wait while the signature is being verified...</td>
11 </tr>
12 <tr class="signInProgressB">
13 <td/>
14 </tr>
15 <tr class="signInProgressH">
16 <td dir="ltr">End of signed message</td>
17 </tr>
18 </table>
19 </div>
20 </div>
21 </body>
22</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-opaque-sign.mbox.tree b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-opaque-sign.mbox.tree
new file mode 100644
index 00000000..a469bb76
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-opaque-sign.mbox.tree
@@ -0,0 +1,4 @@
1 * MimeTreeParser::MessagePartList
2 * MimeTreeParser::SignedMessagePart
3 * MimeTreeParser::TextMessagePart
4 * MimeTreeParser::MessagePart
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-opaque-signed-encrypted-attachment.mbox b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-opaque-signed-encrypted-attachment.mbox
new file mode 100644
index 00000000..2b1a0761
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-opaque-signed-encrypted-attachment.mbox
@@ -0,0 +1,50 @@
1From test@example.com Thu Jun 09 12:52:44 2016
2From: test@example.com
3To: test@example.com
4Subject: Opaque S/MIME signed and encrypted message with attachment
5Date: Thu, 09 Jun 2016 14:52:44 +0200
6MIME-Version: 1.0
7Content-Type: application/pkcs7-mime; name="smime.p7m"; smime-type="enveloped-data"
8Content-Transfer-Encoding: base64
9Content-Disposition: attachment; filename="smime.p7m"
10
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
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-signed-apple.mbox b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-signed-apple.mbox
new file mode 100644
index 00000000..d5cd06f1
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-signed-apple.mbox
@@ -0,0 +1,197 @@
1From: Quack <quack@example.org>
2Content-Type: multipart/signed; boundary="Apple-Mail=_607FF8D2-30E0-4FC3-86D9-1234567890AB"; protocol="application/pkcs7-signature"; micalg=sha1
3Message-Id: <468684BD-9CBD-48CF-B1BD-2824000F9541@example.org>
4Mime-Version: 1.0 (Mac OS X Mail 9.3 \(3124\))
5Subject: Re: PDF bug - modified mail, signature is not valid!
6Date: Fri, 20 Jan 2017 11:51:41 +0100
7To: Konqui <Konqui@kdab.com>
8
9
10--Apple-Mail=_607FF8D2-30E0-4FC3-86D9-1234567890AB
11Content-Type: multipart/alternative;
12 boundary="Apple-Mail=_C5F90221-8F52-4623-99DF-1234567890AB"
13
14
15--Apple-Mail=_C5F90221-8F52-4623-99DF-1234567890AB
16Content-Transfer-Encoding: quoted-printable
17Content-Type: text/plain;
18 charset=utf-8
19
20Ol=C3=A1 Konqui,
21
22Here is the pdf you asked for!
23Cheers,
24
25Quaak
26
27=E2=80=A6
28Quack | UX/UI Designer
29Klar=C3=A4lvdalens Datakonsult AB, a KDAB Group company
30Sweden (HQ) +46-563-540090, Germany +49-30-521325470
31KDAB - The Qt, C++ and OpenGL Experts | www.kdab.com
32
33
34--Apple-Mail=_C5F90221-8F52-4623-99DF-1234567890AB
35Content-Type: multipart/mixed;
36 boundary="Apple-Mail=_1C4D1EDB-36C5-40D7-9AB6-1234567890AB"
37
38
39--Apple-Mail=_1C4D1EDB-36C5-40D7-9AB6-1234567890AB
40Content-Transfer-Encoding: quoted-printable
41Content-Type: text/html;
42 charset=utf-8
43
44<html><head><meta http-equiv=3D"Content-Type" content=3D"text/html =
45charset=3Dutf-8"></head><body style=3D"word-wrap: break-word; =
46-webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" =
47class=3D"">Ol=C3=A1 Konqui,</div><div =
48class=3D"">Here is the pdf you asked for!</div><div =
49class=3D"">Cheers,</div><div =
50class=3D"">Quaack</div></body></html>=
51
52--Apple-Mail=_1C4D1EDB-36C5-40D7-9AB6-1234567890AB
53Content-Disposition: attachment; filename="image.png"
54Content-Transfer-Encoding: base64
55Content-Type: image/png; name="image.png"
56
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
100o/0d59+Lh3Y1HHcAAAAASUVORK5CYII=
101--Apple-Mail=_1C4D1EDB-36C5-40D7-9AB6-1234567890AB
102Content-Transfer-Encoding: quoted-printable
103Content-Type: text/html;
104 charset=utf-8
105
106<html><head><meta http-equiv=3D"Content-Type" content=3D"text/html =
107charset=3Dutf-8"></head><body>=
108<blockquote type=3D"cite" class=3D""><div =
109class=3D"">On 20 Jan 2017, at 10:35, Konqui &lt;<a =
110href=3D"mailto:Konqui@kdab.com">Konqui</a>=
111</div>
112</blockquote>=
113</body></html>=
114
115--Apple-Mail=_1C4D1EDB-36C5-40D7-9AB6-1234567890AB--
116
117--Apple-Mail=_C5F90221-8F52-4623-99DF-1234567890AB--
118
119--Apple-Mail=_607FF8D2-30E0-4FC3-86D9-1234567890AB
120Content-Disposition: attachment;
121 filename=smime.p7s
122Content-Type: application/pkcs7-signature;
123 name=smime.p7s
124Content-Transfer-Encoding: base64
125
126MIAGCSqGSIb3DQEHAqCAMIACAQExCzAJBgUrDgMCGgUAMIAGCSqGSIb3DQEHAQAAoIILdTCCBRow
127ggQCoAMCAQICEG0Z6qcZT2ozIuYiMnqqcd4wDQYJKoZIhvcNAQEFBQAwga4xCzAJBgNVBAYTAlVT
128MQswCQYDVQQIEwJVVDEXMBUGA1UEBxMOU2FsdCBMYWtlIENpdHkxHjAcBgNVBAoTFVRoZSBVU0VS
129VFJVU1QgTmV0d29yazEhMB8GA1UECxMYaHR0cDovL3d3dy51c2VydHJ1c3QuY29tMTYwNAYDVQQD
130Ey1VVE4tVVNFUkZpcnN0LUNsaWVudCBBdXRoZW50aWNhdGlvbiBhbmQgRW1haWwwHhcNMTEwNDI4
131MDAwMDAwWhcNMjAwNTMwMTA0ODM4WjCBkzELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIg
132TWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQx
133OTA3BgNVBAMTMENPTU9ETyBDbGllbnQgQXV0aGVudGljYXRpb24gYW5kIFNlY3VyZSBFbWFpbCBD
134QTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJKEhFtLV5jUXi+LpOFAyKNTWF9mZfEy
135TvefMn1V0HhMVbdClOD5J3EHxcZppLkyxPFAGpDMJ1Zifxe1cWmu5SAb5MtjXmDKokH2auGj/7jf
136H0htZUOMKi4rYzh337EXrMLaggLW1DJq1GdvIBOPXDX65VSAr9hxCh03CgJQU2yVHakQFLSZlVkS
137Mf8JotJM3FLb3uJAAVtIaN3FSrTg7SQfOq9xXwfjrL8UO7AlcWg99A/WF1hGFYE8aIuLgw9teiFX
1385jSw2zJ+40rhpVJyZCaRTqWSD//gsWD9Gm9oUZljjRqLpcxCm5t9ImPTqaD8zp6Q30QZ9FxbNboW
13986eb/8ECAwEAAaOCAUswggFHMB8GA1UdIwQYMBaAFImCZ33EnSZwAEu0UEh83j2uBG59MB0GA1Ud
140DgQWBBR6E04AdFvGeGNkJ8Ev4qBbvHnFezAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB
141/wIBADARBgNVHSAECjAIMAYGBFUdIAAwWAYDVR0fBFEwTzBNoEugSYZHaHR0cDovL2NybC51c2Vy
142dHJ1c3QuY29tL1VUTi1VU0VSRmlyc3QtQ2xpZW50QXV0aGVudGljYXRpb25hbmRFbWFpbC5jcmww
143dAYIKwYBBQUHAQEEaDBmMD0GCCsGAQUFBzAChjFodHRwOi8vY3J0LnVzZXJ0cnVzdC5jb20vVVRO
144QWRkVHJ1c3RDbGllbnRfQ0EuY3J0MCUGCCsGAQUFBzABhhlodHRwOi8vb2NzcC51c2VydHJ1c3Qu
145Y29tMA0GCSqGSIb3DQEBBQUAA4IBAQCF1r54V1VtM39EUv5C1QaoAQOAivsNsv1Kv/avQUn1G1rF
1460q0bc24+6SZ85kyYwTAo38v7QjyhJT4KddbQPTmGZtGhm7VNm2+vKGwdr+XqdFqo2rHA8XV6L566
147k3nK/uKRHlZ0sviN0+BDchvtj/1gOSBH+4uvOmVIPJg9pSW/ve9g4EnlFsjrP0OD8ODuDcHTzTNf
148m9C9YGqzO/761Mk6PB/tm/+bSTO+Qik5g+4zaS6CnUVNqGnagBsePdIaXXxHmaWbCG0SmYbWXVcH
149G6cwvktJRLiQfsrReTjrtDP6oDpdJlieYVUYtCHVmdXgQ0BCML7qpeeU0rD+83X5f27nMIIGUzCC
150BTugAwIBAgIQMFPel8s+Gckd6L+iGIwbpTANBgkqhkiG9w0BAQUFADCBkzELMAkGA1UEBhMCR0Ix
151GzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR
152Q09NT0RPIENBIExpbWl0ZWQxOTA3BgNVBAMTMENPTU9ETyBDbGllbnQgQXV0aGVudGljYXRpb24g
153YW5kIFNlY3VyZSBFbWFpbCBDQTAeFw0xNDA2MDIwMDAwMDBaFw0xNzA2MDEyMzU5NTlaMIIBXzEL
154MAkGA1UEBhMCU0UxDzANBgNVBBETBjY4MyAzMTESMBAGA1UECBMJVmFlcm1sYW5kMRAwDgYDVQQH
155EwdIYWdmb3JzMRgwFgYDVQQJEw9Ob3JyaW5ncyB2YWVnIDIxDzANBgNVBBITBkJveCAzMDEmMCQG
156A1UECgwdS2xhcsOkbHZkYWxlbnMgRGF0YWtvbnN1bHQgQUIxHTAbBgNVBAsTFEEgS0RBQiBHcm91
157cCBDb21wYW55MUMwQQYDVQQLDDpJc3N1ZWQgdGhyb3VnaCBLbGFyw6RsdmRhbGVucyBEYXRha29u
158c3VsdCBBQiBFLVBLSSBNYW5hZ2VyMR8wHQYDVQQLExZDb3Jwb3JhdGUgU2VjdXJlIEVtYWlsMRgw
159FgYDVQQDEw9EaWFuYSBHb25jYWx2ZXMxJzAlBgkqhkiG9w0BCQEWGGRpYW5hLmdvbmNhbHZlc0Br
160ZGFiLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALrHSvWD6MR2tvF9A+wayTDg
161PvK3DahuvHWHzGQsd1p8bEh6qqupqgph2xO73P+ibM6EmNbCtZ+eQtW7l7iIyiC4IGsyEb5RSAtV
162zGAyebsO7SPHokbGIV5SVobaRQiJ+1gOvWUbqHSQ0T9ZPvMX2nNGIKZpqAfocRreZr36AZWRo4AF
1630uf6wz5aLEtq912u2rTWVsM1F966lexaepo0cZB9fdnnD8/pQX3zroj+vBTFNAkZXxxVwGMO24Pz
16492d/B6K8o1SP1ArqV4sxVYIxyQTmfW4X3iV/6bcbLfEcpcUNt6MUsjFulqr6a+j51alpyT3vNuJ9
165V1UI9jz3t/daQr0CAwEAAaOCAdIwggHOMB8GA1UdIwQYMBaAFHoTTgB0W8Z4Y2QnwS/ioFu8ecV7
166MB0GA1UdDgQWBBRIYj+FxAEGllaHmLL+EMhopIEOQjAOBgNVHQ8BAf8EBAMCBaAwDAYDVR0TAQH/
167BAIwADAdBgNVHSUEFjAUBggrBgEFBQcDBAYIKwYBBQUHAwIwRgYDVR0gBD8wPTA7BgwrBgEEAbIx
168AQIBAwUwKzApBggrBgEFBQcCARYdaHR0cHM6Ly9zZWN1cmUuY29tb2RvLm5ldC9DUFMwVwYDVR0f
169BFAwTjBMoEqgSIZGaHR0cDovL2NybC5jb21vZG9jYS5jb20vQ09NT0RPQ2xpZW50QXV0aGVudGlj
170YXRpb25hbmRTZWN1cmVFbWFpbENBLmNybDCBiAYIKwYBBQUHAQEEfDB6MFIGCCsGAQUFBzAChkZo
171dHRwOi8vY3J0LmNvbW9kb2NhLmNvbS9DT01PRE9DbGllbnRBdXRoZW50aWNhdGlvbmFuZFNlY3Vy
172ZUVtYWlsQ0EuY3J0MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5jb21vZG9jYS5jb20wIwYDVR0R
173BBwwGoEYZGlhbmEuZ29uY2FsdmVzQGtkYWIuY29tMA0GCSqGSIb3DQEBBQUAA4IBAQACzCCZ4ppg
174H7nXnCwisxjGLIgQMdwKPB6lnWk39YT0gEqvn85tDaXIZwGiRda7O1HWdWh7RoncolX3yHQ6p/BJ
1758RWkpxoc4es1wXSPmWMpspnglvtqYlfu7NZ/CqI6bvgqoy0w3KSv+GnVkiQ6SVKU4fV6itr5VG9q
176X0JYXAbKO8hOIP3NO3MVacPgzSIv83B9eLpfi/BlG6q6XKxVf4581lYbLL0F7cKQt1UYPiDsmPJG
177+5SEHT6ZOBiLgqQVhAw4Di+6wymUHONBRuH2bH3cjfFlkCCjiFF/cS7Oharro2RFnWQ6beZ3EzCG
178FJILmq/dVMGsBFWme23hLYwtLJSXMYIDqzCCA6cCAQEwgagwgZMxCzAJBgNVBAYTAkdCMRswGQYD
179VQQIExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAOBgNVBAcTB1NhbGZvcmQxGjAYBgNVBAoTEUNPTU9E
180TyBDQSBMaW1pdGVkMTkwNwYDVQQDEzBDT01PRE8gQ2xpZW50IEF1dGhlbnRpY2F0aW9uIGFuZCBT
181ZWN1cmUgRW1haWwgQ0ECEDBT3pfLPhnJHei/ohiMG6UwCQYFKw4DAhoFAKCCAdcwGAYJKoZIhvcN
182AQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMTcwMTIwMTA1MTQxWjAjBgkqhkiG9w0B
183CQQxFgQU/AV0Tj17RqaDDCeGXWhe4epgX6gwgbkGCSsGAQQBgjcQBDGBqzCBqDCBkzELMAkGA1UE
184BhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgG
185A1UEChMRQ09NT0RPIENBIExpbWl0ZWQxOTA3BgNVBAMTMENPTU9ETyBDbGllbnQgQXV0aGVudGlj
186YXRpb24gYW5kIFNlY3VyZSBFbWFpbCBDQQIQMFPel8s+Gckd6L+iGIwbpTCBuwYLKoZIhvcNAQkQ
187AgsxgauggagwgZMxCzAJBgNVBAYTAkdCMRswGQYDVQQIExJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAO
188BgNVBAcTB1NhbGZvcmQxGjAYBgNVBAoTEUNPTU9ETyBDQSBMaW1pdGVkMTkwNwYDVQQDEzBDT01P
189RE8gQ2xpZW50IEF1dGhlbnRpY2F0aW9uIGFuZCBTZWN1cmUgRW1haWwgQ0ECEDBT3pfLPhnJHei/
190ohiMG6UwDQYJKoZIhvcNAQEBBQAEggEAEIfTyPoqjyJwrpYmZWRF6OY5ZCFdpw1UUfSXYUU2IdbL
191ph8QkMCc9uv5wk2IeE/9UxxvUR44J67Bu8hv/PCaeyMSh1j2peOlFG487SwyTjf5wIL+GEs8zvHo
1924+Dd2IPhAExt1Bjhmt6O7caF9LVrGQ/wlI6ZGN8MgjSgdrK4F3Ig4LbMuyTTcy3hDTvb+qzaQ4YI
193E+F4tnwhXG8FGEBnlng6nB4iXhoWSvBsjc1qF6eHEHzsOIZeNL7K6Imn7oKHJg+THGwHxC1TZGFt
194G92u6zV7Sc/i4ENH2MNzXT75mp8Gq/k6gpRz9nw8UVuLN/rDIb6esnEgVH9ad3awD154HAAAAAAA
195AA==
196--Apple-Mail=_607FF8D2-30E0-4FC3-86D9-1234567890AB--
197
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-signed-apple.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-signed-apple.mbox.html
new file mode 100644
index 00000000..11652a14
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-signed-apple.mbox.html
@@ -0,0 +1,58 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <table cellspacing="1" cellpadding="1" class="signErr">
9 <tr class="signErrH">
10 <td dir="ltr">
11 <table cellspacing="0" cellpadding="0" width="100%">
12 <tr>
13 <td>Invalid signature.</td>
14 <td align="right">
15 <a href="kmail:showSignatureDetails">Show Details</a>
16 </td>
17 </tr>
18 </table>
19 </td>
20 </tr>
21 <tr class="signErrB">
22 <td>
23 <a name="att1"/>
24 <div id="attachmentDiv1">
25 <a name="att1.2"/>
26 <div id="attachmentDiv1.2">
27 <a name="att1.2.1"/>
28 <div id="attachmentDiv1.2.1">
29 <div style="position: relative">Olá Konqui,<div class="">Here is the pdf you asked for!</div><div class="">Cheers,</div><div class="">Quaack</div></div>
30 </div>
31 <a name="att1.2.2"/>
32 <div id="attachmentDiv1.2.2">
33 <hr/>
34 <div>
35 <a href="attachment:1.2.2?place=body"><img align="center" height="48" width="48" src="file:image-png.svg" border="0" style="max-width: 100%" alt=""/>image.png</a>
36 </div>
37 <div/>
38 </div>
39 <a name="att1.2.3"/>
40 <div id="attachmentDiv1.2.3">
41 <div style="position: relative">
42 <blockquote type="cite" class="">
43 <div class="">On 20 Jan 2017, at 10:35, Konqui &lt;<a href="mailto:Konqui@kdab.com">Konqui</a></div>
44 </blockquote>
45 </div>
46 </div>
47 </div>
48 </div>
49 </td>
50 </tr>
51 <tr class="signErrH">
52 <td dir="ltr">End of signed message</td>
53 </tr>
54 </table>
55 </div>
56 </div>
57 </body>
58</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-signed-apple.mbox.inProgress.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-signed-apple.mbox.inProgress.html
new file mode 100644
index 00000000..5b57b937
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-signed-apple.mbox.inProgress.html
@@ -0,0 +1,49 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <table cellspacing="1" cellpadding="1" class="signInProgress">
9 <tr class="signInProgressH">
10 <td dir="ltr">Please wait while the signature is being verified...</td>
11 </tr>
12 <tr class="signInProgressB">
13 <td>
14 <a name="att1"/>
15 <div id="attachmentDiv1">
16 <a name="att1.2"/>
17 <div id="attachmentDiv1.2">
18 <a name="att1.2.1"/>
19 <div id="attachmentDiv1.2.1">
20 <div style="position: relative">Olá Konqui,<div class="">Here is the pdf you asked for!</div><div class="">Cheers,</div><div class="">Quaack</div></div>
21 </div>
22 <a name="att1.2.2"/>
23 <div id="attachmentDiv1.2.2">
24 <hr/>
25 <div>
26 <a href="attachment:1.2.2?place=body"><img align="center" height="48" width="48" src="file:image-png.svg" border="0" style="max-width: 100%" alt=""/>image.png</a>
27 </div>
28 <div/>
29 </div>
30 <a name="att1.2.3"/>
31 <div id="attachmentDiv1.2.3">
32 <div style="position: relative">
33 <blockquote type="cite" class="">
34 <div class="">On 20 Jan 2017, at 10:35, Konqui &lt;<a href="mailto:Konqui@kdab.com">Konqui</a></div>
35 </blockquote>
36 </div>
37 </div>
38 </div>
39 </div>
40 </td>
41 </tr>
42 <tr class="signInProgressH">
43 <td dir="ltr">End of signed message</td>
44 </tr>
45 </table>
46 </div>
47 </div>
48 </body>
49</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-signed-apple.mbox.tree b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-signed-apple.mbox.tree
new file mode 100644
index 00000000..3ade4efe
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-signed-apple.mbox.tree
@@ -0,0 +1,3 @@
1 * MimeTreeParser::MessagePartList
2 * MimeTreeParser::SignedMessagePart
3 * MimeTreeParser::AlternativeMessagePart
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-signed-encrypted.mbox b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-signed-encrypted.mbox
new file mode 100644
index 00000000..49857e15
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-signed-encrypted.mbox
@@ -0,0 +1,38 @@
1From test@example.com Wed, 08 Sep 2010 17:51:32 +0200
2From: S/MIME Test <test@example.com>
3To: test@example.com
4Subject: S/MIME signed and encrypted
5Date: Wed, 08 Sep 2010 17:51:32 +0200
6User-Agent: KMail/4.6 pre (Linux/2.6.34-rc2-2-default; KDE/4.5.60; x86_64; ; )
7MIME-Version: 1.0
8Content-Type: application/pkcs7-mime; name="smime.p7m"; smime-type="enveloped-data"
9Content-Disposition: attachment; filename="smime.p7m"
10Content-Transfer-Encoding: base64
11
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
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-signed-encrypted.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-signed-encrypted.mbox.html
new file mode 100644
index 00000000..e13d6841
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-signed-encrypted.mbox.html
@@ -0,0 +1,55 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <table cellspacing="1" cellpadding="1" class="encr">
9 <tr class="encrH">
10 <td dir="ltr">Encrypted message</td>
11 </tr>
12 <tr class="encrB">
13 <td>
14 <div style="position: relative; word-wrap: break-word">
15 <a name="att"/>
16 <div id="attachmentDiv">
17 <table cellspacing="1" cellpadding="1" class="signOkKeyOk">
18 <tr class="signOkKeyOkH">
19 <td dir="ltr">
20 <table cellspacing="0" cellpadding="0" width="100%">
21 <tr>
22 <td>Signed by <a href="mailto:test@example.com">test@example.com</a>.</td>
23 <td align="right">
24 <a href="kmail:showSignatureDetails">Show Details</a>
25 </td>
26 </tr>
27 </table>
28 </td>
29 </tr>
30 <tr class="signOkKeyOkB">
31 <td>
32 <a name="att1"/>
33 <div id="attachmentDiv1">
34 <div class="noquote">
35 <div dir="ltr">encrypted message text</div>
36 </div>
37 </div>
38 </td>
39 </tr>
40 <tr class="signOkKeyOkH">
41 <td dir="ltr">End of signed message</td>
42 </tr>
43 </table>
44 </div>
45 </div>
46 </td>
47 </tr>
48 <tr class="encrH">
49 <td dir="ltr">End of encrypted message</td>
50 </tr>
51 </table>
52 </div>
53 </div>
54 </body>
55</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-signed-encrypted.mbox.tree b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-signed-encrypted.mbox.tree
new file mode 100644
index 00000000..7d5bbeb7
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/smime-signed-encrypted.mbox.tree
@@ -0,0 +1,5 @@
1 * MimeTreeParser::MessagePartList
2 * MimeTreeParser::EncryptedMessagePart
3 * MimeTreeParser::SignedMessagePart
4 * MimeTreeParser::TextMessagePart
5 * MimeTreeParser::MessagePart
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/text+html-maillinglist.mbox b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/text+html-maillinglist.mbox
new file mode 100644
index 00000000..f9c6387f
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/text+html-maillinglist.mbox
@@ -0,0 +1,163 @@
1Return-Path: <bugzilla-bounces@lists.kolabsys.com>
2Received: from kolab01.kolabsys.com ([unix socket])
3 by kolab01.kolabsys.com (Cyrus v2.4.17-Kolab-2.4.17-1.el6.kolab_3.0) with LMTPA;
4 Thu, 11 Apr 2013 09:03:01 +0200
5X-Sieve: CMU Sieve 2.4
6Received: from ext-mx01.kolabsys.com (unknown [10.10.20.253])
7 by kolab01.kolabsys.com (Postfix) with ESMTP id 3D8A9C0AE3
8 for <shared+shared/lists/kolabsys.com/bugzilla@kolabsys.com>; Thu, 11 Apr 2013 09:03:01 +0200 (CEST)
9Received: from localhost (localhost [127.0.0.1])
10 by ext-mx01.kolabsys.com (Postfix) with ESMTP id 89D4E10057E
11 for <shared+shared/lists/kolabsys.com/bugzilla@kolabsys.com>; Thu, 11 Apr 2013 09:02:38 +0200 (CEST)
12X-Virus-Scanned: amavisd-new at example.com
13X-Amavis-Alert: BAD HEADER SECTION, Duplicate header field: "MIME-Version"
14Received: from ext-mx01.kolabsys.com ([127.0.0.1])
15 by localhost (fw01.kolabsys.com [127.0.0.1]) (amavisd-new, port 10024)
16 with ESMTP id BJxrh9Yst1Ac
17 for <shared+shared/lists/kolabsys.com/bugzilla@kolabsys.com>;
18 Thu, 11 Apr 2013 09:02:38 +0200 (CEST)
19Received: from lists.kolabsys.com (static.253.32.46.78.clients.your-server.de [78.46.32.253])
20 by ext-mx01.kolabsys.com (Postfix) with ESMTP id 609821004A7
21 for <shared+shared/lists/kolabsys.com/bugzilla@kolabsys.com>; Thu, 11 Apr 2013 09:02:38 +0200 (CEST)
22Received: from localhost (localhost [127.0.0.1])
23 by lists.kolabsys.com (Postfix) with ESMTP id BFA6941D4EAD;
24 Thu, 11 Apr 2013 09:03:00 +0200 (CEST)
25X-Virus-Scanned: Debian amavisd-new at lists.kolabsys.com
26X-Amavis-Alert: BAD HEADER SECTION, Duplicate header field: "MIME-Version"
27Received: from lists.kolabsys.com ([127.0.0.1])
28 by localhost (lists.kolabsys.com [127.0.0.1]) (amavisd-new, port 10024)
29 with ESMTP id TYU5eAMK5J6T; Thu, 11 Apr 2013 09:02:58 +0200 (CEST)
30Received: from lists.kolabsys.com (localhost [127.0.0.1])
31 by lists.kolabsys.com (Postfix) with ESMTP id 182FC41D2A3B;
32 Thu, 11 Apr 2013 09:02:58 +0200 (CEST)
33Received: from localhost (localhost [127.0.0.1])
34 by lists.kolabsys.com (Postfix) with ESMTP id A2B3641D4EC2
35 for <bugzilla@lists.kolabsys.com>;
36 Thu, 11 Apr 2013 09:02:56 +0200 (CEST)
37X-Virus-Scanned: Debian amavisd-new at lists.kolabsys.com
38Received: from lists.kolabsys.com ([127.0.0.1])
39 by localhost (lists.kolabsys.com [127.0.0.1]) (amavisd-new, port 10024)
40 with ESMTP id cyO-CNB3vFwu for <bugzilla@lists.kolabsys.com>;
41 Thu, 11 Apr 2013 09:02:54 +0200 (CEST)
42Received: from ext-mx02.kolabsys.com (ext-mx02.kolabsys.com [94.230.208.222])
43 by lists.kolabsys.com (Postfix) with ESMTP id 1640E41D2A3B
44 for <bugzilla@lists.kolabsys.com>;
45 Thu, 11 Apr 2013 09:02:54 +0200 (CEST)
46Received: from localhost (localhost [127.0.0.1])
47 by ext-mx01.kolabsys.com (Postfix) with ESMTP id 58CBC160522
48 for <bugzilla@lists.kolabsys.com>;
49 Thu, 11 Apr 2013 10:02:44 +0200 (CEST)
50X-Virus-Scanned: amavisd-new at example.com
51Received: from ext-mx02.kolabsys.com ([127.0.0.1])
52 by localhost (fw02.kolabsys.com [127.0.0.1]) (amavisd-new, port 10024)
53 with ESMTP id 4VjnHg6Y6jo7 for <bugzilla@lists.kolabsys.com>;
54 Thu, 11 Apr 2013 10:02:42 +0200 (CEST)
55Received: from app04.kolabsys.com (unknown [10.10.20.16])
56 by ext-mx02.kolabsys.com (Postfix) with ESMTP id 062DF160521
57 for <bugzilla@lists.kolabsys.com>;
58 Thu, 11 Apr 2013 10:02:42 +0200 (CEST)
59Received: by app04.kolabsys.com (Postfix, from userid 48)
60 id 8E7524048C; Thu, 11 Apr 2013 09:02:51 +0200 (CEST)
61From: Kolab Bugzilla <noreply@kolab.org>
62To: bugzilla@lists.kolabsys.com
63Subject: [Bug 1741] Standard folder name configuration ignored on creating
64 new users
65Date: Thu, 11 Apr 2013 07:02:43 +0000
66X-Bugzilla-Reason: GlobalWatcher
67X-Bugzilla-Type: changed
68X-Bugzilla-Watch-Reason: None
69X-Bugzilla-Product: UCS
70X-Bugzilla-Component: roundcube
71X-Bugzilla-Keywords:
72X-Bugzilla-Severity: normal
73X-Bugzilla-Who: wickert@kolabsys.com
74X-Bugzilla-Status: NEW
75X-Bugzilla-Priority: P3
76X-Bugzilla-Assigned-To: machniak@kolabsys.com
77X-Bugzilla-Target-Milestone: 3.1-next
78X-Bugzilla-Changed-Fields:
79Message-ID: <bug-1741-12-HOSaBe3Z8l@http.issues.kolab.org/>
80In-Reply-To: <bug-1741-12@http.issues.kolab.org/>
81References: <bug-1741-12@http.issues.kolab.org/>
82X-Bugzilla-URL: http://issues.kolab.org/
83Auto-Submitted: auto-generated
84MIME-Version: 1.0
85X-BeenThere: bugzilla@lists.kolabsys.com
86X-Mailman-Version: 2.1.11
87Precedence: list
88Reply-To: server-team@lists.kolabsys.com
89List-Id: All Bugzilla email notifications <bugzilla.lists.kolabsys.com>
90List-Unsubscribe: <https://lists.kolabsys.com/mailman/options/bugzilla>,
91 <mailto:bugzilla-request@lists.kolabsys.com?subject=unsubscribe>
92List-Archive: <http://lists.kolabsys.com/pipermail/bugzilla>
93List-Post: <mailto:bugzilla@lists.kolabsys.com>
94List-Help: <mailto:bugzilla-request@lists.kolabsys.com?subject=help>
95List-Subscribe: <https://lists.kolabsys.com/mailman/listinfo/bugzilla>,
96 <mailto:bugzilla-request@lists.kolabsys.com?subject=subscribe>
97Content-Type: multipart/mixed; boundary="===============1778809852=="
98Mime-version: 1.0
99Sender: bugzilla-bounces@lists.kolabsys.com
100Errors-To: bugzilla-bounces@lists.kolabsys.com
101
102
103--===============1778809852==
104Content-Type: multipart/alternative; boundary="1365663771.ec4d382.10226"; charset="us-ascii"
105
106
107--1365663771.ec4d382.10226
108Date: Thu, 11 Apr 2013 09:02:51 +0200
109MIME-Version: 1.0
110Content-Type: text/plain; charset="UTF-8"
111
112https://issues.kolab.org/show_bug.cgi?id=1741
113
114--- Comment #4 from Christoph Wickert <wickert@kolabsys.com> ---
115You can get everything you need on test93-18.
116
117--
118You are receiving this mail because:
119You are watching all bug changes.
120
121--1365663771.ec4d382.10226
122Date: Thu, 11 Apr 2013 09:02:51 +0200
123MIME-Version: 1.0
124Content-Type: text/html; charset="UTF-8"
125
126<html>
127 <head>
128 <base href="https://issues.kolab.org/" />
129 </head>
130 <body>
131 <b><a class="bz_bug_link
132 bz_status_NEW "
133 title="NEW --- - Standard folder name configuration ignored on creating new users"
134 href="https://issues.kolab.org/show_bug.cgi?id=1741#c4">Comment # 4</a>
135 on <a class="bz_bug_link
136 bz_status_NEW "
137 title="NEW --- - Standard folder name configuration ignored on creating new users"
138 href="https://issues.kolab.org/show_bug.cgi?id=1741">bug 1741</a>
139 from <span class="vcard"><a class="email" href="mailto:wickert&#64;kolabsys.com" title="Christoph Wickert &lt;wickert&#64;kolabsys.com&gt;"> <span class="fn">Christoph Wickert</span></a>
140</span></b>
141 <pre>You can get everything you need on test93-18.</pre>
142 <span>You are receiving this mail because:</span>
143 <ul>
144 <li>You are watching all bug changes.</li>
145 </ul>
146 </body>
147</html>
148
149--1365663771.ec4d382.10226--
150
151--===============1778809852==
152Content-Type: text/plain; charset="us-ascii"
153MIME-Version: 1.0
154Content-Transfer-Encoding: 7bit
155Content-Disposition: inline
156
157_______________________________________________
158bugzilla mailing list
159bugzilla@lists.kolabsys.com
160https://lists.kolabsys.com/mailman/listinfo/bugzilla
161
162--===============1778809852==--
163
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/text+html-maillinglist.mbox.html b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/text+html-maillinglist.mbox.html
new file mode 100644
index 00000000..2b5af631
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/text+html-maillinglist.mbox.html
@@ -0,0 +1,38 @@
1<?xml version="1.0" encoding="UTF8"?>
2<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
3<html>
4 <body>
5 <div style="position: relative; word-wrap: break-word">
6 <a name="att"/>
7 <div id="attachmentDiv">
8 <a name="att1"/>
9 <div id="attachmentDiv1">
10 <a name="att1.2"/>
11 <div id="attachmentDiv1.2">
12 <div style="position: relative">
13 <b><a class="bz_bug_link bz_status_NEW " title="NEW --- - Standard folder name configuration ignored on creating new users" href="https://issues.kolab.org/show_bug.cgi?id=1741#c4">Comment # 4</a>on <a class="bz_bug_link bz_status_NEW " title="NEW --- - Standard folder name configuration ignored on creating new users" href="https://issues.kolab.org/show_bug.cgi?id=1741">bug 1741</a>from <span class="vcard"><a class="email" href="mailto:wickert@kolabsys.com" title="Christoph Wickert &lt;wickert@kolabsys.com&gt;"><span class="fn">Christoph Wickert</span></a></span></b>
14 <pre>You can get everything you need on test93-18.</pre>
15 <span>You are receiving this mail because:</span>
16 <ul>
17 <li>You are watching all bug changes.</li>
18 </ul>
19 </div>
20 </div>
21 </div>
22 <a name="att2"/>
23 <div id="attachmentDiv2">
24 <div class="noquote">
25 <div dir="ltr">_______________________________________________</div>
26 <div dir="ltr">bugzilla mailing list</div>
27 <div dir="ltr">
28 <a href="mailto:bugzilla@lists.kolabsys.com">bugzilla@lists.kolabsys.com</a>
29 </div>
30 <div dir="ltr">
31 <a href="https://lists.kolabsys.com/mailman/listinfo/bugzilla">https://lists.kolabsys.com/mailman/listinfo/bugzilla</a>
32 </div>
33 </div>
34 </div>
35 </div>
36 </div>
37 </body>
38</html>
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/data/text+html-maillinglist.mbox.tree b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/text+html-maillinglist.mbox.tree
new file mode 100644
index 00000000..3738cb37
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/data/text+html-maillinglist.mbox.tree
@@ -0,0 +1,5 @@
1 * MimeTreeParser::MessagePartList
2 * MimeTreeParser::MimeMessagePart
3 * MimeTreeParser::AlternativeMessagePart
4 * MimeTreeParser::AttachmentMessagePart
5 * MimeTreeParser::MessagePart
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/kdepim_add_gpg_crypto_test.cmake b/framework/src/domain/mime/mimetreeparser/otp/autotests/kdepim_add_gpg_crypto_test.cmake
new file mode 100644
index 00000000..ea0ab8d2
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/kdepim_add_gpg_crypto_test.cmake
@@ -0,0 +1,61 @@
1# Copyright (c) 2013 Sandro Knauß <mail@sandroknauss.de>
2#
3# Redistribution and use is allowed according to the terms of the BSD license.
4# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
5
6set( MIMETREEPARSERRELPATH framework/src/domain/mimetreeparser)
7set( GNUPGHOME ${CMAKE_BINARY_DIR}/${MIMETREEPARSERRELPATH}/tests/gnupg_home )
8add_definitions( -DGNUPGHOME="${GNUPGHOME}" )
9
10macro (ADD_GPG_CRYPTO_TEST _target _testname)
11 if (UNIX)
12 if (APPLE)
13 set(_library_path_variable "DYLD_LIBRARY_PATH")
14 elseif (CYGWIN)
15 set(_library_path_variable "PATH")
16 else (APPLE)
17 set(_library_path_variable "LD_LIBRARY_PATH")
18 endif (APPLE)
19
20 if (APPLE)
21 # DYLD_LIBRARY_PATH does not work like LD_LIBRARY_PATH
22 # OSX already has the RPATH in libraries and executables, putting runtime directories in
23 # DYLD_LIBRARY_PATH actually breaks things
24 set(_ld_library_path "${LIBRARY_OUTPUT_PATH}/${CMAKE_CFG_INTDIR}/")
25 else (APPLE)
26 set(_ld_library_path "${LIBRARY_OUTPUT_PATH}/${CMAKE_CFG_INTDIR}/:${LIB_INSTALL_DIR}:${QT_LIBRARY_DIR}")
27 endif (APPLE)
28 set(_executable "$<TARGET_FILE:${_target}>")
29
30 # use add_custom_target() to have the sh-wrapper generated during build time instead of cmake time
31 add_custom_command(TARGET ${_target} POST_BUILD
32 COMMAND ${CMAKE_COMMAND}
33 -D_filename=${_executable}.shell -D_library_path_variable=${_library_path_variable}
34 -D_ld_library_path="${_ld_library_path}" -D_executable=$<TARGET_FILE:${_target}>
35 -D_gnupghome="${GNUPGHOME}"
36 -P ${CMAKE_SOURCE_DIR}/${MIMETREEPARSERRELPATH}/tests/kdepim_generate_crypto_test_wrapper.cmake
37 )
38
39 set_property(DIRECTORY APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES "${_executable}.shell" )
40 add_test(NAME ${_testname} COMMAND ${_executable}.shell)
41
42 else (UNIX)
43 # under windows, set the property WRAPPER_SCRIPT just to the name of the executable
44 # maybe later this will change to a generated batch file (for setting the PATH so that the Qt libs are found)
45 set(_ld_library_path "${LIBRARY_OUTPUT_PATH}/${CMAKE_CFG_INTDIR}\;${LIB_INSTALL_DIR}\;${QT_LIBRARY_DIR}")
46 set(_executable "$<TARGET_FILE:${_target}>")
47
48 # use add_custom_target() to have the batch-file-wrapper generated during build time instead of cmake time
49 add_custom_command(TARGET ${_target} POST_BUILD
50 COMMAND ${CMAKE_COMMAND}
51 -D_filename="${_executable}.bat"
52 -D_ld_library_path="${_ld_library_path}" -D_executable="${_executable}"
53 -D_gnupghome="${GNUPGHOME}"
54 -P ${CMAKE_SOURCE_DIR}/${MIMETREEPARSERRELPATH}/tests/kdepim_generate_crypto_test_wrapper.cmake
55 )
56
57 add_test(NAME ${_testname} COMMAND ${_executable}.bat)
58
59 endif (UNIX)
60endmacro (ADD_GPG_CRYPTO_TEST)
61
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/kdepim_generate_crypto_test_wrapper.cmake b/framework/src/domain/mime/mimetreeparser/otp/autotests/kdepim_generate_crypto_test_wrapper.cmake
new file mode 100644
index 00000000..e1412f37
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/kdepim_generate_crypto_test_wrapper.cmake
@@ -0,0 +1,45 @@
1# Copyright (c) 2006, Alexander Neundorf, <neundorf@kde.org>
2# Copyright (c) 2013, Sandro Knauß <mail@sandroknauss.de>
3#
4# Redistribution and use is allowed according to the terms of the BSD license.
5# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
6
7
8if (UNIX)
9
10file(WRITE "${_filename}"
11"#!/bin/sh
12# created by cmake, don't edit, changes will be lost
13
14# don't mess with a gpg-agent already running on the system
15unset GPG_AGENT_INFO
16
17${_library_path_variable}=${_ld_library_path}\${${_library_path_variable}:+:\$${_library_path_variable}} GNUPGHOME=${_gnupghome} gpg-agent --daemon \"${_executable}\" \"$@\"
18_result=$?
19_pid=`echo GETINFO pid | GNUPGHOME=${_gnupghome} gpg-connect-agent | grep 'D' | cut -d' ' -f2`
20if [ ! -z \"\$_pid\" ]; then
21 echo \"Waiting for gpg-agent to terminate (PID: $_pid)...\"
22 while kill -0 \"\$_pid\"; do
23 sleep 1
24 done
25fi
26exit \$_result
27")
28
29# make it executable
30# since this is only executed on UNIX, it is safe to call chmod
31exec_program(chmod ARGS ug+x \"${_filename}\" OUTPUT_VARIABLE _dummy )
32
33else (UNIX)
34
35file(TO_NATIVE_PATH "${_ld_library_path}" win_path)
36file(TO_NATIVE_PATH "${_gnupghome}" win_gnupghome)
37
38file(WRITE "${_filename}"
39"
40set PATH=${win_path};$ENV{PATH}
41set GNUPGHOME=${win_gnupghome};$ENV{GNUPGHOME}
42gpg-agent --daemon \"${_executable}\" %*
43")
44
45endif (UNIX)
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/nodehelpertest.cpp b/framework/src/domain/mime/mimetreeparser/otp/autotests/nodehelpertest.cpp
new file mode 100644
index 00000000..d2a5d605
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/nodehelpertest.cpp
@@ -0,0 +1,275 @@
1/* Copyright 2015 Sandro Knauß <bugs@sandroknauss.de>
2
3 This program is free software; you can redistribute it and/or
4 modify it under the terms of the GNU General Public License as
5 published by the Free Software Foundation; either version 2 of
6 the License or (at your option) version 3 or any later version
7 accepted by the membership of KDE e.V. (or its successor approved
8 by the membership of KDE e.V.), which shall act as a proxy
9 defined in Section 14 of version 3 of the license.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
18*/
19#include "nodehelpertest.h"
20
21#include "nodehelper.h"
22
23#include <qtest.h>
24
25using namespace MimeTreeParser;
26
27NodeHelperTest::NodeHelperTest()
28 : QObject()
29{
30
31}
32
33void NodeHelperTest::testPersistentIndex()
34{
35 NodeHelper helper;
36
37 KMime::Content *node = new KMime::Content();
38 KMime::Content *node2 = new KMime::Content();
39 KMime::Content *node2Extra = new KMime::Content();
40 KMime::Content *subNode = new KMime::Content();
41 KMime::Content *subsubNode = new KMime::Content(), *subsubNode2 = new KMime::Content();
42 KMime::Content *node2ExtraSubNode = new KMime::Content();
43 KMime::Content *node2ExtraSubsubNode = new KMime::Content();
44 KMime::Content *node2ExtraSubsubNode2 = new KMime::Content();
45 KMime::Content *extra = new KMime::Content(), *extra2 = new KMime::Content();
46 KMime::Content *subExtra = new KMime::Content();
47 KMime::Content *subsubExtra = new KMime::Content();
48 KMime::Content *subsubExtraNode = new KMime::Content();
49
50 subNode->addContent(subsubNode);
51 subNode->addContent(subsubNode2);
52 node->addContent(subNode);
53 subsubExtra->addContent(subsubExtraNode);
54 helper.attachExtraContent(node, extra);
55 helper.attachExtraContent(node, extra2);
56 helper.attachExtraContent(subNode, subExtra);
57 helper.attachExtraContent(subsubNode2, subsubExtra);
58
59 // This simulates Opaque S/MIME signed and encrypted message with attachment
60 // (attachment is node2SubsubNode2)
61 node2Extra->addContent(node2ExtraSubNode);
62 node2ExtraSubNode->addContent(node2ExtraSubsubNode);
63 node2ExtraSubNode->addContent(node2ExtraSubsubNode2);
64 helper.attachExtraContent(node2, node2Extra);
65
66 /* all content has a internal first child, so indexes starts at 2
67 * node ""
68 * -> subNode "2"
69 * -> subsubNode "2.2"
70 * -> subsubNode2 "2.3"
71 *
72 * node ""
73 * -> extra "e0"
74 * -> extra2 "e1"
75 *
76 * subNode "2"
77 * -> subExtra "2:e0"
78 *
79 * subsubNode2 "2.3"
80 * -> subsubExtra "2.3:e0"
81 * -> subsubExtraNode "2.3:e0:2"
82 *
83 * node2 ""
84 *
85 * node2 ""
86 * -> node2Extra "e0"
87 * -> node2ExtraSubNode "e0:2"
88 * -> node2ExtraSubsubNode "e0:2.2"
89 * -> node2ExtraSubsubNode2 "e0:2.3"
90 */
91
92 QCOMPARE(helper.persistentIndex(node), QStringLiteral(""));
93 QCOMPARE(helper.contentFromIndex(node, QStringLiteral("")), node);
94
95 QCOMPARE(helper.persistentIndex(node->contents()[0]), QStringLiteral("1"));
96 QCOMPARE(helper.contentFromIndex(node, QStringLiteral("1")), node->contents()[0]);
97
98 QCOMPARE(helper.persistentIndex(subNode), QStringLiteral("2"));
99 QCOMPARE(helper.contentFromIndex(node, QStringLiteral("2")), subNode);
100
101 QCOMPARE(helper.persistentIndex(subsubNode), QStringLiteral("2.2"));
102 QCOMPARE(helper.contentFromIndex(node, QStringLiteral("2.2")), subsubNode);
103
104 QCOMPARE(helper.persistentIndex(subsubNode2), QStringLiteral("2.3"));
105 QCOMPARE(helper.contentFromIndex(node, QStringLiteral("2.3")), subsubNode2);
106
107 QCOMPARE(helper.persistentIndex(extra), QStringLiteral("e0"));
108 QCOMPARE(helper.contentFromIndex(node, QStringLiteral("e0")), extra);
109
110 QCOMPARE(helper.persistentIndex(extra2), QStringLiteral("e1"));
111 QCOMPARE(helper.contentFromIndex(node, QStringLiteral("e1")), extra2);
112
113 QCOMPARE(helper.persistentIndex(subExtra), QStringLiteral("2:e0"));
114 QCOMPARE(helper.contentFromIndex(node, QStringLiteral("2:e0")), subExtra);
115
116 QCOMPARE(helper.persistentIndex(subsubExtra), QStringLiteral("2.3:e0"));
117 QCOMPARE(helper.contentFromIndex(node, QStringLiteral("2.3:e0")), subsubExtra);
118
119 QCOMPARE(helper.persistentIndex(subsubExtraNode), QStringLiteral("2.3:e0:2"));
120 QCOMPARE(helper.contentFromIndex(node, QStringLiteral("2.3:e0:2")), subsubExtraNode);
121
122 QCOMPARE(helper.persistentIndex(node2ExtraSubsubNode2), QStringLiteral("e0:2.3"));
123 QCOMPARE(helper.contentFromIndex(node2, QStringLiteral("e0:2.3")), node2ExtraSubsubNode2);
124
125 delete node;
126}
127
128void NodeHelperTest::testHREF()
129{
130 NodeHelper helper;
131 KMime::Message::Ptr msg(new KMime::Message);
132 QUrl url;
133
134 KMime::Content *node = msg->topLevel();
135 KMime::Content *subNode = new KMime::Content();
136 KMime::Content *subsubNode = new KMime::Content(), *subsubNode2 = new KMime::Content();
137 KMime::Content *extra = new KMime::Content(), *extra2 = new KMime::Content();
138 KMime::Content *subExtra = new KMime::Content();
139 KMime::Content *subsubExtra = new KMime::Content();
140 KMime::Content *subsubExtraNode = new KMime::Content();
141
142 subNode->addContent(subsubNode);
143 subNode->addContent(subsubNode2);
144 node->addContent(subNode);
145 subsubExtra->addContent(subsubExtraNode);
146 helper.attachExtraContent(node, extra);
147 helper.attachExtraContent(node, extra2);
148 helper.attachExtraContent(subNode, subExtra);
149 helper.attachExtraContent(subsubNode2, subsubExtra);
150
151 url = QUrl(QStringLiteral(""));
152 QCOMPARE(helper.fromHREF(msg, url), node);
153
154 url = QUrl(QStringLiteral("attachment:e0?place=body"));
155 QCOMPARE(helper.fromHREF(msg, url), extra);
156
157 url = QUrl(QStringLiteral("attachment:2.2?place=body"));
158 QCOMPARE(helper.fromHREF(msg, url), subsubNode);
159
160 url = QUrl(QStringLiteral("attachment:2.3:e0:2?place=body"));
161 QCOMPARE(helper.fromHREF(msg, url), subsubExtraNode);
162
163 QCOMPARE(helper.asHREF(node, QStringLiteral("body")), QStringLiteral("attachment:?place=body"));
164 QCOMPARE(helper.asHREF(extra, QStringLiteral("body")), QStringLiteral("attachment:e0?place=body"));
165 QCOMPARE(helper.asHREF(subsubNode, QStringLiteral("body")), QStringLiteral("attachment:2.2?place=body"));
166 QCOMPARE(helper.asHREF(subsubExtraNode, QStringLiteral("body")), QStringLiteral("attachment:2.3:e0:2?place=body"));
167}
168
169void NodeHelperTest::testLocalFiles()
170{
171 NodeHelper helper;
172 KMime::Message::Ptr msg(new KMime::Message);
173
174 KMime::Content *node = msg->topLevel();
175 KMime::Content *subNode = new KMime::Content();
176 KMime::Content *subsubNode = new KMime::Content(), *subsubNode2 = new KMime::Content();
177 KMime::Content *extra = new KMime::Content(), *extra2 = new KMime::Content();
178 KMime::Content *subExtra = new KMime::Content();
179 KMime::Content *subsubExtra = new KMime::Content();
180 KMime::Content *subsubExtraNode = new KMime::Content();
181
182 subNode->addContent(subsubNode);
183 subNode->addContent(subsubNode2);
184 node->addContent(subNode);
185 subsubExtra->addContent(subsubExtraNode);
186 helper.attachExtraContent(node, extra);
187 helper.attachExtraContent(node, extra2);
188 helper.attachExtraContent(subNode, subExtra);
189 helper.attachExtraContent(subsubNode2, subsubExtra);
190
191 helper.writeNodeToTempFile(node);
192 QCOMPARE(helper.fromHREF(msg, helper.tempFileUrlFromNode(node)), node);
193 helper.writeNodeToTempFile(subNode);
194 QCOMPARE(helper.fromHREF(msg, helper.tempFileUrlFromNode(subNode)), subNode);
195 helper.writeNodeToTempFile(subsubNode);
196 QCOMPARE(helper.fromHREF(msg, helper.tempFileUrlFromNode(subsubNode)), subsubNode);
197 helper.writeNodeToTempFile(subsubNode2);
198 QCOMPARE(helper.fromHREF(msg, helper.tempFileUrlFromNode(subsubNode2)), subsubNode2);
199 helper.writeNodeToTempFile(extra);
200 QCOMPARE(helper.fromHREF(msg, helper.tempFileUrlFromNode(extra)), extra);
201 helper.writeNodeToTempFile(subExtra);
202 QCOMPARE(helper.fromHREF(msg, helper.tempFileUrlFromNode(subExtra)), subExtra);
203 helper.writeNodeToTempFile(subsubExtra);
204 QCOMPARE(helper.fromHREF(msg, helper.tempFileUrlFromNode(subsubExtra)), subsubExtra);
205 helper.writeNodeToTempFile(subsubExtraNode);
206 QCOMPARE(helper.fromHREF(msg, helper.tempFileUrlFromNode(subsubExtraNode)), subsubExtraNode);
207}
208
209void NodeHelperTest::testCreateTempDir()
210{
211 QString path;
212 {
213 NodeHelper helper;
214 path = helper.createTempDir(QStringLiteral("foo"));
215
216 QVERIFY(path.endsWith(QStringLiteral(".index.foo")));
217 QVERIFY(QDir(path).exists());
218 QVERIFY(QFile(path).permissions() & QFileDevice::WriteUser);
219 QVERIFY(QFile(path).permissions() & QFileDevice::ExeUser);
220 QVERIFY(QFile(path).permissions() & QFileDevice::ReadUser);
221 }
222 QVERIFY(!QDir(path).exists());
223}
224
225void NodeHelperTest::testFromAsString()
226{
227 const QString tlSender = QStringLiteral("Foo <foo@example.com>");
228 const QString encSender = QStringLiteral("Bar <bar@example.com>");
229
230 NodeHelper helper;
231
232 // msg (KMime::Message)
233 // |- subNode
234 // |- encNode (KMime::Message)
235 // |- encSubNode
236 //
237 // subNode
238 // |- subExtra
239 //
240 // encSubNode
241 // |- encSubExtra
242
243 KMime::Message msg;
244 msg.from(true)->fromUnicodeString(tlSender, "UTF-8");
245 auto node = msg.topLevel();
246 auto subNode = new KMime::Content();
247 auto subExtra = new KMime::Content();
248
249 // Encapsulated message
250 KMime::Message *encMsg = new KMime::Message;
251 encMsg->from(true)->fromUnicodeString(encSender, "UTF-8");
252 auto encNode = encMsg->topLevel();
253 auto encSubNode = new KMime::Content();
254 auto encSubExtra = new KMime::Content();
255
256 node->addContent(subNode);
257 node->addContent(encMsg);
258 encNode->addContent(encSubNode);
259
260 helper.attachExtraContent(subNode, subExtra);
261 helper.attachExtraContent(encSubNode, encSubExtra);
262
263 QCOMPARE(helper.fromAsString(node), tlSender);
264 QCOMPARE(helper.fromAsString(subNode), tlSender);
265 QCOMPARE(helper.fromAsString(subExtra), tlSender);
266 QEXPECT_FAIL("", "Returning sender of encapsulated message is not yet implemented", Continue);
267 QCOMPARE(helper.fromAsString(encNode), encSender);
268 QEXPECT_FAIL("", "Returning sender of encapsulated message is not yet implemented", Continue);
269 QCOMPARE(helper.fromAsString(encSubNode), encSender);
270 QEXPECT_FAIL("", "Returning sender of encapsulated message is not yet implemented", Continue);
271 QCOMPARE(helper.fromAsString(encSubExtra), encSender);
272}
273
274QTEST_GUILESS_MAIN(NodeHelperTest)
275
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/nodehelpertest.h b/framework/src/domain/mime/mimetreeparser/otp/autotests/nodehelpertest.h
new file mode 100644
index 00000000..d2ed772a
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/nodehelpertest.h
@@ -0,0 +1,45 @@
1/* Copyright 2015 Sandro Knauß <bugs@sandroknauss.de>
2
3 This program is free software; you can redistribute it and/or
4 modify it under the terms of the GNU General Public License as
5 published by the Free Software Foundation; either version 2 of
6 the License or (at your option) version 3 or any later version
7 accepted by the membership of KDE e.V. (or its successor approved
8 by the membership of KDE e.V.), which shall act as a proxy
9 defined in Section 14 of version 3 of the license.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
18*/
19#ifndef NODEHELPERTEST_H
20#define NODEHELPERTEST_H
21
22#include <QObject>
23
24#include <KMime/Message>
25
26namespace MimeTreeParser
27{
28
29class NodeHelperTest : public QObject
30{
31 Q_OBJECT
32
33public:
34 NodeHelperTest();
35
36private Q_SLOTS:
37 void testPersistentIndex();
38 void testLocalFiles();
39 void testHREF();
40 void testCreateTempDir();
41 void testFromAsString();
42};
43
44}
45#endif
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/setupenv.cpp b/framework/src/domain/mime/mimetreeparser/otp/autotests/setupenv.cpp
new file mode 100644
index 00000000..be7a8685
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/setupenv.cpp
@@ -0,0 +1,34 @@
1/*
2 Copyright (C) 2010 Klaralvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
3 Copyright (c) 2010 Leo Franchi <lfranchi@kde.org>
4
5 This library is free software; you can redistribute it and/or modify it
6 under the terms of the GNU Library General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or (at your
8 option) any later version.
9
10 This library is distributed in the hope that it will be useful, but WITHOUT
11 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
13 License for more details.
14
15 You should have received a copy of the GNU Library General Public License
16 along with this library; see the file COPYING.LIB. If not, write to the
17 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 02110-1301, USA.
19*/
20
21#include "setupenv.h"
22
23#include <QStandardPaths>
24
25#include <QFile>
26#include <QDir>
27
28void MimeTreeParser::Test::setupEnv()
29{
30 setenv("LC_ALL", "C", 1);
31 setenv("KDEHOME", QFile::encodeName(QDir::homePath() + QString::fromLatin1("/.qttest")).constData(), 1);
32 QStandardPaths::setTestModeEnabled(true);
33}
34
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/setupenv.h b/framework/src/domain/mime/mimetreeparser/otp/autotests/setupenv.h
new file mode 100644
index 00000000..3582853e
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/setupenv.h
@@ -0,0 +1,175 @@
1/*
2 Copyright (C) 2010 Klaralvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
3 Copyright (c) 2010 Leo Franchi <lfranchi@kde.org>
4
5 This library is free software; you can redistribute it and/or modify it
6 under the terms of the GNU Library General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or (at your
8 option) any later version.
9
10 This library is distributed in the hope that it will be useful, but WITHOUT
11 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
13 License for more details.
14
15 You should have received a copy of the GNU Library General Public License
16 along with this library; see the file COPYING.LIB. If not, write to the
17 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 02110-1301, USA.
19*/
20
21#ifndef MESSAGECORE_TESTS_UTIL_H
22#define MESSAGECORE_TESTS_UTIL_H
23
24#include <gpgme++/key.h>
25#include <attachmentstrategy.h>
26#include <bodypartformatter.h>
27#include <bodypartformatterbasefactory.h>
28#include <messagepartrenderer.h>
29#include <objecttreesource.h>
30
31namespace MimeTreeParser
32{
33
34namespace Test
35{
36
37/**
38* setup a environment variables for tests:
39* * set LC_ALL to C
40* * set KDEHOME
41*/
42void setupEnv();
43
44// We can't use EmptySource, since we need to control some emelnets of the source for tests to also test
45// loadExternal and htmlMail.
46class TestObjectTreeSource : public MimeTreeParser::Interface::ObjectTreeSource
47{
48public:
49 TestObjectTreeSource(MimeTreeParser::HtmlWriter *writer)
50 : mWriter(writer)
51 , mAttachmentStrategy(QStringLiteral("smart"))
52 , mPreferredMode(Util::Html)
53 , mHtmlLoadExternal(false)
54 , mDecryptMessage(false)
55 {
56 }
57
58 MimeTreeParser::HtmlWriter *htmlWriter() Q_DECL_OVERRIDE {
59 return mWriter;
60 }
61
62 bool htmlLoadExternal() const Q_DECL_OVERRIDE
63 {
64 return mHtmlLoadExternal;
65 }
66
67 void setHtmlLoadExternal(bool loadExternal)
68 {
69 mHtmlLoadExternal = loadExternal;
70 }
71
72 void setAttachmentStrategy(QString strategy)
73 {
74 mAttachmentStrategy = strategy;
75 }
76
77 const AttachmentStrategy *attachmentStrategy() Q_DECL_OVERRIDE {
78 return AttachmentStrategy::create(mAttachmentStrategy);
79 }
80
81 bool autoImportKeys() const Q_DECL_OVERRIDE
82 {
83 return true;
84 }
85
86 bool showEmoticons() const Q_DECL_OVERRIDE
87 {
88 return false;
89 }
90
91 bool showExpandQuotesMark() const Q_DECL_OVERRIDE
92 {
93 return false;
94 }
95
96 const BodyPartFormatterBaseFactory *bodyPartFormatterFactory() Q_DECL_OVERRIDE {
97 return &mBodyPartFormatterBaseFactory;
98 }
99
100 bool decryptMessage() const Q_DECL_OVERRIDE
101 {
102 return mDecryptMessage;
103 }
104
105 void setAllowDecryption(bool allowDecryption)
106 {
107 mDecryptMessage = allowDecryption;
108 }
109
110 void setShowSignatureDetails(bool showSignatureDetails)
111 {
112 mShowSignatureDetails = showSignatureDetails;
113 }
114
115 bool showSignatureDetails() const Q_DECL_OVERRIDE
116 {
117 return mShowSignatureDetails;
118 }
119
120 void setHtmlMode(MimeTreeParser::Util::HtmlMode mode, const QList<MimeTreeParser::Util::HtmlMode> &availableModes) Q_DECL_OVERRIDE {
121 Q_UNUSED(mode);
122 Q_UNUSED(availableModes);
123 }
124
125 MimeTreeParser::Util::HtmlMode preferredMode() const Q_DECL_OVERRIDE
126 {
127 return mPreferredMode;
128 }
129
130 void setPreferredMode(MimeTreeParser::Util::HtmlMode mode)
131 {
132 mPreferredMode = mode;
133 }
134
135 int levelQuote() const Q_DECL_OVERRIDE
136 {
137 return 1;
138 }
139
140 const QTextCodec *overrideCodec() Q_DECL_OVERRIDE {
141 return nullptr;
142 }
143
144 QString createMessageHeader(KMime::Message *message) Q_DECL_OVERRIDE {
145 Q_UNUSED(message);
146 return QString(); //do nothing
147 }
148
149 QObject *sourceObject() Q_DECL_OVERRIDE {
150 return nullptr;
151 }
152
153 Interface::MessagePartRenderer::Ptr messagePartTheme(Interface::MessagePart::Ptr msgPart) Q_DECL_OVERRIDE {
154 Q_UNUSED(msgPart);
155 return Interface::MessagePartRenderer::Ptr();
156 }
157 bool isPrinting() const Q_DECL_OVERRIDE
158 {
159 return false;
160 }
161private:
162 MimeTreeParser::HtmlWriter *mWriter;
163 QString mAttachmentStrategy;
164 BodyPartFormatterBaseFactory mBodyPartFormatterBaseFactory;
165 MimeTreeParser::Util::HtmlMode mPreferredMode;
166 bool mHtmlLoadExternal;
167 bool mDecryptMessage;
168 bool mShowSignatureDetails;
169};
170
171}
172
173}
174
175#endif
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/testcsshelper.cpp b/framework/src/domain/mime/mimetreeparser/otp/autotests/testcsshelper.cpp
new file mode 100644
index 00000000..0e411e8f
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/testcsshelper.cpp
@@ -0,0 +1,106 @@
1/*
2 testcsshelper.cpp
3
4 This file is part of KMail, the KDE mail client.
5 Copyright (c) 2013 Sandro Knauß <bugs@sandroknauss.de>
6
7 KMail is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License, version 2, as
9 published by the Free Software Foundation.
10
11 KMail is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
20 In addition, as a special exception, the copyright holders give
21 permission to link the code of this program with any edition of
22 the Qt library by Trolltech AS, Norway (or with modified versions
23 of Qt that use the same license as Qt), and distribute linked
24 combinations including the two. You must obey the GNU General
25 Public License in all respects for all of the code used other than
26 Qt. If you modify this file, you may extend this exception to
27 your version of the file, but you are not obligated to do so. If
28 you do not wish to do so, delete this exception statement from
29 your version.
30*/
31
32#include "testcsshelper.h"
33
34#include <QColor>
35#include <QFont>
36#include <QPalette>
37#include <QApplication>
38
39namespace MimeTreeParser
40{
41
42TestCSSHelper::TestCSSHelper(const QPaintDevice *pd) :
43 CSSHelperBase(pd)
44{
45 mRecycleQuoteColors = false;
46 mBackgroundColor = QColor(0xff, 0xff, 0xff);
47 mForegroundColor = QColor(0x1f, 0x1c, 0x1b);
48 mLinkColor = QColor(0x00, 0x57, 0xae);
49 cPgpEncrH = QColor(0x00, 0x80, 0xff);
50 cPgpOk1H = QColor(0x40, 0xff, 0x40);
51 cPgpOk0H = QColor(0xff, 0xff, 0x40);
52 cPgpWarnH = QColor(0xff, 0xff, 0x40);
53 cPgpErrH = QColor(0xff, 0x00, 0x00);
54
55 cPgpEncrHT = QColor(0xff, 0xff, 0xff);
56 cPgpOk1HT = QColor(0x27, 0xae, 0x60);
57 cPgpOk0HT = QColor(0xf6, 0x74, 0x00);
58 cPgpWarnHT = QColor(0xf6, 0x74, 0x00);
59 cPgpErrHT = QColor(0xda, 0x44, 0x53);
60
61 cHtmlWarning = QColor(0xff, 0x40, 0x40);
62 for (int i = 0; i < 3; ++i) {
63 mQuoteColor[i] = QColor(0x00, 0x80 - i * 0x10, 0x00);
64 }
65
66 QFont defaultFont = QFont(QStringLiteral("Sans Serif"), 9);
67 mBodyFont = defaultFont;
68 mPrintFont = defaultFont;
69 mFixedFont = defaultFont;
70 mFixedPrintFont = defaultFont;
71 defaultFont.setItalic(true);
72 for (int i = 0; i < 3; ++i) {
73 mQuoteFont[i] = defaultFont;
74 }
75
76 mShrinkQuotes = false;
77
78 QPalette pal;
79
80 pal.setColor(QPalette::Background, QColor(0xd6, 0xd2, 0xd0));
81 pal.setColor(QPalette::Foreground, QColor(0x22, 0x1f, 0x1e));
82 pal.setColor(QPalette::Highlight, QColor(0x43, 0xac, 0xe8));
83 pal.setColor(QPalette::HighlightedText, QColor(0xff, 0xff, 0xff));
84 pal.setColor(QPalette::Mid, QColor(0xb3, 0xab, 0xa7));
85
86 QApplication::setPalette(pal);
87
88 recalculatePGPColors();
89}
90
91TestCSSHelper::~TestCSSHelper()
92{
93
94}
95
96QString TestCSSHelper::htmlHead(bool fixed) const
97{
98 Q_UNUSED(fixed);
99 return
100 QStringLiteral("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n"
101 "<html>\n"
102 "<body>\n");
103}
104
105}
106
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/testcsshelper.h b/framework/src/domain/mime/mimetreeparser/otp/autotests/testcsshelper.h
new file mode 100644
index 00000000..c21935cf
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/testcsshelper.h
@@ -0,0 +1,50 @@
1/* -*- c++ -*-
2 testcsshelper.h
3
4 This file is part of KMail, the KDE mail client.
5 Copyright (c) 2013 Sandro Knauß <bugs@sandroknauss.de>
6
7 KMail is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License, version 2, as
9 published by the Free Software Foundation.
10
11 KMail is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
20 In addition, as a special exception, the copyright holders give
21 permission to link the code of this program with any edition of
22 the Qt library by Trolltech AS, Norway (or with modified versions
23 of Qt that use the same license as Qt), and distribute linked
24 combinations including the two. You must obey the GNU General
25 Public License in all respects for all of the code used other than
26 Qt. If you modify this file, you may extend this exception to
27 your version of the file, but you are not obligated to do so. If
28 you do not wish to do so, delete this exception statement from
29 your version.
30*/
31
32#ifndef __MIMETREEPARSER_TESTCSSHELPER_H__
33#define __MIMETREEPARSER_TESTCSSHELPER_H__
34
35#include "viewer/csshelperbase.h"
36
37namespace MimeTreeParser
38{
39
40class TestCSSHelper : public CSSHelperBase
41{
42public:
43 explicit TestCSSHelper(const QPaintDevice *pd);
44 virtual ~TestCSSHelper();
45 QString htmlHead(bool fixed) const Q_DECL_OVERRIDE;
46};
47
48}
49
50#endif // __MIMETREEPARSER_TESTCSSHELPER_H__
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/util.cpp b/framework/src/domain/mime/mimetreeparser/otp/autotests/util.cpp
new file mode 100644
index 00000000..5ea415b7
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/util.cpp
@@ -0,0 +1,34 @@
1/*
2 Copyright (c) 2010 Thomas McGuire <thomas.mcguire@kdab.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#include "util.h"
20
21#include <QFile>
22
23KMime::Message::Ptr readAndParseMail(const QString &mailFile)
24{
25 QFile file(QLatin1String(MAIL_DATA_DIR) + QLatin1Char('/') + mailFile);
26 file.open(QIODevice::ReadOnly);
27 Q_ASSERT(file.isOpen());
28 const QByteArray data = KMime::CRLFtoLF(file.readAll());
29 Q_ASSERT(!data.isEmpty());
30 KMime::Message::Ptr msg(new KMime::Message);
31 msg->setContent(data);
32 msg->parse();
33 return msg;
34}
diff --git a/framework/src/domain/mime/mimetreeparser/otp/autotests/util.h b/framework/src/domain/mime/mimetreeparser/otp/autotests/util.h
new file mode 100644
index 00000000..ac4aa54f
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/autotests/util.h
@@ -0,0 +1,43 @@
1/*
2 Copyright (c) 2010 Thomas McGuire <thomas.mcguire@kdab.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#include "htmlwriter.h"
20
21#include <KMime/Message>
22
23class TestHtmlWriter : public MimeTreeParser::HtmlWriter
24{
25public:
26 explicit TestHtmlWriter() {}
27 virtual ~TestHtmlWriter() {}
28
29 void begin(const QString &) Q_DECL_OVERRIDE {}
30 void write(const QString &) Q_DECL_OVERRIDE {}
31 void end() Q_DECL_OVERRIDE {}
32 void reset() Q_DECL_OVERRIDE {}
33 void queue(const QString &str) Q_DECL_OVERRIDE {
34 html.append(str);
35 }
36 void flush() Q_DECL_OVERRIDE {}
37 void embedPart(const QByteArray &, const QString &) Q_DECL_OVERRIDE {}
38 void extraHead(const QString &) Q_DECL_OVERRIDE {}
39
40 QString html;
41};
42
43KMime::Message::Ptr readAndParseMail(const QString &mailFile);
diff --git a/framework/src/domain/mime/mimetreeparser/otp/bodypart.cpp b/framework/src/domain/mime/mimetreeparser/otp/bodypart.cpp
new file mode 100644
index 00000000..62e92d0c
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/bodypart.cpp
@@ -0,0 +1,41 @@
1/*
2 This file is part of KMail's plugin interface.
3 Copyright (c) 2004 Marc Mutz <mutz@kde.org>,
4 Ingo Kloecker <kloecker@kde.org>
5
6 KMail is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 KMail is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
20 In addition, as a special exception, the copyright holders give
21 permission to link the code of this program with any edition of
22 the Qt library by Trolltech AS, Norway (or with modified versions
23 of Qt that use the same license as Qt), and distribute linked
24 combinations including the two. You must obey the GNU General
25 Public License in all respects for all of the code used other than
26 Qt. If you modify this file, you may extend this exception to
27 your version of the file, but you are not obligated to do so. If
28 you do not wish to do so, delete this exception statement from
29 your version.
30*/
31
32#include "bodypart.h"
33
34MimeTreeParser::Interface::BodyPartMemento::~BodyPartMemento()
35{
36}
37
38MimeTreeParser::Interface::BodyPart::~BodyPart()
39{
40}
41
diff --git a/framework/src/domain/mime/mimetreeparser/otp/bodypart.h b/framework/src/domain/mime/mimetreeparser/otp/bodypart.h
new file mode 100644
index 00000000..f50c0360
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/bodypart.h
@@ -0,0 +1,209 @@
1/* -*- mode: C++; c-file-style: "gnu" -*-
2 bodypart.h
3
4 This file is part of KMail's plugin interface.
5 Copyright (c) 2004 Marc Mutz <mutz@kde.org>,
6 Ingo Kloecker <kloecker@kde.org>
7
8 KMail is free software; you can redistribute it and/or modify it
9 under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12
13 KMail is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21
22 In addition, as a special exception, the copyright holders give
23 permission to link the code of this program with any edition of
24 the Qt library by Trolltech AS, Norway (or with modified versions
25 of Qt that use the same license as Qt), and distribute linked
26 combinations including the two. You must obey the GNU General
27 Public License in all respects for all of the code used other than
28 Qt. If you modify this file, you may extend this exception to
29 your version of the file, but you are not obligated to do so. If
30 you do not wish to do so, delete this exception statement from
31 your version.
32*/
33
34#ifndef __MIMETREEPARSER_INTERFACES_BODYPART_H__
35#define __MIMETREEPARSER_INTERFACES_BODYPART_H__
36
37#include <QByteArray>
38#include <QString>
39
40namespace KMime
41{
42class Content;
43}
44
45namespace MimeTreeParser
46{
47class NodeHelper;
48class ObjectTreeParser;
49class ProcessResult;
50
51namespace Interface
52{
53
54class ObjectTreeSource;
55
56/*FIXME(Andras) review, port
57 class Observer;
58 class Observable;
59*/
60/**
61 @short interface of classes that implement status for BodyPartFormatters.
62*/
63class BodyPartMemento
64{
65public:
66 virtual ~BodyPartMemento();
67
68 virtual void detach() = 0;
69#if 0
70//FIXME(Andras) review, port
71 /** If your BodyPartMemento implementation also implements the
72 Observer interface, simply implement these as
73 <code>return this;</code>, else as <code>return
74 0;</code>. This is needed to avoid forcing a dependency of
75 plugins on internal KMail classes.
76 */
77 virtual Observer *asObserver() = 0;
78
79 /** If your BodyPartMemento implementation also implements the
80 Observable interface, simply implement these as
81 <code>return this;</code>, else as <code>return
82 0;</code>. This is needed to avoid forcing a dependency of
83 plugins on internal KMail classes.
84 */
85 virtual Observable *asObservable() = 0;
86#endif
87};
88
89/**
90 @short interface of message body parts.
91*/
92class BodyPart
93{
94public:
95 virtual ~BodyPart();
96
97 /**
98 @return a string respresentation of an URL that can be used
99 to invoke a BodyPartURLHandler for this body part.
100 */
101 virtual QString makeLink(const QString &path) const = 0;
102
103 /**
104 @return the decoded (CTE, canonicalisation, and charset
105 encoding undone) text contained in the body part, or
106 QString(), it the body part is not of type "text".
107 */
108 virtual QString asText() const = 0;
109
110 /**
111 @return the decoded (CTE undone) content of the body part, or
112 a null array if this body part instance is of type text.
113 */
114 virtual QByteArray asBinary() const = 0;
115
116 /**
117 @return the value of the content-type header field parameter
118 with name \a parameter, or QString(), if that that
119 parameter is not present in the body's content-type header
120 field. RFC 2231 encoding is removed first.
121
122 Note that this method will suppress queries to certain
123 standard parameters (most notably "charset") to keep plugins
124 decent.
125
126 Note2 that this method preserves the case of the parameter
127 value returned. So, if the parameter you want to use defines
128 the value to be case-insensitive (such as the smime-type
129 parameter), you need to make sure you do the casemap yourself
130 before comparing to a reference value.
131 */
132 virtual QString contentTypeParameter(const char *parameter) const = 0;
133
134 /**
135 @return the content of the content-description header field,
136 or QString() if that header is not present in this body
137 part. RFC 2047 encoding is decoded first.
138 */
139 virtual QString contentDescription() const = 0;
140
141 //virtual int contentDisposition() const = 0;
142 /**
143 @return the value of the content-disposition header field
144 parameter with name \a parameter, or QString() if that
145 parameter is not present in the body's content-disposition
146 header field. RFC 2231 encoding is removed first.
147
148 The notes made for contentTypeParameter() above apply here as
149 well.
150 */
151 virtual QString contentDispositionParameter(const char *parameter) const = 0;
152
153 /**
154 @return whether this part already has it's complete body
155 fetched e.g. from an IMAP server.
156 */
157 virtual bool hasCompleteBody() const = 0;
158
159 /**
160 @return the BodyPartMemento set for this part, or null, if
161 none is set.
162 */
163 virtual BodyPartMemento *memento() const = 0;
164
165 /**
166 @return register an implementation of the BodyPartMemento
167 interface as a status object with this part.
168 */
169 virtual void setBodyPartMemento(BodyPartMemento *) = 0;
170
171 enum Display { None, AsIcon, Inline };
172 /**
173 @return whether this body part should be displayed iconic or inline
174 */
175 virtual Display defaultDisplay() const = 0;
176
177 /** Returns the KMime::Content node represented here. Makes most of the above obsolete
178 and probably should be used in the interfaces in the first place.
179 */
180 virtual KMime::Content *content() const = 0;
181
182 /**
183 * Returns the top-level content.
184 * Note that this is _not_ necessarily the same as content()->topLevel(), for example the later
185 * will not work for "extra nodes", i.e. nodes in encrypted parts of the mail.
186 * topLevelContent() will return the correct result in this case. Also note that
187 * topLevelContent()
188 */
189 virtual KMime::Content *topLevelContent() const = 0;
190
191 /**
192 * Ok, this is ugly, exposing the node helper here, but there is too much useful stuff in there
193 * for real-world plugins. Still, there should be a nicer way for this.
194 */
195 virtual MimeTreeParser::NodeHelper *nodeHelper() const = 0;
196
197 /**
198 * For making it easier to refactor, add objectTreeParser
199 */
200 virtual MimeTreeParser::ObjectTreeParser *objectTreeParser() const = 0;
201 virtual MimeTreeParser::Interface::ObjectTreeSource *source() const = 0;
202 virtual MimeTreeParser::ProcessResult *processResult() const = 0;
203};
204
205} // namespace Interface
206
207}
208
209#endif // __MIMETREEPARSER_INTERFACES_BODYPART_H__
diff --git a/framework/src/domain/mime/mimetreeparser/otp/bodypartformatter.cpp b/framework/src/domain/mime/mimetreeparser/otp/bodypartformatter.cpp
new file mode 100644
index 00000000..63d7e92c
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/bodypartformatter.cpp
@@ -0,0 +1,147 @@
1/* -*- mode: C++; c-file-style: "gnu" -*-
2 bodypartformatter.cpp
3
4 This file is part of KMail's plugin interface.
5 Copyright (c) 2016 Sandro Knauß <sknauss@kde.org>
6
7 KMail is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 KMail is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20
21 In addition, as a special exception, the copyright holders give
22 permission to link the code of this program with any edition of
23 the Qt library by Trolltech AS, Norway (or with modified versions
24 of Qt that use the same license as Qt), and distribute linked
25 combinations including the two. You must obey the GNU General
26 Public License in all respects for all of the code used other than
27 Qt. If you modify this file, you may extend this exception to
28 your version of the file, but you are not obligated to do so. If
29 you do not wish to do so, delete this exception statement from
30 your version.
31*/
32
33#include "bodypartformatter.h"
34
35#include "bodypart.h"
36#include "queuehtmlwriter.h"
37#include "objecttreeparser.h"
38
39using namespace MimeTreeParser::Interface;
40
41namespace MimeTreeParser
42{
43namespace Interface
44{
45
46class MessagePartPrivate
47{
48public:
49 MessagePartPrivate(const BodyPart *part)
50 : mHtmlWriter(nullptr)
51 , mPart(part)
52 , mParentPart(nullptr)
53 , mCreatedWriter(false)
54 {
55 }
56
57 ~MessagePartPrivate()
58 {
59 if (mCreatedWriter) {
60 delete mHtmlWriter;
61 }
62 }
63
64 MimeTreeParser::HtmlWriter *htmlWriter()
65 {
66 if (!mHtmlWriter && mPart) {
67 mHtmlWriter = mPart->objectTreeParser()->htmlWriter();
68 }
69 return mHtmlWriter;
70 }
71
72 MimeTreeParser::HtmlWriter *mHtmlWriter;
73 const BodyPart *mPart;
74 MessagePart *mParentPart;
75 bool mCreatedWriter;
76
77};
78}
79}
80
81MessagePart::MessagePart()
82 : QObject()
83 , d(new MessagePartPrivate(nullptr))
84{
85}
86
87MessagePart::MessagePart(const BodyPart &part)
88 : QObject()
89 , d(new MessagePartPrivate(&part))
90{
91}
92
93MessagePart::~MessagePart()
94{
95 delete d;
96}
97
98void MessagePart::html(bool decorate)
99{
100 Q_UNUSED(decorate);
101 static_cast<QueueHtmlWriter *>(d->mHtmlWriter)->replay();
102}
103
104QString MessagePart::text() const
105{
106 return QString();
107}
108
109MessagePart *MessagePart::parentPart() const
110{
111 return d->mParentPart;
112}
113
114void MessagePart::setParentPart(MessagePart *parentPart)
115{
116 d->mParentPart = parentPart;
117}
118
119QString MessagePart::htmlContent() const
120{
121 return text();
122}
123
124QString MessagePart::plaintextContent() const
125{
126 return text();
127}
128
129MimeTreeParser::HtmlWriter *MessagePart::htmlWriter() const
130{
131 return d->htmlWriter();
132}
133
134void MessagePart::setHtmlWriter(MimeTreeParser::HtmlWriter *htmlWriter) const
135{
136 if (d->mHtmlWriter) {
137 d->mHtmlWriter = htmlWriter;
138 }
139}
140
141MessagePart::Ptr BodyPartFormatter::process(BodyPart &part) const
142{
143 auto mp = MessagePart::Ptr(new MessagePart(part));
144 mp->setHtmlWriter(new QueueHtmlWriter(mp->htmlWriter()));
145 mp->d->mCreatedWriter = true;
146 return mp;
147}
diff --git a/framework/src/domain/mime/mimetreeparser/otp/bodypartformatter.h b/framework/src/domain/mime/mimetreeparser/otp/bodypartformatter.h
new file mode 100644
index 00000000..0116c2e4
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/bodypartformatter.h
@@ -0,0 +1,149 @@
1/* -*- mode: C++; c-file-style: "gnu" -*-
2 bodypartformatter.h
3
4 This file is part of KMail's plugin interface.
5 Copyright (c) 2004 Marc Mutz <mutz@kde.org>,
6 Ingo Kloecker <kloecker@kde.org>
7
8 KMail is free software; you can redistribute it and/or modify it
9 under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12
13 KMail is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21
22 In addition, as a special exception, the copyright holders give
23 permission to link the code of this program with any edition of
24 the Qt library by Trolltech AS, Norway (or with modified versions
25 of Qt that use the same license as Qt), and distribute linked
26 combinations including the two. You must obey the GNU General
27 Public License in all respects for all of the code used other than
28 Qt. If you modify this file, you may extend this exception to
29 your version of the file, but you are not obligated to do so. If
30 you do not wish to do so, delete this exception statement from
31 your version.
32*/
33
34#ifndef __MIMETREEPARSER_INTERFACE_BODYPARTFORMATTER_H__
35#define __MIMETREEPARSER_INTERFACE_BODYPARTFORMATTER_H__
36
37#include <QObject>
38#include <QSharedPointer>
39
40#include "objecttreeparser.h"
41
42namespace MimeTreeParser
43{
44class HtmlWriter;
45
46namespace Interface
47{
48
49class BodyPartURLHandler;
50class BodyPart;
51class MessagePartPrivate;
52
53class MessagePart : public QObject
54{
55 Q_OBJECT
56 Q_PROPERTY(QString plaintextContent READ plaintextContent)
57 Q_PROPERTY(QString htmlContent READ htmlContent)
58public:
59 typedef QSharedPointer<MessagePart> Ptr;
60 explicit MessagePart();
61 explicit MessagePart(const BodyPart &part);
62 virtual ~MessagePart();
63
64 virtual void html(bool decorate);
65 virtual QString text() const;
66
67 void setParentPart(MessagePart *parentPart);
68 MessagePart *parentPart() const;
69
70 virtual QString plaintextContent() const;
71 virtual QString htmlContent() const;
72
73 virtual MimeTreeParser::HtmlWriter *htmlWriter() const;
74 virtual void setHtmlWriter(MimeTreeParser::HtmlWriter *htmlWriter) const;
75private:
76 MessagePartPrivate *d;
77
78 friend class BodyPartFormatter;
79};
80
81class BodyPartFormatter
82{
83public:
84 virtual ~BodyPartFormatter() {}
85
86 /**
87 @li Ok returned when format() generated some HTML
88 @li NeedContent returned when format() needs the body of the part
89 @li AsIcon returned when the part should be shown iconified
90 @li Failed returned when formatting failed. Currently equivalent to Ok
91 */
92 enum Result { Ok, NeedContent, AsIcon, Failed };
93
94 /**
95 Format body part \a part by generating some HTML and writing
96 that to \a writer.
97
98 @return the result code (see above)
99 */
100 virtual Result format(BodyPart *part, MimeTreeParser::HtmlWriter *writer) const = 0;
101
102 /**
103 Variant of format that allows implementors to hook notifications up to
104 a listener interested in the result, for async operations.
105
106 @return the result code (see above)
107 */
108 virtual Result format(BodyPart *part, MimeTreeParser::HtmlWriter *writer, QObject *asyncResultObserver) const
109 {
110 Q_UNUSED(asyncResultObserver);
111 return format(part, writer);
112 }
113
114 virtual void adaptProcessResult(ProcessResult &result) const
115 {
116 Q_UNUSED(result);
117 }
118 virtual MessagePart::Ptr process(BodyPart &part) const;
119};
120
121/**
122 @short interface for BodyPartFormatter plugins
123
124 The interface is queried by for types, subtypes, and the
125 corresponding bodypart formatter, and the result inserted into
126 the bodypart formatter factory.
127
128 Subtype alone or both type and subtype may be "*", which is
129 taken as a wildcard, so that e.g. type=text subtype=* matches
130 any text subtype, but with lesser specificity than a concrete
131 mimetype such as text/plain. type=* is only allowed when
132 subtype=*, too.
133*/
134class BodyPartFormatterPlugin
135{
136public:
137 virtual ~BodyPartFormatterPlugin() {}
138
139 virtual const BodyPartFormatter *bodyPartFormatter(int idx) const = 0;
140 virtual const char *type(int idx) const = 0;
141 virtual const char *subtype(int idx) const = 0;
142
143 virtual const BodyPartURLHandler *urlHandler(int idx) const = 0;
144};
145
146} // namespace Interface
147
148}
149#endif // __MIMETREEPARSER_INTERFACE_BODYPARTFORMATTER_H__
diff --git a/framework/src/domain/mime/mimetreeparser/otp/bodypartformatter_impl.cpp b/framework/src/domain/mime/mimetreeparser/otp/bodypartformatter_impl.cpp
new file mode 100644
index 00000000..c8622ba3
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/bodypartformatter_impl.cpp
@@ -0,0 +1,193 @@
1/* -*- c++ -*-
2 bodypartformatter.cpp
3
4 This file is part of KMail, the KDE mail client.
5 Copyright (c) 2003 Marc Mutz <mutz@kde.org>
6
7 KMail is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License, version 2, as
9 published by the Free Software Foundation.
10
11 KMail is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
20 In addition, as a special exception, the copyright holders give
21 permission to link the code of this program with any edition of
22 the Qt library by Trolltech AS, Norway (or with modified versions
23 of Qt that use the same license as Qt), and distribute linked
24 combinations including the two. You must obey the GNU General
25 Public License in all respects for all of the code used other than
26 Qt. If you modify this file, you may extend this exception to
27 your version of the file, but you are not obligated to do so. If
28 you do not wish to do so, delete this exception statement from
29 your version.
30*/
31
32#include "mimetreeparser_debug.h"
33
34#include "applicationpgpencrypted.h"
35#include "applicationpkcs7mime.h"
36#include "mailman.h"
37#include "multipartalternative.h"
38#include "multipartmixed.h"
39#include "multipartencrypted.h"
40#include "multipartsigned.h"
41#include "texthtml.h"
42#include "textplain.h"
43
44#include "bodypartformatter.h"
45#include "bodypart.h"
46
47#include "bodypartformatterbasefactory.h"
48#include "bodypartformatterbasefactory_p.h"
49
50#include "attachmentstrategy.h"
51#include "objecttreeparser.h"
52#include "messagepart.h"
53
54#include <KMime/Content>
55
56using namespace MimeTreeParser;
57
58namespace
59{
60class AnyTypeBodyPartFormatter
61 : public MimeTreeParser::Interface::BodyPartFormatter
62{
63 static const AnyTypeBodyPartFormatter *self;
64public:
65 Result format(Interface::BodyPart *, HtmlWriter *) const Q_DECL_OVERRIDE
66 {
67 qCDebug(MIMETREEPARSER_LOG) << "Acting as a Interface::BodyPartFormatter!";
68 return AsIcon;
69 }
70
71 // unhide the overload with three arguments
72 using MimeTreeParser::Interface::BodyPartFormatter::format;
73
74 void adaptProcessResult(ProcessResult &result) const Q_DECL_OVERRIDE
75 {
76 result.setNeverDisplayInline(true);
77 }
78 static const MimeTreeParser::Interface::BodyPartFormatter *create()
79 {
80 if (!self) {
81 self = new AnyTypeBodyPartFormatter();
82 }
83 return self;
84 }
85};
86
87const AnyTypeBodyPartFormatter *AnyTypeBodyPartFormatter::self = nullptr;
88
89class ImageTypeBodyPartFormatter
90 : public MimeTreeParser::Interface::BodyPartFormatter
91{
92 static const ImageTypeBodyPartFormatter *self;
93public:
94 Result format(Interface::BodyPart *, HtmlWriter *) const Q_DECL_OVERRIDE
95 {
96 return AsIcon;
97 }
98
99 // unhide the overload with three arguments
100 using MimeTreeParser::Interface::BodyPartFormatter::format;
101
102 void adaptProcessResult(ProcessResult &result) const Q_DECL_OVERRIDE
103 {
104 result.setNeverDisplayInline(false);
105 result.setIsImage(true);
106 }
107 static const MimeTreeParser::Interface::BodyPartFormatter *create()
108 {
109 if (!self) {
110 self = new ImageTypeBodyPartFormatter();
111 }
112 return self;
113 }
114};
115
116const ImageTypeBodyPartFormatter *ImageTypeBodyPartFormatter::self = nullptr;
117
118class MessageRfc822BodyPartFormatter
119 : public MimeTreeParser::Interface::BodyPartFormatter
120{
121 static const MessageRfc822BodyPartFormatter *self;
122public:
123 Interface::MessagePart::Ptr process(Interface::BodyPart &) const Q_DECL_OVERRIDE;
124 MimeTreeParser::Interface::BodyPartFormatter::Result format(Interface::BodyPart *, HtmlWriter *) const Q_DECL_OVERRIDE;
125 using MimeTreeParser::Interface::BodyPartFormatter::format;
126 static const MimeTreeParser::Interface::BodyPartFormatter *create();
127};
128
129const MessageRfc822BodyPartFormatter *MessageRfc822BodyPartFormatter::self;
130
131const MimeTreeParser::Interface::BodyPartFormatter *MessageRfc822BodyPartFormatter::create()
132{
133 if (!self) {
134 self = new MessageRfc822BodyPartFormatter();
135 }
136 return self;
137}
138
139Interface::MessagePart::Ptr MessageRfc822BodyPartFormatter::process(Interface::BodyPart &part) const
140{
141 const KMime::Message::Ptr message = part.content()->bodyAsMessage();
142 return MessagePart::Ptr(new EncapsulatedRfc822MessagePart(part.objectTreeParser(), part.content(), message));
143}
144
145Interface::BodyPartFormatter::Result MessageRfc822BodyPartFormatter::format(Interface::BodyPart *part, HtmlWriter *writer) const
146{
147 Q_UNUSED(writer)
148 const ObjectTreeParser *otp = part->objectTreeParser();
149 const auto p = process(*part);
150 const auto mp = static_cast<MessagePart *>(p.data());
151 if (mp) {
152 if (!otp->attachmentStrategy()->inlineNestedMessages() && !otp->showOnlyOneMimePart()) {
153 return Failed;
154 } else {
155 mp->html(true);
156 return Ok;
157 }
158 } else {
159 return Failed;
160 }
161}
162
163typedef TextPlainBodyPartFormatter ApplicationPgpBodyPartFormatter;
164
165} // anon namespace
166
167void BodyPartFormatterBaseFactoryPrivate::messageviewer_create_builtin_bodypart_formatters()
168{
169 insert("application", "octet-stream", AnyTypeBodyPartFormatter::create());
170 insert("application", "pgp", ApplicationPgpBodyPartFormatter::create());
171 insert("application", "pkcs7-mime", ApplicationPkcs7MimeBodyPartFormatter::create());
172 insert("application", "x-pkcs7-mime", ApplicationPkcs7MimeBodyPartFormatter::create());
173 insert("application", "pgp-encrypted", ApplicationPGPEncryptedBodyPartFormatter::create());
174 insert("application", "*", AnyTypeBodyPartFormatter::create());
175
176 insert("text", "html", TextHtmlBodyPartFormatter::create());
177 insert("text", "rtf", AnyTypeBodyPartFormatter::create());
178 insert("text", "plain", MailmanBodyPartFormatter::create());
179 insert("text", "plain", TextPlainBodyPartFormatter::create());
180 insert("text", "*", MailmanBodyPartFormatter::create());
181 insert("text", "*", TextPlainBodyPartFormatter::create());
182
183 insert("image", "*", ImageTypeBodyPartFormatter::create());
184
185 insert("message", "rfc822", MessageRfc822BodyPartFormatter::create());
186 insert("message", "*", AnyTypeBodyPartFormatter::create());
187
188 insert("multipart", "alternative", MultiPartAlternativeBodyPartFormatter::create());
189 insert("multipart", "encrypted", MultiPartEncryptedBodyPartFormatter::create());
190 insert("multipart", "signed", MultiPartSignedBodyPartFormatter::create());
191 insert("multipart", "*", MultiPartMixedBodyPartFormatter::create());
192 insert("*", "*", AnyTypeBodyPartFormatter::create());
193}
diff --git a/framework/src/domain/mime/mimetreeparser/otp/bodypartformatterbasefactory.cpp b/framework/src/domain/mime/mimetreeparser/otp/bodypartformatterbasefactory.cpp
new file mode 100644
index 00000000..fb02945b
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/bodypartformatterbasefactory.cpp
@@ -0,0 +1,179 @@
1/*
2 bodypartformatterfactory.cpp
3
4 This file is part of KMail, the KDE mail client.
5 Copyright (c) 2004 Marc Mutz <mutz@kde.org>,
6 Ingo Kloecker <kloecker@kde.org>
7
8 KMail is free software; you can redistribute it and/or modify it
9 under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12
13 KMail is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21
22 In addition, as a special exception, the copyright holders give
23 permission to link the code of this program with any edition of
24 the Qt library by Trolltech AS, Norway (or with modified versions
25 of Qt that use the same license as Qt), and distribute linked
26 combinations including the two. You must obey the GNU General
27 Public License in all respects for all of the code used other than
28 Qt. If you modify this file, you may extend this exception to
29 your version of the file, but you are not obligated to do so. If
30 you do not wish to do so, delete this exception statement from
31 your version.
32*/
33
34#include "bodypartformatterbasefactory.h"
35#include "bodypartformatterbasefactory_p.h"
36#include "mimetreeparser_debug.h"
37
38// Qt
39
40#include <assert.h>
41
42using namespace MimeTreeParser;
43
44BodyPartFormatterBaseFactoryPrivate::BodyPartFormatterBaseFactoryPrivate(BodyPartFormatterBaseFactory *factory)
45 : q(factory)
46 , all(nullptr)
47{
48}
49
50BodyPartFormatterBaseFactoryPrivate::~BodyPartFormatterBaseFactoryPrivate()
51{
52 if (all) {
53 delete all;
54 all = nullptr;
55 }
56}
57
58void BodyPartFormatterBaseFactoryPrivate::setup()
59{
60 if (!all) {
61 all = new TypeRegistry();
62 messageviewer_create_builtin_bodypart_formatters();
63 q->loadPlugins();
64 }
65}
66
67void BodyPartFormatterBaseFactoryPrivate::insert(const char *type, const char *subtype, const Interface::BodyPartFormatter *formatter)
68{
69 if (!type || !*type || !subtype || !*subtype || !formatter || !all) {
70 return;
71 }
72
73 TypeRegistry::iterator type_it = all->find(type);
74 if (type_it == all->end()) {
75 qCDebug(MIMETREEPARSER_LOG) << "BodyPartFormatterBaseFactory: instantiating new Subtype Registry for \""
76 << type << "\"";
77 type_it = all->insert(std::make_pair(type, SubtypeRegistry())).first;
78 assert(type_it != all->end());
79 }
80
81 SubtypeRegistry &subtype_reg = type_it->second;
82
83 subtype_reg.insert(std::make_pair(subtype, formatter));
84}
85
86BodyPartFormatterBaseFactory::BodyPartFormatterBaseFactory()
87 : d(new BodyPartFormatterBaseFactoryPrivate(this))
88{
89}
90
91BodyPartFormatterBaseFactory::~BodyPartFormatterBaseFactory()
92{
93 delete d;
94}
95
96void BodyPartFormatterBaseFactory::insert(const char *type, const char *subtype, const Interface::BodyPartFormatter *formatter)
97{
98 d->insert(type, subtype, formatter);
99}
100
101const SubtypeRegistry &BodyPartFormatterBaseFactory::subtypeRegistry(const char *type) const
102{
103 if (!type || !*type) {
104 type = "*"; //krazy:exclude=doublequote_chars
105 }
106
107 d->setup();
108 assert(d->all);
109
110 static SubtypeRegistry emptyRegistry;
111 if (d->all->empty()) {
112 return emptyRegistry;
113 }
114
115 TypeRegistry::const_iterator type_it = d->all->find(type);
116 if (type_it == d->all->end()) {
117 type_it = d->all->find("*");
118 }
119 if (type_it == d->all->end()) {
120 return emptyRegistry;
121 }
122
123 const SubtypeRegistry &subtype_reg = type_it->second;
124 if (subtype_reg.empty()) {
125 return emptyRegistry;
126 }
127 return subtype_reg;
128}
129
130SubtypeRegistry::const_iterator BodyPartFormatterBaseFactory::createForIterator(const char *type, const char *subtype) const
131{
132 if (!type || !*type) {
133 type = "*"; //krazy:exclude=doublequote_chars
134 }
135 if (!subtype || !*subtype) {
136 subtype = "*"; //krazy:exclude=doublequote_chars
137 }
138
139 d->setup();
140 assert(d->all);
141
142 if (d->all->empty()) {
143 return SubtypeRegistry::const_iterator();
144 }
145
146 TypeRegistry::const_iterator type_it = d->all->find(type);
147 if (type_it == d->all->end()) {
148 type_it = d->all->find("*");
149 }
150 if (type_it == d->all->end()) {
151 return SubtypeRegistry::const_iterator();
152 }
153
154 const SubtypeRegistry &subtype_reg = type_it->second;
155 if (subtype_reg.empty()) {
156 return SubtypeRegistry::const_iterator();
157 }
158
159 SubtypeRegistry::const_iterator subtype_it = subtype_reg.find(subtype);
160 qCWarning(MIMETREEPARSER_LOG) << type << subtype << subtype_reg.size();
161 if (subtype_it == subtype_reg.end()) {
162 subtype_it = subtype_reg.find("*");
163 }
164 if (subtype_it == subtype_reg.end()) {
165 return SubtypeRegistry::const_iterator();
166 }
167
168 if (!(*subtype_it).second) {
169 qCWarning(MIMETREEPARSER_LOG) << "BodyPartFormatterBaseFactory: a null bodypart formatter sneaked in for \""
170 << type << "/" << subtype << "\"!";
171 }
172
173 return subtype_it;
174}
175
176void BodyPartFormatterBaseFactory::loadPlugins()
177{
178 qCDebug(MIMETREEPARSER_LOG) << "plugin loading is not enabled in libmimetreeparser";
179}
diff --git a/framework/src/domain/mime/mimetreeparser/otp/bodypartformatterbasefactory.h b/framework/src/domain/mime/mimetreeparser/otp/bodypartformatterbasefactory.h
new file mode 100644
index 00000000..2bba551d
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/bodypartformatterbasefactory.h
@@ -0,0 +1,85 @@
1/*
2 bodypartformatterfactory.h
3
4 This file is part of KMail, the KDE mail client.
5 Copyright (c) 2004 Marc Mutz <mutz@kde.org>,
6 Ingo Kloecker <kloecker@kde.org>
7
8 KMail is free software; you can redistribute it and/or modify it
9 under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12
13 KMail is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21
22 In addition, as a special exception, the copyright holders give
23 permission to link the code of this program with any edition of
24 the Qt library by Trolltech AS, Norway (or with modified versions
25 of Qt that use the same license as Qt), and distribute linked
26 combinations including the two. You must obey the GNU General
27 Public License in all respects for all of the code used other than
28 Qt. If you modify this file, you may extend this exception to
29 your version of the file, but you are not obligated to do so. If
30 you do not wish to do so, delete this exception statement from
31 your version.
32*/
33
34#ifndef __MIMETREEPARSER_BODYPARTFORMATTERBASEFACTORY_H__
35#define __MIMETREEPARSER_BODYPARTFORMATTERBASEFACTORY_H__
36
37#include <map>
38#include <QByteArray>
39
40namespace MimeTreeParser
41{
42
43namespace Interface
44{
45class BodyPartFormatter;
46}
47
48struct ltstr {
49 bool operator()(const char *s1, const char *s2) const
50 {
51 return qstricmp(s1, s2) < 0;
52 }
53};
54
55typedef std::multimap<const char *, const Interface::BodyPartFormatter *, ltstr> SubtypeRegistry;
56typedef std::map<const char *, MimeTreeParser::SubtypeRegistry, MimeTreeParser::ltstr> TypeRegistry;
57
58class BodyPartFormatterBaseFactoryPrivate;
59
60class BodyPartFormatterBaseFactory
61{
62public:
63 BodyPartFormatterBaseFactory();
64 virtual ~BodyPartFormatterBaseFactory();
65
66 SubtypeRegistry::const_iterator createForIterator(const char *type, const char *subtype) const;
67 const SubtypeRegistry &subtypeRegistry(const char *type) const;
68
69protected:
70 void insert(const char *type, const char *subtype, const Interface::BodyPartFormatter *formatter);
71 virtual void loadPlugins();
72private:
73 static BodyPartFormatterBaseFactory *mSelf;
74
75 BodyPartFormatterBaseFactoryPrivate *d;
76 friend class BodyPartFormatterBaseFactoryPrivate;
77private:
78 // disabled
79 const BodyPartFormatterBaseFactory &operator=(const BodyPartFormatterBaseFactory &);
80 BodyPartFormatterBaseFactory(const BodyPartFormatterBaseFactory &);
81};
82
83}
84
85#endif // __MIMETREEPARSER_BODYPARTFORMATTERFACTORY_H__
diff --git a/framework/src/domain/mime/mimetreeparser/otp/bodypartformatterbasefactory_p.h b/framework/src/domain/mime/mimetreeparser/otp/bodypartformatterbasefactory_p.h
new file mode 100644
index 00000000..1f71f183
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/bodypartformatterbasefactory_p.h
@@ -0,0 +1,57 @@
1/* -*- mode: C++; c-file-style: "gnu" -*-
2 bodypartformatterfactory.h
3
4 This file is part of KMail, the KDE mail client.
5 Copyright (c) 2004 Marc Mutz <mutz@kde.org>,
6 Ingo Kloecker <kloecker@kde.org>
7
8 KMail is free software; you can redistribute it and/or modify it
9 under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12
13 KMail is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21
22 In addition, as a special exception, the copyright holders give
23 permission to link the code of this program with any edition of
24 the Qt library by Trolltech AS, Norway (or with modified versions
25 of Qt that use the same license as Qt), and distribute linked
26 combinations including the two. You must obey the GNU General
27 Public License in all respects for all of the code used other than
28 Qt. If you modify this file, you may extend this exception to
29 your version of the file, but you are not obligated to do so. If
30 you do not wish to do so, delete this exception statement from
31 your version.
32*/
33
34#ifndef __MIMETREEPARSER_BODYPARTFORMATTERBASEFACTORY_P_H__
35#define __MIMETREEPARSER_BODYPARTFORMATTERBASEFACTORY_P_H__
36
37namespace MimeTreeParser
38{
39class BodyPartFormatterBaseFactory;
40
41class BodyPartFormatterBaseFactoryPrivate
42{
43public:
44 BodyPartFormatterBaseFactoryPrivate(BodyPartFormatterBaseFactory *factory);
45 ~BodyPartFormatterBaseFactoryPrivate();
46
47 void setup();
48 void messageviewer_create_builtin_bodypart_formatters(); //defined in bodypartformatter.cpp
49 void insert(const char *type, const char *subtype, const Interface::BodyPartFormatter *formatter);
50
51 BodyPartFormatterBaseFactory *q;
52 TypeRegistry *all;
53};
54
55}
56
57#endif // __MIMETREEPARSER_BODYPARTFORMATTERFACTORY_P_H__
diff --git a/framework/src/domain/mime/mimetreeparser/otp/cryptobodypartmemento.cpp b/framework/src/domain/mime/mimetreeparser/otp/cryptobodypartmemento.cpp
new file mode 100644
index 00000000..a884ec36
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/cryptobodypartmemento.cpp
@@ -0,0 +1,56 @@
1/*
2 Copyright (c) 2014-2017 Montel Laurent <montel@kde.org>
3
4 This program is free software; you can redistribute it and/or modify it
5 under the terms of the GNU General Public License, version 2, as
6 published by the Free Software Foundation.
7
8 This program is distributed in the hope that it will be useful, but
9 WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 General Public License for more details.
12
13 You should have received a copy of the GNU General Public License along
14 with this program; if not, write to the Free Software Foundation, Inc.,
15 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16*/
17
18#include "cryptobodypartmemento.h"
19
20using namespace GpgME;
21using namespace MimeTreeParser;
22
23CryptoBodyPartMemento::CryptoBodyPartMemento()
24 : QObject(nullptr),
25 Interface::BodyPartMemento(),
26 m_running(false)
27{
28
29}
30
31CryptoBodyPartMemento::~CryptoBodyPartMemento()
32{
33
34}
35
36bool CryptoBodyPartMemento::isRunning() const
37{
38 return m_running;
39}
40
41void CryptoBodyPartMemento::setAuditLog(const Error &err, const QString &log)
42{
43 m_auditLogError = err;
44 m_auditLog = log;
45}
46
47void CryptoBodyPartMemento::setRunning(bool running)
48{
49 m_running = running;
50}
51
52void CryptoBodyPartMemento::detach()
53{
54 disconnect(this, SIGNAL(update(MimeTreeParser::UpdateMode)), nullptr, nullptr);
55}
56
diff --git a/framework/src/domain/mime/mimetreeparser/otp/cryptobodypartmemento.h b/framework/src/domain/mime/mimetreeparser/otp/cryptobodypartmemento.h
new file mode 100644
index 00000000..076ed890
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/cryptobodypartmemento.h
@@ -0,0 +1,75 @@
1/*
2 Copyright (c) 2014-2016 Montel Laurent <montel@kde.org>
3
4 This program is free software; you can redistribute it and/or modify it
5 under the terms of the GNU General Public License, version 2, as
6 published by the Free Software Foundation.
7
8 This program is distributed in the hope that it will be useful, but
9 WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 General Public License for more details.
12
13 You should have received a copy of the GNU General Public License along
14 with this program; if not, write to the Free Software Foundation, Inc.,
15 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16*/
17
18#ifndef __MIMETREEPARSER_CRYPTOBODYPARTMEMENTO_H__
19#define __MIMETREEPARSER_CRYPTOBODYPARTMEMENTO_H__
20
21#include <gpgme++/error.h>
22
23#include <QObject>
24#include <QString>
25
26#include "bodypart.h"
27#include "enums.h"
28
29namespace MimeTreeParser
30{
31
32class CryptoBodyPartMemento
33 : public QObject,
34 public Interface::BodyPartMemento
35{
36 Q_OBJECT
37public:
38 CryptoBodyPartMemento();
39 ~CryptoBodyPartMemento();
40
41 virtual bool start() = 0;
42 virtual void exec() = 0;
43 bool isRunning() const;
44
45 const QString &auditLogAsHtml() const
46 {
47 return m_auditLog;
48 }
49 GpgME::Error auditLogError() const
50 {
51 return m_auditLogError;
52 }
53
54 void detach() Q_DECL_OVERRIDE;
55
56Q_SIGNALS:
57 void update(MimeTreeParser::UpdateMode);
58
59protected Q_SLOTS:
60 void notify()
61 {
62 Q_EMIT update(MimeTreeParser::Force);
63 }
64
65protected:
66 void setAuditLog(const GpgME::Error &err, const QString &log);
67 void setRunning(bool running);
68
69private:
70 bool m_running;
71 QString m_auditLog;
72 GpgME::Error m_auditLogError;
73};
74}
75#endif // __MIMETREEPARSER_CRYPTOBODYPARTMEMENTO_H__
diff --git a/framework/src/domain/mime/mimetreeparser/otp/cryptohelper.cpp b/framework/src/domain/mime/mimetreeparser/otp/cryptohelper.cpp
new file mode 100644
index 00000000..8e5df576
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/cryptohelper.cpp
@@ -0,0 +1,150 @@
1/*
2 Copyright (C) 2015 Sandro Knauß <knauss@kolabsys.com>
3 Copyright (C) 2001,2002 the KPGP authors
4 See file AUTHORS.kpgp for details
5
6 Kmail is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 You should have received a copy of the GNU General Public License
12 along with this program; if not, write to the Free Software Foundation,
13 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
14 */
15
16#include "cryptohelper.h"
17
18using namespace MimeTreeParser;
19
20PGPBlockType Block::determineType() const
21{
22 const QByteArray data = text();
23 if (data.startsWith("-----BEGIN PGP SIGNED")) {
24 return ClearsignedBlock;
25 } else if (data.startsWith("-----BEGIN PGP SIGNATURE")) {
26 return SignatureBlock;
27 } else if (data.startsWith("-----BEGIN PGP PUBLIC")) {
28 return PublicKeyBlock;
29 } else if (data.startsWith("-----BEGIN PGP PRIVATE")
30 || data.startsWith("-----BEGIN PGP SECRET")) {
31 return PrivateKeyBlock;
32 } else if (data.startsWith("-----BEGIN PGP MESSAGE")) {
33 if (data.startsWith("-----BEGIN PGP MESSAGE PART")) {
34 return MultiPgpMessageBlock;
35 } else {
36 return PgpMessageBlock;
37 }
38 } else if (data.startsWith("-----BEGIN PGP ARMORED FILE")) {
39 return PgpMessageBlock;
40 } else if (data.startsWith("-----BEGIN PGP ")) {
41 return UnknownBlock;
42 } else {
43 return NoPgpBlock;
44 }
45}
46
47QList<Block> MimeTreeParser::prepareMessageForDecryption(const QByteArray &msg)
48{
49 PGPBlockType pgpBlock = NoPgpBlock;
50 QList<Block> blocks;
51 int start = -1; // start of the current PGP block
52 int lastEnd = -1; // end of the last PGP block
53 const int length = msg.length();
54
55 if (msg.isEmpty()) {
56 return blocks;
57 }
58
59 if (msg.startsWith("-----BEGIN PGP ")) {
60 start = 0;
61 } else {
62 start = msg.indexOf("\n-----BEGIN PGP ") + 1;
63 if (start == 0) {
64 blocks.append(Block(msg, NoPgpBlock));
65 return blocks;
66 }
67 }
68
69 while (start != -1) {
70 int nextEnd, nextStart;
71
72 // is the PGP block a clearsigned block?
73 if (!strncmp(msg.constData() + start + 15, "SIGNED", 6)) {
74 pgpBlock = ClearsignedBlock;
75 } else {
76 pgpBlock = UnknownBlock;
77 }
78
79 nextEnd = msg.indexOf("\n-----END PGP ", start + 15);
80 nextStart = msg.indexOf("\n-----BEGIN PGP ", start + 15);
81
82 if (nextEnd == -1) { // Missing END PGP line
83 if (lastEnd != -1) {
84 blocks.append(Block(msg.mid(lastEnd + 1), UnknownBlock));
85 } else {
86 blocks.append(Block(msg.mid(start), UnknownBlock));
87 }
88 break;
89 }
90
91 if ((nextStart == -1) || (nextEnd < nextStart) || (pgpBlock == ClearsignedBlock)) {
92 // most likely we found a PGP block (but we don't check if it's valid)
93
94 // store the preceding non-PGP block
95 if (start - lastEnd - 1 > 0) {
96 blocks.append(Block(msg.mid(lastEnd + 1, start - lastEnd - 1), NoPgpBlock));
97 }
98
99 lastEnd = msg.indexOf("\n", nextEnd + 14);
100 if (lastEnd == -1) {
101 if (start < length) {
102 blocks.append(Block(msg.mid(start)));
103 }
104 break;
105 } else {
106 blocks.append(Block(msg.mid(start, lastEnd + 1 - start)));
107 if ((nextStart != -1) && (nextEnd > nextStart)) {
108 nextStart = msg.indexOf("\n-----BEGIN PGP ", lastEnd + 1);
109 }
110 }
111 }
112
113 start = nextStart;
114
115 if (start == -1) {
116 if (lastEnd + 1 < length) {
117 //rest of mail is no PGP Block
118 blocks.append(Block(msg.mid(lastEnd + 1), NoPgpBlock));
119 }
120 break;
121 } else {
122 start++; // move start behind the '\n'
123 }
124 }
125
126 return blocks;
127}
128
129Block::Block(const QByteArray &m)
130 : msg(m)
131{
132 mType = determineType();
133}
134
135Block::Block(const QByteArray &m, PGPBlockType t)
136 : msg(m)
137 , mType(t)
138{
139
140}
141
142QByteArray MimeTreeParser::Block::text() const
143{
144 return msg;
145}
146
147PGPBlockType Block::type() const
148{
149 return mType;
150}
diff --git a/framework/src/domain/mime/mimetreeparser/otp/cryptohelper.h b/framework/src/domain/mime/mimetreeparser/otp/cryptohelper.h
new file mode 100644
index 00000000..f09771c3
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/cryptohelper.h
@@ -0,0 +1,62 @@
1/*
2 cryptohelper.h
3
4 Copyright (C) 2015 Sandro Knauß <knauss@kolabsys.com>
5 Copyright (C) 2001,2002 the KPGP authors
6 See file AUTHORS.kpgp for details
7
8 KMail is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12
13 You should have received a copy of the GNU General Public License
14 along with this program; if not, write to the Free Software Foundation,
15 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
16 */
17
18#ifndef __MIMETREEPARSER_CRYPTOHELPER_H__
19#define __MIMETREEPARSER_CRYPTOHELPER_H__
20
21#include <QByteArray>
22#include <QList>
23
24namespace MimeTreeParser
25{
26
27enum PGPBlockType {
28 UnknownBlock = -1, // BEGIN PGP ???
29 NoPgpBlock = 0,
30 PgpMessageBlock = 1, // BEGIN PGP MESSAGE
31 MultiPgpMessageBlock = 2, // BEGIN PGP MESSAGE, PART X[/Y]
32 SignatureBlock = 3, // BEGIN PGP SIGNATURE
33 ClearsignedBlock = 4, // BEGIN PGP SIGNED MESSAGE
34 PublicKeyBlock = 5, // BEGIN PGP PUBLIC KEY BLOCK
35 PrivateKeyBlock = 6 // BEGIN PGP PRIVATE KEY BLOCK (PGP 2.x: ...SECRET...)
36};
37
38class Block
39{
40public:
41 Block(const QByteArray &m);
42
43 Block(const QByteArray &m, PGPBlockType t);
44
45 QByteArray text() const;
46 PGPBlockType type() const;
47 PGPBlockType determineType() const;
48
49 QByteArray msg;
50 PGPBlockType mType;
51};
52
53/** Parses the given message and splits it into OpenPGP blocks and
54 Non-OpenPGP blocks.
55*/
56QList<Block> prepareMessageForDecryption(const QByteArray &msg);
57
58} // namespace MimeTreeParser
59
60Q_DECLARE_TYPEINFO(MimeTreeParser::Block, Q_MOVABLE_TYPE);
61
62#endif
diff --git a/framework/src/domain/mime/mimetreeparser/otp/decryptverifybodypartmemento.cpp b/framework/src/domain/mime/mimetreeparser/otp/decryptverifybodypartmemento.cpp
new file mode 100644
index 00000000..9810797a
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/decryptverifybodypartmemento.cpp
@@ -0,0 +1,86 @@
1/*
2 Copyright (c) 2014-2017 Montel Laurent <montel@kde.org>
3
4 This program is free software; you can redistribute it and/or modify it
5 under the terms of the GNU General Public License, version 2, as
6 published by the Free Software Foundation.
7
8 This program is distributed in the hope that it will be useful, but
9 WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 General Public License for more details.
12
13 You should have received a copy of the GNU General Public License along
14 with this program; if not, write to the Free Software Foundation, Inc.,
15 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16*/
17
18#include "decryptverifybodypartmemento.h"
19
20#include <QGpgME/DecryptVerifyJob>
21
22#include <qstringlist.h>
23
24using namespace QGpgME;
25using namespace GpgME;
26using namespace MimeTreeParser;
27
28DecryptVerifyBodyPartMemento::DecryptVerifyBodyPartMemento(DecryptVerifyJob *job, const QByteArray &cipherText)
29 : CryptoBodyPartMemento(),
30 m_cipherText(cipherText),
31 m_job(job)
32{
33 Q_ASSERT(m_job);
34}
35
36DecryptVerifyBodyPartMemento::~DecryptVerifyBodyPartMemento()
37{
38 if (m_job) {
39 m_job->slotCancel();
40 }
41}
42
43bool DecryptVerifyBodyPartMemento::start()
44{
45 Q_ASSERT(m_job);
46 if (const Error err = m_job->start(m_cipherText)) {
47 m_dr = DecryptionResult(err);
48 return false;
49 }
50 connect(m_job.data(), &DecryptVerifyJob::result,
51 this, &DecryptVerifyBodyPartMemento::slotResult);
52 setRunning(true);
53 return true;
54}
55
56void DecryptVerifyBodyPartMemento::exec()
57{
58 Q_ASSERT(m_job);
59 QByteArray plainText;
60 setRunning(true);
61 const std::pair<DecryptionResult, VerificationResult> p = m_job->exec(m_cipherText, plainText);
62 saveResult(p.first, p.second, plainText);
63 m_job->deleteLater(); // exec'ed jobs don't delete themselves
64 m_job = nullptr;
65}
66
67void DecryptVerifyBodyPartMemento::saveResult(const DecryptionResult &dr,
68 const VerificationResult &vr,
69 const QByteArray &plainText)
70{
71 Q_ASSERT(m_job);
72 setRunning(false);
73 m_dr = dr;
74 m_vr = vr;
75 m_plainText = plainText;
76 setAuditLog(m_job->auditLogError(), m_job->auditLogAsHtml());
77}
78
79void DecryptVerifyBodyPartMemento::slotResult(const DecryptionResult &dr,
80 const VerificationResult &vr,
81 const QByteArray &plainText)
82{
83 saveResult(dr, vr, plainText);
84 m_job = nullptr;
85 notify();
86}
diff --git a/framework/src/domain/mime/mimetreeparser/otp/decryptverifybodypartmemento.h b/framework/src/domain/mime/mimetreeparser/otp/decryptverifybodypartmemento.h
new file mode 100644
index 00000000..4781abe2
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/decryptverifybodypartmemento.h
@@ -0,0 +1,81 @@
1/*
2 Copyright (c) 2014-2016 Montel Laurent <montel@kde.org>
3
4 This program is free software; you can redistribute it and/or modify it
5 under the terms of the GNU General Public License, version 2, as
6 published by the Free Software Foundation.
7
8 This program is distributed in the hope that it will be useful, but
9 WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 General Public License for more details.
12
13 You should have received a copy of the GNU General Public License along
14 with this program; if not, write to the Free Software Foundation, Inc.,
15 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16*/
17
18#ifndef __MIMETREEPARSER_DECRYPTVERIFYBODYPARTMEMENTO_H__
19#define __MIMETREEPARSER_DECRYPTVERIFYBODYPARTMEMENTO_H__
20
21#include "cryptobodypartmemento.h"
22
23#include <gpgme++/verificationresult.h>
24#include <gpgme++/decryptionresult.h>
25
26#include <QPointer>
27
28#include "bodypart.h"
29
30namespace QGpgME
31{
32class DecryptVerifyJob;
33}
34
35namespace MimeTreeParser
36{
37
38class DecryptVerifyBodyPartMemento
39 : public CryptoBodyPartMemento
40{
41 Q_OBJECT
42public:
43 DecryptVerifyBodyPartMemento(QGpgME::DecryptVerifyJob *job, const QByteArray &cipherText);
44 ~DecryptVerifyBodyPartMemento();
45
46 bool start() Q_DECL_OVERRIDE;
47 void exec() Q_DECL_OVERRIDE;
48
49 const QByteArray &plainText() const
50 {
51 return m_plainText;
52 }
53 const GpgME::DecryptionResult &decryptResult() const
54 {
55 return m_dr;
56 }
57 const GpgME::VerificationResult &verifyResult() const
58 {
59 return m_vr;
60 }
61
62private Q_SLOTS:
63 void slotResult(const GpgME::DecryptionResult &dr,
64 const GpgME::VerificationResult &vr,
65 const QByteArray &plainText);
66
67private:
68 void saveResult(const GpgME::DecryptionResult &,
69 const GpgME::VerificationResult &,
70 const QByteArray &);
71private:
72 // input:
73 const QByteArray m_cipherText;
74 QPointer<QGpgME::DecryptVerifyJob> m_job;
75 // output:
76 GpgME::DecryptionResult m_dr;
77 GpgME::VerificationResult m_vr;
78 QByteArray m_plainText;
79};
80}
81#endif // __MIMETREEPARSER_DECRYPTVERIFYBODYPARTMEMENTO_H__
diff --git a/framework/src/domain/mime/mimetreeparser/otp/enums.h b/framework/src/domain/mime/mimetreeparser/otp/enums.h
new file mode 100644
index 00000000..bec5a028
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/enums.h
@@ -0,0 +1,54 @@
1/*
2 Copyright (c) 2016 Sandro Knauß <sknauss@kde.org>
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License along
15 with this program; if not, write to the Free Software Foundation, Inc.,
16 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17*/
18
19#ifndef __MIMETREEPARSER_ENUMS_H__
20#define __MIMETREEPARSER_ENUMS_H__
21
22namespace MimeTreeParser
23{
24
25/**
26 * The display update mode: Force updates the display immediately, Delayed updates
27 * after some time (150ms by default)
28 */
29enum UpdateMode {
30 Force = 0,
31 Delayed
32};
33
34/** Flags for the encryption state. */
35typedef enum {
36 KMMsgEncryptionStateUnknown = ' ',
37 KMMsgNotEncrypted = 'N',
38 KMMsgPartiallyEncrypted = 'P',
39 KMMsgFullyEncrypted = 'F',
40 KMMsgEncryptionProblematic = 'X'
41} KMMsgEncryptionState;
42
43/** Flags for the signature state. */
44typedef enum {
45 KMMsgSignatureStateUnknown = ' ',
46 KMMsgNotSigned = 'N',
47 KMMsgPartiallySigned = 'P',
48 KMMsgFullySigned = 'F',
49 KMMsgSignatureProblematic = 'X'
50} KMMsgSignatureState;
51
52}
53
54#endif
diff --git a/framework/src/domain/mime/mimetreeparser/otp/filehtmlwriter.cpp b/framework/src/domain/mime/mimetreeparser/otp/filehtmlwriter.cpp
new file mode 100644
index 00000000..a143f944
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/filehtmlwriter.cpp
@@ -0,0 +1,119 @@
1/* -*- c++ -*-
2 filehtmlwriter.cpp
3
4 This file is part of KMail, the KDE mail client.
5 Copyright (c) 2003 Marc Mutz <mutz@kde.org>
6
7 KMail is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License, version 2, as
9 published by the Free Software Foundation.
10
11 KMail is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
20 In addition, as a special exception, the copyright holders give
21 permission to link the code of this program with any edition of
22 the Qt library by Trolltech AS, Norway (or with modified versions
23 of Qt that use the same license as Qt), and distribute linked
24 combinations including the two. You must obey the GNU General
25 Public License in all respects for all of the code used other than
26 Qt. If you modify this file, you may extend this exception to
27 your version of the file, but you are not obligated to do so. If
28 you do not wish to do so, delete this exception statement from
29 your version.
30*/
31
32#include "filehtmlwriter.h"
33
34#include "mimetreeparser_debug.h"
35
36namespace MimeTreeParser
37{
38
39FileHtmlWriter::FileHtmlWriter(const QString &filename)
40 : HtmlWriter(),
41 mFile(filename.isEmpty() ? QStringLiteral("filehtmlwriter.out") : filename)
42{
43}
44
45FileHtmlWriter::~FileHtmlWriter()
46{
47 if (mFile.isOpen()) {
48 qCWarning(MIMETREEPARSER_LOG) << "FileHtmlWriter: file still open!";
49 mStream.setDevice(nullptr);
50 mFile.close();
51 }
52}
53
54void FileHtmlWriter::begin(const QString &css)
55{
56 openOrWarn();
57 if (!css.isEmpty()) {
58 write(QLatin1String("<!-- CSS Definitions \n") + css + QLatin1String("-->\n"));
59 }
60}
61
62void FileHtmlWriter::end()
63{
64 flush();
65 mStream.setDevice(nullptr);
66 mFile.close();
67}
68
69void FileHtmlWriter::reset()
70{
71 if (mFile.isOpen()) {
72 mStream.setDevice(nullptr);
73 mFile.close();
74 }
75}
76
77void FileHtmlWriter::write(const QString &str)
78{
79 mStream << str;
80 flush();
81}
82
83void FileHtmlWriter::queue(const QString &str)
84{
85 write(str);
86}
87
88void FileHtmlWriter::flush()
89{
90 mStream.flush();
91 mFile.flush();
92}
93
94void FileHtmlWriter::openOrWarn()
95{
96 if (mFile.isOpen()) {
97 qCWarning(MIMETREEPARSER_LOG) << "FileHtmlWriter: file still open!";
98 mStream.setDevice(nullptr);
99 mFile.close();
100 }
101 if (!mFile.open(QIODevice::WriteOnly)) {
102 qCWarning(MIMETREEPARSER_LOG) << "FileHtmlWriter: Cannot open file" << mFile.fileName();
103 } else {
104 mStream.setDevice(&mFile);
105 mStream.setCodec("UTF-8");
106 }
107}
108
109void FileHtmlWriter::embedPart(const QByteArray &contentId, const QString &url)
110{
111 mStream << "<!-- embedPart(contentID=" << contentId << ", url=" << url << ") -->" << endl;
112 flush();
113}
114void FileHtmlWriter::extraHead(const QString &)
115{
116
117}
118
119} //
diff --git a/framework/src/domain/mime/mimetreeparser/otp/filehtmlwriter.h b/framework/src/domain/mime/mimetreeparser/otp/filehtmlwriter.h
new file mode 100644
index 00000000..5dafb593
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/filehtmlwriter.h
@@ -0,0 +1,70 @@
1/* -*- c++ -*-
2 filehtmlwriter.h
3
4 This file is part of KMail, the KDE mail client.
5 Copyright (c) 2003 Marc Mutz <mutz@kde.org>
6
7 KMail is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License, version 2, as
9 published by the Free Software Foundation.
10
11 KMail is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
20 In addition, as a special exception, the copyright holders give
21 permission to link the code of this program with any edition of
22 the Qt library by Trolltech AS, Norway (or with modified versions
23 of Qt that use the same license as Qt), and distribute linked
24 combinations including the two. You must obey the GNU General
25 Public License in all respects for all of the code used other than
26 Qt. If you modify this file, you may extend this exception to
27 your version of the file, but you are not obligated to do so. If
28 you do not wish to do so, delete this exception statement from
29 your version.
30*/
31
32#ifndef __MIMETREEPARSER_FILEHTMLWRITER_H__
33#define __MIMETREEPARSER_FILEHTMLWRITER_H__
34
35#include "mimetreeparser_export.h"
36#include "mimetreeparser/htmlwriter.h"
37
38#include <QFile>
39#include <QTextStream>
40
41class QString;
42
43namespace MimeTreeParser
44{
45
46class MIMETREEPARSER_EXPORT FileHtmlWriter : public HtmlWriter
47{
48public:
49 explicit FileHtmlWriter(const QString &filename);
50 virtual ~FileHtmlWriter();
51
52 void begin(const QString &cssDefs) Q_DECL_OVERRIDE;
53 void end() Q_DECL_OVERRIDE;
54 void reset() Q_DECL_OVERRIDE;
55 void write(const QString &str) Q_DECL_OVERRIDE;
56 void queue(const QString &str) Q_DECL_OVERRIDE;
57 void flush() Q_DECL_OVERRIDE;
58 void embedPart(const QByteArray &contentId, const QString &url) Q_DECL_OVERRIDE;
59 void extraHead(const QString &str) Q_DECL_OVERRIDE;
60private:
61 void openOrWarn();
62
63private:
64 QFile mFile;
65 QTextStream mStream;
66};
67
68} // namespace MimeTreeParser
69
70#endif // __MIMETREEPARSER_FILEHTMLWRITER_H__
diff --git a/framework/src/domain/mime/mimetreeparser/otp/htmlwriter.cpp b/framework/src/domain/mime/mimetreeparser/otp/htmlwriter.cpp
new file mode 100644
index 00000000..3c98d997
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/htmlwriter.cpp
@@ -0,0 +1,40 @@
1/*
2 This file is part of KMail's plugin interface.
3 Copyright (c) 2003 Marc Mutz <mutz@kde.org>
4
5 KMail is free software; you can redistribute it and/or modify it
6 under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 KMail is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
19 In addition, as a special exception, the copyright holders give
20 permission to link the code of this program with any edition of
21 the Qt library by Trolltech AS, Norway (or with modified versions
22 of Qt that use the same license as Qt), and distribute linked
23 combinations including the two. You must obey the GNU General
24 Public License in all respects for all of the code used other than
25 Qt. If you modify this file, you may extend this exception to
26 your version of the file, but you are not obligated to do so. If
27 you do not wish to do so, delete this exception statement from
28 your version.
29*/
30
31#include "htmlwriter.h"
32
33MimeTreeParser::Interface::HtmlWriter::~HtmlWriter()
34{
35}
36
37MimeTreeParser::HtmlWriter::~HtmlWriter()
38{
39}
40
diff --git a/framework/src/domain/mime/mimetreeparser/otp/htmlwriter.h b/framework/src/domain/mime/mimetreeparser/otp/htmlwriter.h
new file mode 100644
index 00000000..382c80fb
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/htmlwriter.h
@@ -0,0 +1,125 @@
1/* -*- c++ -*-
2 interfaces/htmlwriter.h
3
4 This file is part of KMail's plugin interface.
5 Copyright (c) 2003 Marc Mutz <mutz@kde.org>
6
7 KMail is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 KMail is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20
21 In addition, as a special exception, the copyright holders give
22 permission to link the code of this program with any edition of
23 the Qt library by Trolltech AS, Norway (or with modified versions
24 of Qt that use the same license as Qt), and distribute linked
25 combinations including the two. You must obey the GNU General
26 Public License in all respects for all of the code used other than
27 Qt. If you modify this file, you may extend this exception to
28 your version of the file, but you are not obligated to do so. If
29 you do not wish to do so, delete this exception statement from
30 your version.
31*/
32
33#ifndef __MIMETREEPARSER_INTERFACES_HTMLWRITER_H__
34#define __MIMETREEPARSER_INTERFACES_HTMLWRITER_H__
35
36class QByteArray;
37class QString;
38
39namespace MimeTreeParser
40{
41/**
42 * @short An interface for HTML sinks.
43 * @author Marc Mutz <mutz@kde.org>
44 *
45 */
46namespace Interface
47{
48class HtmlWriter
49{
50public:
51 virtual ~HtmlWriter();
52
53 /** Signal the begin of stuff to write, and give the CSS definitions */
54 virtual void begin(const QString &cssDefinitions) = 0;
55 /** Write out a chunk of text. No HTML escaping is performed. */
56 virtual void write(const QString &html) = 0;
57 /** Signal the end of stuff to write. */
58 virtual void end() = 0;
59};
60}
61
62/**
63 * @short An interface to HTML sinks
64 * @author Marc Mutz <mutz@kde.org>
65 *
66 * @deprecated KMail should be ported to Interface::HtmlWriter. This
67 * interface exposes internal working models. The queuing
68 * vs. writing() issues exposed here should be hidden by using two
69 * different implementations of KHTMLPartHtmlWriter: one for
70 * queuing, and one for writing. This should be fixed before the
71 * release, so we an keep the plugin interface stable.
72 *
73 * Operate this interface in one and only one of the following two
74 * modes:
75 *
76 * @section Sync Mode
77 *
78 * In sync mode, use #begin() to initiate a session, then
79 * #write() some chunks of HTML code and finally #end() the session.
80 *
81 * @section Async Mode
82 *
83 * In async mode, use #begin() to initialize a session, then
84 * #queue() some chunks of HTML code and finally end the
85 * session by calling #flush().
86 *
87 * Queued HTML code is fed to the html sink using a timer. For this
88 * to work, control must return to the event loop so timer events
89 * are delivered.
90 *
91 * @section Combined mode
92 *
93 * You may combine the two modes in the following way only. Any
94 * number of #write() calls can precede #queue() calls,
95 * but once a chunk has been queued, you @em must @em not
96 * #write() more data, only #queue() it.
97 *
98 * Naturally, whenever you queued data in a given session, that
99 * session must be ended by calling #flush(), not #end().
100 */
101class HtmlWriter : public Interface::HtmlWriter
102{
103public:
104 virtual ~HtmlWriter();
105
106 /** Stop all possibly pending processing in order to be able to
107 * call #begin() again. */
108 virtual void reset() = 0;
109
110 virtual void queue(const QString &str) = 0;
111 /** (Start) flushing internal buffers, if any. */
112 virtual void flush() = 0;
113
114 /**
115 * Embed a part with Content-ID @p contentId, using url @p url.
116 */
117 virtual void embedPart(const QByteArray &contentId, const QString &url) = 0;
118
119 virtual void extraHead(const QString &str) = 0;
120};
121
122}
123
124#endif // __MIMETREEPARSER_INTERFACES_HTMLWRITER_H__
125
diff --git a/framework/src/domain/mime/mimetreeparser/otp/mailman.cpp b/framework/src/domain/mime/mimetreeparser/otp/mailman.cpp
new file mode 100644
index 00000000..e79ef0fa
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/mailman.cpp
@@ -0,0 +1,183 @@
1/*
2 Copyright (c) 2016 Sandro Knauß <sknauss@kde.org>
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 "mailman.h"
21
22#include "utils.h"
23
24#include "objecttreeparser.h"
25#include "messagepart.h"
26
27#include <KMime/Content>
28
29#include "mimetreeparser_debug.h"
30
31using namespace MimeTreeParser;
32
33const MailmanBodyPartFormatter *MailmanBodyPartFormatter::self;
34
35const Interface::BodyPartFormatter *MailmanBodyPartFormatter::create()
36{
37 if (!self) {
38 self = new MailmanBodyPartFormatter();
39 }
40 return self;
41}
42Interface::BodyPartFormatter::Result MailmanBodyPartFormatter::format(Interface::BodyPart *part, HtmlWriter *writer) const
43{
44 Q_UNUSED(writer)
45 const auto p = process(*part);
46 const auto mp = static_cast<MessagePart *>(p.data());
47 if (mp) {
48 mp->html(false);
49 return Ok;
50 }
51 return Failed;
52}
53
54bool MailmanBodyPartFormatter::isMailmanMessage(KMime::Content *curNode) const
55{
56 if (!curNode || curNode->head().isEmpty()) {
57 return false;
58 }
59 if (curNode->hasHeader("X-Mailman-Version")) {
60 return true;
61 }
62 if (KMime::Headers::Base *header = curNode->headerByType("X-Mailer")) {
63 if (header->asUnicodeString().contains(QStringLiteral("MAILMAN"), Qt::CaseInsensitive)) {
64 return true;
65 }
66 }
67 return false;
68}
69
70Interface::MessagePart::Ptr MailmanBodyPartFormatter::process(Interface::BodyPart &part) const
71{
72 KMime::Content *curNode = part.content();
73
74 if (!isMailmanMessage(curNode)) {
75 return MessagePart::Ptr();
76 }
77
78 const QString str = QString::fromLatin1(curNode->decodedContent());
79
80 //###
81 const QLatin1String delim1("--__--__--\n\nMessage:");
82 const QLatin1String delim2("--__--__--\r\n\r\nMessage:");
83 const QLatin1String delimZ2("--__--__--\n\n_____________");
84 const QLatin1String delimZ1("--__--__--\r\n\r\n_____________");
85 QString partStr, digestHeaderStr;
86 int thisDelim = str.indexOf(delim1, Qt::CaseInsensitive);
87 if (thisDelim == -1) {
88 thisDelim = str.indexOf(delim2, Qt::CaseInsensitive);
89 }
90 if (thisDelim == -1) {
91 return MessagePart::Ptr();
92 }
93
94 int nextDelim = str.indexOf(delim1, thisDelim + 1, Qt::CaseInsensitive);
95 if (-1 == nextDelim) {
96 nextDelim = str.indexOf(delim2, thisDelim + 1, Qt::CaseInsensitive);
97 }
98 if (-1 == nextDelim) {
99 nextDelim = str.indexOf(delimZ1, thisDelim + 1, Qt::CaseInsensitive);
100 }
101 if (-1 == nextDelim) {
102 nextDelim = str.indexOf(delimZ2, thisDelim + 1, Qt::CaseInsensitive);
103 }
104 if (nextDelim < 0) {
105 return MessagePart::Ptr();
106 }
107
108 //if ( curNode->mRoot )
109 // curNode = curNode->mRoot;
110
111 // at least one message found: build a mime tree
112 digestHeaderStr = QStringLiteral("Content-Type: text/plain\nContent-Description: digest header\n\n");
113 digestHeaderStr += str.midRef(0, thisDelim);
114
115 MessagePartList::Ptr mpl(new MessagePartList(part.objectTreeParser()));
116 mpl->appendSubPart(createAndParseTempNode(part, part.topLevelContent(), digestHeaderStr.toLatin1().constData(), "Digest Header"));
117 //mReader->queueHtml("<br><hr><br>");
118 // temporarily change curent node's Content-Type
119 // to get our embedded RfC822 messages properly inserted
120 curNode->contentType()->setMimeType("multipart/digest");
121 while (-1 < nextDelim) {
122 int thisEoL = str.indexOf(QLatin1String("\nMessage:"), thisDelim, Qt::CaseInsensitive);
123 if (-1 < thisEoL) {
124 thisDelim = thisEoL + 1;
125 } else {
126 thisEoL = str.indexOf(QLatin1String("\n_____________"), thisDelim, Qt::CaseInsensitive);
127 if (-1 < thisEoL) {
128 thisDelim = thisEoL + 1;
129 }
130 }
131 thisEoL = str.indexOf(QLatin1Char('\n'), thisDelim);
132 if (-1 < thisEoL) {
133 thisDelim = thisEoL + 1;
134 } else {
135 thisDelim = thisDelim + 1;
136 }
137 //while( thisDelim < cstr.size() && '\n' == cstr[thisDelim] )
138 // ++thisDelim;
139
140 partStr = QStringLiteral("Content-Type: message/rfc822\nContent-Description: embedded message\n\n");
141 partStr += str.midRef(thisDelim, nextDelim - thisDelim);
142 QString subject = QStringLiteral("embedded message");
143 QString subSearch = QStringLiteral("\nSubject:");
144 int subPos = partStr.indexOf(subSearch, 0, Qt::CaseInsensitive);
145 if (-1 < subPos) {
146 subject = partStr.mid(subPos + subSearch.length());
147 thisEoL = subject.indexOf(QLatin1Char('\n'));
148 if (-1 < thisEoL) {
149 subject.truncate(thisEoL);
150 }
151 }
152 qCDebug(MIMETREEPARSER_LOG) << " embedded message found: \"" << subject;
153 mpl->appendSubPart(createAndParseTempNode(part, part.topLevelContent(), partStr.toLatin1().constData(), subject.toLatin1().constData()));
154 //mReader->queueHtml("<br><hr><br>");
155 thisDelim = nextDelim + 1;
156 nextDelim = str.indexOf(delim1, thisDelim, Qt::CaseInsensitive);
157 if (-1 == nextDelim) {
158 nextDelim = str.indexOf(delim2, thisDelim, Qt::CaseInsensitive);
159 }
160 if (-1 == nextDelim) {
161 nextDelim = str.indexOf(delimZ1, thisDelim, Qt::CaseInsensitive);
162 }
163 if (-1 == nextDelim) {
164 nextDelim = str.indexOf(delimZ2, thisDelim, Qt::CaseInsensitive);
165 }
166 }
167 // reset curent node's Content-Type
168 curNode->contentType()->setMimeType("text/plain");
169 int thisEoL = str.indexOf(QLatin1String("_____________"), thisDelim);
170 if (-1 < thisEoL) {
171 thisDelim = thisEoL;
172 thisEoL = str.indexOf(QLatin1Char('\n'), thisDelim);
173 if (-1 < thisEoL) {
174 thisDelim = thisEoL + 1;
175 }
176 } else {
177 thisDelim = thisDelim + 1;
178 }
179 partStr = QStringLiteral("Content-Type: text/plain\nContent-Description: digest footer\n\n");
180 partStr += str.midRef(thisDelim);
181 mpl->appendSubPart(createAndParseTempNode(part, part.topLevelContent(), partStr.toLatin1().constData(), "Digest Footer"));
182 return mpl;
183}
diff --git a/framework/src/domain/mime/mimetreeparser/otp/mailman.h b/framework/src/domain/mime/mimetreeparser/otp/mailman.h
new file mode 100644
index 00000000..742830b2
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/mailman.h
@@ -0,0 +1,44 @@
1/*
2 Copyright (c) 2016 Sandro Knauß <sknauss@kde.org>
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#ifndef __MIMETREEPARSER_BODYFORAMATTER_MAILMAN_H__
21#define __MIMETREEPARSER_BODYFORAMATTER_MAILMAN_H__
22
23#include "bodypartformatter.h"
24#include "bodypart.h"
25
26namespace MimeTreeParser
27{
28
29class MailmanBodyPartFormatter : public Interface::BodyPartFormatter
30{
31 static const MailmanBodyPartFormatter *self;
32public:
33 Interface::MessagePart::Ptr process(Interface::BodyPart &part) const Q_DECL_OVERRIDE;
34 Interface::BodyPartFormatter::Result format(Interface::BodyPart *, HtmlWriter *) const Q_DECL_OVERRIDE;
35 using Interface::BodyPartFormatter::format;
36 static const Interface::BodyPartFormatter *create();
37
38private:
39 bool isMailmanMessage(KMime::Content *curNode) const;
40};
41
42}
43
44#endif
diff --git a/framework/src/domain/mime/mimetreeparser/otp/messagepart.cpp b/framework/src/domain/mime/mimetreeparser/otp/messagepart.cpp
new file mode 100644
index 00000000..3228a387
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/messagepart.cpp
@@ -0,0 +1,1352 @@
1/*
2 Copyright (c) 2015 Sandro Knauß <sknauss@kde.org>
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 "messagepart.h"
21#include "mimetreeparser_debug.h"
22#include "attachmentstrategy.h"
23#include "cryptohelper.h"
24#include "objecttreeparser.h"
25#include "htmlwriter.h"
26#include "qgpgmejobexecutor.h"
27
28#include "cryptobodypartmemento.h"
29#include "decryptverifybodypartmemento.h"
30#include "verifydetachedbodypartmemento.h"
31#include "verifyopaquebodypartmemento.h"
32
33#include "utils.h"
34
35#include <KMime/Content>
36
37#include <QGpgME/DN>
38#include <QGpgME/Protocol>
39#include <QGpgME/ImportJob>
40#include <QGpgME/KeyListJob>
41#include <QGpgME/VerifyDetachedJob>
42#include <QGpgME/VerifyOpaqueJob>
43
44#include <gpgme++/key.h>
45#include <gpgme++/keylistresult.h>
46#include <gpgme.h>
47
48#include <KLocalizedString>
49
50#include <QTextCodec>
51
52using namespace MimeTreeParser;
53
54//------MessagePart-----------------------
55MessagePart::MessagePart(ObjectTreeParser *otp,
56 const QString &text)
57 : mText(text)
58 , mOtp(otp)
59 , mAttachmentNode(nullptr)
60 , mRoot(false)
61{
62}
63
64MessagePart::~MessagePart()
65{
66}
67
68PartMetaData *MessagePart::partMetaData()
69{
70 return &mMetaData;
71}
72
73void MessagePart::setAttachmentFlag(KMime::Content *node)
74{
75 mAttachmentNode = node;
76}
77
78bool MessagePart::isAttachment() const
79{
80 return mAttachmentNode;
81}
82
83KMime::Content *MessagePart::attachmentNode() const
84{
85 return mAttachmentNode;
86}
87
88void MessagePart::setIsRoot(bool root)
89{
90 mRoot = root;
91}
92
93bool MessagePart::isRoot() const
94{
95 return mRoot;
96}
97
98QString MessagePart::text() const
99{
100 return mText;
101}
102
103void MessagePart::setText(const QString &text)
104{
105 mText = text;
106}
107
108bool MessagePart::isHtml() const
109{
110 return false;
111}
112
113bool MessagePart::isHidden() const
114{
115 return false;
116}
117
118Interface::ObjectTreeSource *MessagePart::source() const
119{
120 Q_ASSERT(mOtp);
121 return mOtp->mSource;
122}
123
124HtmlWriter *MessagePart::htmlWriter() const
125{
126 Q_ASSERT(mOtp);
127 return mOtp->htmlWriter();
128}
129
130void MessagePart::setHtmlWriter(HtmlWriter *htmlWriter) const
131{
132 mOtp->mHtmlWriter = htmlWriter;
133}
134
135void MessagePart::parseInternal(KMime::Content *node, bool onlyOneMimePart)
136{
137 auto subMessagePart = mOtp->parseObjectTreeInternal(node, onlyOneMimePart);
138 mRoot = subMessagePart->isRoot();
139 foreach (const auto &part, subMessagePart->subParts()) {
140 appendSubPart(part);
141 }
142}
143
144QString MessagePart::renderInternalText() const
145{
146 QString text;
147 foreach (const auto &mp, subParts()) {
148 text += mp->text();
149 }
150 return text;
151}
152
153void MessagePart::copyContentFrom() const
154{
155 foreach (const auto &mp, subParts()) {
156 const auto m = mp.dynamicCast<MessagePart>();
157 if (m) {
158 m->copyContentFrom();
159 }
160 }
161}
162
163void MessagePart::fix() const
164{
165 foreach (const auto &mp, subParts()) {
166 const auto m = mp.dynamicCast<MessagePart>();
167 if (m) {
168 m->fix();
169 }
170 }
171}
172
173void MessagePart::appendSubPart(const Interface::MessagePart::Ptr &messagePart)
174{
175 messagePart->setParentPart(this);
176 mBlocks.append(messagePart);
177}
178
179const QVector<Interface::MessagePart::Ptr> &MessagePart::subParts() const
180{
181 return mBlocks;
182}
183
184bool MessagePart::hasSubParts() const
185{
186 return !mBlocks.isEmpty();
187}
188
189//-----MessagePartList----------------------
190MessagePartList::MessagePartList(ObjectTreeParser *otp)
191 : MessagePart(otp, QString())
192{
193}
194
195MessagePartList::~MessagePartList()
196{
197
198}
199
200QString MessagePartList::text() const
201{
202 return renderInternalText();
203}
204
205QString MessagePartList::plaintextContent() const
206{
207 return QString();
208}
209
210QString MessagePartList::htmlContent() const
211{
212 return QString();
213}
214
215//-----TextMessageBlock----------------------
216
217TextMessagePart::TextMessagePart(ObjectTreeParser *otp, KMime::Content *node, bool drawFrame, bool showLink, bool decryptMessage)
218 : MessagePartList(otp)
219 , mNode(node)
220 , mDrawFrame(drawFrame)
221 , mShowLink(showLink)
222 , mDecryptMessage(decryptMessage)
223 , mIsHidden(false)
224{
225 if (!mNode) {
226 qCWarning(MIMETREEPARSER_LOG) << "not a valid node";
227 return;
228 }
229
230 mIsHidden = mOtp->nodeHelper()->isNodeDisplayedHidden(mNode);
231
232 parseContent();
233}
234
235TextMessagePart::~TextMessagePart()
236{
237
238}
239
240bool TextMessagePart::decryptMessage() const
241{
242 return mDecryptMessage;
243}
244
245void TextMessagePart::parseContent()
246{
247 const auto aCodec = mOtp->codecFor(mNode);
248 const QString &fromAddress = mOtp->nodeHelper()->fromAsString(mNode);
249 mSignatureState = KMMsgNotSigned;
250 mEncryptionState = KMMsgNotEncrypted;
251 const auto blocks = prepareMessageForDecryption(mNode->decodedContent());
252
253 const auto cryptProto = QGpgME::openpgp();
254
255 if (!blocks.isEmpty()) {
256
257 /* The (overall) signature/encrypted status is broken
258 * if one unencrypted part is at the beginning or in the middle
259 * because mailmain adds an unencrypted part at the end this should not break the overall status
260 *
261 * That's why we first set the tmp status and if one crypted/signed block comes afterwards, than
262 * the status is set to unencryped
263 */
264 bool fullySignedOrEncrypted = true;
265 bool fullySignedOrEncryptedTmp = true;
266
267 for (const auto &block : blocks) {
268
269 if (!fullySignedOrEncryptedTmp) {
270 fullySignedOrEncrypted = false;
271 }
272
273 if (block.type() == NoPgpBlock && !block.text().trimmed().isEmpty()) {
274 fullySignedOrEncryptedTmp = false;
275 appendSubPart(MessagePart::Ptr(new MessagePart(mOtp, aCodec->toUnicode(block.text()))));
276 } else if (block.type() == PgpMessageBlock) {
277 EncryptedMessagePart::Ptr mp(new EncryptedMessagePart(mOtp, QString(), cryptProto, fromAddress, nullptr));
278 mp->setDecryptMessage(decryptMessage());
279 mp->setIsEncrypted(true);
280 appendSubPart(mp);
281 if (!decryptMessage()) {
282 continue;
283 }
284 mp->startDecryption(block.text(), aCodec);
285 if (mp->partMetaData()->inProgress) {
286 continue;
287 }
288 } else if (block.type() == ClearsignedBlock) {
289 SignedMessagePart::Ptr mp(new SignedMessagePart(mOtp, QString(), cryptProto, fromAddress, nullptr));
290 appendSubPart(mp);
291 mp->startVerification(block.text(), aCodec);
292 } else {
293 continue;
294 }
295
296 const auto mp = subParts().last().staticCast<MessagePart>();
297 const PartMetaData *messagePart(mp->partMetaData());
298
299 if (!messagePart->isEncrypted && !messagePart->isSigned && !block.text().trimmed().isEmpty()) {
300 mp->setText(aCodec->toUnicode(block.text()));
301 }
302
303 if (messagePart->isEncrypted) {
304 mEncryptionState = KMMsgPartiallyEncrypted;
305 }
306
307 if (messagePart->isSigned) {
308 mSignatureState = KMMsgPartiallySigned;
309 }
310 }
311
312 //Do we have an fully Signed/Encrypted Message?
313 if (fullySignedOrEncrypted) {
314 if (mSignatureState == KMMsgPartiallySigned) {
315 mSignatureState = KMMsgFullySigned;
316 }
317 if (mEncryptionState == KMMsgPartiallyEncrypted) {
318 mEncryptionState = KMMsgFullyEncrypted;
319 }
320 }
321 }
322}
323
324KMMsgEncryptionState TextMessagePart::encryptionState() const
325{
326 return mEncryptionState;
327}
328
329KMMsgSignatureState TextMessagePart::signatureState() const
330{
331 return mSignatureState;
332}
333
334bool TextMessagePart::isHidden() const
335{
336 return mIsHidden;
337}
338
339bool TextMessagePart::showLink() const
340{
341 return mShowLink;
342}
343
344bool TextMessagePart::showTextFrame() const
345{
346 return mDrawFrame;
347}
348
349//-----AttachmentMessageBlock----------------------
350
351AttachmentMessagePart::AttachmentMessagePart(ObjectTreeParser *otp, KMime::Content *node, bool drawFrame, bool showLink, bool decryptMessage)
352 : TextMessagePart(otp, node, drawFrame, showLink, decryptMessage)
353 , mIsImage(false)
354 , mNeverDisplayInline(false)
355{
356
357}
358
359AttachmentMessagePart::~AttachmentMessagePart()
360{
361
362}
363
364bool AttachmentMessagePart::neverDisplayInline() const
365{
366 return mNeverDisplayInline;
367}
368
369void AttachmentMessagePart::setNeverDisplayInline(bool displayInline)
370{
371 mNeverDisplayInline = displayInline;
372}
373
374bool AttachmentMessagePart::isImage() const
375{
376 return mIsImage;
377}
378
379void AttachmentMessagePart::setIsImage(bool image)
380{
381 mIsImage = image;
382}
383
384IconType AttachmentMessagePart::asIcon() const
385{
386 const AttachmentStrategy *const as = mOtp->attachmentStrategy();
387 const bool defaultHidden(as && as->defaultDisplay(mNode) == AttachmentStrategy::None);
388 const bool showOnlyOneMimePart(mOtp->showOnlyOneMimePart());
389 auto preferredMode = source()->preferredMode();
390 bool isHtmlPreferred = (preferredMode == Util::Html) || (preferredMode == Util::MultipartHtml);
391
392 QByteArray mediaType("text");
393 QByteArray subType("plain");
394 if (mNode->contentType(false) && !mNode->contentType()->mediaType().isEmpty() &&
395 !mNode->contentType()->subType().isEmpty()) {
396 mediaType = mNode->contentType()->mediaType();
397 subType = mNode->contentType()->subType();
398 }
399 const bool isTextPart = (mediaType == QByteArrayLiteral("text"));
400
401 bool defaultAsIcon = true;
402 if (!neverDisplayInline()) {
403 if (as) {
404 defaultAsIcon = as->defaultDisplay(mNode) == AttachmentStrategy::AsIcon;
405 }
406 }
407 if (isImage() && showOnlyOneMimePart && !neverDisplayInline()) {
408 defaultAsIcon = false;
409 }
410
411 // neither image nor text -> show as icon
412 if (!isImage() && !isTextPart) {
413 defaultAsIcon = true;
414 }
415
416 if (isTextPart) {
417 if (as && as->defaultDisplay(mNode) != AttachmentStrategy::Inline) {
418 return MimeTreeParser::IconExternal;
419 }
420 return MimeTreeParser::NoIcon;
421 } else {
422 if (isImage() && isHtmlPreferred &&
423 mNode->parent() && mNode->parent()->contentType()->subType() == "related") {
424 return MimeTreeParser::IconInline;
425 }
426
427 if (defaultHidden && !showOnlyOneMimePart && mNode->parent()) {
428 return MimeTreeParser::IconInline;
429 }
430
431 if (defaultAsIcon) {
432 return MimeTreeParser::IconExternal;
433 } else if (isImage()) {
434 return MimeTreeParser::IconInline;
435 } else {
436 return MimeTreeParser::NoIcon;
437 }
438 }
439}
440
441bool AttachmentMessagePart::isHidden() const
442{
443 const AttachmentStrategy *const as = mOtp->attachmentStrategy();
444 const bool defaultHidden(as && as->defaultDisplay(mNode) == AttachmentStrategy::None);
445 const bool showOnlyOneMimePart(mOtp->showOnlyOneMimePart());
446 auto preferredMode = source()->preferredMode();
447 bool isHtmlPreferred = (preferredMode == Util::Html) || (preferredMode == Util::MultipartHtml);
448
449 QByteArray mediaType("text");
450 QByteArray subType("plain");
451 if (mNode->contentType(false) && !mNode->contentType()->mediaType().isEmpty() &&
452 !mNode->contentType()->subType().isEmpty()) {
453 mediaType = mNode->contentType()->mediaType();
454 subType = mNode->contentType()->subType();
455 }
456 const bool isTextPart = (mediaType == QByteArrayLiteral("text"));
457
458 bool defaultAsIcon = true;
459 if (!neverDisplayInline()) {
460 if (as) {
461 defaultAsIcon = as->defaultDisplay(mNode) == AttachmentStrategy::AsIcon;
462 }
463 }
464 if (isImage() && showOnlyOneMimePart && !neverDisplayInline()) {
465 defaultAsIcon = false;
466 }
467
468 // neither image nor text -> show as icon
469 if (!isImage() && !isTextPart) {
470 defaultAsIcon = true;
471 }
472
473 bool hidden(false);
474 if (isTextPart) {
475 hidden = defaultHidden && !showOnlyOneMimePart;
476 } else {
477 if (isImage() && isHtmlPreferred &&
478 mNode->parent() && mNode->parent()->contentType()->subType() == "related") {
479 hidden = true;
480 } else {
481 hidden = defaultHidden && !showOnlyOneMimePart && mNode->parent();
482 hidden |= defaultAsIcon && (defaultHidden || showOnlyOneMimePart);
483 }
484 }
485 mOtp->nodeHelper()->setNodeDisplayedHidden(mNode, hidden);
486 return hidden;
487}
488
489//-----HtmlMessageBlock----------------------
490
491HtmlMessagePart::HtmlMessagePart(ObjectTreeParser *otp, KMime::Content *node, Interface::ObjectTreeSource *source)
492 : MessagePart(otp, QString())
493 , mNode(node)
494 , mSource(source)
495{
496 if (!mNode) {
497 qCWarning(MIMETREEPARSER_LOG) << "not a valid node";
498 return;
499 }
500
501 const QByteArray partBody(mNode->decodedContent());
502 mBodyHTML = mOtp->codecFor(mNode)->toUnicode(partBody);
503 mCharset = NodeHelper::charset(mNode);
504}
505
506HtmlMessagePart::~HtmlMessagePart()
507{
508}
509
510void HtmlMessagePart::fix() const
511{
512 mOtp->mHtmlContent += mBodyHTML;
513 mOtp->mHtmlContentCharset = mCharset;
514}
515
516QString HtmlMessagePart::text() const
517{
518 return mBodyHTML;
519}
520
521bool HtmlMessagePart::isHtml() const
522{
523 return true;
524}
525
526//-----MimeMessageBlock----------------------
527
528MimeMessagePart::MimeMessagePart(ObjectTreeParser *otp, KMime::Content *node, bool onlyOneMimePart)
529 : MessagePart(otp, QString())
530 , mNode(node)
531 , mOnlyOneMimePart(onlyOneMimePart)
532{
533 if (!mNode) {
534 qCWarning(MIMETREEPARSER_LOG) << "not a valid node";
535 return;
536 }
537
538 parseInternal(mNode, mOnlyOneMimePart);
539}
540
541MimeMessagePart::~MimeMessagePart()
542{
543
544}
545
546QString MimeMessagePart::text() const
547{
548 return renderInternalText();
549}
550
551QString MimeMessagePart::plaintextContent() const
552{
553 return QString();
554}
555
556QString MimeMessagePart::htmlContent() const
557{
558 return QString();
559}
560
561//-----AlternativeMessagePart----------------------
562
563AlternativeMessagePart::AlternativeMessagePart(ObjectTreeParser *otp, KMime::Content *node, Util::HtmlMode preferredMode)
564 : MessagePart(otp, QString())
565 , mNode(node)
566 , mPreferredMode(preferredMode)
567{
568 KMime::Content *dataIcal = findTypeInDirectChilds(mNode, "text/calendar");
569 KMime::Content *dataHtml = findTypeInDirectChilds(mNode, "text/html");
570 KMime::Content *dataText = findTypeInDirectChilds(mNode, "text/plain");
571
572 if (!dataHtml) {
573 // If we didn't find the HTML part as the first child of the multipart/alternative, it might
574 // be that this is a HTML message with images, and text/plain and multipart/related are the
575 // immediate children of this multipart/alternative node.
576 // In this case, the HTML node is a child of multipart/related.
577 dataHtml = findTypeInDirectChilds(mNode, "multipart/related");
578
579 // Still not found? Stupid apple mail actually puts the attachments inside of the
580 // multipart/alternative, which is wrong. Therefore we also have to look for multipart/mixed
581 // here.
582 // Do this only when prefering HTML mail, though, since otherwise the attachments are hidden
583 // when displaying plain text.
584 if (!dataHtml) {
585 dataHtml = findTypeInDirectChilds(mNode, "multipart/mixed");
586 }
587 }
588
589 if (dataIcal) {
590 mChildNodes[Util::MultipartIcal] = dataIcal;
591 }
592
593 if (dataText) {
594 mChildNodes[Util::MultipartPlain] = dataText;
595 }
596
597 if (dataHtml) {
598 mChildNodes[Util::MultipartHtml] = dataHtml;
599 }
600
601 if (mChildNodes.isEmpty()) {
602 qCWarning(MIMETREEPARSER_LOG) << "no valid nodes";
603 return;
604 }
605
606 QMapIterator<Util::HtmlMode, KMime::Content *> i(mChildNodes);
607 while (i.hasNext()) {
608 i.next();
609 mChildParts[i.key()] = MimeMessagePart::Ptr(new MimeMessagePart(mOtp, i.value(), true));
610 }
611}
612
613AlternativeMessagePart::~AlternativeMessagePart()
614{
615
616}
617
618Util::HtmlMode AlternativeMessagePart::preferredMode() const
619{
620 return mPreferredMode;
621}
622
623QList<Util::HtmlMode> AlternativeMessagePart::availableModes()
624{
625 return mChildParts.keys();
626}
627
628QString AlternativeMessagePart::text() const
629{
630 if (mChildParts.contains(Util::MultipartPlain)) {
631 return mChildParts[Util::MultipartPlain]->text();
632 }
633 return QString();
634}
635
636void AlternativeMessagePart::fix() const
637{
638 if (mChildParts.contains(Util::MultipartPlain)) {
639 mChildParts[Util::MultipartPlain]->fix();
640 }
641
642 const auto mode = preferredMode();
643 if (mode != Util::MultipartPlain && mChildParts.contains(mode)) {
644 mChildParts[mode]->fix();
645 }
646}
647
648void AlternativeMessagePart::copyContentFrom() const
649{
650 if (mChildParts.contains(Util::MultipartPlain)) {
651 mChildParts[Util::MultipartPlain]->copyContentFrom();
652 }
653
654 const auto mode = preferredMode();
655 if (mode != Util::MultipartPlain && mChildParts.contains(mode)) {
656 mChildParts[mode]->copyContentFrom();
657 }
658}
659
660bool AlternativeMessagePart::isHtml() const
661{
662 return mChildParts.contains(Util::MultipartHtml);
663}
664
665QString AlternativeMessagePart::plaintextContent() const
666{
667 return text();
668}
669
670QString AlternativeMessagePart::htmlContent() const
671{
672 if (mChildParts.contains(Util::MultipartHtml)) {
673 return mChildParts[Util::MultipartHtml]->text();
674 } else {
675 return plaintextContent();
676 }
677}
678
679//-----CertMessageBlock----------------------
680
681CertMessagePart::CertMessagePart(ObjectTreeParser *otp, KMime::Content *node, const QGpgME::Protocol *cryptoProto, bool autoImport)
682 : MessagePart(otp, QString())
683 , mNode(node)
684 , mAutoImport(autoImport)
685 , mCryptoProto(cryptoProto)
686{
687 if (!mNode) {
688 qCWarning(MIMETREEPARSER_LOG) << "not a valid node";
689 return;
690 }
691
692 if (!mAutoImport) {
693 return;
694 }
695
696 const QByteArray certData = node->decodedContent();
697
698 QGpgME::ImportJob *import = mCryptoProto->importJob();
699 QGpgMEJobExecutor executor;
700 mImportResult = executor.exec(import, certData);
701}
702
703CertMessagePart::~CertMessagePart()
704{
705
706}
707
708QString CertMessagePart::text() const
709{
710 return QString();
711}
712
713//-----SignedMessageBlock---------------------
714SignedMessagePart::SignedMessagePart(ObjectTreeParser *otp,
715 const QString &text,
716 const QGpgME::Protocol *cryptoProto,
717 const QString &fromAddress,
718 KMime::Content *node)
719 : MessagePart(otp, text)
720 , mCryptoProto(cryptoProto)
721 , mFromAddress(fromAddress)
722 , mNode(node)
723{
724 mMetaData.technicalProblem = (mCryptoProto == nullptr);
725 mMetaData.isSigned = true;
726 mMetaData.isGoodSignature = false;
727 mMetaData.keyTrust = GpgME::Signature::Unknown;
728 mMetaData.status = i18n("Wrong Crypto Plug-In.");
729 mMetaData.status_code = GPGME_SIG_STAT_NONE;
730}
731
732SignedMessagePart::~SignedMessagePart()
733{
734
735}
736
737void SignedMessagePart::setIsSigned(bool isSigned)
738{
739 mMetaData.isSigned = isSigned;
740}
741
742bool SignedMessagePart::isSigned() const
743{
744 return mMetaData.isSigned;
745}
746
747bool SignedMessagePart::okVerify(const QByteArray &data, const QByteArray &signature, KMime::Content *textNode)
748{
749 NodeHelper *nodeHelper = mOtp->nodeHelper();
750 Interface::ObjectTreeSource *_source = source();
751
752 mMetaData.isSigned = false;
753 mMetaData.technicalProblem = (mCryptoProto == nullptr);
754 mMetaData.keyTrust = GpgME::Signature::Unknown;
755 mMetaData.status = i18n("Wrong Crypto Plug-In.");
756 mMetaData.status_code = GPGME_SIG_STAT_NONE;
757
758 const QByteArray mementoName = "verification";
759
760 CryptoBodyPartMemento *m = dynamic_cast<CryptoBodyPartMemento *>(nodeHelper->bodyPartMemento(mNode, mementoName));
761 Q_ASSERT(!m || mCryptoProto); //No CryptoPlugin and having a bodyPartMemento -> there is something completely wrong
762
763 if (!m && mCryptoProto) {
764 if (!signature.isEmpty()) {
765 QGpgME::VerifyDetachedJob *job = mCryptoProto->verifyDetachedJob();
766 if (job) {
767 m = new VerifyDetachedBodyPartMemento(job, mCryptoProto->keyListJob(), signature, data);
768 }
769 } else {
770 QGpgME::VerifyOpaqueJob *job = mCryptoProto->verifyOpaqueJob();
771 if (job) {
772 m = new VerifyOpaqueBodyPartMemento(job, mCryptoProto->keyListJob(), data);
773 }
774 }
775 if (m) {
776 if (mOtp->allowAsync()) {
777 QObject::connect(m, &CryptoBodyPartMemento::update,
778 nodeHelper, &NodeHelper::update);
779 QObject::connect(m, SIGNAL(update(MimeTreeParser::UpdateMode)),
780 _source->sourceObject(), SLOT(update(MimeTreeParser::UpdateMode)));
781
782 if (m->start()) {
783 mMetaData.inProgress = true;
784 mOtp->mHasPendingAsyncJobs = true;
785 }
786 } else {
787 m->exec();
788 }
789 nodeHelper->setBodyPartMemento(mNode, mementoName, m);
790 }
791 } else if (m->isRunning()) {
792 mMetaData.inProgress = true;
793 mOtp->mHasPendingAsyncJobs = true;
794 } else {
795 mMetaData.inProgress = false;
796 mOtp->mHasPendingAsyncJobs = false;
797 }
798
799 if (m && !mMetaData.inProgress) {
800 if (!signature.isEmpty()) {
801 mVerifiedText = data;
802 }
803 setVerificationResult(m, textNode);
804 }
805
806 if (!m && !mMetaData.inProgress) {
807 QString errorMsg;
808 QString cryptPlugLibName;
809 QString cryptPlugDisplayName;
810 if (mCryptoProto) {
811 cryptPlugLibName = mCryptoProto->name();
812 cryptPlugDisplayName = mCryptoProto->displayName();
813 }
814
815 if (!mCryptoProto) {
816 if (cryptPlugDisplayName.isEmpty()) {
817 errorMsg = i18n("No appropriate crypto plug-in was found.");
818 } else {
819 errorMsg = i18nc("%1 is either 'OpenPGP' or 'S/MIME'",
820 "No %1 plug-in was found.",
821 cryptPlugDisplayName);
822 }
823 } else {
824 errorMsg = i18n("Crypto plug-in \"%1\" cannot verify signatures.",
825 cryptPlugLibName);
826 }
827 mMetaData.errorText = i18n("The message is signed, but the "
828 "validity of the signature cannot be "
829 "verified.<br />"
830 "Reason: %1",
831 errorMsg);
832 }
833
834 return mMetaData.isSigned;
835}
836
837static int signatureToStatus(const GpgME::Signature &sig)
838{
839 switch (sig.status().code()) {
840 case GPG_ERR_NO_ERROR:
841 return GPGME_SIG_STAT_GOOD;
842 case GPG_ERR_BAD_SIGNATURE:
843 return GPGME_SIG_STAT_BAD;
844 case GPG_ERR_NO_PUBKEY:
845 return GPGME_SIG_STAT_NOKEY;
846 case GPG_ERR_NO_DATA:
847 return GPGME_SIG_STAT_NOSIG;
848 case GPG_ERR_SIG_EXPIRED:
849 return GPGME_SIG_STAT_GOOD_EXP;
850 case GPG_ERR_KEY_EXPIRED:
851 return GPGME_SIG_STAT_GOOD_EXPKEY;
852 default:
853 return GPGME_SIG_STAT_ERROR;
854 }
855}
856
857QString prettifyDN(const char *uid)
858{
859 return QGpgME::DN(uid).prettyDN();
860}
861
862void SignedMessagePart::sigStatusToMetaData()
863{
864 GpgME::Key key;
865 if (mMetaData.isSigned) {
866 GpgME::Signature signature = mSignatures.front();
867 mMetaData.status_code = signatureToStatus(signature);
868 mMetaData.isGoodSignature = mMetaData.status_code & GPGME_SIG_STAT_GOOD;
869 // save extended signature status flags
870 mMetaData.sigSummary = signature.summary();
871
872 if (mMetaData.isGoodSignature && !key.keyID()) {
873 // Search for the key by its fingerprint so that we can check for
874 // trust etc.
875 QGpgME::KeyListJob *job = mCryptoProto->keyListJob(false); // local, no sigs
876 if (!job) {
877 qCDebug(MIMETREEPARSER_LOG) << "The Crypto backend does not support listing keys. ";
878 } else {
879 std::vector<GpgME::Key> found_keys;
880 // As we are local it is ok to make this synchronous
881 GpgME::KeyListResult res = job->exec(QStringList(QLatin1String(signature.fingerprint())), false, found_keys);
882 if (res.error()) {
883 qCDebug(MIMETREEPARSER_LOG) << "Error while searching key for Fingerprint: " << signature.fingerprint();
884 }
885 if (found_keys.size() > 1) {
886 // Should not Happen
887 qCDebug(MIMETREEPARSER_LOG) << "Oops: Found more then one Key for Fingerprint: " << signature.fingerprint();
888 }
889 if (found_keys.size() != 1) {
890 // Should not Happen at this point
891 qCDebug(MIMETREEPARSER_LOG) << "Oops: Found no Key for Fingerprint: " << signature.fingerprint();
892 } else {
893 key = found_keys[0];
894 }
895 delete job;
896 }
897 }
898
899 if (key.keyID()) {
900 mMetaData.keyId = key.keyID();
901 }
902 if (mMetaData.keyId.isEmpty()) {
903 mMetaData.keyId = signature.fingerprint();
904 }
905 mMetaData.keyTrust = signature.validity();
906 if (key.numUserIDs() > 0 && key.userID(0).id()) {
907 mMetaData.signer = prettifyDN(key.userID(0).id());
908 }
909 for (uint iMail = 0; iMail < key.numUserIDs(); ++iMail) {
910 // The following if /should/ always result in TRUE but we
911 // won't trust implicitely the plugin that gave us these data.
912 if (key.userID(iMail).email()) {
913 QString email = QString::fromUtf8(key.userID(iMail).email());
914 // ### work around gpgme 0.3.QString text() const Q_DECL_OVERRIDE;x / cryptplug bug where the
915 // ### email addresses are specified as angle-addr, not addr-spec:
916 if (email.startsWith(QLatin1Char('<')) && email.endsWith(QLatin1Char('>'))) {
917 email = email.mid(1, email.length() - 2);
918 }
919 if (!email.isEmpty()) {
920 mMetaData.signerMailAddresses.append(email);
921 }
922 }
923 }
924
925 if (signature.creationTime()) {
926 mMetaData.creationTime.setTime_t(signature.creationTime());
927 } else {
928 mMetaData.creationTime = QDateTime();
929 }
930 if (mMetaData.signer.isEmpty()) {
931 if (key.numUserIDs() > 0 && key.userID(0).name()) {
932 mMetaData.signer = prettifyDN(key.userID(0).name());
933 }
934 if (!mMetaData.signerMailAddresses.empty()) {
935 if (mMetaData.signer.isEmpty()) {
936 mMetaData.signer = mMetaData.signerMailAddresses.front();
937 } else {
938 mMetaData.signer += QLatin1String(" <") + mMetaData.signerMailAddresses.front() + QLatin1Char('>');
939 }
940 }
941 }
942 }
943}
944
945void SignedMessagePart::startVerification(const QByteArray &text, const QTextCodec *aCodec)
946{
947 startVerificationDetached(text, nullptr, QByteArray());
948
949 if (!mNode && mMetaData.isSigned) {
950 setText(aCodec->toUnicode(mVerifiedText));
951 }
952}
953
954void SignedMessagePart::startVerificationDetached(const QByteArray &text, KMime::Content *textNode, const QByteArray &signature)
955{
956 mMetaData.isEncrypted = false;
957 mMetaData.isDecryptable = false;
958
959 if (textNode) {
960 parseInternal(textNode, false);
961 }
962
963 okVerify(text, signature, textNode);
964
965 if (!mMetaData.isSigned) {
966 mMetaData.creationTime = QDateTime();
967 }
968}
969
970void SignedMessagePart::setVerificationResult(const CryptoBodyPartMemento *m, KMime::Content *textNode)
971{
972 {
973 const auto vm = dynamic_cast<const VerifyDetachedBodyPartMemento *>(m);
974 if (vm) {
975 mSignatures = vm->verifyResult().signatures();
976 }
977 }
978 {
979 const auto vm = dynamic_cast<const VerifyOpaqueBodyPartMemento *>(m);
980 if (vm) {
981 mVerifiedText = vm->plainText();
982 mSignatures = vm->verifyResult().signatures();
983 }
984 }
985 {
986 const auto vm = dynamic_cast<const DecryptVerifyBodyPartMemento *>(m);
987 if (vm) {
988 mVerifiedText = vm->plainText();
989 mSignatures = vm->verifyResult().signatures();
990 }
991 }
992 mMetaData.auditLogError = m->auditLogError();
993 mMetaData.auditLog = m->auditLogAsHtml();
994 mMetaData.isSigned = !mSignatures.empty();
995
996 if (mMetaData.isSigned) {
997 sigStatusToMetaData();
998 if (mNode) {
999 mOtp->nodeHelper()->setSignatureState(mNode, KMMsgFullySigned);
1000 if (!textNode) {
1001 mOtp->mNodeHelper->setPartMetaData(mNode, mMetaData);
1002
1003 if (!mVerifiedText.isEmpty()) {
1004 auto tempNode = new KMime::Content();
1005 tempNode->setContent(KMime::CRLFtoLF(mVerifiedText.constData()));
1006 tempNode->parse();
1007
1008 if (!tempNode->head().isEmpty()) {
1009 tempNode->contentDescription()->from7BitString("signed data");
1010 }
1011 mOtp->mNodeHelper->attachExtraContent(mNode, tempNode);
1012
1013 parseInternal(tempNode, false);
1014 }
1015 }
1016 }
1017 }
1018}
1019
1020QString SignedMessagePart::plaintextContent() const
1021{
1022 if (!mNode) {
1023 return MessagePart::text();
1024 } else {
1025 return QString();
1026 }
1027}
1028
1029QString SignedMessagePart::htmlContent() const
1030{
1031 if (!mNode) {
1032 return MessagePart::text();
1033 } else {
1034 return QString();
1035 }
1036}
1037
1038//-----CryptMessageBlock---------------------
1039EncryptedMessagePart::EncryptedMessagePart(ObjectTreeParser *otp,
1040 const QString &text,
1041 const QGpgME::Protocol *cryptoProto,
1042 const QString &fromAddress,
1043 KMime::Content *node)
1044 : MessagePart(otp, text)
1045 , mPassphraseError(false)
1046 , mNoSecKey(false)
1047 , mCryptoProto(cryptoProto)
1048 , mFromAddress(fromAddress)
1049 , mNode(node)
1050 , mDecryptMessage(false)
1051{
1052 mMetaData.technicalProblem = (mCryptoProto == nullptr);
1053 mMetaData.isSigned = false;
1054 mMetaData.isGoodSignature = false;
1055 mMetaData.isEncrypted = false;
1056 mMetaData.isDecryptable = false;
1057 mMetaData.keyTrust = GpgME::Signature::Unknown;
1058 mMetaData.status = i18n("Wrong Crypto Plug-In.");
1059 mMetaData.status_code = GPGME_SIG_STAT_NONE;
1060}
1061
1062EncryptedMessagePart::~EncryptedMessagePart()
1063{
1064
1065}
1066
1067void EncryptedMessagePart::setDecryptMessage(bool decrypt)
1068{
1069 mDecryptMessage = decrypt;
1070}
1071
1072bool EncryptedMessagePart::decryptMessage() const
1073{
1074 return mDecryptMessage;
1075}
1076
1077void EncryptedMessagePart::setIsEncrypted(bool encrypted)
1078{
1079 mMetaData.isEncrypted = encrypted;
1080}
1081
1082bool EncryptedMessagePart::isEncrypted() const
1083{
1084 return mMetaData.isEncrypted;
1085}
1086
1087bool EncryptedMessagePart::isDecryptable() const
1088{
1089 return mMetaData.isDecryptable;
1090}
1091
1092bool EncryptedMessagePart::passphraseError() const
1093{
1094 return mPassphraseError;
1095}
1096
1097void EncryptedMessagePart::startDecryption(const QByteArray &text, const QTextCodec *aCodec)
1098{
1099 KMime::Content *content = new KMime::Content;
1100 content->setBody(text);
1101 content->parse();
1102
1103 startDecryption(content);
1104
1105 if (!mMetaData.inProgress && mMetaData.isDecryptable) {
1106 if (hasSubParts()) {
1107 auto _mp = (subParts()[0]).dynamicCast<SignedMessagePart>();
1108 if (_mp) {
1109 _mp->setText(aCodec->toUnicode(mDecryptedData));
1110 } else {
1111 setText(aCodec->toUnicode(mDecryptedData));
1112 }
1113 } else {
1114 setText(aCodec->toUnicode(mDecryptedData));
1115 }
1116 }
1117}
1118
1119bool EncryptedMessagePart::okDecryptMIME(KMime::Content &data)
1120{
1121 mPassphraseError = false;
1122 mMetaData.inProgress = false;
1123 mMetaData.errorText.clear();
1124 mMetaData.auditLogError = GpgME::Error();
1125 mMetaData.auditLog.clear();
1126 bool bDecryptionOk = false;
1127 bool cannotDecrypt = false;
1128 Interface::ObjectTreeSource *_source = source();
1129 NodeHelper *nodeHelper = mOtp->nodeHelper();
1130
1131 Q_ASSERT(decryptMessage());
1132
1133 // Check whether the memento contains a result from last time:
1134 const DecryptVerifyBodyPartMemento *m
1135 = dynamic_cast<DecryptVerifyBodyPartMemento *>(nodeHelper->bodyPartMemento(&data, "decryptverify"));
1136
1137 Q_ASSERT(!m || mCryptoProto); //No CryptoPlugin and having a bodyPartMemento -> there is something completely wrong
1138
1139 if (!m && mCryptoProto) {
1140 QGpgME::DecryptVerifyJob *job = mCryptoProto->decryptVerifyJob();
1141 if (!job) {
1142 cannotDecrypt = true;
1143 } else {
1144 const QByteArray ciphertext = data.decodedContent();
1145 DecryptVerifyBodyPartMemento *newM
1146 = new DecryptVerifyBodyPartMemento(job, ciphertext);
1147 if (mOtp->allowAsync()) {
1148 QObject::connect(newM, &CryptoBodyPartMemento::update,
1149 nodeHelper, &NodeHelper::update);
1150 QObject::connect(newM, SIGNAL(update(MimeTreeParser::UpdateMode)), _source->sourceObject(),
1151 SLOT(update(MimeTreeParser::UpdateMode)));
1152 if (newM->start()) {
1153 mMetaData.inProgress = true;
1154 mOtp->mHasPendingAsyncJobs = true;
1155 } else {
1156 m = newM;
1157 }
1158 } else {
1159 newM->exec();
1160 m = newM;
1161 }
1162 nodeHelper->setBodyPartMemento(&data, "decryptverify", newM);
1163 }
1164 } else if (m->isRunning()) {
1165 mMetaData.inProgress = true;
1166 mOtp->mHasPendingAsyncJobs = true;
1167 m = nullptr;
1168 }
1169
1170 if (m) {
1171 const QByteArray &plainText = m->plainText();
1172 const GpgME::DecryptionResult &decryptResult = m->decryptResult();
1173 const GpgME::VerificationResult &verifyResult = m->verifyResult();
1174 mMetaData.isSigned = verifyResult.signatures().size() > 0;
1175
1176 if (verifyResult.signatures().size() > 0) {
1177 auto subPart = SignedMessagePart::Ptr(new SignedMessagePart(mOtp, MessagePart::text(), mCryptoProto, mFromAddress, mNode));
1178 subPart->setVerificationResult(m, nullptr);
1179 appendSubPart(subPart);
1180 }
1181
1182 mDecryptRecipients = decryptResult.recipients();
1183 bDecryptionOk = !decryptResult.error();
1184// std::stringstream ss;
1185// ss << decryptResult << '\n' << verifyResult;
1186// qCDebug(MIMETREEPARSER_LOG) << ss.str().c_str();
1187
1188 if (!bDecryptionOk && mMetaData.isSigned) {
1189 //Only a signed part
1190 mMetaData.isEncrypted = false;
1191 bDecryptionOk = true;
1192 mDecryptedData = plainText;
1193 } else {
1194 mPassphraseError = decryptResult.error().isCanceled() || decryptResult.error().code() == GPG_ERR_NO_SECKEY;
1195 mMetaData.isEncrypted = decryptResult.error().code() != GPG_ERR_NO_DATA;
1196 mMetaData.errorText = QString::fromLocal8Bit(decryptResult.error().asString());
1197 if (mMetaData.isEncrypted && decryptResult.numRecipients() > 0) {
1198 mMetaData.keyId = decryptResult.recipient(0).keyID();
1199 }
1200
1201 if (bDecryptionOk) {
1202 mDecryptedData = plainText;
1203 } else {
1204 mNoSecKey = true;
1205 foreach (const GpgME::DecryptionResult::Recipient &recipient, decryptResult.recipients()) {
1206 mNoSecKey &= (recipient.status().code() == GPG_ERR_NO_SECKEY);
1207 }
1208 if (!mPassphraseError && !mNoSecKey) { // GpgME do not detect passphrase error correctly
1209 mPassphraseError = true;
1210 }
1211 }
1212 }
1213 }
1214
1215 if (!bDecryptionOk) {
1216 QString cryptPlugLibName;
1217 if (mCryptoProto) {
1218 cryptPlugLibName = mCryptoProto->name();
1219 }
1220
1221 if (!mCryptoProto) {
1222 mMetaData.errorText = i18n("No appropriate crypto plug-in was found.");
1223 } else if (cannotDecrypt) {
1224 mMetaData.errorText = i18n("Crypto plug-in \"%1\" cannot decrypt messages.",
1225 cryptPlugLibName);
1226 } else if (!passphraseError()) {
1227 mMetaData.errorText = i18n("Crypto plug-in \"%1\" could not decrypt the data.", cryptPlugLibName)
1228 + QLatin1String("<br />")
1229 + i18n("Error: %1", mMetaData.errorText);
1230 }
1231 }
1232 return bDecryptionOk;
1233}
1234
1235void EncryptedMessagePart::startDecryption(KMime::Content *data)
1236{
1237 if (!mNode && !data) {
1238 return;
1239 }
1240
1241 if (!data) {
1242 data = mNode;
1243 }
1244
1245 mMetaData.isEncrypted = true;
1246
1247 bool bOkDecrypt = okDecryptMIME(*data);
1248
1249 if (mMetaData.inProgress) {
1250 return;
1251 }
1252 mMetaData.isDecryptable = bOkDecrypt;
1253
1254 if (!mMetaData.isDecryptable) {
1255 setText(QString::fromUtf8(mDecryptedData.constData()));
1256 }
1257
1258 if (mMetaData.isEncrypted && !decryptMessage()) {
1259 mMetaData.isDecryptable = true;
1260 }
1261
1262 if (mNode && !mMetaData.isSigned) {
1263 mOtp->mNodeHelper->setPartMetaData(mNode, mMetaData);
1264
1265 if (decryptMessage()) {
1266 auto tempNode = new KMime::Content();
1267 tempNode->setContent(KMime::CRLFtoLF(mDecryptedData.constData()));
1268 tempNode->parse();
1269
1270 if (!tempNode->head().isEmpty()) {
1271 tempNode->contentDescription()->from7BitString("encrypted data");
1272 }
1273 mOtp->mNodeHelper->attachExtraContent(mNode, tempNode);
1274
1275 parseInternal(tempNode, false);
1276 }
1277 }
1278}
1279
1280QString EncryptedMessagePart::plaintextContent() const
1281{
1282 if (!mNode) {
1283 return MessagePart::text();
1284 } else {
1285 return QString();
1286 }
1287}
1288
1289QString EncryptedMessagePart::htmlContent() const
1290{
1291 if (!mNode) {
1292 return MessagePart::text();
1293 } else {
1294 return QString();
1295 }
1296}
1297
1298QString EncryptedMessagePart::text() const
1299{
1300 if (hasSubParts()) {
1301 auto _mp = (subParts()[0]).dynamicCast<SignedMessagePart>();
1302 if (_mp) {
1303 return _mp->text();
1304 } else {
1305 return MessagePart::text();
1306 }
1307 } else {
1308 return MessagePart::text();
1309 }
1310}
1311
1312EncapsulatedRfc822MessagePart::EncapsulatedRfc822MessagePart(ObjectTreeParser *otp, KMime::Content *node, const KMime::Message::Ptr &message)
1313 : MessagePart(otp, QString())
1314 , mMessage(message)
1315 , mNode(node)
1316{
1317 mMetaData.isEncrypted = false;
1318 mMetaData.isSigned = false;
1319 mMetaData.isEncapsulatedRfc822Message = true;
1320
1321 mOtp->nodeHelper()->setNodeDisplayedEmbedded(mNode, true);
1322 mOtp->nodeHelper()->setPartMetaData(mNode, mMetaData);
1323
1324 if (!mMessage) {
1325 qCWarning(MIMETREEPARSER_LOG) << "Node is of type message/rfc822 but doesn't have a message!";
1326 return;
1327 }
1328
1329 // The link to "Encapsulated message" is clickable, therefore the temp file needs to exists,
1330 // since the user can click the link and expect to have normal attachment operations there.
1331 mOtp->nodeHelper()->writeNodeToTempFile(message.data());
1332
1333 parseInternal(message.data(), false);
1334}
1335
1336EncapsulatedRfc822MessagePart::~EncapsulatedRfc822MessagePart()
1337{
1338
1339}
1340
1341QString EncapsulatedRfc822MessagePart::text() const
1342{
1343 return renderInternalText();
1344}
1345
1346void EncapsulatedRfc822MessagePart::copyContentFrom() const
1347{
1348}
1349
1350void EncapsulatedRfc822MessagePart::fix() const
1351{
1352}
diff --git a/framework/src/domain/mime/mimetreeparser/otp/messagepart.h b/framework/src/domain/mime/mimetreeparser/otp/messagepart.h
new file mode 100644
index 00000000..433f3f6b
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/messagepart.h
@@ -0,0 +1,422 @@
1/*
2 Copyright (c) 2015 Sandro Knauß <sknauss@kde.org>
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#ifndef __MIMETREEPARSER_MESSAGEPART_H__
21#define __MIMETREEPARSER_MESSAGEPART_H__
22
23#include "bodypartformatter.h"
24#include "util.h"
25
26#include <KMime/Message>
27
28#include <gpgme++/verificationresult.h>
29#include <gpgme++/decryptionresult.h>
30#include <gpgme++/importresult.h>
31
32#include <QString>
33#include <QSharedPointer>
34
35class QTextCodec;
36class PartPrivate;
37
38namespace GpgME
39{
40class ImportResult;
41}
42
43namespace QGpgME
44{
45class Protocol;
46}
47
48namespace KMime
49{
50class Content;
51}
52
53namespace MimeTreeParser
54{
55class ObjectTreeParser;
56class HtmlWriter;
57class HTMLBlock;
58typedef QSharedPointer<HTMLBlock> HTMLBlockPtr;
59class CryptoBodyPartMemento;
60class MultiPartAlternativeBodyPartFormatter;
61namespace Interface
62{
63class ObjectTreeSource;
64}
65
66class MessagePart : public Interface::MessagePart
67{
68 Q_OBJECT
69 Q_PROPERTY(bool attachment READ isAttachment)
70 Q_PROPERTY(bool root READ isRoot)
71 Q_PROPERTY(bool isHtml READ isHtml)
72 Q_PROPERTY(bool isHidden READ isHidden)
73public:
74 typedef QSharedPointer<MessagePart> Ptr;
75 MessagePart(ObjectTreeParser *otp,
76 const QString &text);
77
78 virtual ~MessagePart();
79
80 virtual QString text() const Q_DECL_OVERRIDE;
81 void setText(const QString &text);
82 void setAttachmentFlag(KMime::Content *node);
83 bool isAttachment() const;
84
85 void setIsRoot(bool root);
86 bool isRoot() const;
87
88 virtual bool isHtml() const;
89 virtual bool isHidden() const;
90
91 PartMetaData *partMetaData();
92
93 /* only a function that should be removed if the refactoring is over */
94 virtual void fix() const;
95 virtual void copyContentFrom() const;
96
97 void appendSubPart(const Interface::MessagePart::Ptr &messagePart);
98 const QVector<Interface::MessagePart::Ptr> &subParts() const;
99 bool hasSubParts() const;
100
101 HtmlWriter *htmlWriter() const Q_DECL_OVERRIDE;
102 void setHtmlWriter(HtmlWriter *htmlWriter) const Q_DECL_OVERRIDE;
103
104 Interface::ObjectTreeSource *source() const;
105 KMime::Content *attachmentNode() const;
106
107protected:
108 void parseInternal(KMime::Content *node, bool onlyOneMimePart);
109 QString renderInternalText() const;
110
111 QString mText;
112 ObjectTreeParser *mOtp;
113 PartMetaData mMetaData;
114
115private:
116 QVector<Interface::MessagePart::Ptr> mBlocks;
117
118 KMime::Content *mAttachmentNode;
119 bool mRoot;
120};
121
122class MimeMessagePart : public MessagePart
123{
124 Q_OBJECT
125public:
126 typedef QSharedPointer<MimeMessagePart> Ptr;
127 MimeMessagePart(MimeTreeParser::ObjectTreeParser *otp, KMime::Content *node, bool onlyOneMimePart);
128 virtual ~MimeMessagePart();
129
130 QString text() const Q_DECL_OVERRIDE;
131
132 QString plaintextContent() const Q_DECL_OVERRIDE;
133 QString htmlContent() const Q_DECL_OVERRIDE;
134private:
135 KMime::Content *mNode;
136 bool mOnlyOneMimePart;
137
138 friend class AlternativeMessagePart;
139 friend class ::PartPrivate;
140};
141
142class MessagePartList : public MessagePart
143{
144 Q_OBJECT
145public:
146 typedef QSharedPointer<MessagePartList> Ptr;
147 MessagePartList(MimeTreeParser::ObjectTreeParser *otp);
148 virtual ~MessagePartList();
149
150 QString text() const Q_DECL_OVERRIDE;
151
152 QString plaintextContent() const Q_DECL_OVERRIDE;
153 QString htmlContent() const Q_DECL_OVERRIDE;
154private:
155};
156
157enum IconType {
158 NoIcon = 0,
159 IconExternal,
160 IconInline
161};
162
163class TextMessagePart : public MessagePartList
164{
165 Q_OBJECT
166public:
167 typedef QSharedPointer<TextMessagePart> Ptr;
168 TextMessagePart(MimeTreeParser::ObjectTreeParser *otp, KMime::Content *node, bool drawFrame, bool showLink, bool decryptMessage);
169 virtual ~TextMessagePart();
170
171 KMMsgSignatureState signatureState() const;
172 KMMsgEncryptionState encryptionState() const;
173
174 bool decryptMessage() const;
175
176 bool isHidden() const Q_DECL_OVERRIDE;
177
178 bool showLink() const;
179 bool showTextFrame() const;
180
181protected:
182 KMime::Content *mNode;
183
184private:
185 void parseContent();
186
187 KMMsgSignatureState mSignatureState;
188 KMMsgEncryptionState mEncryptionState;
189 bool mDrawFrame;
190 bool mShowLink;
191 bool mDecryptMessage;
192 bool mIsHidden;
193
194 friend class DefaultRendererPrivate;
195 friend class ObjectTreeParser;
196 friend class ::PartPrivate;
197};
198
199class AttachmentMessagePart : public TextMessagePart
200{
201 Q_OBJECT
202public:
203 typedef QSharedPointer<AttachmentMessagePart> Ptr;
204 AttachmentMessagePart(MimeTreeParser::ObjectTreeParser *otp, KMime::Content *node, bool drawFrame, bool showLink, bool decryptMessage);
205 virtual ~AttachmentMessagePart();
206
207 IconType asIcon() const;
208 bool neverDisplayInline() const;
209 void setNeverDisplayInline(bool displayInline);
210 bool isImage() const;
211 void setIsImage(bool image);
212
213 bool isHidden() const Q_DECL_OVERRIDE;
214
215private:
216 bool mIsImage;
217 bool mNeverDisplayInline;
218};
219
220class HtmlMessagePart : public MessagePart
221{
222 Q_OBJECT
223public:
224 typedef QSharedPointer<HtmlMessagePart> Ptr;
225 HtmlMessagePart(MimeTreeParser::ObjectTreeParser *otp, KMime::Content *node, MimeTreeParser::Interface::ObjectTreeSource *source);
226 virtual ~HtmlMessagePart();
227
228 QString text() const Q_DECL_OVERRIDE;
229
230 void fix() const Q_DECL_OVERRIDE;
231 bool isHtml() const Q_DECL_OVERRIDE;
232
233private:
234 KMime::Content *mNode;
235 Interface::ObjectTreeSource *mSource;
236 QString mBodyHTML;
237 QByteArray mCharset;
238
239 friend class DefaultRendererPrivate;
240 friend class ::PartPrivate;
241};
242
243class AlternativeMessagePart : public MessagePart
244{
245 Q_OBJECT
246public:
247 typedef QSharedPointer<AlternativeMessagePart> Ptr;
248 AlternativeMessagePart(MimeTreeParser::ObjectTreeParser *otp, KMime::Content *node, Util::HtmlMode preferredMode);
249 virtual ~AlternativeMessagePart();
250
251 QString text() const Q_DECL_OVERRIDE;
252
253 Util::HtmlMode preferredMode() const;
254
255 bool isHtml() const Q_DECL_OVERRIDE;
256
257 QString plaintextContent() const Q_DECL_OVERRIDE;
258 QString htmlContent() const Q_DECL_OVERRIDE;
259
260 QList<Util::HtmlMode> availableModes();
261
262 void fix() const Q_DECL_OVERRIDE;
263 void copyContentFrom() const Q_DECL_OVERRIDE;
264private:
265 KMime::Content *mNode;
266
267 Util::HtmlMode mPreferredMode;
268
269 QMap<Util::HtmlMode, KMime::Content *> mChildNodes;
270 QMap<Util::HtmlMode, MimeMessagePart::Ptr> mChildParts;
271
272 friend class DefaultRendererPrivate;
273 friend class ObjectTreeParser;
274 friend class MultiPartAlternativeBodyPartFormatter;
275 friend class ::PartPrivate;
276};
277
278class CertMessagePart : public MessagePart
279{
280 Q_OBJECT
281public:
282 typedef QSharedPointer<CertMessagePart> Ptr;
283 CertMessagePart(MimeTreeParser::ObjectTreeParser *otp, KMime::Content *node, const QGpgME::Protocol *cryptoProto, bool autoImport);
284 virtual ~CertMessagePart();
285
286 QString text() const Q_DECL_OVERRIDE;
287
288private:
289 KMime::Content *mNode;
290 bool mAutoImport;
291 GpgME::ImportResult mImportResult;
292 const QGpgME::Protocol *mCryptoProto;
293 friend class DefaultRendererPrivate;
294};
295
296class EncapsulatedRfc822MessagePart : public MessagePart
297{
298 Q_OBJECT
299public:
300 typedef QSharedPointer<EncapsulatedRfc822MessagePart> Ptr;
301 EncapsulatedRfc822MessagePart(MimeTreeParser::ObjectTreeParser *otp, KMime::Content *node, const KMime::Message::Ptr &message);
302 virtual ~EncapsulatedRfc822MessagePart();
303
304 QString text() const Q_DECL_OVERRIDE;
305
306 void copyContentFrom() const Q_DECL_OVERRIDE;
307 void fix() const Q_DECL_OVERRIDE;
308private:
309 const KMime::Message::Ptr mMessage;
310 KMime::Content *mNode;
311
312 friend class DefaultRendererPrivate;
313};
314
315class EncryptedMessagePart : public MessagePart
316{
317 Q_OBJECT
318 Q_PROPERTY(bool decryptMessage READ decryptMessage WRITE setDecryptMessage)
319 Q_PROPERTY(bool isEncrypted READ isEncrypted)
320 Q_PROPERTY(bool passphraseError READ passphraseError)
321public:
322 typedef QSharedPointer<EncryptedMessagePart> Ptr;
323 EncryptedMessagePart(ObjectTreeParser *otp,
324 const QString &text,
325 const QGpgME::Protocol *cryptoProto,
326 const QString &fromAddress,
327 KMime::Content *node);
328
329 virtual ~EncryptedMessagePart();
330
331 QString text() const Q_DECL_OVERRIDE;
332
333 void setDecryptMessage(bool decrypt);
334 bool decryptMessage() const;
335
336 void setIsEncrypted(bool encrypted);
337 bool isEncrypted() const;
338
339 bool isDecryptable() const;
340
341 bool passphraseError() const;
342
343 void startDecryption(const QByteArray &text, const QTextCodec *aCodec);
344 void startDecryption(KMime::Content *data = nullptr);
345
346 QByteArray mDecryptedData;
347
348 QString plaintextContent() const Q_DECL_OVERRIDE;
349 QString htmlContent() const Q_DECL_OVERRIDE;
350
351private:
352 /** Handles the dectyptioon of a given content
353 * returns true if the decryption was successfull
354 * if used in async mode, check if mMetaData.inProgress is true, it inicates a running decryption process.
355 */
356 bool okDecryptMIME(KMime::Content &data);
357
358protected:
359 bool mPassphraseError;
360 bool mNoSecKey;
361 const QGpgME::Protocol *mCryptoProto;
362 QString mFromAddress;
363 KMime::Content *mNode;
364 bool mDecryptMessage;
365 QByteArray mVerifiedText;
366 std::vector<GpgME::DecryptionResult::Recipient> mDecryptRecipients;
367
368 friend class DefaultRendererPrivate;
369 friend class ::PartPrivate;
370};
371
372class SignedMessagePart : public MessagePart
373{
374 Q_OBJECT
375 Q_PROPERTY(bool isSigned READ isSigned)
376public:
377 typedef QSharedPointer<SignedMessagePart> Ptr;
378 SignedMessagePart(ObjectTreeParser *otp,
379 const QString &text,
380 const QGpgME::Protocol *cryptoProto,
381 const QString &fromAddress,
382 KMime::Content *node);
383
384 virtual ~SignedMessagePart();
385
386 void setIsSigned(bool isSigned);
387 bool isSigned() const;
388
389 void startVerification(const QByteArray &text, const QTextCodec *aCodec);
390 void startVerificationDetached(const QByteArray &text, KMime::Content *textNode, const QByteArray &signature);
391
392 QByteArray mDecryptedData;
393 std::vector<GpgME::Signature> mSignatures;
394
395 QString plaintextContent() const Q_DECL_OVERRIDE;
396 QString htmlContent() const Q_DECL_OVERRIDE;
397
398private:
399 /** Handles the verification of data
400 * If signature is empty it is handled as inline signature otherwise as detached signature mode.
401 * Returns true if the verfication was successfull and the block is signed.
402 * If used in async mode, check if mMetaData.inProgress is true, it inicates a running verification process.
403 */
404 bool okVerify(const QByteArray &data, const QByteArray &signature, KMime::Content *textNode);
405
406 void sigStatusToMetaData();
407
408 void setVerificationResult(const CryptoBodyPartMemento *m, KMime::Content *textNode);
409protected:
410 const QGpgME::Protocol *mCryptoProto;
411 QString mFromAddress;
412 KMime::Content *mNode;
413 QByteArray mVerifiedText;
414
415 friend EncryptedMessagePart;
416 friend class DefaultRendererPrivate;
417 friend class ::PartPrivate;
418};
419
420}
421
422#endif //__MIMETREEPARSER_MESSAGEPART_H__
diff --git a/framework/src/domain/mime/mimetreeparser/otp/messagepartrenderer.cpp b/framework/src/domain/mime/mimetreeparser/otp/messagepartrenderer.cpp
new file mode 100644
index 00000000..7f622268
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/messagepartrenderer.cpp
@@ -0,0 +1,23 @@
1/*
2 Copyright (C) 2016 Sandro Knauß <sknauss@kde.org>
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License along
15 with this program; if not, write to the Free Software Foundation, Inc.,
16 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17*/
18
19#include "messagepartrenderer.h"
20
21MimeTreeParser::Interface::MessagePartRenderer::~MessagePartRenderer()
22{
23}
diff --git a/framework/src/domain/mime/mimetreeparser/otp/messagepartrenderer.h b/framework/src/domain/mime/mimetreeparser/otp/messagepartrenderer.h
new file mode 100644
index 00000000..a90c17e6
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/messagepartrenderer.h
@@ -0,0 +1,43 @@
1/*
2 Copyright (C) 2016 Sandro Knauß <sknauss@kde.org>
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License along
15 with this program; if not, write to the Free Software Foundation, Inc.,
16 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17*/
18
19#ifndef __MIMETREEPARSER_MESSAGEPARTRENDERER_IF_H__
20#define __MIMETREEPARSER_MESSAGEPARTRENDERER_IF_H__
21
22#include <QSharedPointer>
23
24namespace MimeTreeParser
25{
26namespace Interface
27{
28/**
29* Interface for rendering messageparts to html.
30* @author Andras Mantia <sknauss@kde.org>
31*/
32class MessagePartRenderer
33{
34public:
35 typedef QSharedPointer<MessagePartRenderer> Ptr;
36
37 virtual ~MessagePartRenderer();
38
39 virtual QString html() const = 0;
40};
41}
42}
43#endif
diff --git a/framework/src/domain/mime/mimetreeparser/otp/mimetreeparser_debug.cpp b/framework/src/domain/mime/mimetreeparser/otp/mimetreeparser_debug.cpp
new file mode 100644
index 00000000..f8ac36cd
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/mimetreeparser_debug.cpp
@@ -0,0 +1,3 @@
1#include "mimetreeparser_debug.h"
2
3Q_LOGGING_CATEGORY(MIMETREEPARSER_LOG, "mimetreeparser")
diff --git a/framework/src/domain/mime/mimetreeparser/otp/mimetreeparser_debug.h b/framework/src/domain/mime/mimetreeparser/otp/mimetreeparser_debug.h
new file mode 100644
index 00000000..ddfa6315
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/mimetreeparser_debug.h
@@ -0,0 +1,4 @@
1#pragma once
2
3#include <QLoggingCategory>
4Q_DECLARE_LOGGING_CATEGORY(MIMETREEPARSER_LOG)
diff --git a/framework/src/domain/mime/mimetreeparser/otp/multipartalternative.cpp b/framework/src/domain/mime/mimetreeparser/otp/multipartalternative.cpp
new file mode 100644
index 00000000..42c70e28
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/multipartalternative.cpp
@@ -0,0 +1,94 @@
1/*
2 Copyright (c) 2016 Sandro Knauß <sknauss@kde.org>
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 "multipartalternative.h"
21
22#include "utils.h"
23
24#include "objecttreeparser.h"
25#include "messagepart.h"
26
27#include <KMime/Content>
28
29#include "mimetreeparser_debug.h"
30
31using namespace MimeTreeParser;
32
33const MultiPartAlternativeBodyPartFormatter *MultiPartAlternativeBodyPartFormatter::self;
34
35const Interface::BodyPartFormatter *MultiPartAlternativeBodyPartFormatter::create()
36{
37 if (!self) {
38 self = new MultiPartAlternativeBodyPartFormatter();
39 }
40 return self;
41}
42Interface::BodyPartFormatter::Result MultiPartAlternativeBodyPartFormatter::format(Interface::BodyPart *part, HtmlWriter *writer) const
43{
44 Q_UNUSED(writer)
45 const auto p = process(*part);
46 const auto mp = static_cast<MessagePart *>(p.data());
47 if (mp) {
48 mp->html(false);
49 return Ok;
50 }
51 return Failed;
52}
53
54Interface::MessagePart::Ptr MultiPartAlternativeBodyPartFormatter::process(Interface::BodyPart &part) const
55{
56 KMime::Content *node = part.content();
57 if (node->contents().isEmpty()) {
58 return MessagePart::Ptr();
59 }
60
61 auto preferredMode = part.source()->preferredMode();
62 AlternativeMessagePart::Ptr mp(new AlternativeMessagePart(part.objectTreeParser(), node, preferredMode));
63 if (mp->mChildNodes.isEmpty()) {
64 MimeMessagePart::Ptr _mp(new MimeMessagePart(part.objectTreeParser(), node->contents().at(0), false));
65 return _mp;
66 }
67
68 KMime::Content *dataIcal = mp->mChildNodes.contains(Util::MultipartIcal) ? mp->mChildNodes[Util::MultipartIcal] : nullptr;
69 KMime::Content *dataHtml = mp->mChildNodes.contains(Util::MultipartHtml) ? mp->mChildNodes[Util::MultipartHtml] : nullptr;
70 KMime::Content *dataPlain = mp->mChildNodes.contains(Util::MultipartPlain) ? mp->mChildNodes[Util::MultipartPlain] : nullptr;
71
72 // Make sure that in default ical is prefered over html and plain text
73 if (dataIcal && ((preferredMode != Util::MultipartHtml && preferredMode != Util::MultipartPlain))) {
74 if (dataHtml) {
75 part.nodeHelper()->setNodeProcessed(dataHtml, false);
76 }
77 if (dataPlain) {
78 part.nodeHelper()->setNodeProcessed(dataPlain, false);
79 }
80 preferredMode = Util::MultipartIcal;
81 } else if ((dataHtml && (preferredMode == Util::MultipartHtml || preferredMode == Util::Html)) ||
82 (dataHtml && dataPlain && dataPlain->body().isEmpty())) {
83 if (dataPlain) {
84 part.nodeHelper()->setNodeProcessed(dataPlain, false);
85 }
86 preferredMode = Util::MultipartHtml;
87 } else if (!(preferredMode == Util::MultipartHtml) && dataPlain) {
88 part.nodeHelper()->setNodeProcessed(dataHtml, false);
89 preferredMode = Util::MultipartPlain;
90 }
91 part.source()->setHtmlMode(preferredMode, mp->availableModes());
92 mp->mPreferredMode = preferredMode;
93 return mp;
94}
diff --git a/framework/src/domain/mime/mimetreeparser/otp/multipartalternative.h b/framework/src/domain/mime/mimetreeparser/otp/multipartalternative.h
new file mode 100644
index 00000000..78e5ef38
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/multipartalternative.h
@@ -0,0 +1,41 @@
1/*
2 Copyright (c) 2016 Sandro Knauß <sknauss@kde.org>
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#ifndef __MIMETREEPARSER_BODYFORAMATTER_MULTIPARTALTERNATIVE_H__
21#define __MIMETREEPARSER_BODYFORAMATTER_MULTIPARTALTERNATIVE_H__
22
23#include "bodypartformatter.h"
24#include "bodypart.h"
25
26namespace MimeTreeParser
27{
28
29class MultiPartAlternativeBodyPartFormatter : public Interface::BodyPartFormatter
30{
31 static const MultiPartAlternativeBodyPartFormatter *self;
32public:
33 Interface::MessagePart::Ptr process(Interface::BodyPart &part) const Q_DECL_OVERRIDE;
34 Interface::BodyPartFormatter::Result format(Interface::BodyPart *, HtmlWriter *) const Q_DECL_OVERRIDE;
35 using Interface::BodyPartFormatter::format;
36 static const Interface::BodyPartFormatter *create();
37};
38
39}
40
41#endif
diff --git a/framework/src/domain/mime/mimetreeparser/otp/multipartencrypted.cpp b/framework/src/domain/mime/mimetreeparser/otp/multipartencrypted.cpp
new file mode 100644
index 00000000..7a049318
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/multipartencrypted.cpp
@@ -0,0 +1,111 @@
1/*
2 Copyright (c) 2016 Sandro Knauß <sknauss@kde.org>
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 "multipartencrypted.h"
21
22#include "utils.h"
23
24#include "objecttreeparser.h"
25#include "messagepart.h"
26
27#include <KMime/Content>
28
29#include <QGpgME/Protocol>
30
31#include "mimetreeparser_debug.h"
32
33using namespace MimeTreeParser;
34
35const MultiPartEncryptedBodyPartFormatter *MultiPartEncryptedBodyPartFormatter::self;
36
37const Interface::BodyPartFormatter *MultiPartEncryptedBodyPartFormatter::create()
38{
39 if (!self) {
40 self = new MultiPartEncryptedBodyPartFormatter();
41 }
42 return self;
43}
44Interface::BodyPartFormatter::Result MultiPartEncryptedBodyPartFormatter::format(Interface::BodyPart *part, HtmlWriter *writer) const
45{
46 Q_UNUSED(writer)
47 const auto p = process(*part);
48 const auto mp = static_cast<MessagePart *>(p.data());
49 if (mp) {
50 mp->html(false);
51 return Ok;
52 }
53 return Failed;
54}
55
56Interface::MessagePart::Ptr MultiPartEncryptedBodyPartFormatter::process(Interface::BodyPart &part) const
57{
58 KMime::Content *node = part.content();
59
60 if (node->contents().isEmpty()) {
61 Q_ASSERT(false);
62 return MessagePart::Ptr();
63 }
64
65 const QGpgME::Protocol *useThisCryptProto = nullptr;
66
67 /*
68 ATTENTION: This code is to be replaced by the new 'auto-detect' feature. --------------------------------------
69 */
70 KMime::Content *data = findTypeInDirectChilds(node, "application/octet-stream");
71 if (data) {
72 useThisCryptProto = QGpgME::openpgp();
73 }
74 if (!data) {
75 data = findTypeInDirectChilds(node, "application/pkcs7-mime");
76 if (data) {
77 useThisCryptProto = QGpgME::smime();
78 }
79 }
80 /*
81 ---------------------------------------------------------------------------------------------------------------
82 */
83
84 if (!data) {
85 return MessagePart::Ptr(new MimeMessagePart(part.objectTreeParser(), node->contents().at(0), false));
86 }
87
88 part.nodeHelper()->setEncryptionState(node, KMMsgFullyEncrypted);
89
90 EncryptedMessagePart::Ptr mp(new EncryptedMessagePart(part.objectTreeParser(),
91 data->decodedText(), useThisCryptProto,
92 part.nodeHelper()->fromAsString(data), node));
93 mp->setIsEncrypted(true);
94 mp->setDecryptMessage(part.source()->decryptMessage());
95 PartMetaData *messagePart(mp->partMetaData());
96 if (!part.source()->decryptMessage()) {
97 part.nodeHelper()->setNodeProcessed(data, false); // Set the data node to done to prevent it from being processed
98 } else if (KMime::Content *newNode = part.nodeHelper()->decryptedNodeForContent(data)) {
99 // if we already have a decrypted node for part.objectTreeParser() encrypted node, don't do the decryption again
100 return MessagePart::Ptr(new MimeMessagePart(part.objectTreeParser(), newNode, true));
101 } else {
102 mp->startDecryption(data);
103
104 qCDebug(MIMETREEPARSER_LOG) << "decrypted, signed?:" << messagePart->isSigned;
105
106 if (!messagePart->inProgress) {
107 part.nodeHelper()->setNodeProcessed(data, false); // Set the data node to done to prevent it from being processed
108 }
109 }
110 return mp;
111}
diff --git a/framework/src/domain/mime/mimetreeparser/otp/multipartencrypted.h b/framework/src/domain/mime/mimetreeparser/otp/multipartencrypted.h
new file mode 100644
index 00000000..0d2e01a8
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/multipartencrypted.h
@@ -0,0 +1,41 @@
1/*
2 Copyright (c) 2016 Sandro Knauß <sknauss@kde.org>
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#ifndef __MIMETREEPARSER_BODYFORAMATTER_MULTIPARTENCRYPTED_H__
21#define __MIMETREEPARSER_BODYFORAMATTER_MULTIPARTENCRYPTED_H__
22
23#include "bodypartformatter.h"
24#include "bodypart.h"
25
26namespace MimeTreeParser
27{
28
29class MultiPartEncryptedBodyPartFormatter : public Interface::BodyPartFormatter
30{
31 static const MultiPartEncryptedBodyPartFormatter *self;
32public:
33 Interface::MessagePart::Ptr process(Interface::BodyPart &part) const Q_DECL_OVERRIDE;
34 Interface::BodyPartFormatter::Result format(Interface::BodyPart *, HtmlWriter *) const Q_DECL_OVERRIDE;
35 using Interface::BodyPartFormatter::format;
36 static const Interface::BodyPartFormatter *create();
37};
38
39}
40
41#endif
diff --git a/framework/src/domain/mime/mimetreeparser/otp/multipartmixed.cpp b/framework/src/domain/mime/mimetreeparser/otp/multipartmixed.cpp
new file mode 100644
index 00000000..43056e51
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/multipartmixed.cpp
@@ -0,0 +1,61 @@
1/*
2 Copyright (c) 2016 Sandro Knauß <sknauss@kde.org>
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 "multipartmixed.h"
21
22#include "objecttreeparser.h"
23#include "messagepart.h"
24
25#include <KMime/Content>
26
27#include "mimetreeparser_debug.h"
28
29using namespace MimeTreeParser;
30
31const MultiPartMixedBodyPartFormatter *MultiPartMixedBodyPartFormatter::self;
32
33const Interface::BodyPartFormatter *MultiPartMixedBodyPartFormatter::create()
34{
35 if (!self) {
36 self = new MultiPartMixedBodyPartFormatter();
37 }
38 return self;
39}
40Interface::BodyPartFormatter::Result MultiPartMixedBodyPartFormatter::format(Interface::BodyPart *part, HtmlWriter *writer) const
41{
42 Q_UNUSED(writer)
43 const auto p = process(*part);
44 const auto mp = static_cast<MessagePart *>(p.data());
45 if (mp) {
46 mp->html(false);
47 return Ok;
48 }
49 return Failed;
50}
51
52Interface::MessagePart::Ptr MultiPartMixedBodyPartFormatter::process(Interface::BodyPart &part) const
53{
54 if (part.content()->contents().isEmpty()) {
55 return MessagePart::Ptr();
56 }
57
58 // normal treatment of the parts in the mp/mixed container
59 MimeMessagePart::Ptr mp(new MimeMessagePart(part.objectTreeParser(), part.content()->contents().at(0), false));
60 return mp;
61}
diff --git a/framework/src/domain/mime/mimetreeparser/otp/multipartmixed.h b/framework/src/domain/mime/mimetreeparser/otp/multipartmixed.h
new file mode 100644
index 00000000..0029501b
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/multipartmixed.h
@@ -0,0 +1,41 @@
1/*
2 Copyright (c) 2016 Sandro Knauß <sknauss@kde.org>
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#ifndef __MIMETREEPARSER_BODYFORAMATTER_MULTIPARTMIXED_H__
21#define __MIMETREEPARSER_BODYFORAMATTER_MULTIPARTMIXED_H__
22
23#include "bodypartformatter.h"
24#include "bodypart.h"
25
26namespace MimeTreeParser
27{
28
29class MultiPartMixedBodyPartFormatter : public Interface::BodyPartFormatter
30{
31 static const MultiPartMixedBodyPartFormatter *self;
32public:
33 Interface::MessagePart::Ptr process(Interface::BodyPart &part) const Q_DECL_OVERRIDE;
34 Interface::BodyPartFormatter::Result format(Interface::BodyPart *, HtmlWriter *) const Q_DECL_OVERRIDE;
35 using Interface::BodyPartFormatter::format;
36 static const Interface::BodyPartFormatter *create();
37};
38
39}
40
41#endif
diff --git a/framework/src/domain/mime/mimetreeparser/otp/multipartsigned.cpp b/framework/src/domain/mime/mimetreeparser/otp/multipartsigned.cpp
new file mode 100644
index 00000000..cb0def6c
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/multipartsigned.cpp
@@ -0,0 +1,114 @@
1/*
2 Copyright (c) 2016 Sandro Knauß <sknauss@kde.org>
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 "multipartsigned.h"
21
22#include "objecttreeparser.h"
23#include "messagepart.h"
24
25#include <KMime/Content>
26
27#include <QGpgME/Protocol>
28
29#include "mimetreeparser_debug.h"
30
31#include <QTextCodec>
32
33using namespace MimeTreeParser;
34
35const MultiPartSignedBodyPartFormatter *MultiPartSignedBodyPartFormatter::self;
36
37const Interface::BodyPartFormatter *MultiPartSignedBodyPartFormatter::create()
38{
39 if (!self) {
40 self = new MultiPartSignedBodyPartFormatter();
41 }
42 return self;
43}
44Interface::BodyPartFormatter::Result MultiPartSignedBodyPartFormatter::format(Interface::BodyPart *part, HtmlWriter *writer) const
45{
46 Q_UNUSED(writer)
47 const auto p = process(*part);
48 const auto mp = static_cast<MessagePart *>(p.data());
49 if (mp) {
50 mp->html(false);
51 return Ok;
52 }
53 return Failed;
54}
55
56Interface::MessagePart::Ptr MultiPartSignedBodyPartFormatter::process(Interface::BodyPart &part) const
57{
58 KMime::Content *node = part.content();
59 if (node->contents().size() != 2) {
60 qCDebug(MIMETREEPARSER_LOG) << "mulitpart/signed must have exactly two child parts!" << endl
61 << "processing as multipart/mixed";
62 if (!node->contents().isEmpty()) {
63 return MessagePart::Ptr(new MimeMessagePart(part.objectTreeParser(), node->contents().at(0), false));
64 } else {
65 return MessagePart::Ptr();
66 }
67 }
68
69 KMime::Content *signedData = node->contents().at(0);
70 KMime::Content *signature = node->contents().at(1);
71 Q_ASSERT(signedData);
72 Q_ASSERT(signature);
73
74 QString protocolContentType = node->contentType()->parameter(QStringLiteral("protocol")).toLower();
75 const QString signatureContentType = QLatin1String(signature->contentType()->mimeType().toLower());
76 if (protocolContentType.isEmpty()) {
77 qCWarning(MIMETREEPARSER_LOG) << "Message doesn't set the protocol for the multipart/signed content-type, "
78 "using content-type of the signature:" << signatureContentType;
79 protocolContentType = signatureContentType;
80 }
81
82 const QGpgME::Protocol *protocol = nullptr;
83 if (protocolContentType == QLatin1String("application/pkcs7-signature") ||
84 protocolContentType == QLatin1String("application/x-pkcs7-signature")) {
85 protocol = QGpgME::smime();
86 } else if (protocolContentType == QLatin1String("application/pgp-signature") ||
87 protocolContentType == QLatin1String("application/x-pgp-signature")) {
88 protocol = QGpgME::openpgp();
89 }
90
91 if (!protocol) {
92 return MessagePart::Ptr(new MimeMessagePart(part.objectTreeParser(), signedData, false));
93 }
94
95 part.nodeHelper()->setNodeProcessed(signature, true);
96
97 part.nodeHelper()->setSignatureState(node, KMMsgFullySigned);
98
99 const QByteArray cleartext = KMime::LFtoCRLF(signedData->encodedContent());
100 const QTextCodec *aCodec(part.objectTreeParser()->codecFor(signedData));
101
102 SignedMessagePart::Ptr mp(new SignedMessagePart(part.objectTreeParser(),
103 aCodec->toUnicode(cleartext), protocol,
104 part.nodeHelper()->fromAsString(node), signature));
105 PartMetaData *messagePart(mp->partMetaData());
106
107 if (protocol) {
108 mp->startVerificationDetached(cleartext, signedData, signature->decodedContent());
109 } else {
110 messagePart->auditLogError = GpgME::Error(GPG_ERR_NOT_IMPLEMENTED);
111 }
112
113 return mp;
114}
diff --git a/framework/src/domain/mime/mimetreeparser/otp/multipartsigned.h b/framework/src/domain/mime/mimetreeparser/otp/multipartsigned.h
new file mode 100644
index 00000000..4b8921ad
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/multipartsigned.h
@@ -0,0 +1,41 @@
1/*
2 Copyright (c) 2016 Sandro Knauß <sknauss@kde.org>
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#ifndef __MIMETREEPARSER_BODYFORAMATTER_MULTIPARTSIGNED_H__
21#define __MIMETREEPARSER_BODYFORAMATTER_MULTIPARTSIGNED_H__
22
23#include "bodypartformatter.h"
24#include "bodypart.h"
25
26namespace MimeTreeParser
27{
28
29class MultiPartSignedBodyPartFormatter : public Interface::BodyPartFormatter
30{
31 static const MultiPartSignedBodyPartFormatter *self;
32public:
33 Interface::MessagePart::Ptr process(Interface::BodyPart &part) const Q_DECL_OVERRIDE;
34 Interface::BodyPartFormatter::Result format(Interface::BodyPart *, HtmlWriter *) const Q_DECL_OVERRIDE;
35 using Interface::BodyPartFormatter::format;
36 static const Interface::BodyPartFormatter *create();
37};
38
39}
40
41#endif
diff --git a/framework/src/domain/mime/mimetreeparser/otp/nodehelper.cpp b/framework/src/domain/mime/mimetreeparser/otp/nodehelper.cpp
new file mode 100644
index 00000000..8e224f1b
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/nodehelper.cpp
@@ -0,0 +1,1069 @@
1/*
2 Copyright (C) 2009 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net
3 Copyright (c) 2009 Andras Mantia <andras@kdab.net>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18*/
19
20#include "nodehelper.h"
21#include "mimetreeparser_debug.h"
22#include "partmetadata.h"
23#include "bodypart.h"
24#include "attachmenttemporaryfilesdirs.h"
25
26#include <KMime/Content>
27#include <KMime/Message>
28#include <KMime/Headers>
29
30#include <QTemporaryFile>
31#include <KLocalizedString>
32#include <kcharsets.h>
33
34#include <QUrl>
35#include <QDir>
36#include <QTextCodec>
37
38#include <string>
39#include <sstream>
40#include <algorithm>
41#include <KCharsets>
42#include <QMimeDatabase>
43#include <QMimeType>
44#include <QFileDevice>
45
46namespace MimeTreeParser
47{
48
49QStringList replySubjPrefixes(QStringList() << QStringLiteral("Re\\s*:") << QStringLiteral("Re\\[\\d+\\]:") << QStringLiteral("Re\\d+:"));
50QStringList forwardSubjPrefixes(QStringList() << QStringLiteral("Fwd:") << QStringLiteral("FW:"));
51
52NodeHelper::NodeHelper() :
53 mAttachmentFilesDir(new AttachmentTemporaryFilesDirs())
54{
55 //TODO(Andras) add methods to modify these prefixes
56
57 mLocalCodec = QTextCodec::codecForLocale();
58
59 // In the case of Japan. Japanese locale name is "eucjp" but
60 // The Japanese mail systems normally used "iso-2022-jp" of locale name.
61 // We want to change locale name from eucjp to iso-2022-jp at KMail only.
62
63 // (Introduction to i18n, 6.6 Limit of Locale technology):
64 // EUC-JP is the de-facto standard for UNIX systems, ISO 2022-JP
65 // is the standard for Internet, and Shift-JIS is the encoding
66 // for Windows and Macintosh.
67 if (mLocalCodec) {
68 const QByteArray codecNameLower = mLocalCodec->name().toLower();
69 if (codecNameLower == "eucjp"
70#if defined Q_OS_WIN || defined Q_OS_MACX
71 || codecNameLower == "shift-jis" // OK?
72#endif
73 ) {
74 mLocalCodec = QTextCodec::codecForName("jis7");
75 // QTextCodec *cdc = QTextCodec::codecForName("jis7");
76 // QTextCodec::setCodecForLocale(cdc);
77 // KLocale::global()->setEncoding(cdc->mibEnum());
78 }
79 }
80}
81
82NodeHelper::~NodeHelper()
83{
84 if (mAttachmentFilesDir) {
85 mAttachmentFilesDir->forceCleanTempFiles();
86 delete mAttachmentFilesDir;
87 mAttachmentFilesDir = nullptr;
88 }
89 clear();
90}
91
92void NodeHelper::setNodeProcessed(KMime::Content *node, bool recurse)
93{
94 if (!node) {
95 return;
96 }
97 mProcessedNodes.append(node);
98 qCDebug(MIMETREEPARSER_LOG) << "Node processed: " << node->index().toString() << node->contentType()->as7BitString();
99 //<< " decodedContent" << node->decodedContent();
100 if (recurse) {
101 const auto contents = node->contents();
102 for (KMime::Content *c : contents) {
103 setNodeProcessed(c, true);
104 }
105 }
106}
107
108void NodeHelper::setNodeUnprocessed(KMime::Content *node, bool recurse)
109{
110 if (!node) {
111 return;
112 }
113 mProcessedNodes.removeAll(node);
114
115 //avoid double addition of extra nodes, eg. encrypted attachments
116 const QMap<KMime::Content *, QList<KMime::Content *> >::iterator it = mExtraContents.find(node);
117 if (it != mExtraContents.end()) {
118 Q_FOREACH (KMime::Content *c, it.value()) {
119 KMime::Content *p = c->parent();
120 if (p) {
121 p->removeContent(c);
122 }
123 }
124 qDeleteAll(it.value());
125 qCDebug(MIMETREEPARSER_LOG) << "mExtraContents deleted for" << it.key();
126 mExtraContents.erase(it);
127 }
128
129 qCDebug(MIMETREEPARSER_LOG) << "Node UNprocessed: " << node;
130 if (recurse) {
131 const auto contents = node->contents();
132 for (KMime::Content *c : contents) {
133 setNodeUnprocessed(c, true);
134 }
135 }
136}
137
138bool NodeHelper::nodeProcessed(KMime::Content *node) const
139{
140 if (!node) {
141 return true;
142 }
143 return mProcessedNodes.contains(node);
144}
145
146static void clearBodyPartMemento(QMap<QByteArray, Interface::BodyPartMemento *> &bodyPartMementoMap)
147{
148 for (QMap<QByteArray, Interface::BodyPartMemento *>::iterator
149 it = bodyPartMementoMap.begin(), end = bodyPartMementoMap.end();
150 it != end; ++it) {
151 Interface::BodyPartMemento *memento = it.value();
152 memento->detach();
153 delete memento;
154 }
155 bodyPartMementoMap.clear();
156}
157
158void NodeHelper::clear()
159{
160 mProcessedNodes.clear();
161 mEncryptionState.clear();
162 mSignatureState.clear();
163 mOverrideCodecs.clear();
164 std::for_each(mBodyPartMementoMap.begin(), mBodyPartMementoMap.end(),
165 &clearBodyPartMemento);
166 mBodyPartMementoMap.clear();
167 QMap<KMime::Content *, QList<KMime::Content *> >::ConstIterator end(mExtraContents.constEnd());
168
169 for (QMap<KMime::Content *, QList<KMime::Content *> >::ConstIterator it = mExtraContents.constBegin(); it != end; ++it) {
170 Q_FOREACH (KMime::Content *c, it.value()) {
171 KMime::Content *p = c->parent();
172 if (p) {
173 p->removeContent(c);
174 }
175 }
176 qDeleteAll(it.value());
177 qCDebug(MIMETREEPARSER_LOG) << "mExtraContents deleted for" << it.key();
178 }
179 mExtraContents.clear();
180 mDisplayEmbeddedNodes.clear();
181 mDisplayHiddenNodes.clear();
182}
183
184void NodeHelper::setEncryptionState(const KMime::Content *node, const KMMsgEncryptionState state)
185{
186 mEncryptionState[node] = state;
187}
188
189KMMsgEncryptionState NodeHelper::encryptionState(const KMime::Content *node) const
190{
191 return mEncryptionState.value(node, KMMsgNotEncrypted);
192}
193
194void NodeHelper::setSignatureState(const KMime::Content *node, const KMMsgSignatureState state)
195{
196 mSignatureState[node] = state;
197}
198
199KMMsgSignatureState NodeHelper::signatureState(const KMime::Content *node) const
200{
201 return mSignatureState.value(node, KMMsgNotSigned);
202}
203
204PartMetaData NodeHelper::partMetaData(KMime::Content *node)
205{
206 return mPartMetaDatas.value(node, PartMetaData());
207}
208
209void NodeHelper::setPartMetaData(KMime::Content *node, const PartMetaData &metaData)
210{
211 mPartMetaDatas.insert(node, metaData);
212}
213
214QString NodeHelper::writeNodeToTempFile(KMime::Content *node)
215{
216 // If the message part is already written to a file, no point in doing it again.
217 // This function is called twice actually, once from the rendering of the attachment
218 // in the body and once for the header.
219 QUrl existingFileName = tempFileUrlFromNode(node);
220 if (!existingFileName.isEmpty()) {
221 return existingFileName.toLocalFile();
222 }
223
224 QString fname = createTempDir(persistentIndex(node));
225 if (fname.isEmpty()) {
226 return QString();
227 }
228
229 QString fileName = NodeHelper::fileName(node);
230 // strip off a leading path
231 int slashPos = fileName.lastIndexOf(QLatin1Char('/'));
232 if (-1 != slashPos) {
233 fileName = fileName.mid(slashPos + 1);
234 }
235 if (fileName.isEmpty()) {
236 fileName = QStringLiteral("unnamed");
237 }
238 fname += QLatin1Char('/') + fileName;
239
240 qCDebug(MIMETREEPARSER_LOG) << "Create temp file: " << fname;
241 QByteArray data = node->decodedContent();
242 if (node->contentType()->isText() && !data.isEmpty()) {
243 // convert CRLF to LF before writing text attachments to disk
244 data = KMime::CRLFtoLF(data);
245 }
246 QFile f(fname);
247 if (!f.open(QIODevice::ReadWrite)) {
248 qCWarning(MIMETREEPARSER_LOG) << "Failed to write note to file:" << f.errorString();
249 return QString();
250 }
251 f.write(data);
252 mAttachmentFilesDir->addTempFile(fname);
253 // make file read-only so that nobody gets the impression that he might
254 // edit attached files (cf. bug #52813)
255 f.setPermissions(QFileDevice::ReadUser);
256 f.close();
257
258 return fname;
259}
260
261QUrl NodeHelper::tempFileUrlFromNode(const KMime::Content *node)
262{
263 if (!node) {
264 return QUrl();
265 }
266
267 const QString index = persistentIndex(node);
268
269 foreach (const QString &path, mAttachmentFilesDir->temporaryFiles()) {
270 const int right = path.lastIndexOf(QLatin1Char('/'));
271 int left = path.lastIndexOf(QLatin1String(".index."), right);
272 if (left != -1) {
273 left += 7;
274 }
275
276 QStringRef storedIndex(&path, left, right - left);
277 if (left != -1 && storedIndex == index) {
278 return QUrl::fromLocalFile(path);
279 }
280 }
281 return QUrl();
282}
283
284QString NodeHelper::createTempDir(const QString &param)
285{
286 QTemporaryFile *tempFile = new QTemporaryFile(QDir::tempPath() + QLatin1String("/messageviewer_XXXXXX") + QLatin1String(".index.") + param);
287 tempFile->open();
288 const QString fname = tempFile->fileName();
289 delete tempFile;
290
291 QFile fFile(fname);
292 if (!(fFile.permissions() & QFileDevice::WriteUser)) {
293 // Not there or not writable
294 if (!QDir().mkpath(fname) ||
295 !fFile.setPermissions(QFileDevice::WriteUser | QFileDevice::ReadUser | QFileDevice::ExeUser)) {
296 return QString(); //failed create
297 }
298 }
299
300 Q_ASSERT(!fname.isNull());
301
302 mAttachmentFilesDir->addTempDir(fname);
303 return fname;
304}
305
306void NodeHelper::forceCleanTempFiles()
307{
308 mAttachmentFilesDir->forceCleanTempFiles();
309 delete mAttachmentFilesDir;
310 mAttachmentFilesDir = nullptr;
311}
312
313void NodeHelper::removeTempFiles()
314{
315 //Don't delete it it will delete in class
316 mAttachmentFilesDir->removeTempFiles();
317 mAttachmentFilesDir = new AttachmentTemporaryFilesDirs();
318}
319
320void NodeHelper::addTempFile(const QString &file)
321{
322 mAttachmentFilesDir->addTempFile(file);
323}
324
325bool NodeHelper::isInEncapsulatedMessage(KMime::Content *node)
326{
327 const KMime::Content *const topLevel = node->topLevel();
328 const KMime::Content *cur = node;
329 while (cur && cur != topLevel) {
330 const bool parentIsMessage = cur->parent() && cur->parent()->contentType(false) &&
331 cur->parent()->contentType()->mimeType().toLower() == "message/rfc822";
332 if (parentIsMessage && cur->parent() != topLevel) {
333 return true;
334 }
335 cur = cur->parent();
336 }
337 return false;
338}
339
340QByteArray NodeHelper::charset(KMime::Content *node)
341{
342 if (node->contentType(false)) {
343 return node->contentType(false)->charset();
344 } else {
345 return node->defaultCharset();
346 }
347}
348
349KMMsgEncryptionState NodeHelper::overallEncryptionState(KMime::Content *node) const
350{
351 KMMsgEncryptionState myState = KMMsgEncryptionStateUnknown;
352 if (!node) {
353 return myState;
354 }
355
356 KMime::Content *parent = node->parent();
357 auto contents = parent ? parent->contents() : KMime::Content::List();
358 if (contents.isEmpty()) {
359 contents.append(node);
360 }
361 int i = contents.indexOf(const_cast<KMime::Content *>(node));
362 for (; i < contents.size(); ++i) {
363 auto next = contents.at(i);
364 KMMsgEncryptionState otherState = encryptionState(next);
365
366 // NOTE: children are tested ONLY when parent is not encrypted
367 if (otherState == KMMsgNotEncrypted && !next->contents().isEmpty()) {
368 otherState = overallEncryptionState(next->contents().at(0));
369 }
370
371 if (otherState == KMMsgNotEncrypted && !extraContents(next).isEmpty()) {
372 otherState = overallEncryptionState(extraContents(next).at(0));
373 }
374
375 if (next == node) {
376 myState = otherState;
377 }
378
379 switch (otherState) {
380 case KMMsgEncryptionStateUnknown:
381 break;
382 case KMMsgNotEncrypted:
383 if (myState == KMMsgFullyEncrypted) {
384 myState = KMMsgPartiallyEncrypted;
385 } else if (myState != KMMsgPartiallyEncrypted) {
386 myState = KMMsgNotEncrypted;
387 }
388 break;
389 case KMMsgPartiallyEncrypted:
390 myState = KMMsgPartiallyEncrypted;
391 break;
392 case KMMsgFullyEncrypted:
393 if (myState != KMMsgFullyEncrypted) {
394 myState = KMMsgPartiallyEncrypted;
395 }
396 break;
397 case KMMsgEncryptionProblematic:
398 break;
399 }
400 }
401
402 qCDebug(MIMETREEPARSER_LOG) << "\n\n KMMsgEncryptionState:" << myState;
403
404 return myState;
405}
406
407KMMsgSignatureState NodeHelper::overallSignatureState(KMime::Content *node) const
408{
409 KMMsgSignatureState myState = KMMsgSignatureStateUnknown;
410 if (!node) {
411 return myState;
412 }
413
414 KMime::Content *parent = node->parent();
415 auto contents = parent ? parent->contents() : KMime::Content::List();
416 if (contents.isEmpty()) {
417 contents.append(node);
418 }
419 int i = contents.indexOf(const_cast<KMime::Content *>(node));
420 for (; i < contents.size(); ++i) {
421 auto next = contents.at(i);
422 KMMsgSignatureState otherState = signatureState(next);
423
424 // NOTE: children are tested ONLY when parent is not encrypted
425 if (otherState == KMMsgNotSigned && !next->contents().isEmpty()) {
426 otherState = overallSignatureState(next->contents().at(0));
427 }
428
429 if (otherState == KMMsgNotSigned && !extraContents(next).isEmpty()) {
430 otherState = overallSignatureState(extraContents(next).at(0));
431 }
432
433 if (next == node) {
434 myState = otherState;
435 }
436
437 switch (otherState) {
438 case KMMsgSignatureStateUnknown:
439 break;
440 case KMMsgNotSigned:
441 if (myState == KMMsgFullySigned) {
442 myState = KMMsgPartiallySigned;
443 } else if (myState != KMMsgPartiallySigned) {
444 myState = KMMsgNotSigned;
445 }
446 break;
447 case KMMsgPartiallySigned:
448 myState = KMMsgPartiallySigned;
449 break;
450 case KMMsgFullySigned:
451 if (myState != KMMsgFullySigned) {
452 myState = KMMsgPartiallySigned;
453 }
454 break;
455 case KMMsgSignatureProblematic:
456 break;
457 }
458 }
459
460 qCDebug(MIMETREEPARSER_LOG) << "\n\n KMMsgSignatureState:" << myState;
461
462 return myState;
463}
464
465void NodeHelper::magicSetType(KMime::Content *node, bool aAutoDecode)
466{
467 const QByteArray body = (aAutoDecode) ? node->decodedContent() : node->body();
468 QMimeDatabase db;
469 QMimeType mime = db.mimeTypeForData(body);
470
471 QString mimetype = mime.name();
472 node->contentType()->setMimeType(mimetype.toLatin1());
473}
474
475// static
476QString NodeHelper::replacePrefixes(const QString &str,
477 const QStringList &prefixRegExps,
478 bool replace,
479 const QString &newPrefix)
480{
481 bool recognized = false;
482 // construct a big regexp that
483 // 1. is anchored to the beginning of str (sans whitespace)
484 // 2. matches at least one of the part regexps in prefixRegExps
485 QString bigRegExp = QStringLiteral("^(?:\\s+|(?:%1))+\\s*")
486 .arg(prefixRegExps.join(QStringLiteral(")|(?:")));
487 QRegExp rx(bigRegExp, Qt::CaseInsensitive);
488 if (!rx.isValid()) {
489 qCWarning(MIMETREEPARSER_LOG) << "bigRegExp = \""
490 << bigRegExp << "\"\n"
491 << "prefix regexp is invalid!";
492 // try good ole Re/Fwd:
493 recognized = str.startsWith(newPrefix);
494 } else { // valid rx
495 QString tmp = str;
496 if (rx.indexIn(tmp) == 0) {
497 recognized = true;
498 if (replace) {
499 return tmp.replace(0, rx.matchedLength(), newPrefix + QLatin1Char(' '));
500 }
501 }
502 }
503 if (!recognized) {
504 return newPrefix + QLatin1Char(' ') + str;
505 } else {
506 return str;
507 }
508}
509
510QString NodeHelper::cleanSubject(KMime::Message *message)
511{
512 return cleanSubject(message, replySubjPrefixes + forwardSubjPrefixes,
513 true, QString()).trimmed();
514}
515
516QString NodeHelper::cleanSubject(KMime::Message *message,
517 const QStringList &prefixRegExps,
518 bool replace,
519 const QString &newPrefix)
520{
521 QString cleanStr;
522 if (message) {
523 cleanStr =
524 NodeHelper::replacePrefixes(
525 message->subject()->asUnicodeString(), prefixRegExps, replace, newPrefix);
526 }
527 return cleanStr;
528}
529
530void NodeHelper::setOverrideCodec(KMime::Content *node, const QTextCodec *codec)
531{
532 if (!node) {
533 return;
534 }
535
536 mOverrideCodecs[node] = codec;
537}
538
539const QTextCodec *NodeHelper::codec(KMime::Content *node)
540{
541 if (! node) {
542 return mLocalCodec;
543 }
544
545 const QTextCodec *c = mOverrideCodecs.value(node, nullptr);
546 if (!c) {
547 // no override-codec set for this message, try the CT charset parameter:
548 QByteArray charset = node->contentType()->charset();
549
550 // utf-8 is a superset of us-ascii, so we don't loose anything, if we it insead
551 // utf-8 is nowadays that widely, that it is a good guess to use it to fix issus with broken clients.
552 if (charset.toLower() == "us-ascii") {
553 charset = "utf-8";
554 }
555 c = codecForName(charset);
556 }
557 if (!c) {
558 // no charset means us-ascii (RFC 2045), so using local encoding should
559 // be okay
560 c = mLocalCodec;
561 }
562 return c;
563}
564
565const QTextCodec *NodeHelper::codecForName(const QByteArray &_str)
566{
567 if (_str.isEmpty()) {
568 return nullptr;
569 }
570 QByteArray codec = _str.toLower();
571 return KCharsets::charsets()->codecForName(QLatin1String(codec));
572}
573
574QString NodeHelper::fileName(const KMime::Content *node)
575{
576 QString name = const_cast<KMime::Content *>(node)->contentDisposition()->filename();
577 if (name.isEmpty()) {
578 name = const_cast<KMime::Content *>(node)->contentType()->name();
579 }
580
581 name = name.trimmed();
582 return name;
583}
584
585//FIXME(Andras) review it (by Marc?) to see if I got it right. This is supposed to be the partNode::internalBodyPartMemento replacement
586Interface::BodyPartMemento *NodeHelper::bodyPartMemento(KMime::Content *node,
587 const QByteArray &which) const
588{
589 const QMap< QString, QMap<QByteArray, Interface::BodyPartMemento *> >::const_iterator nit
590 = mBodyPartMementoMap.find(persistentIndex(node));
591 if (nit == mBodyPartMementoMap.end()) {
592 return nullptr;
593 }
594 const QMap<QByteArray, Interface::BodyPartMemento *>::const_iterator it =
595 nit->find(which.toLower());
596 return it != nit->end() ? it.value() : nullptr;
597}
598
599//FIXME(Andras) review it (by Marc?) to see if I got it right. This is supposed to be the partNode::internalSetBodyPartMemento replacement
600void NodeHelper::setBodyPartMemento(KMime::Content *node, const QByteArray &which,
601 Interface::BodyPartMemento *memento)
602{
603 QMap<QByteArray, Interface::BodyPartMemento *> &mementos
604 = mBodyPartMementoMap[persistentIndex(node)];
605
606 const QByteArray whichLower = which.toLower();
607 const QMap<QByteArray, Interface::BodyPartMemento *>::iterator it =
608 mementos.lowerBound(whichLower);
609
610 if (it != mementos.end() && it.key() == whichLower) {
611 delete it.value();
612 if (memento) {
613 it.value() = memento;
614 } else {
615 mementos.erase(it);
616 }
617 } else {
618 mementos.insert(whichLower, memento);
619 }
620}
621
622bool NodeHelper::isNodeDisplayedEmbedded(KMime::Content *node) const
623{
624 qCDebug(MIMETREEPARSER_LOG) << "IS NODE: " << mDisplayEmbeddedNodes.contains(node);
625 return mDisplayEmbeddedNodes.contains(node);
626}
627
628void NodeHelper::setNodeDisplayedEmbedded(KMime::Content *node, bool displayedEmbedded)
629{
630 qCDebug(MIMETREEPARSER_LOG) << "SET NODE: " << node << displayedEmbedded;
631 if (displayedEmbedded) {
632 mDisplayEmbeddedNodes.insert(node);
633 } else {
634 mDisplayEmbeddedNodes.remove(node);
635 }
636}
637
638bool NodeHelper::isNodeDisplayedHidden(KMime::Content *node) const
639{
640 return mDisplayHiddenNodes.contains(node);
641}
642
643void NodeHelper::setNodeDisplayedHidden(KMime::Content *node, bool displayedHidden)
644{
645 if (displayedHidden) {
646 mDisplayHiddenNodes.insert(node);
647 } else {
648 mDisplayEmbeddedNodes.remove(node);
649 }
650}
651
652/*!
653 Creates a persistent index string that bridges the gap between the
654 permanent nodes and the temporary ones.
655
656 Used internally for robust indexing.
657*/
658QString NodeHelper::persistentIndex(const KMime::Content *node) const
659{
660 if (!node) {
661 return QString();
662 }
663
664 QString indexStr = node->index().toString();
665 if (indexStr.isEmpty()) {
666 QMapIterator<KMime::Message::Content *, QList<KMime::Content *> > it(mExtraContents);
667 while (it.hasNext()) {
668 it.next();
669 const auto &extraNodes = it.value();
670 for (int i = 0; i < extraNodes.size(); i++) {
671 if (extraNodes[i] == node) {
672 indexStr = QString::fromLatin1("e%1").arg(i);
673 const QString parentIndex = persistentIndex(it.key());
674 if (!parentIndex.isEmpty()) {
675 indexStr = QString::fromLatin1("%1:%2").arg(parentIndex, indexStr);
676 }
677 return indexStr;
678 }
679 }
680 }
681 } else {
682 const KMime::Content *const topLevel = node->topLevel();
683 //if the node is an extra node, prepend the index of the extra node to the url
684 QMapIterator<KMime::Message::Content *, QList<KMime::Content *> > it(mExtraContents);
685 while (it.hasNext()) {
686 it.next();
687 const QList<KMime::Content *> &extraNodes = extraContents(it.key());
688 for (int i = 0; i < extraNodes.size(); ++i) {
689 KMime::Content *const extraNode = extraNodes[i];
690 if (topLevel == extraNode) {
691 indexStr.prepend(QStringLiteral("e%1:").arg(i));
692 const QString parentIndex = persistentIndex(it.key());
693 if (!parentIndex.isEmpty()) {
694 indexStr = QStringLiteral("%1:%2").arg(parentIndex, indexStr);
695 }
696 return indexStr;
697 }
698 }
699 }
700 }
701
702 return indexStr;
703}
704
705KMime::Content *NodeHelper::contentFromIndex(KMime::Content *node, const QString &persistentIndex) const
706{
707 KMime::Content *c = node->topLevel();
708 if (c) {
709 const QStringList pathParts = persistentIndex.split(QLatin1Char(':'), QString::SkipEmptyParts);
710 const int pathPartsSize(pathParts.size());
711 for (int i = 0; i < pathPartsSize; ++i) {
712 const QString &path = pathParts[i];
713 if (path.startsWith(QLatin1Char('e'))) {
714 const QList<KMime::Content *> &extraParts = mExtraContents.value(c);
715 const int idx = path.midRef(1, -1).toInt();
716 c = (idx < extraParts.size()) ? extraParts[idx] : nullptr;
717 } else {
718 c = c->content(KMime::ContentIndex(path));
719 }
720 if (!c) {
721 break;
722 }
723 }
724 }
725 return c;
726}
727
728QString NodeHelper::asHREF(const KMime::Content *node, const QString &place) const
729{
730 return QStringLiteral("attachment:%1?place=%2").arg(persistentIndex(node), place);
731}
732
733KMime::Content *NodeHelper::fromHREF(const KMime::Message::Ptr &mMessage, const QUrl &url) const
734{
735 if (url.isEmpty()) {
736 return mMessage.data();
737 }
738
739 if (!url.isLocalFile()) {
740 return contentFromIndex(mMessage.data(), url.adjusted(QUrl::StripTrailingSlash).path());
741 } else {
742 const QString path = url.toLocalFile();
743 // extract from /<path>/qttestn28554.index.2.3:0:2/unnamed -> "2.3:0:2"
744 // start of the index is something that is not a number followed by a dot: \D.
745 // index is only made of numbers,"." and ":": ([0-9.:]+)
746 // index is the last part of the folder name: /
747 const QRegExp rIndex(QStringLiteral("\\D\\.([e0-9.:]+)/"));
748
749 //search the occurence at most at the end
750 if (rIndex.lastIndexIn(path) != -1) {
751 return contentFromIndex(mMessage.data(), rIndex.cap(1));
752 }
753 return mMessage.data();
754 }
755}
756
757QString NodeHelper::fixEncoding(const QString &encoding)
758{
759 QString returnEncoding = encoding;
760 // According to http://www.iana.org/assignments/character-sets, uppercase is
761 // preferred in MIME headers
762 const QString returnEncodingToUpper = returnEncoding.toUpper();
763 if (returnEncodingToUpper.contains(QStringLiteral("ISO "))) {
764 returnEncoding = returnEncodingToUpper;
765 returnEncoding.replace(QLatin1String("ISO "), QStringLiteral("ISO-"));
766 }
767 return returnEncoding;
768}
769
770//-----------------------------------------------------------------------------
771QString NodeHelper::encodingForName(const QString &descriptiveName)
772{
773 QString encoding = KCharsets::charsets()->encodingForName(descriptiveName);
774 return NodeHelper::fixEncoding(encoding);
775}
776
777QStringList NodeHelper::supportedEncodings(bool usAscii)
778{
779 QStringList encodingNames = KCharsets::charsets()->availableEncodingNames();
780 QStringList encodings;
781 QMap<QString, bool> mimeNames;
782 QStringList::ConstIterator constEnd(encodingNames.constEnd());
783 for (QStringList::ConstIterator it = encodingNames.constBegin();
784 it != constEnd; ++it) {
785 QTextCodec *codec = KCharsets::charsets()->codecForName(*it);
786 QString mimeName = (codec) ? QString::fromLatin1(codec->name()).toLower() : (*it);
787 if (!mimeNames.contains(mimeName)) {
788 encodings.append(KCharsets::charsets()->descriptionForEncoding(*it));
789 mimeNames.insert(mimeName, true);
790 }
791 }
792 encodings.sort();
793 if (usAscii) {
794 encodings.prepend(KCharsets::charsets()->descriptionForEncoding(QStringLiteral("us-ascii")));
795 }
796 return encodings;
797}
798
799QString NodeHelper::fromAsString(KMime::Content *node) const
800{
801 if (auto topLevel = dynamic_cast<KMime::Message *>(node->topLevel())) {
802 return topLevel->from()->asUnicodeString();
803 } else {
804 auto realNode = std::find_if(mExtraContents.cbegin(), mExtraContents.cend(),
805 [node](const QList<KMime::Content *> &nodes) {
806 return nodes.contains(node);
807 });
808 if (realNode != mExtraContents.cend()) {
809 return fromAsString(realNode.key());
810 }
811 }
812
813 return QString();
814}
815
816void NodeHelper::attachExtraContent(KMime::Content *topLevelNode, KMime::Content *content)
817{
818 qCDebug(MIMETREEPARSER_LOG) << "mExtraContents added for" << topLevelNode << " extra content: " << content;
819 mExtraContents[topLevelNode].append(content);
820}
821
822QList< KMime::Content * > NodeHelper::extraContents(KMime::Content *topLevelnode) const
823{
824 return mExtraContents.value(topLevelnode);
825}
826
827void NodeHelper::mergeExtraNodes(KMime::Content *node)
828{
829 if (!node) {
830 return;
831 }
832
833 const QList<KMime::Content * > extraNodes = extraContents(node);
834 for (KMime::Content *extra : extraNodes) {
835 if (node->bodyIsMessage()) {
836 qCWarning(MIMETREEPARSER_LOG) << "Asked to attach extra content to a kmime::message, this does not make sense. Attaching to:" << node <<
837 node->encodedContent() << "\n====== with =======\n" << extra << extra->encodedContent();
838 continue;
839 }
840 KMime::Content *c = new KMime::Content(node);
841 c->setContent(extra->encodedContent());
842 c->parse();
843 node->addContent(c);
844 }
845
846 Q_FOREACH (KMime::Content *child, node->contents()) {
847 mergeExtraNodes(child);
848 }
849}
850
851void NodeHelper::cleanFromExtraNodes(KMime::Content *node)
852{
853 if (!node) {
854 return;
855 }
856 const QList<KMime::Content * > extraNodes = extraContents(node);
857 for (KMime::Content *extra : extraNodes) {
858 QByteArray s = extra->encodedContent();
859 const auto children = node->contents();
860 for (KMime::Content *c : children) {
861 if (c->encodedContent() == s) {
862 node->removeContent(c);
863 }
864 }
865 }
866 Q_FOREACH (KMime::Content *child, node->contents()) {
867 cleanFromExtraNodes(child);
868 }
869}
870
871KMime::Message *NodeHelper::messageWithExtraContent(KMime::Content *topLevelNode)
872{
873 /*The merge is done in several steps:
874 1) merge the extra nodes into topLevelNode
875 2) copy the modified (merged) node tree into a new node tree
876 3) restore the original node tree in topLevelNode by removing the extra nodes from it
877
878 The reason is that extra nodes are assigned by pointer value to the nodes in the original tree.
879 */
880 if (!topLevelNode) {
881 return nullptr;
882 }
883
884 mergeExtraNodes(topLevelNode);
885
886 KMime::Message *m = new KMime::Message;
887 m->setContent(topLevelNode->encodedContent());
888 m->parse();
889
890 cleanFromExtraNodes(topLevelNode);
891// qCDebug(MIMETREEPARSER_LOG) << "MESSAGE WITH EXTRA: " << m->encodedContent();
892// qCDebug(MIMETREEPARSER_LOG) << "MESSAGE WITHOUT EXTRA: " << topLevelNode->encodedContent();
893
894 return m;
895}
896
897KMime::Content *NodeHelper::decryptedNodeForContent(KMime::Content *content) const
898{
899 const QList<KMime::Content *> xc = extraContents(content);
900 if (!xc.empty()) {
901 if (xc.size() == 1) {
902 return xc.front();
903 } else {
904 qCWarning(MIMETREEPARSER_LOG) << "WTF, encrypted node has multiple extra contents?";
905 }
906 }
907 return nullptr;
908}
909
910bool NodeHelper::unencryptedMessage_helper(KMime::Content *node, QByteArray &resultingData, bool addHeaders,
911 int recursionLevel)
912{
913 bool returnValue = false;
914 if (node) {
915 KMime::Content *curNode = node;
916 KMime::Content *decryptedNode = nullptr;
917 const QByteArray type = node->contentType(false) ? QByteArray(node->contentType()->mediaType()).toLower() : "text";
918 const QByteArray subType = node->contentType(false) ? node->contentType()->subType().toLower() : "plain";
919 const bool isMultipart = node->contentType(false) && node->contentType()->isMultipart();
920 bool isSignature = false;
921
922 qCDebug(MIMETREEPARSER_LOG) << "(" << recursionLevel << ") Looking at" << type << "/" << subType;
923
924 if (isMultipart) {
925 if (subType == "signed") {
926 isSignature = true;
927 } else if (subType == "encrypted") {
928 decryptedNode = decryptedNodeForContent(curNode);
929 }
930 } else if (type == "application") {
931 if (subType == "octet-stream") {
932 decryptedNode = decryptedNodeForContent(curNode);
933 } else if (subType == "pkcs7-signature") {
934 isSignature = true;
935 } else if (subType == "pkcs7-mime") {
936 // note: subtype pkcs7-mime can also be signed
937 // and we do NOT want to remove the signature!
938 if (encryptionState(curNode) != KMMsgNotEncrypted) {
939 decryptedNode = decryptedNodeForContent(curNode);
940 }
941 }
942 }
943
944 if (decryptedNode) {
945 qCDebug(MIMETREEPARSER_LOG) << "Current node has an associated decrypted node, adding a modified header "
946 "and then processing the children.";
947
948 Q_ASSERT(addHeaders);
949 KMime::Content headers;
950 headers.setHead(curNode->head());
951 headers.parse();
952 if (decryptedNode->contentType(false)) {
953 headers.contentType()->from7BitString(decryptedNode->contentType()->as7BitString(false));
954 } else {
955 headers.removeHeader<KMime::Headers::ContentType>();
956 }
957 if (decryptedNode->contentTransferEncoding(false)) {
958 headers.contentTransferEncoding()->from7BitString(decryptedNode->contentTransferEncoding()->as7BitString(false));
959 } else {
960 headers.removeHeader<KMime::Headers::ContentTransferEncoding>();
961 }
962 if (decryptedNode->contentDisposition(false)) {
963 headers.contentDisposition()->from7BitString(decryptedNode->contentDisposition()->as7BitString(false));
964 } else {
965 headers.removeHeader<KMime::Headers::ContentDisposition>();
966 }
967 if (decryptedNode->contentDescription(false)) {
968 headers.contentDescription()->from7BitString(decryptedNode->contentDescription()->as7BitString(false));
969 } else {
970 headers.removeHeader<KMime::Headers::ContentDescription>();
971 }
972 headers.assemble();
973
974 resultingData += headers.head() + '\n';
975 unencryptedMessage_helper(decryptedNode, resultingData, false, recursionLevel + 1);
976
977 returnValue = true;
978 }
979
980 else if (isSignature) {
981 qCDebug(MIMETREEPARSER_LOG) << "Current node is a signature, adding it as-is.";
982 // We can't change the nodes under the signature, as that would invalidate it. Add the signature
983 // and its child as-is
984 if (addHeaders) {
985 resultingData += curNode->head() + '\n';
986 }
987 resultingData += curNode->encodedBody();
988 returnValue = false;
989 }
990
991 else if (isMultipart) {
992 qCDebug(MIMETREEPARSER_LOG) << "Current node is a multipart node, adding its header and then processing all children.";
993 // Normal multipart node, add the header and all of its children
994 bool somethingChanged = false;
995 if (addHeaders) {
996 resultingData += curNode->head() + '\n';
997 }
998 const QByteArray boundary = curNode->contentType()->boundary();
999 foreach (KMime::Content *child, curNode->contents()) {
1000 resultingData += "\n--" + boundary + '\n';
1001 const bool changed = unencryptedMessage_helper(child, resultingData, true, recursionLevel + 1);
1002 if (changed) {
1003 somethingChanged = true;
1004 }
1005 }
1006 resultingData += "\n--" + boundary + "--\n\n";
1007 returnValue = somethingChanged;
1008 }
1009
1010 else if (curNode->bodyIsMessage()) {
1011 qCDebug(MIMETREEPARSER_LOG) << "Current node is a message, adding the header and then processing the child.";
1012 if (addHeaders) {
1013 resultingData += curNode->head() + '\n';
1014 }
1015
1016 returnValue = unencryptedMessage_helper(curNode->bodyAsMessage().data(), resultingData, true, recursionLevel + 1);
1017 }
1018
1019 else {
1020 qCDebug(MIMETREEPARSER_LOG) << "Current node is an ordinary leaf node, adding it as-is.";
1021 if (addHeaders) {
1022 resultingData += curNode->head() + '\n';
1023 }
1024 resultingData += curNode->body();
1025 returnValue = false;
1026 }
1027 }
1028
1029 qCDebug(MIMETREEPARSER_LOG) << "(" << recursionLevel << ") done.";
1030 return returnValue;
1031}
1032
1033KMime::Message::Ptr NodeHelper::unencryptedMessage(const KMime::Message::Ptr &originalMessage)
1034{
1035 QByteArray resultingData;
1036 const bool messageChanged = unencryptedMessage_helper(originalMessage.data(), resultingData, true);
1037 if (messageChanged) {
1038#if 0
1039 qCDebug(MIMETREEPARSER_LOG) << "Resulting data is:" << resultingData;
1040 QFile bla("stripped.mbox");
1041 bla.open(QIODevice::WriteOnly);
1042 bla.write(resultingData);
1043 bla.close();
1044#endif
1045 KMime::Message::Ptr newMessage(new KMime::Message);
1046 newMessage->setContent(resultingData);
1047 newMessage->parse();
1048 return newMessage;
1049 } else {
1050 return KMime::Message::Ptr();
1051 }
1052}
1053
1054QVector<KMime::Content *> NodeHelper::attachmentsOfExtraContents() const
1055{
1056 QVector<KMime::Content *> result;
1057 for (auto it = mExtraContents.begin(); it != mExtraContents.end(); ++it) {
1058 foreach (auto content, it.value()) {
1059 if (KMime::isAttachment(content)) {
1060 result.push_back(content);
1061 } else {
1062 result += content->attachments();
1063 }
1064 }
1065 }
1066 return result;
1067}
1068
1069}
diff --git a/framework/src/domain/mime/mimetreeparser/otp/nodehelper.h b/framework/src/domain/mime/mimetreeparser/otp/nodehelper.h
new file mode 100644
index 00000000..70253f21
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/nodehelper.h
@@ -0,0 +1,290 @@
1/*
2 Copyright (C) 2009 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net
3 Copyright (c) 2009 Andras Mantia <andras@kdab.net>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18*/
19
20#ifndef __MIMETREEPARSER_NODEHELPER_H__
21#define __MIMETREEPARSER_NODEHELPER_H__
22
23#include "partmetadata.h"
24#include "enums.h"
25
26#include <KMime/Message>
27
28#include <QList>
29#include <QMap>
30#include <QSet>
31
32class QUrl;
33class QTextCodec;
34
35namespace MimeTreeParser
36{
37class AttachmentTemporaryFilesDirs;
38namespace Interface
39{
40class BodyPartMemento;
41}
42}
43
44namespace MimeTreeParser
45{
46
47/**
48 * @author Andras Mantia <andras@kdab.net>
49 */
50class NodeHelper: public QObject
51{
52 Q_OBJECT
53public:
54 NodeHelper();
55
56 ~NodeHelper();
57
58 void setNodeProcessed(KMime::Content *node, bool recurse);
59 void setNodeUnprocessed(KMime::Content *node, bool recurse);
60 bool nodeProcessed(KMime::Content *node) const;
61 void clear();
62 void forceCleanTempFiles();
63
64 void setEncryptionState(const KMime::Content *node, const KMMsgEncryptionState state);
65 KMMsgEncryptionState encryptionState(const KMime::Content *node) const;
66
67 void setSignatureState(const KMime::Content *node, const KMMsgSignatureState state);
68 KMMsgSignatureState signatureState(const KMime::Content *node) const;
69
70 KMMsgSignatureState overallSignatureState(KMime::Content *node) const;
71 KMMsgEncryptionState overallEncryptionState(KMime::Content *node) const;
72
73 void setPartMetaData(KMime::Content *node, const PartMetaData &metaData);
74 PartMetaData partMetaData(KMime::Content *node);
75
76 /**
77 * Set the 'Content-Type' by mime-magic from the contents of the body.
78 * If autoDecode is true the decoded body will be used for mime type
79 * determination (this does not change the body itself).
80 */
81 void magicSetType(KMime::Content *node, bool autoDecode = true);
82
83 /**
84 * Return this mails subject, with all "forward" and "reply"
85 * prefixes removed
86 */
87 static QString cleanSubject(KMime::Message *message);
88
89 /** Attach an extra node to an existing node */
90 void attachExtraContent(KMime::Content *topLevelNode, KMime::Content *content);
91
92 /** Get the extra nodes attached to the @param topLevelNode and all sub-nodes of @param topLevelNode */
93 QList<KMime::Content *> extraContents(KMime::Content *topLevelNode) const;
94
95 /** Return a modified message (node tree) starting from @param topLevelNode that has the original nodes and the extra nodes.
96 The caller has the responsibility to delete the new message.
97 */
98 KMime::Message *messageWithExtraContent(KMime::Content *topLevelNode);
99
100 /** Get a QTextCodec suitable for this message part */
101 const QTextCodec *codec(KMime::Content *node);
102
103 /** Set the charset the user selected for the message to display */
104 void setOverrideCodec(KMime::Content *node, const QTextCodec *codec);
105
106 Interface::BodyPartMemento *bodyPartMemento(KMime::Content *node, const QByteArray &which) const;
107
108 void setBodyPartMemento(KMime::Content *node, const QByteArray &which,
109 Interface::BodyPartMemento *memento);
110
111 // A flag to remember if the node was embedded. This is useful for attachment nodes, the reader
112 // needs to know if they were displayed inline or not.
113 bool isNodeDisplayedEmbedded(KMime::Content *node) const;
114 void setNodeDisplayedEmbedded(KMime::Content *node, bool displayedEmbedded);
115
116 // Same as above, but this time determines if the node was hidden or not
117 bool isNodeDisplayedHidden(KMime::Content *node) const;
118 void setNodeDisplayedHidden(KMime::Content *node, bool displayedHidden);
119
120 /**
121 * Writes the given message part to a temporary file and returns the
122 * name of this file or QString() if writing failed.
123 */
124 QString writeNodeToTempFile(KMime::Content *node);
125
126 /**
127 * Returns the temporary file path and name where this node was saved, or an empty url
128 * if it wasn't saved yet with writeNodeToTempFile()
129 */
130 QUrl tempFileUrlFromNode(const KMime::Content *node);
131
132 /**
133 * Creates a temporary dir for saving attachments, etc.
134 * Will be automatically deleted when another message is viewed.
135 * @param param Optional part of the directory name.
136 */
137 QString createTempDir(const QString &param = QString());
138
139 /**
140 * Cleanup the attachment temp files
141 */
142 void removeTempFiles();
143
144 /**
145 * Add a file to the list of managed temporary files
146 */
147 void addTempFile(const QString &file);
148
149 // Get a href in the form attachment:<nodeId>?place=<place>, used by ObjectTreeParser and
150 // UrlHandlerManager.
151 QString asHREF(const KMime::Content *node, const QString &place) const;
152 KMime::Content *fromHREF(const KMime::Message::Ptr &mMessage, const QUrl &href) const;
153
154 /**
155 * @return true if this node is a child or an encapsulated message
156 */
157 static bool isInEncapsulatedMessage(KMime::Content *node);
158
159 /**
160 * Returns the charset for the given node. If no charset is specified
161 * for the node, the defaultCharset() is returned.
162 */
163 static QByteArray charset(KMime::Content *node);
164
165 /**
166 * Check for prefixes @p prefixRegExps in @p str. If none
167 * is found, @p newPrefix + ' ' is prepended to @p str and the
168 * resulting string is returned. If @p replace is true, any
169 * sequence of whitespace-delimited prefixes at the beginning of
170 * @p str is replaced by @p newPrefix.
171 */
172 static QString replacePrefixes(const QString &str,
173 const QStringList &prefixRegExps,
174 bool replace,
175 const QString &newPrefix);
176
177 /**
178 * Return a QTextCodec for the specified charset.
179 * This function is a bit more tolerant, than QTextCodec::codecForName
180 */
181 static const QTextCodec *codecForName(const QByteArray &_str);
182
183 /**
184 * Returns a usable filename for a node, that can be the filename from the
185 * content disposition header, or if that one is empty, the name from the
186 * content type header.
187 */
188 static QString fileName(const KMime::Content *node);
189
190 /**
191 * Fixes an encoding received by a KDE function and returns the proper,
192 * MIME-compilant encoding name instead.
193 * @see encodingForName
194 */
195 static QString fixEncoding(const QString &encoding); //TODO(Andras) move to a utility class?
196
197 /**
198 * Drop-in replacement for KCharsets::encodingForName(). The problem with
199 * the KCharsets function is that it returns "human-readable" encoding names
200 * like "ISO 8859-15" instead of valid encoding names like "ISO-8859-15".
201 * This function fixes this by replacing whitespace with a hyphen.
202 */
203 static QString encodingForName(const QString &descriptiveName); //TODO(Andras) move to a utility class?
204
205 /**
206 * Return a list of the supported encodings
207 * @param usAscii if true, US-Ascii encoding will be prepended to the list.
208 */
209 static QStringList supportedEncodings(bool usAscii); //TODO(Andras) move to a utility class?
210
211 QString fromAsString(KMime::Content *node) const;
212
213 KMime::Content *decryptedNodeForContent(KMime::Content *content) const;
214
215 /**
216 * This function returns the unencrypted message that is based on @p originalMessage.
217 * All encrypted MIME parts are removed and replaced by their decrypted plain-text versions.
218 * Encrypted parts that are within signed parts are not replaced, since that would invalidate
219 * the signature.
220 *
221 * This only works if the message was run through ObjectTreeParser::parseObjectTree() with the
222 * currrent NodeHelper before, because parseObjectTree() actually decrypts the message and stores
223 * the decrypted nodes by calling attachExtraContent().
224 *
225 * @return the unencrypted message or an invalid pointer if the original message didn't contain
226 * a part that needed to be modified.
227 */
228 KMime::Message::Ptr unencryptedMessage(const KMime::Message::Ptr &originalMessage);
229
230 /**
231 * Returns a list of attachments of attached extra content nodes.
232 * This is mainly useful is order to get attachments of encrypted messages.
233 * Note that this does not include attachments from the primary node tree.
234 * @see KMime::Content::attachments().
235 */
236 QVector<KMime::Content *> attachmentsOfExtraContents() const;
237
238Q_SIGNALS:
239 void update(MimeTreeParser::UpdateMode);
240
241private:
242 Q_DISABLE_COPY(NodeHelper)
243 bool unencryptedMessage_helper(KMime::Content *node, QByteArray &resultingData, bool addHeaders,
244 int recursionLevel = 1);
245
246 /** Check for prefixes @p prefixRegExps in #subject(). If none
247 is found, @p newPrefix + ' ' is prepended to the subject and the
248 resulting string is returned. If @p replace is true, any
249 sequence of whitespace-delimited prefixes at the beginning of
250 #subject() is replaced by @p newPrefix
251 **/
252 static QString cleanSubject(KMime::Message *message, const QStringList &prefixRegExps,
253 bool replace, const QString &newPrefix);
254
255 void mergeExtraNodes(KMime::Content *node);
256 void cleanFromExtraNodes(KMime::Content *node);
257
258 /** Creates a persistent index string that bridges the gap between the
259 permanent nodes and the temporary ones.
260
261 Used internally for robust indexing.
262 **/
263 QString persistentIndex(const KMime::Content *node) const;
264
265 /** Translates the persistentIndex into a node back
266
267 node: any node of the actually message to what the persistentIndex is interpreded
268 **/
269 KMime::Content *contentFromIndex(KMime::Content *node, const QString &persistentIndex) const;
270
271private:
272 QList<KMime::Content *> mProcessedNodes;
273 QList<KMime::Content *> mNodesUnderProcess;
274 QMap<const KMime::Content *, KMMsgEncryptionState> mEncryptionState;
275 QMap<const KMime::Content *, KMMsgSignatureState> mSignatureState;
276 QSet<KMime::Content *> mDisplayEmbeddedNodes;
277 QSet<KMime::Content *> mDisplayHiddenNodes;
278 QTextCodec *mLocalCodec;
279 QMap<KMime::Content *, const QTextCodec *> mOverrideCodecs;
280 QMap<QString, QMap<QByteArray, Interface::BodyPartMemento *> > mBodyPartMementoMap;
281 QMap<KMime::Content *, PartMetaData> mPartMetaDatas;
282 QMap<KMime::Message::Content *, QList<KMime::Content *> > mExtraContents;
283 AttachmentTemporaryFilesDirs *mAttachmentFilesDir;
284
285 friend class NodeHelperTest;
286};
287
288}
289
290#endif
diff --git a/framework/src/domain/mime/mimetreeparser/otp/objecttreeparser.cpp b/framework/src/domain/mime/mimetreeparser/otp/objecttreeparser.cpp
new file mode 100644
index 00000000..b0d514b6
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/objecttreeparser.cpp
@@ -0,0 +1,495 @@
1/*
2 objecttreeparser.cpp
3
4 This file is part of KMail, the KDE mail client.
5 Copyright (c) 2003 Marc Mutz <mutz@kde.org>
6 Copyright (C) 2002-2004 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net
7 Copyright (c) 2009 Andras Mantia <andras@kdab.net>
8 Copyright (c) 2015 Sandro Knauß <sknauss@kde.org>
9
10 KMail is free software; you can redistribute it and/or modify it
11 under the terms of the GNU General Public License, version 2, as
12 published by the Free Software Foundation.
13
14 KMail is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22
23 In addition, as a special exception, the copyright holders give
24 permission to link the code of this program with any edition of
25 the Qt library by Trolltech AS, Norway (or with modified versions
26 of Qt that use the same license as Qt), and distribute linked
27 combinations including the two. You must obey the GNU General
28 Public License in all respects for all of the code used other than
29 Qt. If you modify this file, you may extend this exception to
30 your version of the file, but you are not obligated to do so. If
31 you do not wish to do so, delete this exception statement from
32 your version.
33*/
34
35// MessageViewer includes
36
37#include "objecttreeparser.h"
38
39#include "attachmentstrategy.h"
40#include "bodypartformatterbasefactory.h"
41#include "nodehelper.h"
42#include "messagepart.h"
43#include "partnodebodypart.h"
44
45#include "mimetreeparser_debug.h"
46
47#include "utils.h"
48#include "bodypartformatter.h"
49#include "htmlwriter.h"
50#include "messagepartrenderer.h"
51#include "util.h"
52
53#include <KMime/Headers>
54#include <KMime/Message>
55
56// KDE includes
57
58// Qt includes
59#include <QByteArray>
60#include <QTextCodec>
61#include <QUrl>
62
63using namespace MimeTreeParser;
64
65ObjectTreeParser::ObjectTreeParser(const ObjectTreeParser *topLevelParser,
66 bool showOnlyOneMimePart,
67 const AttachmentStrategy *strategy)
68 : mSource(topLevelParser->mSource),
69 mNodeHelper(topLevelParser->mNodeHelper),
70 mHtmlWriter(topLevelParser->mHtmlWriter),
71 mTopLevelContent(topLevelParser->mTopLevelContent),
72 mShowOnlyOneMimePart(showOnlyOneMimePart),
73 mHasPendingAsyncJobs(false),
74 mAllowAsync(topLevelParser->mAllowAsync),
75 mAttachmentStrategy(strategy)
76{
77 init();
78}
79
80ObjectTreeParser::ObjectTreeParser(Interface::ObjectTreeSource *source,
81 MimeTreeParser::NodeHelper *nodeHelper,
82 bool showOnlyOneMimePart,
83 const AttachmentStrategy *strategy)
84 : mSource(source),
85 mNodeHelper(nodeHelper),
86 mHtmlWriter(nullptr),
87 mTopLevelContent(nullptr),
88 mShowOnlyOneMimePart(showOnlyOneMimePart),
89 mHasPendingAsyncJobs(false),
90 mAllowAsync(false),
91 mAttachmentStrategy(strategy)
92{
93 init();
94}
95
96void ObjectTreeParser::init()
97{
98 Q_ASSERT(mSource);
99 if (!attachmentStrategy()) {
100 mAttachmentStrategy = mSource->attachmentStrategy();
101 }
102
103 if (!mNodeHelper) {
104 mNodeHelper = new NodeHelper();
105 mDeleteNodeHelper = true;
106 } else {
107 mDeleteNodeHelper = false;
108 }
109}
110
111ObjectTreeParser::ObjectTreeParser(const ObjectTreeParser &other)
112 : mSource(other.mSource),
113 mNodeHelper(other.nodeHelper()), //TODO(Andras) hm, review what happens if mDeleteNodeHelper was true in the source
114 mHtmlWriter(other.mHtmlWriter),
115 mTopLevelContent(other.mTopLevelContent),
116 mShowOnlyOneMimePart(other.showOnlyOneMimePart()),
117 mHasPendingAsyncJobs(other.hasPendingAsyncJobs()),
118 mAllowAsync(other.allowAsync()),
119 mAttachmentStrategy(other.attachmentStrategy()),
120 mDeleteNodeHelper(false)
121{
122
123}
124
125ObjectTreeParser::~ObjectTreeParser()
126{
127 if (mDeleteNodeHelper) {
128 delete mNodeHelper;
129 mNodeHelper = nullptr;
130 }
131}
132
133void ObjectTreeParser::setAllowAsync(bool allow)
134{
135 Q_ASSERT(!mHasPendingAsyncJobs);
136 mAllowAsync = allow;
137}
138
139bool ObjectTreeParser::allowAsync() const
140{
141 return mAllowAsync;
142}
143
144bool ObjectTreeParser::hasPendingAsyncJobs() const
145{
146 return mHasPendingAsyncJobs;
147}
148
149QString ObjectTreeParser::plainTextContent() const
150{
151 return mPlainTextContent;
152}
153
154QString ObjectTreeParser::htmlContent() const
155{
156 return mHtmlContent;
157}
158
159void ObjectTreeParser::copyContentFrom(const ObjectTreeParser *other)
160{
161 mPlainTextContent += other->plainTextContent();
162 mHtmlContent += other->htmlContent();
163 if (!other->plainTextContentCharset().isEmpty()) {
164 mPlainTextContentCharset = other->plainTextContentCharset();
165 }
166 if (!other->htmlContentCharset().isEmpty()) {
167 mHtmlContentCharset = other->htmlContentCharset();
168 }
169}
170
171//-----------------------------------------------------------------------------
172
173void ObjectTreeParser::parseObjectTree(KMime::Content *node)
174{
175 mTopLevelContent = node;
176 mParsedPart = parseObjectTreeInternal(node, showOnlyOneMimePart());
177
178 if (mParsedPart) {
179 mParsedPart->fix();
180 mParsedPart->copyContentFrom();
181 if (auto mp = toplevelTextNode(mParsedPart)) {
182 if (auto _mp = mp.dynamicCast<TextMessagePart>()) {
183 extractNodeInfos(_mp->mNode, true);
184 } else if (auto _mp = mp.dynamicCast<AlternativeMessagePart>()) {
185 if (_mp->mChildNodes.contains(Util::MultipartPlain)) {
186 extractNodeInfos(_mp->mChildNodes[Util::MultipartPlain], true);
187 }
188 }
189 setPlainTextContent(mp->text());
190 }
191
192 if (htmlWriter()) {
193 const auto renderer = mSource->messagePartTheme(mParsedPart);
194 if (renderer) {
195 mHtmlWriter->queue(renderer->html());
196 }
197 }
198 }
199}
200
201MessagePartPtr ObjectTreeParser::parsedPart() const
202{
203 return mParsedPart;
204}
205
206bool ObjectTreeParser::processType(KMime::Content *node, ProcessResult &processResult, const QByteArray &mediaType, const QByteArray &subType, Interface::MessagePartPtr &mpRet, bool onlyOneMimePart)
207{
208 bool bRendered = false;
209 const auto sub = mSource->bodyPartFormatterFactory()->subtypeRegistry(mediaType.constData());
210 auto range = sub.equal_range(subType.constData());
211 for (auto it = range.first; it != range.second; ++it) {
212 const auto formatter = (*it).second;
213 if (!formatter) {
214 continue;
215 }
216 PartNodeBodyPart part(this, &processResult, mTopLevelContent, node, mNodeHelper);
217 // Set the default display strategy for this body part relying on the
218 // identity of Interface::BodyPart::Display and AttachmentStrategy::Display
219 part.setDefaultDisplay((Interface::BodyPart::Display) attachmentStrategy()->defaultDisplay(node));
220
221 mNodeHelper->setNodeDisplayedEmbedded(node, true);
222
223 const Interface::MessagePart::Ptr result = formatter->process(part);
224 if (!result) {
225 continue;
226 }
227
228 if (const auto mp = result.dynamicCast<MessagePart>()) {
229 mp->setAttachmentFlag(node);
230 mpRet = result;
231 bRendered = true;
232 break;
233 } else if (dynamic_cast<MimeTreeParser::Interface::MessagePart *>(result.data())) {
234 QObject *asyncResultObserver = allowAsync() ? mSource->sourceObject() : nullptr;
235 const auto r = formatter->format(&part, result->htmlWriter(), asyncResultObserver);
236 if (r == Interface::BodyPartFormatter::AsIcon) {
237 processResult.setNeverDisplayInline(true);
238 formatter->adaptProcessResult(processResult);
239 mNodeHelper->setNodeDisplayedEmbedded(node, false);
240 const Interface::MessagePart::Ptr mp = defaultHandling(node, processResult, onlyOneMimePart);
241 if (mp) {
242 if (auto _mp = mp.dynamicCast<MessagePart>()) {
243 _mp->setAttachmentFlag(node);
244 }
245 mpRet = mp;
246 }
247 bRendered = true;
248 break;
249 } else if (r == Interface::BodyPartFormatter::Ok) {
250 processResult.setNeverDisplayInline(true);
251 formatter->adaptProcessResult(processResult);
252 mpRet = result;
253 bRendered = true;
254 break;
255 }
256 continue;
257 } else {
258 continue;
259 }
260 }
261 return bRendered;
262}
263
264MessagePart::Ptr ObjectTreeParser::parseObjectTreeInternal(KMime::Content *node, bool onlyOneMimePart)
265{
266 if (!node) {
267 return MessagePart::Ptr();
268 }
269
270 // reset pending async jobs state (we'll rediscover pending jobs as we go)
271 mHasPendingAsyncJobs = false;
272
273 // reset "processed" flags for...
274 if (onlyOneMimePart) {
275 // ... this node and all descendants
276 mNodeHelper->setNodeUnprocessed(node, false);
277 if (!node->contents().isEmpty()) {
278 mNodeHelper->setNodeUnprocessed(node, true);
279 }
280 } else if (!node->parent()) {
281 // ...this node and all it's siblings and descendants
282 mNodeHelper->setNodeUnprocessed(node, true);
283 }
284
285 const bool isRoot = node->isTopLevel();
286 auto parsedPart = MessagePart::Ptr(new MessagePartList(this));
287 parsedPart->setIsRoot(isRoot);
288 KMime::Content *parent = node->parent();
289 auto contents = parent ? parent->contents() : KMime::Content::List();
290 if (contents.isEmpty()) {
291 contents.append(node);
292 }
293 int i = contents.indexOf(const_cast<KMime::Content *>(node));
294 for (; i < contents.size(); ++i) {
295 node = contents.at(i);
296 if (mNodeHelper->nodeProcessed(node)) {
297 continue;
298 }
299
300 ProcessResult processResult(mNodeHelper);
301
302 QByteArray mediaType("text");
303 QByteArray subType("plain");
304 if (node->contentType(false) && !node->contentType()->mediaType().isEmpty() &&
305 !node->contentType()->subType().isEmpty()) {
306 mediaType = node->contentType()->mediaType();
307 subType = node->contentType()->subType();
308 }
309
310 Interface::MessagePartPtr mp;
311 if (processType(node, processResult, mediaType, subType, mp, onlyOneMimePart)) {
312 if (mp) {
313 parsedPart->appendSubPart(mp);
314 }
315 } else if (processType(node, processResult, mediaType, "*", mp, onlyOneMimePart)) {
316 if (mp) {
317 parsedPart->appendSubPart(mp);
318 }
319 } else {
320 qCWarning(MIMETREEPARSER_LOG) << "THIS SHOULD NO LONGER HAPPEN:" << mediaType << '/' << subType;
321 const auto mp = defaultHandling(node, processResult, onlyOneMimePart);
322 if (mp) {
323 if (auto _mp = mp.dynamicCast<MessagePart>()) {
324 _mp->setAttachmentFlag(node);
325 }
326 parsedPart->appendSubPart(mp);
327 }
328 }
329 mNodeHelper->setNodeProcessed(node, false);
330
331 // adjust signed/encrypted flags if inline PGP was found
332 processResult.adjustCryptoStatesOfNode(node);
333
334 if (onlyOneMimePart) {
335 break;
336 }
337 }
338
339 return parsedPart;
340}
341
342Interface::MessagePart::Ptr ObjectTreeParser::defaultHandling(KMime::Content *node, ProcessResult &result, bool onlyOneMimePart)
343{
344 Interface::MessagePart::Ptr mp;
345 ProcessResult processResult(mNodeHelper);
346
347 if (node->contentType()->mimeType() == QByteArrayLiteral("application/octet-stream") &&
348 (node->contentType()->name().endsWith(QLatin1String("p7m")) ||
349 node->contentType()->name().endsWith(QLatin1String("p7s")) ||
350 node->contentType()->name().endsWith(QLatin1String("p7c"))
351 ) &&
352 processType(node, processResult, "application", "pkcs7-mime", mp, onlyOneMimePart)) {
353 return mp;
354 }
355
356 const auto _mp = AttachmentMessagePart::Ptr(new AttachmentMessagePart(this, node, false, true, mSource->decryptMessage()));
357 result.setInlineSignatureState(_mp->signatureState());
358 result.setInlineEncryptionState(_mp->encryptionState());
359 _mp->setNeverDisplayInline(result.neverDisplayInline());
360 _mp->setIsImage(result.isImage());
361 mp = _mp;
362
363 // always show images in multipart/related when showing in html, not with an additional icon
364 auto preferredMode = mSource->preferredMode();
365 bool isHtmlPreferred = (preferredMode == Util::Html) || (preferredMode == Util::MultipartHtml);
366 if (result.isImage() && node->parent() &&
367 node->parent()->contentType()->subType() == "related" && isHtmlPreferred && !onlyOneMimePart) {
368 QString fileName = mNodeHelper->writeNodeToTempFile(node);
369 QString href = QUrl::fromLocalFile(fileName).url();
370 QByteArray cid = node->contentID()->identifier();
371 if (htmlWriter()) {
372 htmlWriter()->embedPart(cid, href);
373 }
374 nodeHelper()->setNodeDisplayedEmbedded(node, true);
375 mNodeHelper->setNodeDisplayedHidden(node, true);
376 return mp;
377 }
378
379 // Show it inline if showOnlyOneMimePart(), which means the user clicked the image
380 // in the message structure viewer manually, and therefore wants to see the full image
381 if (result.isImage() && onlyOneMimePart && !result.neverDisplayInline()) {
382 mNodeHelper->setNodeDisplayedEmbedded(node, true);
383 }
384
385 return mp;
386}
387
388KMMsgSignatureState ProcessResult::inlineSignatureState() const
389{
390 return mInlineSignatureState;
391}
392
393void ProcessResult::setInlineSignatureState(KMMsgSignatureState state)
394{
395 mInlineSignatureState = state;
396}
397
398KMMsgEncryptionState ProcessResult::inlineEncryptionState() const
399{
400 return mInlineEncryptionState;
401}
402
403void ProcessResult::setInlineEncryptionState(KMMsgEncryptionState state)
404{
405 mInlineEncryptionState = state;
406}
407
408bool ProcessResult::neverDisplayInline() const
409{
410 return mNeverDisplayInline;
411}
412
413void ProcessResult::setNeverDisplayInline(bool display)
414{
415 mNeverDisplayInline = display;
416}
417
418bool ProcessResult::isImage() const
419{
420 return mIsImage;
421}
422
423void ProcessResult::setIsImage(bool image)
424{
425 mIsImage = image;
426}
427
428void ProcessResult::adjustCryptoStatesOfNode(const KMime::Content *node) const
429{
430 if ((inlineSignatureState() != KMMsgNotSigned) ||
431 (inlineEncryptionState() != KMMsgNotEncrypted)) {
432 mNodeHelper->setSignatureState(node, inlineSignatureState());
433 mNodeHelper->setEncryptionState(node, inlineEncryptionState());
434 }
435}
436
437void ObjectTreeParser::extractNodeInfos(KMime::Content *curNode, bool isFirstTextPart)
438{
439 if (isFirstTextPart) {
440 mPlainTextContent += curNode->decodedText();
441 mPlainTextContentCharset += NodeHelper::charset(curNode);
442 }
443}
444
445void ObjectTreeParser::setPlainTextContent(const QString &plainTextContent)
446{
447 mPlainTextContent = plainTextContent;
448}
449
450const QTextCodec *ObjectTreeParser::codecFor(KMime::Content *node) const
451{
452 Q_ASSERT(node);
453 if (mSource->overrideCodec()) {
454 return mSource->overrideCodec();
455 }
456 return mNodeHelper->codec(node);
457}
458
459QByteArray ObjectTreeParser::plainTextContentCharset() const
460{
461 return mPlainTextContentCharset;
462}
463
464QByteArray ObjectTreeParser::htmlContentCharset() const
465{
466 return mHtmlContentCharset;
467}
468
469bool ObjectTreeParser::showOnlyOneMimePart() const
470{
471 return mShowOnlyOneMimePart;
472}
473
474void ObjectTreeParser::setShowOnlyOneMimePart(bool show)
475{
476 mShowOnlyOneMimePart = show;
477}
478
479const AttachmentStrategy *ObjectTreeParser::attachmentStrategy() const
480{
481 return mAttachmentStrategy;
482}
483
484HtmlWriter *ObjectTreeParser::htmlWriter() const
485{
486 if (mHtmlWriter) {
487 return mHtmlWriter;
488 }
489 return mSource->htmlWriter();
490}
491
492MimeTreeParser::NodeHelper *ObjectTreeParser::nodeHelper() const
493{
494 return mNodeHelper;
495}
diff --git a/framework/src/domain/mime/mimetreeparser/otp/objecttreeparser.h b/framework/src/domain/mime/mimetreeparser/otp/objecttreeparser.h
new file mode 100644
index 00000000..3f29a673
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/objecttreeparser.h
@@ -0,0 +1,406 @@
1/*
2 objecttreeparser.h
3
4 This file is part of KMail, the KDE mail client.
5 Copyright (c) 2003 Marc Mutz <mutz@kde.org>
6 Copyright (C) 2002-2003, 2009 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net
7 Copyright (c) 2009 Andras Mantia <andras@kdab.net>
8
9 KMail is free software; you can redistribute it and/or modify it
10 under the terms of the GNU General Public License, version 2, as
11 published by the Free Software Foundation.
12
13 KMail is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21
22 In addition, as a special exception, the copyright holders give
23 permission to link the code of this program with any edition of
24 the Qt library by Trolltech AS, Norway (or with modified versions
25 of Qt that use the same license as Qt), and distribute linked
26 combinations including the two. You must obey the GNU General
27 Public License in all respects for all of the code used other than
28 Qt. If you modify this file, you may extend this exception to
29 your version of the file, but you are not obligated to do so. If
30 you do not wish to do so, delete this exception statement from
31 your version.
32*/
33
34#ifndef __MIMETREEPARSER_OBJECTTREEPARSER_H__
35#define __MIMETREEPARSER_OBJECTTREEPARSER_H__
36
37#include "nodehelper.h"
38#include "objecttreesource.h"
39
40#include <gpgme++/verificationresult.h>
41
42class QString;
43
44namespace KMime
45{
46class Content;
47}
48
49namespace MimeTreeParser
50{
51
52namespace Interface
53{
54class MessagePart;
55typedef QSharedPointer<MessagePart> MessagePartPtr;
56}
57
58class PartMetaData;
59class ViewerPrivate;
60class HtmlWriter;
61class AttachmentStrategy;
62class NodeHelper;
63class MessagePart;
64class MimeMessagePart;
65
66typedef QSharedPointer<MessagePart> MessagePartPtr;
67typedef QSharedPointer<MimeMessagePart> MimeMessagePartPtr;
68
69class ProcessResult
70{
71public:
72 explicit ProcessResult(NodeHelper *nodeHelper, KMMsgSignatureState inlineSignatureState = KMMsgNotSigned,
73 KMMsgEncryptionState inlineEncryptionState = KMMsgNotEncrypted,
74 bool neverDisplayInline = false,
75 bool isImage = false)
76 : mInlineSignatureState(inlineSignatureState),
77 mInlineEncryptionState(inlineEncryptionState),
78 mNeverDisplayInline(neverDisplayInline),
79 mIsImage(isImage),
80 mNodeHelper(nodeHelper) {}
81
82 KMMsgSignatureState inlineSignatureState() const;
83 void setInlineSignatureState(KMMsgSignatureState state);
84
85 KMMsgEncryptionState inlineEncryptionState() const;
86 void setInlineEncryptionState(KMMsgEncryptionState state);
87
88 bool neverDisplayInline() const;
89 void setNeverDisplayInline(bool display);
90
91 bool isImage() const;
92 void setIsImage(bool image);
93
94 void adjustCryptoStatesOfNode(const KMime::Content *node) const;
95
96private:
97 KMMsgSignatureState mInlineSignatureState;
98 KMMsgEncryptionState mInlineEncryptionState;
99 bool mNeverDisplayInline : 1;
100 bool mIsImage : 1;
101 NodeHelper *mNodeHelper;
102};
103
104/**
105\brief Parses messages and generates HTML display code out of them
106
107\par Introduction
108
109First, have a look at the documentation in Mainpage.dox and at the documentation of ViewerPrivate
110to understand the broader picture.
111
112Just a note on the terminology: 'Node' refers to a MIME part here, which in KMime is a
113KMime::Content.
114
115\par Basics
116
117The ObjectTreeParser basically has two modes: Generating the HTML code for the Viewer, or only
118extracting the plainTextContent() for situations where only the message text is needed, for example
119when inline forwarding a message. The mode depends on the Interface::ObjectTreeSource passed to the
120constructor: If Interface::ObjectTreeSource::htmlWriter() is not 0, then the HTML code generation mode is
121used.
122
123Basically, all the ObjectTreeParser does is going through the tree of MIME parts and operating on
124those nodes. Operating here means creating the HTML code for the node or extracting the textual
125content from it. This process is started with parseObjectTree(), where we loop over the subnodes
126of the current root node. For each of those subnodes, we try to find a BodyPartFormatter that can
127handle the type of the node. This can either be an internal function, such as
128processMultiPartAlternativeSubtype() or processTextHtmlSubtype(), or it can be an external plugin.
129More on external plugins later. When no matching formatter is found, defaultHandling() is called
130for that node.
131
132\par Multipart Nodes
133
134Those nodes that are of type multipart have subnodes. If one of those children needs to be
135processed normally, the processMultipartXXX() functions call stdChildHandling() for the node that
136should be handled normally. stdChildHandling() creates its own ObjectTreeParser, which is a clone
137of the current ObjectTreeParser, and processes the node. stdChildHandling() is not called for all
138children of the multipart node, for example processMultiPartAlternativeSubtype() only calls it on
139one of the children, as the other one doesn't need to be displayed. Similary,
140processMultiPartSignedSubtype() doesn't call stdChildHandling() for the signature node, only for the
141signed node.
142
143\par Processed and Unprocessed Nodes
144
145When a BodyPartFormatter has finished processing a node, it is processed. Nodes are set to being
146not processed at the beginning of parseObjectTree(). The processed state of a node is saved in a
147list in NodeHelper, see NodeHelper::setNodeProcessed(), NodeHelper::nodeProcessed() and the other
148related helper functions.
149
150It is the responsibility of the BodyPartFormatter to correctly call setNodeProcessed() and the
151related functions. This is important so that processing the same node twice can be prevented. The
152check that prevents duplicate processing is in parseObjectTree().
153
154An example where duplicate processing would happen if we didn't check for it is in stdChildHandling(),
155which is for example called from processMultiPartAlternativeSubtype(). Let's say the setting is to
156prefer HTML over plain text. In this case, processMultiPartAlternativeSubtype() would call
157stdChildHandling() on the HTML node, which would create a new ObjectTreeParser and call
158parseObjectTree() on it. parseObjectTree() processes the node and all its siblings, and one of the
159siblings is the plain text node, which shouldn't be processed! Therefore
160processMultiPartAlternativeSubtype() sets the plain text node as been processed already.
161
162\par Plain Text Output
163
164Various nodes have plain text that should be displayed. This plain text is usually processed though
165writeBodyString() first. That method checks if the provided text is an inline PGP text and decrypts
166it if necessary. It also pushes the text through quotedHTML(), which does a number of things like
167coloring quoted lines or detecting links and creating real link tags for them.
168
169\par Modifying the Message
170
171The ObjectTreeParser does not only parse its message, in some circumstances it also modifies it
172before displaying. This is for example the case when displaying a decrypted message: The original
173message only contains a binary blob of crypto data, and processMultiPartEncryptedSubtype() decrypts
174that blob. After decryption, the current node is replaced with the decrypted node, which happens
175in insertAndParseNewChildNode().
176
177\par Crypto Operations
178
179For signature and decryption handling, there are functions which help with generating the HTML code
180for the signature header and footer. These are writeDeferredDecryptionBlock(), writeSigstatFooter()
181and writeSigstatHeader(). As the name writeDeferredDecryptionBlock() suggests, a setting can cause
182the message to not be decrypted unless the user clicks a link. Whether the message should be
183decrypted or not can be controlled by Interface::ObjectTreeSource::decryptMessage(). When the user clicks the
184decryption link, the URLHandler for 'kmail:' URLs sets that variable to true and triggers an update
185of the Viewer, which will cause parseObjectTree() to be called again.
186
187\par Async Crypto Operations
188
189The above case describes decryption the message in place. However, decryption and also verifying of
190the signature can take a long time, so synchronous decryption and verifing would cause the Viewer to
191block. Therefore it is possible to run these operations in async mode, see allowAsync().
192In the first run of the async mode, all the ObjectTreeParser does is starting the decrypt or the
193verify job, and informing the user that the operation is in progress with
194writeDecryptionInProgressBlock() or with writeSigstatHeader(). Then, it creates and associates a
195BodyPartMemento with the current node, for example a VerifyDetachedBodyPartMemento. Each node can
196have multiple mementos associated with it, which are differeniated by name.
197
198NodeHelper::setBodyPartMemento() and NodeHelper::bodyPartMemento() provide means to store and
199retrieve these mementos. A memento is basically a thin wrapper around the crypto job, it stores the
200job pointer, the job input data and the job result. Mementos can be used for any async situation,
201not just for crypto jobs, but I'll describe crypto jobs here.
202
203So in the first run of decrypting or verifying a message, the BodyPartFormatter only starts the
204crypto job, creates the BodyPartMemento and writes the HTML code that tells the user that the
205operation is in progress. parseObjectTree() thus finishes without waiting for anything, and the
206message is displayed.
207
208At some point, the crypto jobs then finish, which will cause slotResult() of the BodyPartMemento
209to be called. slotResult() then saves the result to some member variable and calls
210BodyPartMemento::notify(), which in the end will trigger an update of the Viewer. That update
211will, in ViewerPrivate::parseMsg(), create a new ObjectTreeParser and call parseObjectTree() on it.
212This is where the second run begins.
213
214The functions that deal with decrypting of verifying, like processMultiPartSignedSubtype() or
215processMultiPartEncryptedSubtype() will look if they find a BodyPartMemento that is associated with
216the current node. Now it finds that memento, since it was created in the first run. It checks if the
217memento's job has finished, and if so, the result can be written out (either the decrypted data or
218the verified signature).
219
220When dealing with encrypted nodes, new nodes are created with the decrypted data. It is important to
221note that the original MIME tree is never modified, and remains the same as the original one. The method
222createAndParseTempNode is called with the newly decrypted data, and it generates a new temporary node to
223store the decrypted data. When these nodes are created, it is important to keep track of them as otherwise
224some mementos that are added to the newly created temporary nodes will be constantly regenerated. As the
225regeneration triggers a viewer update when complete, it results in an infinite refresh loop. The function
226NodeHelper::linkAsPermanentDecrypted will create a link between the newly created node and the original parent.
227Conversely, the function NodeHelper::attachExtraContent will create a link in the other direction, from the parent
228node to the newly created temporary node.
229
230When generating some mementos for nodes that may be temporary nodes (for example, contact photo mementos), the
231function NodeHelper::setBodyPartMementoForPermanentParent is used. This will save the given body part memento for
232the closest found permanent parent node, rather than the transient node itself. Then when checking for the existence
233of a certain memento in a node, NodeHelper::findPermanentParentBodyPartMemento will check to see if any parent of the
234given temporary node is a permanent (encrypted) node that has been used to generate the asked-for node.
235
236To conclude: For async operations, parseObjectTree() is called twice: The first call starts the
237crypto operation and creates the BodyPartMemento, the second calls sees that the BodyPartMemento is
238there and can use its result for writing out the HTML.
239
240\par PartMetaData and ProcessResult
241
242For crypto operations, the class PartMetaData is used a lot, mainly to pass around info about the
243crypto state of a node. A PartMetaData can also be associated with a node by using
244NodeHelper::setPartMetaData(). The only user of that however is MessageAnalyzer::processPart() of
245the Nepomuk E-Mail Feeder, which also uses the ObjectTreeParser to analyze the message.
246
247You'll notice that a ProcessResult is passed to each formatter. The formatter is supposed to modify
248the ProcessResult to tell the callers something about the state of the nodes that were processed.
249One example for its use is to tell the caller about the crypto state of the node.
250
251\par BodyPartFormatter Plugins
252
253As mentioned way earlier, BodyPartFormatter can either be plugins or be internal. bodypartformatter.cpp
254contains some trickery so that the processXXX() methods of the ObjectTreeParser are called from
255a BodyPartFormatter associated with them, see the CREATE_BODY_PART_FORMATTER macro.
256
257The BodyPartFormatter code is work in progress, it was supposed to be refactored, but that has not
258yet happened at the time of writing. Therefore the code can seem a bit chaotic.
259
260External plugins are loaded with loadPlugins() in bodypartformatterfactory.cpp. External plugins
261can only use the classes in the interfaces/ directory, they include BodyPart, BodyPartMemento,
262BodyPartFormatterPlugin, BodyPartFormatter, BodyPartURLHandler, HtmlWriter and URLHandler. Therefore
263external plugins have powerful capabilities, which are needed for example in the iCal formatter or
264in the vCard formatter.
265
266\par Special HTML tags
267
268As also mentioned in the documentation of ViewerPrivate, the ObjectTreeParser writes out special
269links that are only understood by the viewer, for example 'kmail:' URLs or 'attachment:' URLs.
270Also, some special HTML tags are created, which the Viewer later uses for post-processing. For
271example a div with the id 'attachmentInjectionPoint', or a div with the id 'attachmentDiv', which
272is used to mark an attachment in the body with a yellow border when the user clicks the attachment
273in the header. Finally, parseObjectTree() creates an anchor with the id 'att%1', which is used in
274the Viewer to scroll to the attachment.
275*/
276class ObjectTreeParser
277{
278 /**
279 * @internal
280 * Copies the context of @p other, but not it's rawDecryptedBody, plainTextContent or htmlContent.
281 */
282 ObjectTreeParser(const ObjectTreeParser &other);
283
284public:
285 explicit ObjectTreeParser(Interface::ObjectTreeSource *source,
286 NodeHelper *nodeHelper = nullptr,
287 bool showOneMimePart = false,
288 const AttachmentStrategy *attachmentStrategy = nullptr);
289
290 explicit ObjectTreeParser(const ObjectTreeParser *topLevelParser,
291 bool showOneMimePart = false,
292 const AttachmentStrategy *attachmentStrategy = nullptr);
293 virtual ~ObjectTreeParser();
294
295 void setAllowAsync(bool allow);
296 bool allowAsync() const;
297
298 bool hasPendingAsyncJobs() const;
299
300 /**
301 * The text of the message, ie. what would appear in the
302 * composer's text editor if this was edited or replied to.
303 * This is usually the content of the first text/plain MIME part.
304 */
305 QString plainTextContent() const;
306
307 /**
308 * Similar to plainTextContent(), but returns the HTML source of the first text/html MIME part.
309 *
310 * Not to be consfused with the HTML code that the message viewer widget displays, that HTML
311 * is written out by htmlWriter() and a totally different pair of shoes.
312 */
313 QString htmlContent() const;
314
315 /**
316 * The original charset of MIME part the plain text was extracted from.
317 *
318 * If there were more than one text/plain MIME parts in the mail, the this is the charset
319 * of the last MIME part processed.
320 */
321 QByteArray plainTextContentCharset() const;
322 QByteArray htmlContentCharset() const;
323
324 bool showOnlyOneMimePart() const;
325 void setShowOnlyOneMimePart(bool show);
326
327 const AttachmentStrategy *attachmentStrategy() const;
328
329 HtmlWriter *htmlWriter() const;
330
331 NodeHelper *nodeHelper() const;
332
333 /** Parse beginning at a given node and recursively parsing
334 the children of that node and it's next sibling. */
335 void parseObjectTree(KMime::Content *node);
336 MessagePartPtr parsedPart() const;
337
338private:
339 void extractNodeInfos(KMime::Content *curNode, bool isFirstTextPart);
340 void setPlainTextContent(const QString &plainTextContent);
341
342 /**
343 * Does the actual work for parseObjectTree. Unlike parseObjectTree(), this does not change the
344 * top-level content.
345 */
346 MessagePartPtr parseObjectTreeInternal(KMime::Content *node, bool mOnlyOneMimePart);
347 bool processType(KMime::Content *node, MimeTreeParser::ProcessResult &processResult, const QByteArray &mediaType, const QByteArray &subType, Interface::MessagePartPtr &mpRet, bool onlyOneMimePart);
348
349 Interface::MessagePartPtr defaultHandling(KMime::Content *node, MimeTreeParser::ProcessResult &result, bool onlyOneMimePart);
350
351private:
352
353 /** ctor helper */
354 void init();
355
356 const QTextCodec *codecFor(KMime::Content *node) const;
357
358 void copyContentFrom(const ObjectTreeParser *other);
359
360private:
361 Interface::ObjectTreeSource *mSource;
362 NodeHelper *mNodeHelper;
363 HtmlWriter *mHtmlWriter;
364 QByteArray mPlainTextContentCharset;
365 QByteArray mHtmlContentCharset;
366 QString mPlainTextContent;
367 QString mHtmlContent;
368 KMime::Content *mTopLevelContent;
369 MessagePartPtr mParsedPart;
370
371 /// Show only one mime part means that the user has selected some node in the message structure
372 /// viewer that is not the root, which means the user wants to only see the selected node and its
373 /// children. If that is the case, this variable is set to true.
374 /// The code needs to behave differently if this is set. For example, it should not process the
375 /// siblings. Also, consider inline images: Normally, those nodes are completely hidden, as the
376 /// HTML node embedds them. However, when showing only the node of the image, one has to show them,
377 /// as their is no HTML node in which they are displayed. There are many more cases where this
378 /// variable needs to be obeyed.
379 /// This variable is set to false again when processing the children in stdChildHandling(), as
380 /// the children can be completely displayed again.
381 bool mShowOnlyOneMimePart;
382
383 bool mHasPendingAsyncJobs;
384 bool mAllowAsync;
385 const AttachmentStrategy *mAttachmentStrategy;
386 // DataUrl Icons cache
387 QString mCollapseIcon;
388 QString mExpandIcon;
389 bool mDeleteNodeHelper;
390
391 friend class PartNodeBodyPart;
392 friend class MessagePart;
393 friend class EncryptedMessagePart;
394 friend class SignedMessagePart;
395 friend class EncapsulatedRfc822MessagePart;
396 friend class TextMessagePart;
397 friend class HtmlMessagePart;
398 friend class TextPlainBodyPartFormatter;
399 friend class MultiPartSignedBodyPartFormatter;
400 friend class ApplicationPkcs7MimeBodyPartFormatter;
401};
402
403}
404
405#endif // __MIMETREEPARSER_OBJECTTREEPARSER_H__
406
diff --git a/framework/src/domain/mime/mimetreeparser/otp/objecttreesource.cpp b/framework/src/domain/mime/mimetreeparser/otp/objecttreesource.cpp
new file mode 100644
index 00000000..45f96c58
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/objecttreesource.cpp
@@ -0,0 +1,28 @@
1/*
2 Copyright (C) 2009 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net
3 Copyright (c) 2009 Andras Mantia <andras@kdab.net>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18*/
19
20#include "objecttreesource.h"
21#include "bodypartformatter.h"
22#include "messagepart.h"
23
24using namespace MimeTreeParser;
25
26Interface::ObjectTreeSource::~ObjectTreeSource()
27{
28}
diff --git a/framework/src/domain/mime/mimetreeparser/otp/objecttreesource.h b/framework/src/domain/mime/mimetreeparser/otp/objecttreesource.h
new file mode 100644
index 00000000..afada4c4
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/objecttreesource.h
@@ -0,0 +1,109 @@
1/*
2 Copyright (C) 2009 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net
3 Copyright (c) 2009 Andras Mantia <andras@kdab.net>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18*/
19
20#ifndef __MIMETREEPARSER_OBJECTTREESOURCE_IF_H__
21#define __MIMETREEPARSER_OBJECTTREESOURCE_IF_H__
22
23#include "util.h"
24
25#include <KMime/Message>
26
27#include <QSharedPointer>
28class QTextCodec;
29
30namespace MimeTreeParser
31{
32class HtmlWriter;
33class AttachmentStrategy;
34class BodyPartFormatterBaseFactory;
35namespace Interface
36{
37class MessagePart;
38typedef QSharedPointer<MessagePart> MessagePartPtr;
39class MessagePartRenderer;
40typedef QSharedPointer<MessagePartRenderer> MessagePartRendererPtr;
41}
42}
43
44namespace MimeTreeParser
45{
46namespace Interface
47{
48
49/**
50 * Interface for object tree sources.
51 * @author Andras Mantia <amantia@kdab.net>
52 */
53class ObjectTreeSource
54{
55
56public:
57 virtual ~ObjectTreeSource();
58
59 /**
60 * Sets the type of mail that is currently displayed. Applications can display this
61 * information to the user, for example KMail displays a HTML status bar.
62 * Note: This is not called when the mode is "Normal".
63 */
64 virtual void setHtmlMode(MimeTreeParser::Util::HtmlMode mode, const QList<MimeTreeParser::Util::HtmlMode> &availableModes) = 0;
65
66 /** Return the mode that is the preferred to display */
67 virtual MimeTreeParser::Util::HtmlMode preferredMode() const = 0;
68
69 /** Return true if an encrypted mail should be decrypted */
70 virtual bool decryptMessage() const = 0;
71
72 /** Return true if external sources should be loaded in a html mail */
73 virtual bool htmlLoadExternal() const = 0;
74
75 /** Return true to include the signature details in the generated html */
76 virtual bool showSignatureDetails() const = 0;
77
78 virtual int levelQuote() const = 0;
79
80 /** The override codec that should be used for the mail */
81 virtual const QTextCodec *overrideCodec() = 0;
82
83 virtual QString createMessageHeader(KMime::Message *message) = 0;
84
85 /** Return the wanted attachment startegy */
86 virtual const AttachmentStrategy *attachmentStrategy() = 0;
87
88 /** Return the html write object */
89 virtual HtmlWriter *htmlWriter() = 0;
90
91 /** The source object behind the interface. */
92 virtual QObject *sourceObject() = 0;
93
94 /** should keys be imported automatically **/
95 virtual bool autoImportKeys() const = 0;
96
97 virtual bool showEmoticons() const = 0;
98
99 virtual bool showExpandQuotesMark() const = 0;
100
101 virtual const BodyPartFormatterBaseFactory *bodyPartFormatterFactory() = 0;
102
103 virtual MessagePartRendererPtr messagePartTheme(MessagePartPtr msgPart) = 0;
104
105 virtual bool isPrinting() const = 0;
106};
107}
108}
109#endif
diff --git a/framework/src/domain/mime/mimetreeparser/otp/partmetadata.h b/framework/src/domain/mime/mimetreeparser/otp/partmetadata.h
new file mode 100644
index 00000000..41399837
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/partmetadata.h
@@ -0,0 +1,67 @@
1/* -*- c++ -*-
2 partmetadata.h
3
4 KMail, the KDE mail client.
5 Copyright (c) 2002-2003 Karl-Heinz Zimmer <khz@kde.org>
6 Copyright (c) 2003 Marc Mutz <mutz@kde.org>
7
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License,
10 version 2.0, as published by the Free Software Foundation.
11 You should have received a copy of the GNU General Public License
12 along with this program; if not, write to the Free Software Foundation,
13 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, US
14*/
15
16#ifndef __MIMETREEPARSER_PARTMETADATA_H__
17#define __MIMETREEPARSER_PARTMETADATA_H__
18
19#include <gpgme++/verificationresult.h>
20#include <gpgme++/context.h>
21
22#include <QStringList>
23#include <QDateTime>
24
25namespace MimeTreeParser
26{
27
28class PartMetaData
29{
30public:
31 PartMetaData()
32 : sigSummary(GpgME::Signature::None),
33 isSigned(false),
34 isGoodSignature(false),
35 isEncrypted(false),
36 isDecryptable(false),
37 inProgress(false),
38 technicalProblem(false),
39 isEncapsulatedRfc822Message(false)
40 {
41 }
42 GpgME::Signature::Summary sigSummary;
43 QString signClass;
44 QString signer;
45 QStringList signerMailAddresses;
46 QByteArray keyId;
47 GpgME::Signature::Validity keyTrust;
48 QString status; // to be used for unknown plug-ins
49 int status_code; // to be used for i18n of OpenPGP and S/MIME CryptPlugs
50 QString errorText;
51 QDateTime creationTime;
52 QString decryptionError;
53 QString auditLog;
54 GpgME::Error auditLogError;
55 bool isSigned : 1;
56 bool isGoodSignature : 1;
57 bool isEncrypted : 1;
58 bool isDecryptable : 1;
59 bool inProgress : 1;
60 bool technicalProblem : 1;
61 bool isEncapsulatedRfc822Message : 1;
62};
63
64}
65
66#endif // __MIMETREEPARSER_PARTMETADATA_H__
67
diff --git a/framework/src/domain/mime/mimetreeparser/otp/partnodebodypart.cpp b/framework/src/domain/mime/mimetreeparser/otp/partnodebodypart.cpp
new file mode 100644
index 00000000..ec509787
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/partnodebodypart.cpp
@@ -0,0 +1,125 @@
1/*
2 partnodebodypart.cpp
3
4 This file is part of KMail, the KDE mail client.
5 Copyright (c) 2004 Marc Mutz <mutz@kde.org>,
6 Ingo Kloecker <kloecker@kde.org>
7
8 KMail is free software; you can redistribute it and/or modify it
9 under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12
13 KMail is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21
22 In addition, as a special exception, the copyright holders give
23 permission to link the code of this program with any edition of
24 the Qt library by Trolltech AS, Norway (or with modified versions
25 of Qt that use the same license as Qt), and distribute linked
26 combinations including the two. You must obey the GNU General
27 Public License in all respects for all of the code used other than
28 Qt. If you modify this file, you may extend this exception to
29 your version of the file, but you are not obligated to do so. If
30 you do not wish to do so, delete this exception statement from
31 your version.
32*/
33
34#include "partnodebodypart.h"
35#include "nodehelper.h"
36#include "objecttreeparser.h"
37#include "mimetreeparser_debug.h"
38#include <KMime/Content>
39
40#include <QTextCodec>
41#include <QUrl>
42
43using namespace MimeTreeParser;
44
45static int serial = 0;
46
47PartNodeBodyPart::PartNodeBodyPart(ObjectTreeParser *otp, ProcessResult *result, KMime::Content *topLevelContent, KMime::Content *content,
48 NodeHelper *nodeHelper)
49 : Interface::BodyPart(), mTopLevelContent(topLevelContent), mContent(content),
50 mDefaultDisplay(Interface::BodyPart::None), mNodeHelper(nodeHelper)
51 , mObjectTreeParser(otp)
52 , mProcessResult(result)
53{}
54
55QString PartNodeBodyPart::makeLink(const QString &path) const
56{
57 // FIXME: use a PRNG for the first arg, instead of a serial number
58 return QStringLiteral("x-kmail:/bodypart/%1/%2/%3")
59 .arg(serial++).arg(mContent->index().toString())
60 .arg(QString::fromLatin1(QUrl::toPercentEncoding(path, "/")));
61}
62
63QString PartNodeBodyPart::asText() const
64{
65 if (!mContent->contentType()->isText()) {
66 return QString();
67 }
68 return mContent->decodedText();
69}
70
71QByteArray PartNodeBodyPart::asBinary() const
72{
73 return mContent->decodedContent();
74}
75
76QString PartNodeBodyPart::contentTypeParameter(const char *param) const
77{
78 return mContent->contentType()->parameter(QString::fromLatin1(param));
79}
80
81QString PartNodeBodyPart::contentDescription() const
82{
83 return mContent->contentDescription()->asUnicodeString();
84}
85
86QString PartNodeBodyPart::contentDispositionParameter(const char *param) const
87{
88 return mContent->contentDisposition()->parameter(QString::fromLatin1(param));
89}
90
91bool PartNodeBodyPart::hasCompleteBody() const
92{
93 qCWarning(MIMETREEPARSER_LOG) << "Sorry, not yet implemented.";
94 return true;
95}
96
97Interface::BodyPartMemento *PartNodeBodyPart::memento() const
98{
99 /*TODO(Andras) Volker suggests to use a ContentIndex->Mememnto mapping
100 Also review if the reader's bodyPartMemento should be returned or the NodeHelper's one
101 */
102 return mNodeHelper->bodyPartMemento(mContent, "__plugin__");
103}
104
105void PartNodeBodyPart::setBodyPartMemento(Interface::BodyPartMemento *memento)
106{
107 /*TODO(Andras) Volker suggests to use a ContentIndex->Memento mapping
108 Also review if the reader's bodyPartMemento should be set or the NodeHelper's one */
109 mNodeHelper->setBodyPartMemento(mContent, "__plugin__", memento);
110}
111
112Interface::BodyPart::Display PartNodeBodyPart::defaultDisplay() const
113{
114 return mDefaultDisplay;
115}
116
117void PartNodeBodyPart::setDefaultDisplay(Interface::BodyPart::Display d)
118{
119 mDefaultDisplay = d;
120}
121
122Interface::ObjectTreeSource *PartNodeBodyPart::source() const
123{
124 return mObjectTreeParser->mSource;
125}
diff --git a/framework/src/domain/mime/mimetreeparser/otp/partnodebodypart.h b/framework/src/domain/mime/mimetreeparser/otp/partnodebodypart.h
new file mode 100644
index 00000000..ded0ee2c
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/partnodebodypart.h
@@ -0,0 +1,108 @@
1/*
2 partnodebodypart.h
3
4 This file is part of KMail, the KDE mail client.
5 Copyright (c) 2004 Marc Mutz <mutz@kde.org>,
6 Ingo Kloecker <kloecker@kde.org>
7
8 KMail is free software; you can redistribute it and/or modify it
9 under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2 of the License, or
11 (at your option) any later version.
12
13 KMail is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21
22 In addition, as a special exception, the copyright holders give
23 permission to link the code of this program with any edition of
24 the Qt library by Trolltech AS, Norway (or with modified versions
25 of Qt that use the same license as Qt), and distribute linked
26 combinations including the two. You must obey the GNU General
27 Public License in all respects for all of the code used other than
28 Qt. If you modify this file, you may extend this exception to
29 your version of the file, but you are not obligated to do so. If
30 you do not wish to do so, delete this exception statement from
31 your version.
32*/
33
34#ifndef __MIMETREEPARSER_PARTNODEBODYPART_H__
35#define __MIMETREEPARSER_PARTNODEBODYPART_H__
36
37#include "bodypart.h"
38
39namespace KMime
40{
41class Content;
42}
43
44namespace MimeTreeParser
45{
46class NodeHelper;
47}
48
49namespace MimeTreeParser
50{
51
52/**
53 @short an implementation of the BodyPart interface using KMime::Content's
54*/
55class PartNodeBodyPart : public Interface::BodyPart
56{
57public:
58 explicit PartNodeBodyPart(ObjectTreeParser *otp, ProcessResult *result, KMime::Content *topLevelContent, KMime::Content *content,
59 NodeHelper *nodeHelper);
60
61 QString makeLink(const QString &path) const Q_DECL_OVERRIDE;
62 QString asText() const Q_DECL_OVERRIDE;
63 QByteArray asBinary() const Q_DECL_OVERRIDE;
64 QString contentTypeParameter(const char *param) const Q_DECL_OVERRIDE;
65 QString contentDescription() const Q_DECL_OVERRIDE;
66 QString contentDispositionParameter(const char *param) const Q_DECL_OVERRIDE;
67 bool hasCompleteBody() const Q_DECL_OVERRIDE;
68
69 Interface::BodyPartMemento *memento() const Q_DECL_OVERRIDE;
70 void setBodyPartMemento(Interface::BodyPartMemento *memento) Q_DECL_OVERRIDE;
71 BodyPart::Display defaultDisplay() const Q_DECL_OVERRIDE;
72 void setDefaultDisplay(BodyPart::Display);
73 KMime::Content *content() const Q_DECL_OVERRIDE
74 {
75 return mContent;
76 }
77 KMime::Content *topLevelContent() const Q_DECL_OVERRIDE
78 {
79 return mTopLevelContent;
80 }
81 NodeHelper *nodeHelper() const Q_DECL_OVERRIDE
82 {
83 return mNodeHelper;
84 }
85
86 ObjectTreeParser *objectTreeParser() const Q_DECL_OVERRIDE
87 {
88 return mObjectTreeParser;
89 }
90
91 ProcessResult *processResult() const Q_DECL_OVERRIDE
92 {
93 return mProcessResult;
94 }
95
96 Interface::ObjectTreeSource *source() const Q_DECL_OVERRIDE;
97private:
98 KMime::Content *mTopLevelContent;
99 KMime::Content *mContent;
100 BodyPart::Display mDefaultDisplay;
101 NodeHelper *mNodeHelper;
102 ObjectTreeParser *mObjectTreeParser;
103 ProcessResult *mProcessResult;
104};
105
106}
107
108#endif // __MIMETREEPARSER_PARTNODEBODYPART_H__
diff --git a/framework/src/domain/mime/mimetreeparser/otp/qgpgmejobexecutor.cpp b/framework/src/domain/mime/mimetreeparser/otp/qgpgmejobexecutor.cpp
new file mode 100644
index 00000000..1f453342
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/qgpgmejobexecutor.cpp
@@ -0,0 +1,158 @@
1/*
2 Copyright (c) 2008 Volker Krause <vkrause@kde.org>
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17*/
18
19#include "qgpgmejobexecutor.h"
20#include "mimetreeparser_debug.h"
21
22#include <QGpgME/DecryptVerifyJob>
23#include <QGpgME/ImportJob>
24#include <QGpgME/VerifyDetachedJob>
25#include <QGpgME/VerifyOpaqueJob>
26
27#include <QEventLoop>
28
29#include <cassert>
30
31using namespace GpgME;
32using namespace MimeTreeParser;
33
34QGpgMEJobExecutor::QGpgMEJobExecutor(QObject *parent) : QObject(parent)
35{
36 setObjectName(QStringLiteral("KleoJobExecutor"));
37 mEventLoop = new QEventLoop(this);
38}
39
40GpgME::VerificationResult QGpgMEJobExecutor::exec(
41 QGpgME::VerifyDetachedJob *job,
42 const QByteArray &signature,
43 const QByteArray &signedData)
44{
45 qCDebug(MIMETREEPARSER_LOG) << "Starting detached verification job";
46 connect(job, SIGNAL(result(GpgME::VerificationResult)), SLOT(verificationResult(GpgME::VerificationResult)));
47 GpgME::Error err = job->start(signature, signedData);
48 if (err) {
49 return VerificationResult(err);
50 }
51 mEventLoop->exec(QEventLoop::ExcludeUserInputEvents);
52 return mVerificationResult;
53}
54
55GpgME::VerificationResult QGpgMEJobExecutor::exec(
56 QGpgME::VerifyOpaqueJob *job,
57 const QByteArray &signedData,
58 QByteArray &plainText)
59{
60 qCDebug(MIMETREEPARSER_LOG) << "Starting opaque verification job";
61 connect(job, SIGNAL(result(GpgME::VerificationResult,QByteArray)), SLOT(verificationResult(GpgME::VerificationResult,QByteArray)));
62 GpgME::Error err = job->start(signedData);
63 if (err) {
64 plainText.clear();
65 return VerificationResult(err);
66 }
67 mEventLoop->exec(QEventLoop::ExcludeUserInputEvents);
68 plainText = mData;
69 return mVerificationResult;
70}
71
72std::pair< GpgME::DecryptionResult, GpgME::VerificationResult > QGpgMEJobExecutor::exec(
73 QGpgME::DecryptVerifyJob *job,
74 const QByteArray &cipherText,
75 QByteArray &plainText)
76{
77 qCDebug(MIMETREEPARSER_LOG) << "Starting decryption job";
78 connect(job, &QGpgME::DecryptVerifyJob::result, this, &QGpgMEJobExecutor::decryptResult);
79 GpgME::Error err = job->start(cipherText);
80 if (err) {
81 plainText.clear();
82 return std::make_pair(DecryptionResult(err), VerificationResult(err));
83 }
84 mEventLoop->exec(QEventLoop::ExcludeUserInputEvents);
85 plainText = mData;
86 return std::make_pair(mDecryptResult, mVerificationResult);
87}
88
89GpgME::ImportResult QGpgMEJobExecutor::exec(QGpgME::ImportJob *job, const QByteArray &certData)
90{
91 connect(job, SIGNAL(result(GpgME::ImportResult)), SLOT(importResult(GpgME::ImportResult)));
92 GpgME::Error err = job->start(certData);
93 if (err) {
94 return ImportResult(err);
95 }
96 mEventLoop->exec(QEventLoop::ExcludeUserInputEvents);
97 return mImportResult;
98}
99
100Error QGpgMEJobExecutor::auditLogError() const
101{
102 return mAuditLogError;
103}
104
105void QGpgMEJobExecutor::verificationResult(const GpgME::VerificationResult &result)
106{
107 qCDebug(MIMETREEPARSER_LOG) << "Detached verification job finished";
108 QGpgME::Job *job = qobject_cast<QGpgME::Job *>(sender());
109 assert(job);
110 mVerificationResult = result;
111 mAuditLogError = job->auditLogError();
112 mAuditLog = job->auditLogAsHtml();
113 mEventLoop->quit();
114}
115
116void QGpgMEJobExecutor::verificationResult(const GpgME::VerificationResult &result, const QByteArray &plainText)
117{
118 qCDebug(MIMETREEPARSER_LOG) << "Opaque verification job finished";
119 QGpgME::Job *job = qobject_cast<QGpgME::Job *>(sender());
120 assert(job);
121 mVerificationResult = result;
122 mData = plainText;
123 mAuditLogError = job->auditLogError();
124 mAuditLog = job->auditLogAsHtml();
125 mEventLoop->quit();
126}
127
128void QGpgMEJobExecutor::decryptResult(
129 const GpgME::DecryptionResult &decryptionresult,
130 const GpgME::VerificationResult &verificationresult,
131 const QByteArray &plainText)
132{
133 qCDebug(MIMETREEPARSER_LOG) << "Decryption job finished";
134 QGpgME::Job *job = qobject_cast<QGpgME::Job *>(sender());
135 assert(job);
136 mVerificationResult = verificationresult;
137 mDecryptResult = decryptionresult;
138 mData = plainText;
139 mAuditLogError = job->auditLogError();
140 mAuditLog = job->auditLogAsHtml();
141 mEventLoop->quit();
142}
143
144void QGpgMEJobExecutor::importResult(const GpgME::ImportResult &result)
145{
146 QGpgME::Job *job = qobject_cast<QGpgME::Job *>(sender());
147 assert(job);
148 mImportResult = result;
149 mAuditLogError = job->auditLogError();
150 mAuditLog = job->auditLogAsHtml();
151 mEventLoop->quit();
152}
153
154QString QGpgMEJobExecutor::auditLogAsHtml() const
155{
156 return mAuditLog;
157}
158
diff --git a/framework/src/domain/mime/mimetreeparser/otp/qgpgmejobexecutor.h b/framework/src/domain/mime/mimetreeparser/otp/qgpgmejobexecutor.h
new file mode 100644
index 00000000..8a81b078
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/qgpgmejobexecutor.h
@@ -0,0 +1,86 @@
1/*
2 Copyright (c) 2008 Volker Krause <vkrause@kde.org>
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17*/
18
19#ifndef __MIMETREEPARSER_KLEOJOBEXECUTOR_H__
20#define __MIMETREEPARSER_KLEOJOBEXECUTOR_H__
21
22#include <gpgme++/decryptionresult.h>
23#include <gpgme++/importresult.h>
24#include <gpgme++/verificationresult.h>
25
26#include <QObject>
27
28#include <utility>
29
30class QEventLoop;
31
32namespace QGpgME
33{
34class DecryptVerifyJob;
35class ImportJob;
36class VerifyDetachedJob;
37class VerifyOpaqueJob;
38}
39
40namespace MimeTreeParser
41{
42
43/**
44 Helper class for synchronous execution of Kleo crypto jobs.
45*/
46class QGpgMEJobExecutor : public QObject
47{
48 Q_OBJECT
49public:
50 explicit QGpgMEJobExecutor(QObject *parent = nullptr);
51
52 GpgME::VerificationResult exec(QGpgME::VerifyDetachedJob *job,
53 const QByteArray &signature,
54 const QByteArray &signedData);
55 GpgME::VerificationResult exec(QGpgME::VerifyOpaqueJob *job,
56 const QByteArray &signedData,
57 QByteArray &plainText);
58 std::pair<GpgME::DecryptionResult, GpgME::VerificationResult> exec(QGpgME::DecryptVerifyJob *job,
59 const QByteArray &cipherText,
60 QByteArray &plainText);
61 GpgME::ImportResult exec(QGpgME::ImportJob *job, const QByteArray &certData);
62
63 GpgME::Error auditLogError() const;
64 QString auditLogAsHtml() const;
65
66private Q_SLOTS:
67 void verificationResult(const GpgME::VerificationResult &result);
68 void verificationResult(const GpgME::VerificationResult &result, const QByteArray &plainText);
69 void decryptResult(const GpgME::DecryptionResult &decryptionresult,
70 const GpgME::VerificationResult &verificationresult,
71 const QByteArray &plainText);
72 void importResult(const GpgME::ImportResult &result);
73
74private:
75 QEventLoop *mEventLoop;
76 GpgME::VerificationResult mVerificationResult;
77 GpgME::DecryptionResult mDecryptResult;
78 GpgME::ImportResult mImportResult;
79 QByteArray mData;
80 GpgME::Error mAuditLogError;
81 QString mAuditLog;
82};
83
84}
85
86#endif
diff --git a/framework/src/domain/mime/mimetreeparser/otp/queuehtmlwriter.cpp b/framework/src/domain/mime/mimetreeparser/otp/queuehtmlwriter.cpp
new file mode 100644
index 00000000..ea17bf5c
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/queuehtmlwriter.cpp
@@ -0,0 +1,136 @@
1/*
2 Copyright (c) 2015 Sandro Knauß <sknauss@kde.org>
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 "queuehtmlwriter.h"
21
22#include "mimetreeparser_debug.h"
23
24#include<QByteArray>
25#include<QString>
26
27using namespace MimeTreeParser;
28
29QueueHtmlWriter::QueueHtmlWriter(HtmlWriter *base)
30 : HtmlWriter()
31 , mBase(base)
32{
33}
34
35QueueHtmlWriter::~QueueHtmlWriter()
36{
37}
38
39void QueueHtmlWriter::setBase(HtmlWriter *base)
40{
41 mBase = base;
42}
43
44void QueueHtmlWriter::begin(const QString &css)
45{
46 Command cmd;
47 cmd.type = Command::Begin;
48 cmd.s = css;
49 mQueue.append(cmd);
50}
51
52void QueueHtmlWriter::end()
53{
54 Command cmd;
55 cmd.type = Command::End;
56 mQueue.append(cmd);
57}
58
59void QueueHtmlWriter::reset()
60{
61 Command cmd;
62 cmd.type = Command::Reset;
63 mQueue.append(cmd);
64}
65
66void QueueHtmlWriter::write(const QString &str)
67{
68 Command cmd;
69 cmd.type = Command::Write;
70 cmd.s = str;
71 mQueue.append(cmd);
72}
73
74void QueueHtmlWriter::queue(const QString &str)
75{
76 Command cmd;
77 cmd.type = Command::Queue;
78 cmd.s = str;
79 mQueue.append(cmd);
80}
81
82void QueueHtmlWriter::flush()
83{
84 Command cmd;
85 cmd.type = Command::Flush;
86 mQueue.append(cmd);
87}
88
89void QueueHtmlWriter::replay()
90{
91 foreach (const auto &entry, mQueue) {
92 switch (entry.type) {
93 case Command::Begin:
94 mBase->begin(entry.s);
95 break;
96 case Command::End:
97 mBase->end();
98 break;
99 case Command::Reset:
100 mBase->reset();
101 break;
102 case Command::Write:
103 mBase->write(entry.s);
104 break;
105 case Command::Queue:
106 mBase->queue(entry.s);
107 break;
108 case Command::Flush:
109 mBase->flush();
110 break;
111 case Command::EmbedPart:
112 mBase->embedPart(entry.b, entry.s);
113 break;
114 case Command::ExtraHead:
115 mBase->extraHead(entry.s);
116 break;
117 }
118 }
119}
120
121void QueueHtmlWriter::embedPart(const QByteArray &contentId, const QString &url)
122{
123 Command cmd;
124 cmd.type = Command::EmbedPart;
125 cmd.s = url;
126 cmd.b = contentId;
127 mQueue.append(cmd);
128}
129void QueueHtmlWriter::extraHead(const QString &extra)
130{
131 Command cmd;
132 cmd.type = Command::ExtraHead;
133 cmd.s = extra;
134 mQueue.append(cmd);
135}
136
diff --git a/framework/src/domain/mime/mimetreeparser/otp/queuehtmlwriter.h b/framework/src/domain/mime/mimetreeparser/otp/queuehtmlwriter.h
new file mode 100644
index 00000000..9e7a4659
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/queuehtmlwriter.h
@@ -0,0 +1,75 @@
1/*
2 Copyright (c) 2015 Sandro Knauß <sknauss@kde.org>
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#ifndef __MIMETREEPARSER_QUEUEHTMLWRITER_H__
21#define __MIMETREEPARSER_QUEUEHTMLWRITER_H__
22
23#include "htmlwriter.h"
24
25#include<QVector>
26#include<QVariant>
27
28class QString;
29class QByteArray;
30
31namespace MimeTreeParser
32{
33/**
34\brief Cache HTML output and not write them directy.
35
36This class is needed to make it possible to first process the mime tree and
37afterwards render the HTML.
38
39Please do not use this class - it is only added to make it possible to slowly
40move ObjectTreeParser to a process fist / render later.
41
42*/
43struct Command {
44 enum { Begin, End, Reset, Write, Queue, Flush, EmbedPart, ExtraHead } type;
45 QString s;
46 QByteArray b;
47};
48
49class QueueHtmlWriter : public HtmlWriter
50{
51public:
52 explicit QueueHtmlWriter(MimeTreeParser::HtmlWriter *base);
53 virtual ~QueueHtmlWriter();
54
55 void setBase(HtmlWriter *base);
56
57 void begin(const QString &cssDefs) Q_DECL_OVERRIDE;
58 void end() Q_DECL_OVERRIDE;
59 void reset() Q_DECL_OVERRIDE;
60 void write(const QString &str) Q_DECL_OVERRIDE;
61 void queue(const QString &str) Q_DECL_OVERRIDE;
62 void flush() Q_DECL_OVERRIDE;
63 void embedPart(const QByteArray &contentId, const QString &url) Q_DECL_OVERRIDE;
64 void extraHead(const QString &str) Q_DECL_OVERRIDE;
65
66 void replay();
67
68private:
69 HtmlWriter *mBase;
70 QVector<Command> mQueue;
71};
72
73} // namespace MimeTreeParser
74
75#endif // __MIMETREEPARSER_QUEUEHTMLWRITER_H__
diff --git a/framework/src/domain/mime/mimetreeparser/otp/texthtml.cpp b/framework/src/domain/mime/mimetreeparser/otp/texthtml.cpp
new file mode 100644
index 00000000..51332cff
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/texthtml.cpp
@@ -0,0 +1,58 @@
1/*
2 Copyright (c) 2016 Sandro Knauß <sknauss@kde.org>
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 "texthtml.h"
21
22#include "attachmentstrategy.h"
23#include "objecttreeparser.h"
24#include "messagepart.h"
25
26#include <KMime/Content>
27
28#include "mimetreeparser_debug.h"
29
30using namespace MimeTreeParser;
31
32const TextHtmlBodyPartFormatter *TextHtmlBodyPartFormatter::self;
33
34const Interface::BodyPartFormatter *TextHtmlBodyPartFormatter::create()
35{
36 if (!self) {
37 self = new TextHtmlBodyPartFormatter();
38 }
39 return self;
40}
41Interface::BodyPartFormatter::Result TextHtmlBodyPartFormatter::format(Interface::BodyPart *part, HtmlWriter *writer) const
42{
43 Q_UNUSED(writer)
44 const auto p = process(*part);
45 const auto mp = static_cast<MessagePart *>(p.data());
46 if (mp) {
47 mp->html(false);
48 return Ok;
49 }
50 return Failed;
51}
52
53Interface::MessagePart::Ptr TextHtmlBodyPartFormatter::process(Interface::BodyPart &part) const
54{
55 KMime::Content *node = part.content();
56 HtmlMessagePart::Ptr mp(new HtmlMessagePart(part.objectTreeParser(), node, part.source()));
57 return mp;
58}
diff --git a/framework/src/domain/mime/mimetreeparser/otp/texthtml.h b/framework/src/domain/mime/mimetreeparser/otp/texthtml.h
new file mode 100644
index 00000000..a03cfe50
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/texthtml.h
@@ -0,0 +1,41 @@
1/*
2 Copyright (c) 2016 Sandro Knauß <sknauss@kde.org>
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#ifndef __MIMETREEPARSER_BODYFORAMATTER_TEXTHTML_H__
21#define __MIMETREEPARSER_BODYFORAMATTER_TEXTHTML_H__
22
23#include "bodypartformatter.h"
24#include "bodypart.h"
25
26namespace MimeTreeParser
27{
28
29class TextHtmlBodyPartFormatter : public Interface::BodyPartFormatter
30{
31 static const TextHtmlBodyPartFormatter *self;
32public:
33 Interface::MessagePart::Ptr process(Interface::BodyPart &part) const Q_DECL_OVERRIDE;
34 Interface::BodyPartFormatter::Result format(Interface::BodyPart *, HtmlWriter *) const Q_DECL_OVERRIDE;
35 using Interface::BodyPartFormatter::format;
36 static const Interface::BodyPartFormatter *create();
37};
38
39}
40
41#endif
diff --git a/framework/src/domain/mime/mimetreeparser/otp/textplain.cpp b/framework/src/domain/mime/mimetreeparser/otp/textplain.cpp
new file mode 100644
index 00000000..d3437f04
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/textplain.cpp
@@ -0,0 +1,78 @@
1/*
2 Copyright (c) 2016 Sandro Knauß <sknauss@kde.org>
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 "textplain.h"
21
22#include "attachmentstrategy.h"
23#include "objecttreeparser.h"
24#include "messagepart.h"
25
26#include <KMime/Content>
27
28#include "mimetreeparser_debug.h"
29
30using namespace MimeTreeParser;
31
32const TextPlainBodyPartFormatter *TextPlainBodyPartFormatter::self;
33
34const Interface::BodyPartFormatter *TextPlainBodyPartFormatter::create()
35{
36 if (!self) {
37 self = new TextPlainBodyPartFormatter();
38 }
39 return self;
40}
41Interface::BodyPartFormatter::Result TextPlainBodyPartFormatter::format(Interface::BodyPart *part, HtmlWriter *writer) const
42{
43 Q_UNUSED(writer)
44 const auto p = process(*part);
45 const auto mp = static_cast<MessagePart *>(p.data());
46 if (mp) {
47 mp->html(false);
48 return Ok;
49 }
50 return Failed;
51}
52
53Interface::MessagePart::Ptr TextPlainBodyPartFormatter::process(Interface::BodyPart &part) const
54{
55 KMime::Content *node = part.content();
56 const bool isFirstTextPart = (node->topLevel()->textContent() == node);
57
58 QString label = NodeHelper::fileName(node);
59
60 const bool bDrawFrame = !isFirstTextPart
61 && !part.objectTreeParser()->showOnlyOneMimePart()
62 && !label.isEmpty();
63 const QString fileName = part.nodeHelper()->writeNodeToTempFile(node);
64
65 TextMessagePart::Ptr mp;
66 if (isFirstTextPart) {
67 mp = TextMessagePart::Ptr(new TextMessagePart(part.objectTreeParser(), node, bDrawFrame, fileName.isEmpty(), part.source()->decryptMessage()));
68 } else {
69 mp = TextMessagePart::Ptr(new AttachmentMessagePart(part.objectTreeParser(), node, bDrawFrame, fileName.isEmpty(), part.source()->decryptMessage()));
70 }
71
72 part.processResult()->setInlineSignatureState(mp->signatureState());
73 part.processResult()->setInlineEncryptionState(mp->encryptionState());
74
75 part.nodeHelper()->setNodeDisplayedEmbedded(node, true);
76
77 return mp;
78}
diff --git a/framework/src/domain/mime/mimetreeparser/otp/textplain.h b/framework/src/domain/mime/mimetreeparser/otp/textplain.h
new file mode 100644
index 00000000..c97a6aec
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/textplain.h
@@ -0,0 +1,41 @@
1/*
2 Copyright (c) 2016 Sandro Knauß <sknauss@kde.org>
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#ifndef __MIMETREEPARSER_BODYFORAMATTER_TEXTPLAIN_H__
21#define __MIMETREEPARSER_BODYFORAMATTER_TEXTPLAIN_H__
22
23#include "bodypartformatter.h"
24#include "bodypart.h"
25
26namespace MimeTreeParser
27{
28
29class TextPlainBodyPartFormatter : public Interface::BodyPartFormatter
30{
31 static const TextPlainBodyPartFormatter *self;
32public:
33 Interface::MessagePart::Ptr process(Interface::BodyPart &part) const Q_DECL_OVERRIDE;
34 Interface::BodyPartFormatter::Result format(Interface::BodyPart *, HtmlWriter *) const Q_DECL_OVERRIDE;
35 using Interface::BodyPartFormatter::format;
36 static const Interface::BodyPartFormatter *create();
37};
38
39}
40
41#endif
diff --git a/framework/src/domain/mime/mimetreeparser/otp/util.cpp b/framework/src/domain/mime/mimetreeparser/otp/util.cpp
new file mode 100644
index 00000000..5ca8d828
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/util.cpp
@@ -0,0 +1,136 @@
1/*
2 Copyright (c) 2016 Sandro Knauß <sknauss@kde.org>
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License along
15 with this program; if not, write to the Free Software Foundation, Inc.,
16 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17*/
18
19#include "util.h"
20
21#include "mimetreeparser_debug.h"
22
23#include "nodehelper.h"
24
25#include <KMime/Content>
26
27#include <QMimeDatabase>
28#include <QString>
29
30using namespace MimeTreeParser::Util;
31
32bool MimeTreeParser::Util::isTypeBlacklisted(KMime::Content *node)
33{
34 const QByteArray mediaTypeLower = node->contentType()->mediaType().toLower();
35 bool typeBlacklisted = mediaTypeLower == "multipart";
36 if (!typeBlacklisted) {
37 typeBlacklisted = KMime::isCryptoPart(node);
38 }
39 typeBlacklisted = typeBlacklisted || node == node->topLevel();
40 const bool firstTextChildOfEncapsulatedMsg =
41 mediaTypeLower == "text" &&
42 node->contentType()->subType().toLower() == "plain" &&
43 node->parent() && node->parent()->contentType()->mediaType().toLower() == "message";
44 return typeBlacklisted || firstTextChildOfEncapsulatedMsg;
45}
46
47QString MimeTreeParser::Util::labelForContent(KMime::Content *node)
48{
49 const QString name = node->contentType()->name();
50 QString label = name.isEmpty() ? NodeHelper::fileName(node) : name;
51 if (label.isEmpty()) {
52 label = node->contentDescription()->asUnicodeString();
53 }
54 return label;
55}
56
57QMimeType MimeTreeParser::Util::mimetype(const QString &name)
58{
59 QMimeDatabase db;
60 // consider the filename if mimetype cannot be found by content-type
61 const auto mimeTypes = db.mimeTypesForFileName(name);
62 for (const auto &mt : mimeTypes) {
63 if (mt.name() != QLatin1String("application/octet-stream")) {
64 return mt;
65 }
66 }
67
68 // consider the attachment's contents if neither the Content-Type header
69 // nor the filename give us a clue
70 return db.mimeTypeForFile(name);
71}
72
73QString MimeTreeParser::Util::iconNameForMimetype(const QString &mimeType,
74 const QString &fallbackFileName1,
75 const QString &fallbackFileName2)
76{
77 QString fileName;
78 QString tMimeType = mimeType;
79
80 // convert non-registered types to registered types
81 if (mimeType == QLatin1String("application/x-vnd.kolab.contact")) {
82 tMimeType = QStringLiteral("text/x-vcard");
83 } else if (mimeType == QLatin1String("application/x-vnd.kolab.event")) {
84 tMimeType = QStringLiteral("application/x-vnd.akonadi.calendar.event");
85 } else if (mimeType == QLatin1String("application/x-vnd.kolab.task")) {
86 tMimeType = QStringLiteral("application/x-vnd.akonadi.calendar.todo");
87 } else if (mimeType == QLatin1String("application/x-vnd.kolab.journal")) {
88 tMimeType = QStringLiteral("application/x-vnd.akonadi.calendar.journal");
89 } else if (mimeType == QLatin1String("application/x-vnd.kolab.note")) {
90 tMimeType = QStringLiteral("application/x-vnd.akonadi.note");
91 } else if (mimeType == QLatin1String("image/jpg")) {
92 tMimeType = QStringLiteral("image/jpeg");
93 }
94 QMimeDatabase mimeDb;
95 auto mime = mimeDb.mimeTypeForName(tMimeType);
96 if (mime.isValid()) {
97 fileName = mime.iconName();
98 } else {
99 fileName = QStringLiteral("unknown");
100 if (!tMimeType.isEmpty()) {
101 qCWarning(MIMETREEPARSER_LOG) << "unknown mimetype" << tMimeType;
102 }
103 }
104 //WorkAround for #199083
105 if (fileName == QLatin1String("text-vcard")) {
106 fileName = QStringLiteral("text-x-vcard");
107 }
108
109 if (fileName.isEmpty()) {
110 fileName = fallbackFileName1;
111 if (fileName.isEmpty()) {
112 fileName = fallbackFileName2;
113 }
114 if (!fileName.isEmpty()) {
115 fileName = mimeDb.mimeTypeForFile(QLatin1String("/tmp/") + fileName).iconName();
116 }
117 }
118
119 return fileName;
120}
121
122QString MimeTreeParser::Util::iconNameForContent(KMime::Content *node)
123{
124 if (!node) {
125 return QString();
126 }
127
128 QByteArray mimeType = node->contentType()->mimeType();
129 if (mimeType.isNull() || mimeType == "application/octet-stream") {
130 const QString mime = mimetype(node->contentDisposition()->filename()).name();
131 mimeType = mime.toLatin1();
132 }
133 mimeType = mimeType.toLower();
134 return iconNameForMimetype(QLatin1String(mimeType), node->contentDisposition()->filename(),
135 node->contentType()->name());
136}
diff --git a/framework/src/domain/mime/mimetreeparser/otp/util.h b/framework/src/domain/mime/mimetreeparser/otp/util.h
new file mode 100644
index 00000000..099c647a
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/util.h
@@ -0,0 +1,67 @@
1/*
2 Copyright (c) 2016 Sandro Knauß <sknauss@kde.org>
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License along
15 with this program; if not, write to the Free Software Foundation, Inc.,
16 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17*/
18
19#ifndef __MIMETREEPARSER_UTILS_UTIL_H__
20#define __MIMETREEPARSER_UTILS_UTIL_H__
21
22#include <QString>
23
24class QMimeType;
25
26namespace KMime
27{
28class Content;
29}
30
31namespace MimeTreeParser
32{
33
34/**
35 * The Util namespace contains a collection of helper functions use in
36 * various places.
37 */
38namespace Util
39{
40
41/**
42 * Describes the type of the displayed message. This depends on the MIME structure
43 * of the mail and on whether HTML mode is enabled (which is decided by htmlMail())
44 */
45enum HtmlMode {
46 Normal, ///< A normal plaintext message, non-multipart
47 Html, ///< A HTML message, non-multipart
48 MultipartPlain, ///< A multipart/alternative message, the plain text part is currently displayed
49 MultipartHtml, ///< A multipart/altervative message, the HTML part is currently displayed
50 MultipartIcal ///< A multipart/altervative message, the ICal part is currently displayed
51};
52
53bool isTypeBlacklisted(KMime::Content *node);
54
55QString labelForContent(KMime::Content *node);
56
57QMimeType mimetype(const QString &name);
58
59QString iconNameForMimetype(const QString &mimeType,
60 const QString &fallbackFileName1 = QString(),
61 const QString &fallbackFileName2 = QString());
62
63QString iconNameForContent(KMime::Content *node);
64}
65}
66
67#endif
diff --git a/framework/src/domain/mime/mimetreeparser/otp/utils.cpp b/framework/src/domain/mime/mimetreeparser/otp/utils.cpp
new file mode 100644
index 00000000..8f718143
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/utils.cpp
@@ -0,0 +1,70 @@
1/*
2 Copyright (c) 2016 Sandro Knauß <sknauss@kde.org>
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 "utils.h"
21
22using namespace MimeTreeParser;
23
24MimeMessagePart::Ptr MimeTreeParser::createAndParseTempNode(Interface::BodyPart &part, KMime::Content *parentNode, const char *content, const char *cntDesc)
25{
26 KMime::Content *newNode = new KMime::Content();
27 newNode->setContent(KMime::CRLFtoLF(content));
28 newNode->parse();
29
30 if (!newNode->head().isEmpty()) {
31 newNode->contentDescription()->from7BitString(cntDesc);
32 }
33 part.nodeHelper()->attachExtraContent(parentNode, newNode);
34
35 return MimeMessagePart::Ptr(new MimeMessagePart(part.objectTreeParser(), newNode, false));
36}
37
38KMime::Content *MimeTreeParser::findTypeInDirectChilds(KMime::Content *content, const QByteArray &mimeType)
39{
40 if (mimeType.isEmpty()) {
41 return content;
42 }
43
44 foreach (auto child, content->contents()) {
45 if ((!child->contentType()->isEmpty())
46 && (mimeType == child->contentType()->mimeType())) {
47 return child;
48 }
49 }
50 return nullptr;
51}
52
53MessagePart::Ptr MimeTreeParser::toplevelTextNode(MessagePart::Ptr messageTree)
54{
55 foreach (const auto &mp, messageTree->subParts()) {
56 auto text = mp.dynamicCast<TextMessagePart>();
57 auto attach = mp.dynamicCast<AttachmentMessagePart>();
58 if (text && !attach) {
59 return text;
60 } else if (const auto alternative = mp.dynamicCast<AlternativeMessagePart>()) {
61 return alternative;
62 } else if (const auto m = mp.dynamicCast<MessagePart>()) {
63 auto ret = toplevelTextNode(m);
64 if (ret) {
65 return ret;
66 }
67 }
68 }
69 return MessagePart::Ptr();
70}
diff --git a/framework/src/domain/mime/mimetreeparser/otp/utils.h b/framework/src/domain/mime/mimetreeparser/otp/utils.h
new file mode 100644
index 00000000..d4aaa43a
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/utils.h
@@ -0,0 +1,42 @@
1/*
2 Copyright (c) 2016 Sandro Knauß <sknauss@kde.org>
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#ifndef __MIMETREEPARSER_BODYFORAMATTER_UTILS_H__
21#define __MIMETREEPARSER_BODYFORAMATTER_UTILS_H__
22
23#include "bodypart.h"
24#include "messagepart.h"
25
26#include <KMime/Content>
27
28namespace MimeTreeParser
29{
30/**
31 1. Create a new partNode using 'content' data and Content-Description
32 found in 'cntDesc'.
33 2. Parse the 'node' to display the content.
34*/
35MimeMessagePart::Ptr createAndParseTempNode(Interface::BodyPart &part, KMime::Content *parentNode, const char *content, const char *cntDesc);
36
37KMime::Content *findTypeInDirectChilds(KMime::Content *content, const QByteArray &mimeType);
38
39MessagePart::Ptr toplevelTextNode(MessagePart::Ptr messageTree);
40}
41
42#endif
diff --git a/framework/src/domain/mime/mimetreeparser/otp/verifydetachedbodypartmemento.cpp b/framework/src/domain/mime/mimetreeparser/otp/verifydetachedbodypartmemento.cpp
new file mode 100644
index 00000000..56c1d1a7
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/verifydetachedbodypartmemento.cpp
@@ -0,0 +1,177 @@
1/*
2 Copyright (c) 2014-2017 Montel Laurent <montel@kde.org>
3
4 This program is free software; you can redistribute it and/or modify it
5 under the terms of the GNU General Public License, version 2, as
6 published by the Free Software Foundation.
7
8 This program is distributed in the hope that it will be useful, but
9 WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 General Public License for more details.
12
13 You should have received a copy of the GNU General Public License along
14 with this program; if not, write to the Free Software Foundation, Inc.,
15 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16*/
17
18#include "verifydetachedbodypartmemento.h"
19#include "mimetreeparser_debug.h"
20
21#include <QGpgME/VerifyDetachedJob>
22#include <QGpgME/KeyListJob>
23
24#include <gpgme++/keylistresult.h>
25
26#include <qstringlist.h>
27
28#include <cassert>
29
30using namespace QGpgME;
31using namespace GpgME;
32using namespace MimeTreeParser;
33
34VerifyDetachedBodyPartMemento::VerifyDetachedBodyPartMemento(VerifyDetachedJob *job,
35 KeyListJob *klj,
36 const QByteArray &signature,
37 const QByteArray &plainText)
38 : CryptoBodyPartMemento(),
39 m_signature(signature),
40 m_plainText(plainText),
41 m_job(job),
42 m_keylistjob(klj)
43{
44 assert(m_job);
45}
46
47VerifyDetachedBodyPartMemento::~VerifyDetachedBodyPartMemento()
48{
49 if (m_job) {
50 m_job->slotCancel();
51 }
52 if (m_keylistjob) {
53 m_keylistjob->slotCancel();
54 }
55}
56
57bool VerifyDetachedBodyPartMemento::start()
58{
59 assert(m_job);
60#ifdef DEBUG_SIGNATURE
61 qCDebug(MIMETREEPARSER_LOG) << "tokoe: VerifyDetachedBodyPartMemento started";
62#endif
63 connect(m_job, SIGNAL(result(GpgME::VerificationResult)),
64 this, SLOT(slotResult(GpgME::VerificationResult)));
65 if (const Error err = m_job->start(m_signature, m_plainText)) {
66 m_vr = VerificationResult(err);
67#ifdef DEBUG_SIGNATURE
68 qCDebug(MIMETREEPARSER_LOG) << "tokoe: VerifyDetachedBodyPartMemento stopped with error";
69#endif
70 return false;
71 }
72 setRunning(true);
73 return true;
74}
75
76void VerifyDetachedBodyPartMemento::exec()
77{
78 assert(m_job);
79 setRunning(true);
80#ifdef DEBUG_SIGNATURE
81 qCDebug(MIMETREEPARSER_LOG) << "tokoe: VerifyDetachedBodyPartMemento execed";
82#endif
83 saveResult(m_job->exec(m_signature, m_plainText));
84 m_job->deleteLater(); // exec'ed jobs don't delete themselves
85 m_job = nullptr;
86#ifdef DEBUG_SIGNATURE
87 qCDebug(MIMETREEPARSER_LOG) << "tokoe: VerifyDetachedBodyPartMemento after execed";
88#endif
89 if (canStartKeyListJob()) {
90 std::vector<GpgME::Key> keys;
91 m_keylistjob->exec(keyListPattern(), /*secretOnly=*/false, keys);
92 if (!keys.empty()) {
93 m_key = keys.back();
94 }
95 }
96 if (m_keylistjob) {
97 m_keylistjob->deleteLater(); // exec'ed jobs don't delete themselves
98 }
99 m_keylistjob = nullptr;
100 setRunning(false);
101}
102
103bool VerifyDetachedBodyPartMemento::canStartKeyListJob() const
104{
105 if (!m_keylistjob) {
106 return false;
107 }
108 const char *const fpr = m_vr.signature(0).fingerprint();
109 return fpr && *fpr;
110}
111
112QStringList VerifyDetachedBodyPartMemento::keyListPattern() const
113{
114 assert(canStartKeyListJob());
115 return QStringList(QString::fromLatin1(m_vr.signature(0).fingerprint()));
116}
117
118void VerifyDetachedBodyPartMemento::saveResult(const VerificationResult &vr)
119{
120 assert(m_job);
121#ifdef DEBUG_SIGNATURE
122 qCDebug(MIMETREEPARSER_LOG) << "tokoe: VerifyDetachedBodyPartMemento::saveResult called";
123#endif
124 m_vr = vr;
125 setAuditLog(m_job->auditLogError(), m_job->auditLogAsHtml());
126}
127
128void VerifyDetachedBodyPartMemento::slotResult(const VerificationResult &vr)
129{
130#ifdef DEBUG_SIGNATURE
131 qCDebug(MIMETREEPARSER_LOG) << "tokoe: VerifyDetachedBodyPartMemento::slotResult called";
132#endif
133 saveResult(vr);
134 m_job = nullptr;
135 if (canStartKeyListJob() && startKeyListJob()) {
136#ifdef DEBUG_SIGNATURE
137 qCDebug(MIMETREEPARSER_LOG) << "tokoe: VerifyDetachedBodyPartMemento: canStartKeyListJob && startKeyListJob";
138#endif
139 return;
140 }
141 if (m_keylistjob) {
142 m_keylistjob->deleteLater();
143 }
144 m_keylistjob = nullptr;
145 setRunning(false);
146 notify();
147}
148
149bool VerifyDetachedBodyPartMemento::startKeyListJob()
150{
151 assert(canStartKeyListJob());
152 if (const GpgME::Error err = m_keylistjob->start(keyListPattern())) {
153 return false;
154 }
155 connect(m_keylistjob, SIGNAL(done()), this, SLOT(slotKeyListJobDone()));
156 connect(m_keylistjob, SIGNAL(nextKey(GpgME::Key)),
157 this, SLOT(slotNextKey(GpgME::Key)));
158 return true;
159}
160
161void VerifyDetachedBodyPartMemento::slotNextKey(const GpgME::Key &key)
162{
163#ifdef DEBUG_SIGNATURE
164 qCDebug(MIMETREEPARSER_LOG) << "tokoe: VerifyDetachedBodyPartMemento::slotNextKey called";
165#endif
166 m_key = key;
167}
168
169void VerifyDetachedBodyPartMemento::slotKeyListJobDone()
170{
171#ifdef DEBUG_SIGNATURE
172 qCDebug(MIMETREEPARSER_LOG) << "tokoe: VerifyDetachedBodyPartMemento::slotKeyListJobDone called";
173#endif
174 m_keylistjob = nullptr;
175 setRunning(false);
176 notify();
177}
diff --git a/framework/src/domain/mime/mimetreeparser/otp/verifydetachedbodypartmemento.h b/framework/src/domain/mime/mimetreeparser/otp/verifydetachedbodypartmemento.h
new file mode 100644
index 00000000..f37dfe81
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/verifydetachedbodypartmemento.h
@@ -0,0 +1,87 @@
1/*
2 Copyright (c) 2014-2016 Montel Laurent <montel@kde.org>
3
4 This program is free software; you can redistribute it and/or modify it
5 under the terms of the GNU General Public License, version 2, as
6 published by the Free Software Foundation.
7
8 This program is distributed in the hope that it will be useful, but
9 WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 General Public License for more details.
12
13 You should have received a copy of the GNU General Public License along
14 with this program; if not, write to the Free Software Foundation, Inc.,
15 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16*/
17
18#ifndef __MIMETREEPARSER_VERIFYDETACHEDBODYPARTMEMENTO_H__
19#define __MIMETREEPARSER_VERIFYDETACHEDBODYPARTMEMENTO_H__
20
21#include "cryptobodypartmemento.h"
22#include <gpgme++/verificationresult.h>
23#include <gpgme++/key.h>
24
25#include <QString>
26#include <QPointer>
27
28#include "bodypart.h"
29
30namespace QGpgME
31{
32class VerifyDetachedJob;
33class KeyListJob;
34}
35
36class QStringList;
37
38namespace MimeTreeParser
39{
40
41class VerifyDetachedBodyPartMemento
42 : public CryptoBodyPartMemento
43{
44 Q_OBJECT
45public:
46 VerifyDetachedBodyPartMemento(QGpgME::VerifyDetachedJob *job,
47 QGpgME::KeyListJob *klj,
48 const QByteArray &signature,
49 const QByteArray &plainText);
50 ~VerifyDetachedBodyPartMemento();
51
52 bool start() Q_DECL_OVERRIDE;
53 void exec() Q_DECL_OVERRIDE;
54
55 const GpgME::VerificationResult &verifyResult() const
56 {
57 return m_vr;
58 }
59 const GpgME::Key &signingKey() const
60 {
61 return m_key;
62 }
63
64private Q_SLOTS:
65 void slotResult(const GpgME::VerificationResult &vr);
66 void slotKeyListJobDone();
67 void slotNextKey(const GpgME::Key &);
68
69private:
70 void saveResult(const GpgME::VerificationResult &);
71 bool canStartKeyListJob() const;
72 QStringList keyListPattern() const;
73 bool startKeyListJob();
74private:
75 // input:
76 const QByteArray m_signature;
77 const QByteArray m_plainText;
78 QPointer<QGpgME::VerifyDetachedJob> m_job;
79 QPointer<QGpgME::KeyListJob> m_keylistjob;
80 // output:
81 GpgME::VerificationResult m_vr;
82 GpgME::Key m_key;
83};
84
85}
86
87#endif // __MIMETREEPARSER_VERIFYDETACHEDBODYPARTMEMENTO_H__
diff --git a/framework/src/domain/mime/mimetreeparser/otp/verifyopaquebodypartmemento.cpp b/framework/src/domain/mime/mimetreeparser/otp/verifyopaquebodypartmemento.cpp
new file mode 100644
index 00000000..99eb8b8e
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/verifyopaquebodypartmemento.cpp
@@ -0,0 +1,179 @@
1/*
2 Copyright (c) 2014-2017 Montel Laurent <montel@kde.org>
3
4 This program is free software; you can redistribute it and/or modify it
5 under the terms of the GNU General Public License, version 2, as
6 published by the Free Software Foundation.
7
8 This program is distributed in the hope that it will be useful, but
9 WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 General Public License for more details.
12
13 You should have received a copy of the GNU General Public License along
14 with this program; if not, write to the Free Software Foundation, Inc.,
15 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16*/
17
18#include "verifyopaquebodypartmemento.h"
19#include "mimetreeparser_debug.h"
20
21#include <QGpgME/VerifyOpaqueJob>
22#include <QGpgME/KeyListJob>
23
24#include <gpgme++/keylistresult.h>
25
26#include <qstringlist.h>
27
28#include <cassert>
29
30using namespace QGpgME;
31using namespace GpgME;
32using namespace MimeTreeParser;
33
34VerifyOpaqueBodyPartMemento::VerifyOpaqueBodyPartMemento(VerifyOpaqueJob *job,
35 KeyListJob *klj,
36 const QByteArray &signature)
37 : CryptoBodyPartMemento(),
38 m_signature(signature),
39 m_job(job),
40 m_keylistjob(klj)
41{
42 assert(m_job);
43}
44
45VerifyOpaqueBodyPartMemento::~VerifyOpaqueBodyPartMemento()
46{
47 if (m_job) {
48 m_job->slotCancel();
49 }
50 if (m_keylistjob) {
51 m_keylistjob->slotCancel();
52 }
53}
54
55bool VerifyOpaqueBodyPartMemento::start()
56{
57 assert(m_job);
58#ifdef DEBUG_SIGNATURE
59 qCDebug(MIMETREEPARSER_LOG) << "tokoe: VerifyOpaqueBodyPartMemento started";
60#endif
61 if (const Error err = m_job->start(m_signature)) {
62 m_vr = VerificationResult(err);
63#ifdef DEBUG_SIGNATURE
64 qCDebug(MIMETREEPARSER_LOG) << "tokoe: VerifyOpaqueBodyPartMemento stopped with error";
65#endif
66 return false;
67 }
68 connect(m_job, SIGNAL(result(GpgME::VerificationResult,QByteArray)),
69 this, SLOT(slotResult(GpgME::VerificationResult,QByteArray)));
70 setRunning(true);
71 return true;
72}
73
74void VerifyOpaqueBodyPartMemento::exec()
75{
76 assert(m_job);
77 setRunning(true);
78 QByteArray plainText;
79#ifdef DEBUG_SIGNATURE
80 qCDebug(MIMETREEPARSER_LOG) << "tokoe: VerifyOpaqueBodyPartMemento execed";
81#endif
82 saveResult(m_job->exec(m_signature, plainText), plainText);
83#ifdef DEBUG_SIGNATURE
84 qCDebug(MIMETREEPARSER_LOG) << "tokoe: VerifyOpaqueBodyPartMemento after execed";
85#endif
86 m_job->deleteLater(); // exec'ed jobs don't delete themselves
87 m_job = nullptr;
88 if (canStartKeyListJob()) {
89 std::vector<GpgME::Key> keys;
90 m_keylistjob->exec(keyListPattern(), /*secretOnly=*/false, keys);
91 if (!keys.empty()) {
92 m_key = keys.back();
93 }
94 }
95 if (m_keylistjob) {
96 m_keylistjob->deleteLater(); // exec'ed jobs don't delete themselves
97 }
98 m_keylistjob = nullptr;
99 setRunning(false);
100}
101
102bool VerifyOpaqueBodyPartMemento::canStartKeyListJob() const
103{
104 if (!m_keylistjob) {
105 return false;
106 }
107 const char *const fpr = m_vr.signature(0).fingerprint();
108 return fpr && *fpr;
109}
110
111QStringList VerifyOpaqueBodyPartMemento::keyListPattern() const
112{
113 assert(canStartKeyListJob());
114 return QStringList(QString::fromLatin1(m_vr.signature(0).fingerprint()));
115}
116
117void VerifyOpaqueBodyPartMemento::saveResult(const VerificationResult &vr,
118 const QByteArray &plainText)
119{
120 assert(m_job);
121#ifdef DEBUG_SIGNATURE
122 qCDebug(MIMETREEPARSER_LOG) << "tokoe: VerifyOpaqueBodyPartMemento::saveResult called";
123#endif
124 m_vr = vr;
125 m_plainText = plainText;
126 setAuditLog(m_job->auditLogError(), m_job->auditLogAsHtml());
127}
128
129void VerifyOpaqueBodyPartMemento::slotResult(const VerificationResult &vr,
130 const QByteArray &plainText)
131{
132#ifdef DEBUG_SIGNATURE
133 qCDebug(MIMETREEPARSER_LOG) << "tokoe: VerifyOpaqueBodyPartMemento::slotResult called";
134#endif
135 saveResult(vr, plainText);
136 m_job = nullptr;
137 if (canStartKeyListJob() && startKeyListJob()) {
138#ifdef DEBUG_SIGNATURE
139 qCDebug(MIMETREEPARSER_LOG) << "tokoe: VerifyOpaqueBodyPartMemento: canStartKeyListJob && startKeyListJob";
140#endif
141 return;
142 }
143 if (m_keylistjob) {
144 m_keylistjob->deleteLater();
145 }
146 m_keylistjob = nullptr;
147 setRunning(false);
148 notify();
149}
150
151bool VerifyOpaqueBodyPartMemento::startKeyListJob()
152{
153 assert(canStartKeyListJob());
154 if (const GpgME::Error err = m_keylistjob->start(keyListPattern())) {
155 return false;
156 }
157 connect(m_keylistjob, SIGNAL(done()), this, SLOT(slotKeyListJobDone()));
158 connect(m_keylistjob, SIGNAL(nextKey(GpgME::Key)),
159 this, SLOT(slotNextKey(GpgME::Key)));
160 return true;
161}
162
163void VerifyOpaqueBodyPartMemento::slotNextKey(const GpgME::Key &key)
164{
165#ifdef DEBUG_SIGNATURE
166 qCDebug(MIMETREEPARSER_LOG) << "tokoe: VerifyOpaqueBodyPartMemento::slotNextKey called";
167#endif
168 m_key = key;
169}
170
171void VerifyOpaqueBodyPartMemento::slotKeyListJobDone()
172{
173#ifdef DEBUG_SIGNATURE
174 qCDebug(MIMETREEPARSER_LOG) << "tokoe: VerifyOpaqueBodyPartMemento::slotKeyListJobDone called";
175#endif
176 m_keylistjob = nullptr;
177 setRunning(false);
178 notify();
179}
diff --git a/framework/src/domain/mime/mimetreeparser/otp/verifyopaquebodypartmemento.h b/framework/src/domain/mime/mimetreeparser/otp/verifyopaquebodypartmemento.h
new file mode 100644
index 00000000..02d30a13
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/otp/verifyopaquebodypartmemento.h
@@ -0,0 +1,93 @@
1/*
2 Copyright (c) 2014-2016 Montel Laurent <montel@kde.org>
3
4 This program is free software; you can redistribute it and/or modify it
5 under the terms of the GNU General Public License, version 2, as
6 published by the Free Software Foundation.
7
8 This program is distributed in the hope that it will be useful, but
9 WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 General Public License for more details.
12
13 You should have received a copy of the GNU General Public License along
14 with this program; if not, write to the Free Software Foundation, Inc.,
15 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16*/
17
18#ifndef __MIMETREEPARSER_VERIFYOPAQUEBODYPARTMEMENTO_H__
19#define __MIMETREEPARSER_VERIFYOPAQUEBODYPARTMEMENTO_H__
20
21#include "cryptobodypartmemento.h"
22#include <gpgme++/verificationresult.h>
23#include <gpgme++/decryptionresult.h>
24#include <gpgme++/key.h>
25
26#include <QString>
27#include <QPointer>
28
29#include "bodypart.h"
30
31namespace QGpgME
32{
33class VerifyOpaqueJob;
34class KeyListJob;
35}
36
37class QStringList;
38
39namespace MimeTreeParser
40{
41
42class VerifyOpaqueBodyPartMemento
43 : public CryptoBodyPartMemento
44{
45 Q_OBJECT
46public:
47 VerifyOpaqueBodyPartMemento(QGpgME::VerifyOpaqueJob *job,
48 QGpgME::KeyListJob *klj,
49 const QByteArray &signature);
50 ~VerifyOpaqueBodyPartMemento();
51
52 bool start() Q_DECL_OVERRIDE;
53 void exec() Q_DECL_OVERRIDE;
54
55 const QByteArray &plainText() const
56 {
57 return m_plainText;
58 }
59 const GpgME::VerificationResult &verifyResult() const
60 {
61 return m_vr;
62 }
63 const GpgME::Key &signingKey() const
64 {
65 return m_key;
66 }
67
68private Q_SLOTS:
69 void slotResult(const GpgME::VerificationResult &vr,
70 const QByteArray &plainText);
71 void slotKeyListJobDone();
72 void slotNextKey(const GpgME::Key &);
73
74private:
75 void saveResult(const GpgME::VerificationResult &,
76 const QByteArray &);
77 bool canStartKeyListJob() const;
78 QStringList keyListPattern() const;
79 bool startKeyListJob();
80private:
81 // input:
82 const QByteArray m_signature;
83 QPointer<QGpgME::VerifyOpaqueJob> m_job;
84 QPointer<QGpgME::KeyListJob> m_keylistjob;
85 // output:
86 GpgME::VerificationResult m_vr;
87 QByteArray m_plainText;
88 GpgME::Key m_key;
89};
90
91}
92
93#endif // __MIMETREEPARSER_VERIFYOPAQUEBODYPARTMEMENTO_H__
diff --git a/framework/src/domain/mime/mimetreeparser/stringhtmlwriter.cpp b/framework/src/domain/mime/mimetreeparser/stringhtmlwriter.cpp
new file mode 100644
index 00000000..88034492
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/stringhtmlwriter.cpp
@@ -0,0 +1,150 @@
1/* -*- c++ -*-
2 filehtmlwriter.cpp
3
4 This file is part of KMail, the KDE mail client.
5 Copyright (c) 2003 Marc Mutz <mutz@kde.org>
6
7 KMail is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License, version 2, as
9 published by the Free Software Foundation.
10
11 KMail is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
20 In addition, as a special exception, the copyright holders give
21 permission to link the code of this program with any edition of
22 the Qt library by Trolltech AS, Norway (or with modified versions
23 of Qt that use the same license as Qt), and distribute linked
24 combinations including the two. You must obey the GNU General
25 Public License in all respects for all of the code used other than
26 Qt. If you modify this file, you may extend this exception to
27 your version of the file, but you are not obligated to do so. If
28 you do not wish to do so, delete this exception statement from
29 your version.
30*/
31
32#include "stringhtmlwriter.h"
33
34#include <QDebug>
35#include <QTextStream>
36#include <QUrl>
37
38StringHtmlWriter::StringHtmlWriter()
39 : MimeTreeParser::HtmlWriter()
40 , mState(Ended)
41{
42}
43
44StringHtmlWriter::~StringHtmlWriter()
45{
46}
47
48void StringHtmlWriter::begin(const QString &css)
49{
50 if (mState != Ended) {
51 qWarning() << "begin() called on non-ended session!";
52 reset();
53 }
54
55 mState = Begun;
56 mExtraHead.clear();
57 mHtml.clear();
58
59 if (!css.isEmpty()) {
60 write(QLatin1String("<!-- CSS Definitions \n") + css + QLatin1String("-->\n"));
61 }
62}
63
64void StringHtmlWriter::end()
65{
66 if (mState != Begun) {
67 qWarning() << "Called on non-begun or queued session!";
68 }
69
70 if (!mExtraHead.isEmpty()) {
71 insertExtraHead();
72 mExtraHead.clear();
73 }
74 resolveCidUrls();
75 mState = Ended;
76}
77
78void StringHtmlWriter::reset()
79{
80 if (mState != Ended) {
81 mHtml.clear();
82 mExtraHead.clear();
83 mState = Begun; // don't run into end()'s warning
84 end();
85 mState = Ended;
86 }
87}
88
89void StringHtmlWriter::write(const QString &str)
90{
91 if (mState != Begun) {
92 qWarning() << "Called in Ended or Queued state!";
93 }
94 mHtml.append(str);
95}
96
97void StringHtmlWriter::queue(const QString &str)
98{
99 write(str);
100}
101
102void StringHtmlWriter::flush()
103{
104 mState = Begun; // don't run into end()'s warning
105 end();
106}
107
108void StringHtmlWriter::embedPart(const QByteArray &contentId, const QString &url)
109{
110 write("<!-- embedPart(contentID=" + contentId + ", url=" + url + ") -->\n");
111 mEmbeddedPartMap.insert(contentId, url);
112}
113
114void StringHtmlWriter::resolveCidUrls()
115{
116 for (const auto &cid : mEmbeddedPartMap.keys()) {
117 mHtml.replace(QString("src=\"cid:%1\"").arg(QString(cid)), QString("src=\"%1\"").arg(mEmbeddedPartMap.value(cid).toString()));
118 }
119}
120
121void StringHtmlWriter::extraHead(const QString &extraHead)
122{
123 if (mState != Ended) {
124 qWarning() << "Called on non-started session!";
125 }
126 mExtraHead.append(extraHead);
127}
128
129
130void StringHtmlWriter::insertExtraHead()
131{
132 const QString headTag(QStringLiteral("<head>"));
133 const int index = mHtml.indexOf(headTag);
134 if (index != -1) {
135 mHtml.insert(index + headTag.length(), mExtraHead);
136 }
137}
138
139QMap<QByteArray, QUrl> StringHtmlWriter::embeddedParts() const
140{
141 return mEmbeddedPartMap;
142}
143
144QString StringHtmlWriter::html() const
145{
146 if (mState != Ended) {
147 qWarning() << "Called on non-ended session!";
148 }
149 return mHtml;
150}
diff --git a/framework/src/domain/mime/mimetreeparser/stringhtmlwriter.h b/framework/src/domain/mime/mimetreeparser/stringhtmlwriter.h
new file mode 100644
index 00000000..20f6763e
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/stringhtmlwriter.h
@@ -0,0 +1,71 @@
1/* -*- c++ -*-
2
3 Copyright (c) 2016 Sandro Knauß <sknauss@kde.org>
4
5 Kube is free software; you can redistribute it and/or modify it
6 under the terms of the GNU General Public License, version 2, as
7 published by the Free Software Foundation.
8
9 Kube is distributed in the hope that it will be useful, but
10 WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
18 In addition, as a special exception, the copyright holders give
19 permission to link the code of this program with any edition of
20 the Qt library by Trolltech AS, Norway (or with modified versions
21 of Qt that use the same license as Qt), and distribute linked
22 combinations including the two. You must obey the GNU General
23 Public License in all respects for all of the code used other than
24 Qt. If you modify this file, you may extend this exception to
25 your version of the file, but you are not obligated to do so. If
26 you do not wish to do so, delete this exception statement from
27 your version.
28*/
29
30#ifndef __KUBE_FRAMEWORK_MAIL_STRINGHTMLWRITER_H__
31#define __KUBE_FRAMEWORK_MAIL_STRINGHTMLWRITER_H__
32
33#include <otp/htmlwriter.h>
34
35#include <QFile>
36#include <QTextStream>
37
38class QString;
39
40class StringHtmlWriter : public MimeTreeParser::HtmlWriter
41{
42public:
43 explicit StringHtmlWriter();
44 virtual ~StringHtmlWriter();
45
46 void begin(const QString &cssDefs) Q_DECL_OVERRIDE;
47 void end() Q_DECL_OVERRIDE;
48 void reset() Q_DECL_OVERRIDE;
49 void write(const QString &str) Q_DECL_OVERRIDE;
50 void queue(const QString &str) Q_DECL_OVERRIDE;
51 void flush() Q_DECL_OVERRIDE;
52 void embedPart(const QByteArray &contentId, const QString &url) Q_DECL_OVERRIDE;
53 void extraHead(const QString &str) Q_DECL_OVERRIDE;
54
55 QString html() const;
56 QMap<QByteArray, QUrl> embeddedParts() const;
57private:
58 void insertExtraHead();
59 void resolveCidUrls();
60
61 QString mHtml;
62 QString mExtraHead;
63 enum State {
64 Begun,
65 Queued,
66 Ended
67 } mState;
68 QMap<QByteArray, QUrl> mEmbeddedPartMap;
69};
70
71#endif // __MESSAGEVIEWER_FILEHTMLWRITER_H__
diff --git a/framework/src/domain/mime/mimetreeparser/tests/CMakeLists.txt b/framework/src/domain/mime/mimetreeparser/tests/CMakeLists.txt
new file mode 100644
index 00000000..a7f28bb1
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/tests/CMakeLists.txt
@@ -0,0 +1,23 @@
1add_subdirectory(gnupg_home)
2add_definitions( -DMAIL_DATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/data" )
3include(${CMAKE_CURRENT_SOURCE_DIR}/kdepim_add_gpg_crypto_test.cmake)
4include_directories(
5 ${CMAKE_CURRENT_BINARY_DIR}
6 ${CMAKE_CURRENT_SOURCE_DIR}/..
7 )
8
9include(ECMAddTests)
10
11add_executable(mimetreeparsertest interfacetest.cpp)
12add_gpg_crypto_test(mimetreeparsertest mimetreeparsertest)
13qt5_use_modules(mimetreeparsertest Core Test)
14target_link_libraries(mimetreeparsertest mimetreeparser kube_otp)
15
16#find_package(Gpgmepp 1.7.1 CONFIG)
17#find_package(QGpgme 1.7.1 CONFIG)
18#
19#ecm_add_test(gpgerrortest.cpp
20# TEST_NAME "gpgerrortest"
21# NAME_PREFIX "mimetreeparser-"
22# LINK_LIBRARIES Qt5::Core Qt5::Test mimetreeparser Gpgmepp QGpgme
23#)
diff --git a/framework/src/domain/mime/mimetreeparser/tests/data/alternative.mbox b/framework/src/domain/mime/mimetreeparser/tests/data/alternative.mbox
new file mode 100644
index 00000000..6522c34b
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/tests/data/alternative.mbox
@@ -0,0 +1,28 @@
1Return-Path: <konqi@example.org>
2Date: Wed, 8 Jun 2016 20:34:44 -0700
3From: Konqi <konqi@example.org>
4To: konqi@kde.org
5Subject: A random subject with alternative contenttype
6MIME-Version: 1.0
7Content-Type: multipart/alternative;
8 boundary="----=_Part_12345678_12345678"
9
10
11------=_Part_12345678_12345678
12Content-Type: text/plain; charset=utf-8
13Content-Transfer-Encoding: quoted-printable
14
15If you can see this text it means that your email client couldn't display o=
16ur newsletter properly.
17Please visit this link to view the newsletter on our website: http://www.go=
18g.com/newsletter/
19
20
21------=_Part_12345678_12345678
22Content-Transfer-Encoding: 7Bit
23Content-Type: text/html; charset="windows-1252"
24
25<html><body><p><span>HTML</span> text</p></body></html>
26
27
28------=_Part_12345678_12345678--
diff --git a/framework/src/domain/mime/mimetreeparser/tests/data/cid-links.mbox b/framework/src/domain/mime/mimetreeparser/tests/data/cid-links.mbox
new file mode 100644
index 00000000..40ff5282
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/tests/data/cid-links.mbox
@@ -0,0 +1,1384 @@
1Message-ID: <851f01d15e53$31734730$790bc9ad@info>
2From: "OculusLab" <info@findermanze.co.ua>
3To: <info@example.org>
4Subject: CID links for images
5Date: Wed, 03 Feb 2016 07:19:17 +0200
6MIME-Version: 1.0
7Content-Type: multipart/related;
8 type="multipart/alternative";
9 boundary="----=_NextPart_000_000F_01D15E52.0BD654A0"
10X-MSMail-Priority: Normal
11X-Mailer: Microsoft Windows Live Mail 14.0.8117.416
12X-MimeOLE: Produced By Microsoft MimeOLE V14.0.8117.416
13
14 This is a multi-part message in MIME format.
15
16------=_NextPart_000_000F_01D15E52.0BD654A0
17Content-Type: multipart/alternative;
18 boundary="----=_NextPart_000_0010_01D15E52.0BD654A0"
19
20------=_NextPart_000_0010_01D15E52.0BD654A0
21Content-Type: text/plain;
22 charset="windows-1251"
23Content-Transfer-Encoding: quoted-printable
24
25=0D=0A=0D=0A=0D=0A=0D=0ASuperkombipackung f&#252;r nur 45 Euro=0D=
26=0A=0D=0A
27------=_NextPart_000_0010_01D15E52.0BD654A0
28Content-Type: text/html;
29 charset="windows-1251"
30Content-Transfer-Encoding: quoted-printable
31
32<HTML><HEAD>=0D=0A<META http-equiv=3D"Content-Type" content=3D"te=
33xt/html; charset=3Dwindows-1251">=0D=0A</HEAD>=0D=0A<BODY bgColor=
34=3D#ffffff>=0D=0A<DIV align=3Dcenter><FONT size=3D2 face=3DArial>=
35<A =0D=0Ahref=3D"http://intenices.co.ua/drugs-store/index.html"><=
36STRONG><FONT =0D=0Asize=3D4>Superkombipackung f&#252;r nur 45 Eur=
37o</FONT></STRONG></A><BR><BR><A =0D=0Ahref=3D"http://intenices.co=
38.ua/drugs-store/index.html"><IMG border=3D0 hspace=3D0 alt=3D""=20=
39src=3D"cid:9359201d15e53f31a68c307b3369b6@info" width=3D650 heigh=
40t=3D763></A></FONT></DIV></BODY></HTML>
41
42------=_NextPart_000_0010_01D15E52.0BD654A0--
43
44------=_NextPart_000_000F_01D15E52.0BD654A0
45Content-Type: image/jpeg;
46 name="aqnaozisxya.jpeg"
47Content-Transfer-Encoding: base64
48Content-ID: <9359201d15e53f31a68c307b3369b6@info>
49
50/9j/4QAYRXhpZgAASUkqAAgAAAAAAAAAAAAAAP/sABFEdWNreQABAAQAAAA8AAD/4QMqaHR0cDov
51L25zLmFkb2JlLmNvbS94YXAvMS4wLwA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENl
52aGlIenJlU3pOVGN6a2M5ZCI/PiA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5zOm1ldGEvIiB4
53OnhtcHRrPSJBZG9iZSBYTVAgQ29yZSA1LjUtYzAxNCA3OS4xNTE0ODEsIDIwMTMvMDMvMTMtMTI6
54MDk6MTUgICAgICAgICI+IDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5
55OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiIHht
56bG5zOnhtcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIgeG1sbnM6eG1wTU09Imh0dHA6
57Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUu
58Y29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBo
59b3Rvc2hvcCBDQyAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6QjdCRTg5MTBD
60OUNGMTFFNUJBOTdEMkQyNzU0ODI3RDciIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6QjdCRTg5
61MTFDOUNGMTFFNUJBOTdEMkQyNzU0ODI3RDciPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5z
62dGFuY2VJRD0ieG1wLmlpZDpCN0JFODkwRUM5Q0YxMUU1QkE5N0QyRDI3NTQ4MjdENyIgc3RSZWY6
63ZG9jdW1lbnRJRD0ieG1wLmRpZDpCN0JFODkwRkM5Q0YxMUU1QkE5N0QyRDI3NTQ4MjdENyIvPiA8
64L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0i
65ciI/Pv/uAA5BZG9iZQBkwAAAAAH/2wCEAAYEBAQFBAYFBQYJBgUGCQsIBgYICwwKCgsKCgwQDAwM
66DAwMEAwODxAPDgwTExQUExMcGxsbHB8fHx8fHx8fHx8BBwcHDQwNGBAQGBoVERUaHx8fHx8fHx8f
67Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fH//AABEIAvsCigMBEQACEQED
68EQH/xADJAAEAAgMBAQEAAAAAAAAAAAAAAwQBAgUGBwgBAQEBAQEBAQAAAAAAAAAAAAABAgMEBQYQ
69AAEEAgEDAgQCBAgKCQMACwIAAQMEEQUSIRMGMUFRIjIUYXGBQhUHkaGxUiMzFhfB0WJy0+OkZZVW
708ILSsyQ0lFU24UN1U4MlssJzhLQ1djcRAQABAgMEBQkFBwQBBAIDAAABEQIhMQNBUWEScYGRoQTw
71scHRIjJSYhPhQpKyFfFygqIzUwXCI2Nzk9JDsxSDNPLD0//aAAwDAQACEQMRAD8A/VKAgICAgICA
72gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA
73gICAgICAgICAgICAgICAgICAg1y6Bl0DLoGXQMugZdBrIZMBOz9WZBpWkM68Zk+SIWd3QS8nQOTo
74HJ0Dk6BydA5OgcnQOToGXQMugZdAy6Bl0DLoGXQMugOT4dAYnwyA7ughaWTPqgkIyaNnz16INQkN
753bL+6BJIbHhn6INozJ2fL+6DbLoI5JDYmZn6KDJGTR8s9VQiM39Xyg1GWRy6v0ygkMiYXdn6sghG
76aRxd8/yItGAnldupfxMoUZ70nx/kQO9J8f5EKAzSO3r/ABMhRnuyfH+RCg0snXqgNNI/v/Igd2T4
77/wAiDLSyfFCjDSyfH+RUZ7snx/kUBpTz6oMd2TPr/Igz3JPj/Igy0knx/kQY7smX6oMd2T4/yIMj
78Kb+6B3T5O2eiVKHdP4oUO5J8UqtGHlk5Yz/IpUoz3ZPiqUO7J8UKDynluqIy8h/FA7h/FKlApDZv
79VAaU/ilSjPcP4oDyHjOUGO4eG6oUZ5l8UGeZY9VUatIePVRR5Dz6pUoyxnn1QBkN3fqgy5l8UQYy
80+KDcXd2fPxVGUBBqgICAgICDSb+qL8nQR0//ACkP+YyEpkBAQEBAQEBEEBAQEBAQEBAL0dAb0ZFH
819HQQD6oJJP6pv0INY/Vvz/wIMS/1n8CCSL6X/N0G6CKT62UGS/qv0/4VQhQRh9Tfmglm/qyQQD9D
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
2906/oo2w6Iy3oisug1QYRGW9UVsiCKwgwgICDCAgwgyyCnfiCRmYxYhJsOzrF00lu3Fw7Ol6u8J4b+
291aX+NI1Fmxz5YZIvkkbBN8PgukTViYom1sxMfa9Xd8MuV8OlsvSRBxFh+H8qwqYWVgSMqyytDUzYR
292cnfDN1d/wZQeQ2Ft7Vo5f1fQG/yW9F2tjBiZVSdlWXpfH6ghSad2/pJny7v8GfDKDqY9kG3p0Qbe
29335oCDP4INmUBAd8MgrkOCz8VpGweuP4FBszoMOzKg3wRWRfOVEbCitmUGyAoMICAgINx9EVlAQao
294CAgwgICDDogisIMOyDCDCBhBhAQYwgYQZwgccoMPGLoNeyPwZBloRb2QSMDMg2ZsIMogg1k+h0FM
295v6+D/Of+R1Wdq6/oo2wiMt6IrLoNVRhRGW9UVsiCKwgwgwgygwgwgINZJBBsv1z6MpMrEOfanPPM
296vpb1b4MuV0u1sNc8myyzRXK2kfUT/Q66acsakMaOtztFM/pE2G/zn/xLWpLNr0IsuTaRmWkbsqg6
297DjeQX+3C1YH+eX6/wD/6rdsJMudrNTLaxJJmOv8AH3L/ADf8a6ObuHq6nZEBiBhB+RM45d2/P1QW
298w6C2McW6fL6N+hBI2PX+BQFUG9VFbfigMg2woDvhkGj/ABVRgmz/AIFRoPq6Df2yorHtj4KoIp+s
299ojLP1RWzOg3ZQZ9lBrl1FMqh1QMug3D0/Sg2QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBA
300QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBA
301QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBA
302QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBA
303QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBA
304QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBA
305QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBA
306QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBA
307QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBA
308QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBA
309QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBA
310QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBA
311QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBA
312QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBA
313QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBA
314QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBA
315QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBA
316QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQRWbVWpAdi1MEFeNsyTS
317kwALZx1InZmQYK5TG2FMp42tyAUsddzFpCAHZiMQzycWcmZ3SPMSji2urmnGCK5BJOTyCMQSARu8
318BMMrMLPn+jJ2Yvg/qkYk4eXX5sVpSZpFRW12zo7LXwbGlL3adgGkhlwQZB/fBsJN+llq6KZm2nGn
319Zglq2q1uvHZqzBYryixRTRExgQv6OJDlnZJigRWq0ss0MUwSS13YZ4xJnKMiFiZjZuou4uztn2U2
320VEiAg5Nvy3xinParWNrVG3SiOe1TaUDsBHGHcMngF3l6B830+ibK7PKPOR70W7ZdSKWOWIJY35Ry
321CxA/plibLeqt1sxNJS26JisbWyiiAgIIq1uraAjrTRzgBlEZRkxsxxvxMHcXfBCTYdvZNlTglQEB
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
354flm71A2J56MNalbhGzNLYMJLBThIwyTEZ8X7AvxzhvbCxGNld10x1cts+mVuwujjbXvl5zYy+XbX
355beQFSmgqz6myMVOWfaWagV42ijkCSWnFBJDOEju7uUpPnqLccJo0pbM7bprtyupThh141qupjM2x
356uw64z40nqwpTOZayW/tN/ToWthcarLb34zBDZmichgtRDEHOMhNhjYvl4u2PRumVNOIm3/8AHE/z
357yt8zFf3rf/irPf61WntPJNiep0zSPYidtoLFNsbOuksFRvPXjZ7NaKaYzCEcuOW5Z5PnCsRXH5NO
358fxRjNOzhFcsWbvZrEZc90dlKRXt4+znnX1taHyQfBZ68uyrBvGinhh2DTPLEB8yCHnMQRuRAPETJ
359wzyy+FnUpMxT5a7K5Vpu5tnS1ZhM148adPR5nl696y26qeOWf2lqfuLEY7VpNjLbEmOCeSAa9xze
360aPvHE/JmcH+VmZm5delsRdjsjm4YxyduF1fOxdM2xxmmPCebHhjHLltdCzpq9jzDY1gvXGjraSDt
361yw25QlYxs2WHnNGQym4Y9DJ8/rZXGb5jTvujOJj8s7MnaLY5rLdk83+jr8typqJ9h5FJWO9sLkbS
362+M0LxBVsS1h+6lKZyl/oSDr09PR/dn6Lr4n2PqzH3bsOyXPQ9qNOJ281eNJsVKG08r8lepDzHuNp
363aF2P/wDaVnVk8tmMnlsM1aCbvMJszOJvxH+b8y1q2ct19MKXzEbaRSJjCevppwY07sLYnHDtxmM+
364im7PbhSv39nHtBnnKK7sw3FA5TrvmKadtETu8b4H5TP06LF11ImbY/vU7IdIt2XT93Tr/wCWXZ8K
365/tRbk0e6ltQfa34yLYEW0s2nsucLlxipyV4oIJI5Wy4xE3FmJnyul1ttszbspht2xjXdTqmuTnE3
366XRXKa48N8U8pwzl3LGykr+W7sLFooacOor2IxORxjB2lsNJIzO/Fn6DyL8l5b5/2r99f9OHe9Fse
3673Zxr57XmfHmvbmKlHc2d9hbxfX237NueEnsyPLmYijISI/lbOX+b9bK7+K9n6sx927DhhLloY8kT
368tm6vbazrdjY8i1ks27vz1wq6ClejCCxJTA5LUEhzWJHhKPm3IGHiWQb4dVnxdsWxqU2XXW9VIphx
369mZ7MDw0zN1kT013zzTHdERP8XQ7QbG7rf3RVr9J+Nqvp4Dikxy4P2BzJxf14N836F18VFdaYnCJv
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
482gkD6gLD9CH3Z0nKuw20ZrWq1qvHZqyhPXlFiimiJjAhf0cSHLOysxQSKAgIK02ypQ7Ctr5JONy2E
483sleLiT8hh49x+TNxbj3B9XSMa8CcIrxp559CygICAgICAgICAgICAgICAgICAgICAgICAgICAgIC
484AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgION5nsL2u8V2l6g/G3Xrmc
485R45cMN1k4v68GyWPwUwmYicIm62J6JmInuWK40is0mkb5phHXLx/kwjrquz19C/Zu07vjuxtWgs2
486JLeCjABhmE5SNwaRpDbiLsL46N0V1MromKcs29VZnDu27uLWh71k1xm7tjfwph2tNtJd00FiGnet
4878bfjF66by2JZHCzVGFo5YeRYhfEz9I+LenToumvnqcLre+bq+aHLwsf0p34T2W+XnN1sdx48xy62
4883ZszWtBZvSNamksM1iucAtMAydwY8DOTkICwvj6VdSI5r4yiL7I6IuuuiceiOpnSmZtsuzmYu66W
489xMd+HGubt+JU9/X2wzTzwfsu1UeRoW2tnaSSysYOE8b2YYe2HEiYmB+PUejJdSImJzrGzLOvHdnu
490IrNJ8p9H7XK8mtWNLutvqKhPHL5dHC+rJv1bhENS0Tf5kJRy/od1zssi+OScou/kmt135buu6HW6
4917kmNThT+KPd7a06LUVvlU1HkloL9ija8ZJq2mpxTyRwxxw14yrCdcSaOfvkX/wBwSznA4wt88zy3
492Zzffj+OlOHs0nfjXczZZETyThbbbGP8ADWbuqa8PZ6Vye92rXl+z2dq+0GreLs1q1iSNou7Qi59s
493GIQzykd255ES+bo/VZiPZimMzfNvfbTy3LF2MTOyyLp6pvr5striW9hvtZftUCmlpwsOqtFD+07G
494xkjaTYhHIRzTsJxscb4IGJwx+lb06TdSdl8Rlvtuw44xGbN2Vd9l89lMeGc5eh293a3F7yPyHXan
495YM0kFfU4qlZKAXI5rBTwhIHJ4ZZohZuQjy9PwdYs92JnH257OS3zTjTpavwmNnsf6vKHm9luNpBs
496aBaitdju0G2cF+G5O9+esDDSOc60hnL9w4RnzASP6untxWraYzM+zNueX36eeM8cMcUpNKRHtRfF
497I2TPJdMfsw6s3U8kvxyUrRaK1bsNqNVFabZzbexVhYZBkOGZmBpfupC45LujwfDD8Vi+Zt5rqUpd
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
546jtmnTkupNKzti7V/PZ5orPVlOT0c1TyansNfQt3nqUbmzrAFSrtLV6wIFWtPMx2LEcMzRyvGDiOX
547w7O4u3TGbaTMRPz8PuxTLbE17WcYtmY3W8fvxj2TR6jwyWcZd9rjnlng1mxevUKxIc0rRHWhn4lL
548I5SHgpiw5O74UnGy2ds17rro80E4XzGz2e+HDmK74tWksAVHd+PXdm0jg+RuDNdtN9JN3Y7BRyH0
549bAPhvXopp48lk9ET319c9a6n3ro646IpMeiI6nKi2fkew3LacJHnry29wYDNsbOuKQq9xgCILFeO
550aXEUb5aMXFsfgKacVtid1sd911Z45R0V4rqzS6YjbMfksmnCtZnjTPOvo70e1j/dbt4trYitXY9f
551fA5oJXnFxEZWAXlIInMxBmEycWyTOsa8xSKfL24VnrdPDRMakfvelxNrJb09OSLW7G3L9z43et2+
5525YkleKWCKP7exFksQuTmbM0fEXx6dF115xvjdMds3Th1x5nLwkRMac75iOqm7hh24rA2NnV29a/t
553Dt2qFqzTgo3aN82CApRjjaCzRcgjPlLnmeDLBe2Om6RzzbGdb+iac09VIjthyif9uLvlt6caY8az
554P2Oz51XG4VPW1JrUe7vc46hVrlusEMTYea1KFeWITaJnbjy9ScR91wsit3Db0euco7crXeZpbXs4
555z6ozn1y4W22NrVeMeeQvsrAya0QioWJ7BvMGaELRuMhFy5HJl8t6ln3XS2eabJpnqf64w7O4i2k0
556/wCP/wBWPaj30myebym9Hs7sE+tua4KAxWDGKJpYq3c/ocvGfPuPkZBIffGcqaf3eOpNvVWIYr7P
557Rpc3X7fqem8bGap5RvdW1mxPTgip2IRszSWCA52laTicrkTCXbZ+OcN7MykY2dF0x1ctk+mS7C+O
558NtevmuhFvqFEPPvGLwV4huyvciltMAtKQDWJxAjxycWd+jZTSmk3Rvsn82masVi2fnj8uo4G9Y/2
559R+8LvY/aP3Mf2P8A+k/8rB9lw9/6/PDH6+VdLLT/AOzHp5//AEU/hav966uX0+7lur/NV3vPbkEm
560maoE8RlXu6wtvBzFyjqyW4+byiz5ECEXzno7ZTT/AKls7Oae3lnl668vczj9Oa+9yf8A8u6qOO3r
561qXlXlFq4QDqoa+uOdybkH3Y91+g4fMvDs4Zmz9OPZS2aWceeafhty6697V0Vvj9zH8V32rviGvvN
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
690QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBA
691QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBA
692QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBA
693QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBA
694QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBA
695QEBAQEBAQEBAQEBAQEBAQEBBpPPBXgknnkGGCIXOWWR2EAAWyRET9GZm9XdJlYiuEI5r9GGsNqax
696FHWNwYJzMRB3kdhDBO+H5OTMPxVpNabWYmJiuxOoqhqt7rdqU7UDklCuXApnhmCEny4v2pTAY5cO
697Ls7xkWFaYRO8nCaL6gICAghu3qVGsdq7Yiq1o+sk8xjHGLfiROzMlViHP/tf4k1H9oftuh9hzaL7
698v7qHs9x2d2DucuPLDP0yrNswzE16l6hsdfsao29faiuVTzwsQGMsb46Pgwd2dJiYzImJyVZvJvG4
699KA7GbbU4tecjwhcOxEMLyM7s4NI5ceTcX6Z9lN3Fd/DPgqD574MYmQ+RawhjblI7Xa7sI5Ycv8/R
700skzK8spzQsz+V+LV6le7PuKMNO3n7WzJZhGKXD4ftm5cSx+DpNs1ptImJiux0YZoZogmhMZIpGYo
7015AdiEhfqzs7dHZ0mKZrE1Qa3aUdnXezRk71djONpeJCJFGTiTg5M3Ict0Iej+zpTCJ34m2Y3LSgI
702Kmz2+p1VdrO0uwUK5EwDNZlCEHN2d2FiNxbOGfonAba/Z67ZVmta61DcrE7sM9eQZY3dvXBA7srM
703TGaRMSsqKjmtVoCiGeYIinPtQMZMLnI7OXAM/UWBd8N8EjcJEFbY7Klrar27snarsccbnxIvmmkG
704IGwLO/UzZkjGYjbJsmd0V7DY7Klrar27snarsccbnxIvmmkGIGwLO/UzZkjGYjbJsmd0V7FlAQVr
705OypVrVSpPJwsXjOOqHEn5lGDyE2WZ2bAC79UiK4cK+aPTBM0ivGnl2LKAgIKG13+h1DRvttlV17T
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
930bdNduV1KcMOvGtW7qzPL8sU64z40nqwpTOZ7fn+ugvWfF45zmFi2og7155q74KrO74KE4yzkWw+c
931t1+LrGn/AFP4bvMsz/tz/D+a3y73Ifb26/i8IFflG0/kw0gc5zeYom2rD2eRFzJux+r/ADfwW9P2
932rtPjbNfwXenvNSKRqU2Up/K73hEctgtnfs2rNicNlsK0QSzyFEEIWiYQGLl2/l49Hdss3Rnx0WY/
933p28bfTK3+/MbqfkteY8xcdjo/MrexuzxS62Z6lOoFmWCEI+1EUfOEDGOV5ikd8yMXwb0V0c9Odt1
9348d2pTzRXvTU+/GyLJ77K17a27sOlnd7jff2mu6qvM5VLW0grO0tyakAj+zAmGALEQTSQ92Tr8gs5
935P0z8ymlFYx+fum3tpEzNC+aREx8NvfN+PdEV49Ex67xWtt6usvV9xZido55Ow0duS4deB4xLty2Z
93644ZCISciZzbPF26upq0mzqms5bZ80YdS2RMXZbsM/KrztefbeJ09LqXjo7vXyOVfTWYWeO0xhBJJ
937GZRv3QkywYOQDH1zhNS+Zrsvi2Z4YRu2d+4stjOvszdFf4rt+2mfVVV2UMc/7uquyLZ2bN7avrZb
938JnORi8pXIHN4oTco4e2RY4xizfzsrrdbFutbbHuxfH7a8ezcxZdM2XXThdy3YbvZu9nq7cE+2nva
939i9vYat629LUDqttiaxNMTRlPK1wOchGbxlDBng78W9mWLJikTOXPNvVNtsd03VW+MZiNtkz12zM4
940dNKUhTDfeQFZjrNZnll31gNzqwYyFxpxNLK9YcOzsDhXhY2+Mr/FZmJttnD2tO2Znrtw6aXzMdFs
941N4TNa0tvmLY6roiZj96z2u1UCfy8/FP7QjeihC1rLctucdrasSTSPTkMexUOCKGtLFMLPiIm4sxN
9421W9aItrEZbO2Ma50p1TXJNCt91szGPNFfTbTynDOXoqtFtt5JTenbvgGuCG1uZxv3OycxRs8VRoO
94372OrYkl+T0w36zrU+zddP3a3RHTt6rfP+7MONuNlvxTETPR67vNWdsS7Wxt2IvONNB3jjqTUb7nF
944ydozkjOu4u454uQi5Y+DZXKyntV3R55r6He73Y/e/wBMvJaK1a3VrRQS7O3JStyb8pCr2pY+7HDe
945EYP6WMhPiAP8jiTYbo3Rbtt3/wBqyevBm6aTd/2U6uW5L4/cu7ezrdPs79kKUUe0dpY7EteawdG+
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
1024/wDHE/ztasVik/Fblx0q+XXvl7HzStuLEFGPWl3Gadzt0I7Z0J7MLRk3GKxH84uJuJuzO2cYd2XO
1025PexypPox8t7Wzy7HkPHBpXvJrG0qftEp4NQMletau2HkeavctRFFJwlcJgEwYW5chf16u7u+r7pt
1026077o+WYw32YYeXBIti66yJw9q6Jx3TZt8t05UUZP7UW/DZN1Lag+1v6q4WwItpZtPZc6UhcYqcle
1027KCCSOVsuMRNxZiZ8q69tttbdmFNv3oxrup1TXJfDzddfbOU80V4b4p5ThnK95DVlq0LeujtW5q2x
10288YvW7EUtiY/6es0LAUbcv6JnaZ2II+Iu3qyviP8A3Plut75ur5oZ8J/7U/Fh3W+Vc3vfGqNanoqs
1029daSWSKSIJWOeeayWSBvQ5jkLj8GZ8fBTxk43Rur6XPw8exE74jzPn3jJWtV454fc1tmzavXo5Ip9
1030ec5nDJCFaWX5YHftx9qQAbkIs/sTvlNa6YrT+3XriLad+HW78sTdMzh/uTHbfNe6t3UgCfy8/FP7
1031QjeihC1rLctucdrasSTSPTkMexUOCKGtLFMLPiIm4sxN1V1oi2sRls7YxrnSnVNcmdCt91szGPNF
1032fTbTynDOXtPKLtuDxClainkik7+t704mQlwO1C0nImfPEhd2LPt6rV0R9aI2c0+lz0Zro128nocH
1033yzcXv2t5JXp7GaJq0eiAWgldnhknvyDLxZndhI43Hl06tjPRY0ory126tOrlt9NW9TD/AMV09eKW
10349BuodlvdFqL8riMettQR3LkzSE88szWIIrRvLLF3Qr4Hj9L/AE4UtxtiZ2XzHVyRPXSZr0LdhPTb
1035381PsdzxHZV21GxcorsB62eSO5Wu2CuyRmEQSEMU7nKUgcSZ2yWcu7dPRTWuiLObZSenCZj0Jp21
1036v5duHe8La2+3qVWsVJrNWrtdJsbcLz7Se7aPt12khneIm7dYxz/9k8dcezK6kTHNbOdsR1e1EZ57
1037+lvSui6+y6Mrr+6kzl1Rxjreu0rWaPlWtqjcs2IdnqJbdobM8k7PPBJXEZAaRyGPLTlkQYR/Bdb4
1038iupHwzFOvnr5oeeyZ5bLtt1a9keXnUvIZPItn5be1NVxGKnSgnqC+zs6wuUryMc7NWgm77CQsLib
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
1069RkMpTaMWFikkfkZvjGSJ3y7+6nBVKx4147Z2AbGxqqc2wB2ILkleIphcfR2kcXJsfmrbMxkTjmnP
1070Uao9gGyOlAWxjB4wuvEDziD5yLSO3Nh6+mVIwrxzJxpwQVvGvHasc0VbVU4IrBjLYCOvEAySRlyA
1071zYRZiISbLO/o6tZw4ZExjM721rQaK3VKpb1tWxVOQpzrywxnGUpu5FI4ELi5O7u7l6qbuBXPini1
1072uuiminiqwxzwRfbwyjGLGEOWftCTNlgyLfK3TorWceKUwpuWFFEBAQEBAQEBAQEBAQEBAQEBAQEB
1073AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB
1074AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB
1075AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB
1076AQEBAQEBAQEBAQEBAQEBAQEBBX2Owqa6hYv3JGiqVYymnlfL8QBuRPhuvoykysRWXJi8qleGeezp
1077b9KvFAdkJrL1AjMI25O3NrBDE7t1/puH44w61dFM/L0Jb7UxTb5dPcvyb/SQ2KtWxfrV7lwRKrUl
1078miGWTl6cA5ZP/q5V5ZrMRsZi7CJ2Ss3b1KjWO1dsR1asTZlnmMY4xb0yRE7MyzVuIVD8l8cCpFcP
1079a0xqTAUkNh7ETRmEbsJkJ8uLiLkzO7emVeWa02+vJImsVhuO+0ZSyQjsapSwjJJLG00bkAQlwlIm
1080zlmAvlJ39H9VNldh6f2oYvJtNZjqy0LUF+vbsfahPWnrnG0nAjxl5B5PgfpDkX4Yy7XlmtOFexKx
1081SZ3fsVdz5jqaNK1NVnr37FKevBbqRThziexOEGZGHm4ceecO3XCWxWbd100qs4V3xbM9kVdGpu9L
1082cqzW6d+tZq13IZ7EM0ZxxuDZJjMXcR4t65UnCKzkRjNNqKLybxuamN2La05KZG8Q2QsRFE8gi5uD
1083GxceTCLu7Z9FZiYzIxyW6GwobCqFuhZit1ZM9uxAYyxlh8PgwdxfDpMTGaRMS1/ams7Pf+7h7Pd+
108437vcDj3ufa7XLOOfc+Tj68unqkRlxWcK8EIeQaE7wUA2VUr0jmIVGnjeUiid2kZo+XJ3Bxdi6dEi
1085K5E4Zsx77Ry7I9XFsap7OPLyURmjecWZsvmJn5t/AkRWKxkTNJpKz93U78lfvR9+IGllh5NzGMnd
1086hMhzlhdxfD/g6lcK7Fp3qNjyjxqtFHNY21KGKUI5YpJLEQiUcue2Yu5MzifF+L+/sryzWm1K4V2K
10875+ZeOx+Sf2dluxRbJ4opowkliHuPMRCMYM58yk+Xlx4+js/ults3Vps/b3bUumLaV+99nnrh0Sth
10885F4+c9qANnUKeiJSXomnjc4AD6ilHlkGH3clNldi0xptbRb3STEIxbCtIRzPWARmjJ3nYO48TYfq
1089fD5uPrjqryz5cM+xKx5u/Lt2LyiiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC
1090AgICAgICAgICAgICAgICAgICAgICAgICAgp7mOrLqrcVuqV2rJEQT1AHmUgE2CFhy2css3RE5tWz
1091MTg+eXK20OltaOgfb2dGeovR2KmzgsM4WHiYa0dV7cYWpCL5mdmchxj3wt31m2ebhTfWuOWym80a
1092RfZTD2sd1P20yRj47dkl2ut21jcwR7Y4jgjoVa8sM0X28YAJWDqzFXkiKNx/pJQZuhMu3NHNhnbf
1093M/zTdE8cKccKbnCyJi2K5TZEfy0mKdNZ3Y76vTeeUNjMekt15LYVKFspbhUYorFkGKE4wlCGWKw0
1094nAi6sMblh8t6LjZNL6z8Mx11j0RMfZV0mPYpG+OyK+mk9W+jl6XQDF5XqdhCOwuV5v2lamubGuEJ
1095DNMFUGfthDX7PPtlhijEnfk/XOVu2aVjCPY/119KXYxX5o7rbo9XcgtaHbSeKbMK9eeGZ/IJr08c
1096McbWJqwXnk5RjOJBI/BmIGIXYsYWbJpGnXZGPD3vTMTv2w3fFZviNsW0/DZWOvGN2/ahtaGzat09
1097jrrG2uWrV+EZ7mwqjV7XZqWwCTshXqGzCUosUhhh/lZn6KXRNKRSK239s2xHoS2YznGnL2RfbPrT
1098WKr2f3f1NGGnsjsNf+z4bcB1ZOLOFqDv8JHHhMJcCMijcmx1LC63XROrbfHu80dUbursYtibbLrZ
1099xu5buueWcevtxZ8u0e5s7ndHRhsDWcNLPI9eMHKYa1icpxhaYThkkAGAuLs+cM2OrLnpzSImf7l0
11009tkRE/i82GMNXxXCPgp/NWnXFY68cGv7CazttZtIS2uzebaVSuTbKoFZhGtWtMJ9ka1Qm4vILPIY
1101Yf5WYui3ZPLdGUR7c9c2xHoScbZ30tj+eJ9b0/ilWzX2Xk3dhOGGbaPLWchcRMCqV+RhlvmZ5GLq
11023vlYj+nbH7357lu9+Z4W+Z5axFsIta2kbXXZLgeSRXDkCvK8DVj2jWWmabHbIe2/zMLuQ/rMzM7r
1103WjnZPw2zE9PJdHl6zWyv+aIp/KmfT3h0U3CjKNovKht9IiaR4f2oLvN0bPDs5fl6cfwU0p/p12W3
1104flv+w1cfqcYtp1RZ6YWNBFfp+Rw0deNqxqHs2rFuDY0DhemUvcNzr3XGMJecp8eLOZcS+rDJpzW3
1105HZbERv2YTHR5jU97DObsd3T5b9i9tTsa7yzYWypWrMOx1kNeo9WCSZnngknIozIGcYstMOCkcR/F
1106cborp327Zx/lp5bdzrbPtWTsivnhyPDtJcjep95QkBx8UoUyeaImxKzy92H5m6G2W5B6+mV38XNY
11071qbbsOOEuXh8J067Ju89rXxqttqRatrNe3BPe8ao0IrH280nZuQ9xyGdwEuy491nzJhvXqr4mOad
1108WLc7prHZdtTRpbGnMxhbN1eubaeZyg8ctTeKfZznu5dvqdZbiDWyVII6wTSU5ICGOeKrF9wJkXys
1109ExkT8XJvVTWuia3W7cONKxNKcKbqbmtCJi62Lpyuiftr1znNccX0FhpVKugrzaqSwbFHHWKOuJjT
1110kaAv6U3fHZHDOHJvd8e6upNdWafNj6OvvcdOKaVtY+HDjv6ncXJ2EBAQEBAQEBAQEBAQEBAQEBAQ
1111EBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQ
1112EBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQ
1113EBAQEBAQEBAQEBAQEHLk8m00E1kLdqGpHWk7Tzzz1xApBi70gt/SOQlGHzExiL46+nVemPCakxE2
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
11743TERWkThM283L0x6JZ+pGOOEV/l97snDv2uuO01hRRTDcgeKeV4IJGkBxOVncXjB84I+QE3FuvR1
11755fo31mKTWIrls39DXNHZ66efDpVv7TeN/tMtV+1af7UF2EqH3EX3DOX0s8XLnl/boun/ANTW5Ofk
1176u5PipNO3Im6IwlvV8g0NvYz6yrsqtjZVmzZpRTxnPG2cfPGJOY9fiyl/htW2yL7rbosnKaTSeiSb
1177oiabTa7/AEWoaJ9tsquvacuED25o4GMv5odwh5P19GU0fDaurX6dt11M6RM+YmYiKzk52p898T2u
117862Wmo7KvLf1T4sxDNC7vgWKRwFjc3GLkwmXHDF09WdejW/xuvpadupdbMW35YT1bKY7N8YpzRzcu
117939uHTgvVPJfHLlGe/U2tOzRqu42bcViI4onH6mkkEnEXb3y643+E1bLotusui67KJiaz0Qc8Y45Z
11808GtryvxepUe5a3FGvUaV672JbMIRtML4KLmRMPNvcfVWzwetddy22XTdStItmtN/RxXmjHgvT3ad
1181eodyxPHDUAe4diQxGMQxnk5u7CzfiuNundddyxEzdu2nNFK7EFHd6XYPZahfrW3pn27jQTRydk2b
1182PGTg78Hx7Et6nh9SynNbdbzZViYr0byJrNNrjbb95Xg+t057c9zTsUgsBU517MBs88hMLR8ubAxC
1183z8iyXQWd36MvXo/4nxOpqRpxZdF1K42zlvy6uM4JN0Umfh8qdM7HoadypdqxW6c8dmpODSQWISGS
1184MwJsiQGLuJM7ejsvDqad1l023RMXRnE5rExOSVYUQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBA
1185QEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQfH6+m8obU6vZx6yb7+LdWS1tKYH
1186/ovvJ7MklywOP6MXOQer9Wjbp1NxX6m7X0ee6yb45Z0reaY28ttkRbbvnCf4uFtXGYml9fapfWOP
1187t4dXLSInZ7U5UWLvht4bfkuvgqWD1oamAZrZA7lfsRjZn7YiLZkeSxZeSbDYd8D15Ozc9Px1vLpX
1188zMc/1JpHwR7FteFLbaW9uyG9O2fqxWaxMRX8V3N2+zHC3D4Up6DyOSz4xsGpShubk5STTOLOOurx
11890Za9cZXfLM8QWDPh15Sk7fT1aR4jSiNWzmj6dsfjmb7brqdPLEV2WRE5uFsXcls7ptinCLbs8vvU
1190uu/DnEJ/HdbY081izZ1V2XXanaTwaanFC8k8012ftFedicfkCKT+tJ/R5SfPR35+J1Y1YiIvti+/
1191TibpmaREWxXk6ZmPdjdZDrdbSbt0Uu/enljt9c7OVJtru4jt+VlHrLtjyC0ceu0krVJTrxU5o444
11925Rn49ngM8hyzMxcunVsCymjp6c26VbrY0ordf7UVm6JmZimdeWIttwpunFZvm2+bqV5Y9njhXqmb
1193sJ4RGaSppb2po+SbTV6wyt6jXjp/F6xA7SFBRruTPExMz/01g3bp9TAP4LOpr26t2nZfd7Opfz6k
11948brtvRbHVWTR04tuiJxiyIjpnOZ68LZrtiXLvQ7UtRTq+Pa++NbxfTST66xYqywyzX5onqxFHFMw
1195SFLFH3yISFnciH4r02XWfUuu1brK62rEXRF0TEWRPNNZjCkzyxExOUTuYtm7kikVuxumuFbojCJ/
1196eumu7DDhbafU2NjSipa/YBo/GKJTNwpWBuPav5rBIEJg1h5I4e8cmY+T8mLr78uW+2y6brrPqa11
1197Pet5eW32piZieWkzyxGNMKYLb92Ixp7c14ViK76zMz0wueF6HyAtTJrm2dqpqqIQQ6Da/YwU9l2W
1198H+mjlhtwSiwZEG5FABE7P09Hfl4/xOl9Tn5Lbr7qzfbzzdZXZMTbdHH70xHcacTGEe7TbnWs16cN
1199vGVeWvty8s7+pkv3LBtX12+rbfXca01OsUnOeK00deHlIxk7DE5C5O2QFs43F1n0KXxZbGN1k2X+
12001F00pE21unCmd1JpHvTtt0TExSfaikcKVxyymk9dIzVKB2S8V0Ut/W7D7Wzs5bnllT7G0c7TSDJY
1201GJ4Gi7s0AWCjDlGBC4i36uV11LbY174tusrbpxbpzz20pFLa1rS26beaaTMTWd7M1mLsM7sf3dlN
1202+EW2zStba5rl/QX9zvClsa+SGhv7lQLcJg7M2t1InOD2GZnESs2DYOBde30fqzi3LS8TbpadIuib
1203tK26Y/f1KW+z+7bjWPvZbJm3xWsxti2zqrN108ImK2+qrWfVbS75lugtXt1rJZXCrrI6FKrNTPXj
1204CD9LVmnZiiIpSk5g8oO+G+V8MrbrWWeHspbpXxndzXXRdz1n7tt9szhSk8s9OazMxfXKlOWnf0TX
1205DZhEL1imO00ui8aqay7Bpo7rVbn30Tg70tS7uJH65CzLDGI8scwd3xhcbdT6epqa111s6nLzRyz9
12067U9NsTNae7MJFnJp/Ttrst6ts9FIm2u+cN7h+R6va2rdzYyQX6WrvbWOpckoVO/bHXayCRq7/anD
1207ZeSOS65F0hLIOPTj1Xr8JrWW222RNl19unN0c11Lee+Yr7XNbSYs+aPartNW2azSPht/hxumY655
1208ZjGsRK4OkOr5ho7lqTb7OveN7E+wtVQd2mqRvDRhlio1oBhD/wAZLJzmAcODcnbouf8A9jm0NS22
1209NOybYpFsXbLprfMTfdPNPsWxS2ZwnDazdbldjNZiJw2W1ujClfemOzc+mL889AgICAgICAgICAgI
1210CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgrbLV6
1211zaUpKGzqQ3qM3HvVbMYTRHxJjHkBsQvghZ2y3qumlrX6d0XWTNt0bYmk9sJMVzcL+7D92v8Aylpv
1212+H1f9Gvb+seM/vav47vWz9O3dB/dh+7X/lLTf8Pq/wCjT9Y8Z/e1fx3es+nbug/uw/dr/wApab/h
12139X/Rp+seM/vav47vWfTt3Qf3Yfu1/wCUtN/w+r/o0/WPGf3tX8d3rPp27oP7sP3a/wDKWm/4fV/0
1214afrHjP72r+O71n07d0H92H7tf+UtN/w+r/o0/WPGf3tX8d3rPp27oR2P3UfuxsRPFJ4pqRF/V46U
1215ERdP8qMBL+Nat/zXjLZrGtqfiun0n07d0Nov3WfuzijGMfE9O4i2Gc6NYy/SRA5P+l1J/wAz4yZr
12169bU/Hd6yNO3c2/uw/dr/AMpab/h9X/RqfrHjP72r+O71n07d0H92H7tf+UtN/wAPq/6NP1jxn97V
1217/Hd6z6du6D+7D92v/KWm/wCH1f8ARp+seM/vav47vWfTt3Qf3Yfu1/5S03/D6v8Ao0/WPGf3tX8d
12183rPp27oP7sP3a/8AKWm/4fV/0afrHjP72r+O71n07d0H92H7tf8AlLTf8Pq/6NP1jxn97V/Hd6z6
1219du6D+7D92v8Aylpv+H1f9Gn6x4z+9q/ju9Z9O3dCWr+7r931SzDaq+MamvarmMsE8VGsEkcgPyEw
1220IQZxIXbLOyzf/lfFXRNt2rqTE4TE33Y968lu56FeBoQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE
1221BAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE
1222BAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE
1223BAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE
1224BAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQE
1225BAQEBAQEHwOx5Z5NPMcpbS0Lm7u4xzGAtn2YRdmZl+ijw+nEU5YfBnXvmc5R/wBpvJP/AHa5/wCo
1226l/7Sv0NP4Y7E+tf8U9p/abyT/wB2uf8AqJf+0n0NP4Y7D61/xT2n9pvJP/drn/qJf+0n0NP4Y7D6
12271/xT2n9pvJP/AHa5/wCol/7SfQ0/hjsPrX/FPaf2m8k/92uf+ol/7SfQ0/hjsPrX/FPaf2m8k/8A
1228drn/AKiX/tJ9DT+GOw+tf8U9p/abyT/3a5/6iX/tJ9DT+GOw+tf8U9p/abyT/wB2uf8AqJf+0n0N
1229P4Y7D61/xT2n9pvJP/drn/qJf+0n0NP4Y7D61/xT2n9pvJP/AHa5/wCol/7SfQ0/hjsPrX/FPaf2
1230m8k/92uf+ol/7SfQ0/hjsPrX/FPaf2m8k/8Adrn/AKiX/tJ9DT+GOw+tf8U9q/s7PneraMr9u/XG
1231XrGRTyOz++MsTtn8Fzst0b/di2ep0vnVtzme1VLd+WjWGyWwvtXMnAJnmm4OTerMXLGVv6WlWlLa
12329EMfU1KVrNEX9pvJP/drn/qJf+0r9DT+GOxPrX/FPaf2m8k/92uf+ol/7SfQ0/hjsPrX/FPaf2m8
1233k/8Adrn/AKiX/tJ9DT+GOw+tf8U9r0n7vfI95N5RWqWb09ivYGQZI5pCkb5YyNnbk74fI+y8vjNG
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
1264ojMcUUY8YwEB9cCzM38SCxSu2qVqO1VkeKxE/KOQfVn/AEqXWxdFJyW26bZrC5F5Hu4tpJtI7Zjf
1265lZ2knwLu7OzNh2duOOjeyxOjZNvLTBuNa6LuauLnyyyTSnLKTnJITkZv1dyd8u7rpEUwc5mrVUEH
1266pf3c/wDzPX//AK7/ALiReTxv9KerzvT4T+pHlsfbV8F9oQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB
1267AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEHzqf9z0JTGUG0KOJ3dwjKHm4t
12687M5dwc/wL6cf5KaY29750/4+K4Sj/ub/AN7/AOz/AOtV/Uvl7/sT9P8Am7vtP7m/97/7P/rU/Uvl
12697/sP0/5u77T+5v8A3v8A7P8A61P1L5e/7D9P+bu+0/ub/wB7/wCz/wCtT9S+Xv8AsP0/5u77T+5v
1270/e/+z/61P1L5e/7D9P8Am7vtP7m/97/7P/rU/Uvl7/sP0/5u77T+5v8A3v8A7P8A61P1L5e/7D9P
1271+bu+0/ub/wB7/wCz/wCtT9S+Xv8AsP0/5u77T+5v/e/+z/61P1L5e/7D9P8Am7vtP7m/97/7P/rU
1272/Uvl7/sP0/5u77T+5v8A3v8A7P8A61P1L5e/7D9P+bu+0/ub/wB7/wCz/wCtT9S+Xv8AsP0/5u77
1273T+5v/e/+z/61P1L5e/7D9P8Am7vtP7m/97/7P/rU/Uvl7/sP0/5u77T+5v8A3v8A7P8A61P1L5e/
12747D9P+bu+0/ub/wB7/wCz/wCtT9S+Xv8AsP0/5u77T+5v/e/+z/61P1L5e/7D9P8Am7vtdbxj928O
1275k2obE7z2pIhJogaPtszmLi7v8x5+V3XHX8bOpby0o7aPg4surWr2a8L2CAgICAgICAgICAgICAgI
1276CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI
1277CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI
1278CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI
1279CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI
1280CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI
1281CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI
1282CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI
1283CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI
1284CAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgI
1285CAg81e2k4bO1Bc2MmpjEhGiXaB4ZGcWdzOWQDbPJ8cWIVLcemq3YTwWp/IJ42tzQ1WsUte/C3Y7n
1286A3cWYpHij4kxcGfrkm/BWu2cImfs86RGzOaObb2F79sSDBMZB+0K4RxvIQg4lTc+D4zgSLq/T8VL
1287axH/AJO6PQzM17LfzJaHk9uHSUbGxaD7i5gYJDnaICwzuRyu8YjGzM36vLK3dnEcPRDUbZ4+mU1b
1288yyW5NDWp1Yp7EhzAZDYZ4WeFgLIyiBchIZG/Vz+CzGPZXvokzTy4OrrNi9/XDaGLhI/MShd84kjJ
1289wIeXTLch9VLpwrG6rUZ0nZLj6baTWZoQtbSSHYvkrGrlhjib0fIxcgE34+vLmSXTERMxjgzPHDFF
1290B5pG1ODrCU/24zzfd2Y4CflniIYjZjN+P80W9Fq7Du81ViPPPno7w3pbGthu0YwkaYBlAZzeJmAm
12915dSEJerN+Cl8csls1UNV5FPdsV4pagwjaGY4ZQleQSCFxFjbIRvg+XT8PzVjHsie1JmnbRQs+TMN
12923W2jY44j+9h+1AnJ5ZIpBijZm+VnIn9PhlZsmteNsU65W7CZj4bvRLo/tC5PalrSMdOavUaxIMRx
1293yNyl5Mw5OJ+ocOjt0+LOpdNIun4f2/YtsVutjf8AsQQ+SXZghatSGUyox3jeSbhhj5Nw6Rvkvl+D
1294N+S1dNOadlv7WbMaRtmvc5m122ztzy2qeQrQ6wb1dvuJInZz5FzMABxkceGOBPx/hUuw5uExC2zW
1295kb6+h0ZfILNIaUuxHg0leaYxgNjEu2MbjnlEBcic8MzOzN+Ptu6PauiNn/qozZNYifLKq7T2189m
1296FC5TCucld7AkEzy4ZiEeJM4B1+b2d1N/Bd072m12ttjuVKEDSyVoHksTHK8TBzZ+DBgDciwLv7N+
1297K53T7My6WR7UcXNr+S/Y1az2jKR3oVJGeSQBEpZicMkRDyb0yREbtj2XW7O796ndMuVuUdE+j1pG
129882heT7dhrFY7gg0gWmKrgwImd52Do/yO3Hh6rMY+XR62pwSWvLftzeGSOtHajj7s0c1sIxdnd+Ax
1299E4v3HIRz6N6tlSufDtWIy4n9rZDhtW4KTHRqDFLLKUvE3jmjGTIhwLJCxdWcm/Napv8AiozE1yzp
1300Xz+pd8mt262llmpS9mw5QjHLxYsdyUQd8Ezt6Es0nmiN8rF0Urwq5ZeUywlTktl2xhitNsoBYXfv
1301V+DMzO/plyyPX3VrEzMxlSKdd1PsSk5ce6kykDzSN7EcJRwSObxu51rIziASSDETm7AOCEjHp6P8
1302VYis08tvqJmkV8vKnmT/ANrIn+4YaxOcNoa0Y8usgETs8o9Pbtn0/BZrhE9PdHN5qNTGMx0d8086
1303lc8huXtK9qAI4YTKuQzV7PckDlPG3bkFhBwJxJ8szv8ABaiPbtifihi6cJpulOXmdcp2CFq5xnM9
1304aJnsi07nycBJ4WEnYHP3znHXisTNba8PtbnCtdirW3fkp6vSzuEBHcnEDN5cPKzgb4MWhxH9PqOV
13050p7UR8voZrhPT/qot3fMq9SWcDavio4haB7IjK5YZzaGJxzIw59+OfZZjHtp6FmJ66OjqtpZvyWH
1306+2GKtBLLA0rycjIoj45YODdHb4l/jSMoneld3lhVVu+R2a82w40mkraxw+4leXiTiYCbuAcHy4sX
1307o5N+aW4xEzvotNkbq+f1MFv5Q2dmjFGViyU4RVojMQDDwtKZchDkIi3rnk+fRLcY67u6nrSu3hHf
1308X1IZdluY95MwVmN46ISyVSncYmdpJMkDsBZcmbpkW/HCkTS26d0x5lpWYjp9CSludlc3UY1wjfXT
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
1369oz5YFj4tlnEvwVv9qvGvekRSI6PMpQeGQQ1RrDYdopIPt7zCDD3uOXjkbr8hg/v16dFZn0dsUxX7
1370eydi3Y02yt0nq3L4yNmJxIIOGe3IMmTyZZJ+GPl4t19Eifaid01SYwpwXIdf29pZv9zP3McUfbxj
1371j2nN85z1zz+CkYV6fQt2Mxwj0q2x1ezuR2Kz3gGlZZxMHg5SiBNghCTmw/k7g6lK5rWmSu/i7BaK
13723Vs9mwMkclYnDmwMELQEBtybmxC3xZ1qs9899PUzEYU4R3V9br1orQRO1mZppSd3cgDtg34COSfH
13735k6zdFYosOTrvHLFcKMNm4M9bXZKvGEXbdzcXFiMnM88WJ8MzMrdj00okx56+lnW+OT6+WqcFsS7
1374Ncathjid+4Am5s44NuBfM/ryVnGsbJp3RQp5575aweN2oKtWKO6LSa83KhN2eogTOxBK3PEjOL46
1375cUrOE7aU83qXf01dMfvYKkhzO92x1fhCIRZ9uICZ4b/rH+lZvisUIeaqUNhXq6aD9mXS/ZBEXL/w
1376TdzlGcfp92/H68+6t/tT/Dy+b1GyY33V75lX1ms3mqCL9nU7YSdgILXdjpyBJ2nfgYi10HAmYnb6
1377nb8FZmvd5oj0H2981drRRXYtlemnp2o2vPGZSz/aswvFEMfXszSO7lxz0BmSMLacZntSmNeHr9bu
1378qKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC
1379AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC
1380AgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC
1381D//Z
1382------=_NextPart_000_000F_01D15E52.0BD654A0--
1383
1384
diff --git a/framework/src/domain/mime/mimetreeparser/tests/data/html.mbox b/framework/src/domain/mime/mimetreeparser/tests/data/html.mbox
new file mode 100644
index 00000000..bf5c685d
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/tests/data/html.mbox
@@ -0,0 +1,15 @@
1From foo@example.com Thu, 26 May 2011 01:16:54 +0100
2From: Thomas McGuire <foo@example.com>
3Subject: HTML test
4Date: Thu, 26 May 2011 01:16:54 +0100
5Message-ID: <1501334.pROlBb7MZF@herrwackelpudding.localhost>
6X-KMail-Transport: GMX
7X-KMail-Fcc: 28
8X-KMail-Drafts: 7
9X-KMail-Templates: 9
10User-Agent: KMail/4.6 beta5 (Linux/2.6.34.7-0.7-desktop; KDE/4.6.41; x86_64; git-0269848; 2011-04-19)
11MIME-Version: 1.0
12Content-Transfer-Encoding: 7Bit
13Content-Type: text/html; charset="windows-1252"
14
15<html><body><p><span>HTML</span> text</p></body></html> \ No newline at end of file
diff --git a/framework/src/domain/mime/mimetreeparser/tests/data/openpgp-encrypted-attachment-and-non-encrypted-attachment.mbox b/framework/src/domain/mime/mimetreeparser/tests/data/openpgp-encrypted-attachment-and-non-encrypted-attachment.mbox
new file mode 100644
index 00000000..2d9726ea
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/tests/data/openpgp-encrypted-attachment-and-non-encrypted-attachment.mbox
@@ -0,0 +1,115 @@
1From test@kolab.org Fri May 01 15:12:47 2015
2From: testkey <test@kolab.org>
3To: you@you.com
4Subject: enc & non enc attachment
5Date: Fri, 01 May 2015 17:12:47 +0200
6Message-ID: <13897561.XENKdJMSlR@tabin.local>
7X-KMail-Identity: 1197256126
8User-Agent: KMail/4.13.0.1 (Linux/3.19.1-towo.1-siduction-amd64; KDE/4.14.2; x86_64; git-cd33034; 2015-04-11)
9MIME-Version: 1.0
10Content-Type: multipart/mixed; boundary="nextPart1939768.sIoLGH0PD8"
11Content-Transfer-Encoding: 7Bit
12
13This is a multi-part message in MIME format.
14
15--nextPart1939768.sIoLGH0PD8
16Content-Type: multipart/encrypted; boundary="nextPart2814166.CHKktCGlQ3"; protocol="application/pgp-encrypted"
17
18
19--nextPart2814166.CHKktCGlQ3
20Content-Type: application/pgp-encrypted
21Content-Disposition: attachment
22Content-Transfer-Encoding: 7Bit
23
24Version: 1
25--nextPart2814166.CHKktCGlQ3
26Content-Type: application/octet-stream
27Content-Disposition: inline; filename="msg.asc"
28Content-Transfer-Encoding: 7Bit
29
30-----BEGIN PGP MESSAGE-----
31Version: GnuPG v2
32
33hIwDGJlthTT7oq0BA/9cXFQ6mN9Vxnc2B9M10odS3/6z1tsIY9oJdsiOjpfxqapX
34P7nOzR/jNWdFQanXoG1SjAcY2FeZEN0c3SkxEM6R5QVF1vMh/Xsni1clI+peZyVT
35Z4OSU74YCfYLg+cgDnPCF3kyNPVe6Z1pnfWOCZNCG3rpApw6UVLN63ScWC6eQIUB
36DAMMzkNap8zaOwEIANKHn1svvj+hBOIZYf8R+q2Bw7cd4xEChiJ7uQLnD98j0Fh1
3785v7/8JbZx6rEDDenPp1mCciDodb0aCmi0XLuzJz2ANGTVflfq+ZA+v1pwLksWCs
380YcHLEjOJzjr3KKmvu6wqnun5J2yV69K3OW3qTTGhNvcYZulqQ617pPa48+sFCgh
39nM8TMAD0ElVEwmMtrS3AWoJz52Af+R3YzpAnX8NzV317/JG+b6e2ksl3tR7TWp1q
402FOqC1sXAxuv+DIz4GgRfaK1+xYr2ckkg+H/3HJqa5LmJ7rGCyv+Epfp9u+OvdBG
41PBvuCtO3tm0crmnttMw57Gy35BKutRf/8MpBj/nS6QFX0t7XOLeL4Me7/a2H20wz
42HZsuRGDXMCh0lL0FYCBAwdbbYvvy0gz/5iaNvoADtaIu+VtbFNrTUN0SwuL+AIFS
43+WIiaSbFt4Ng3t9YmqL6pqB7fjxI10S+PK0s7ABqe4pgbzUWWt1yzBcxfk8l/47Q
44JrlvcE7HuDOhNOHfZIgUP2Dbeu+pVvHIJbmLsNWpl4s+nHhoxc9HrVhYG/MTZtQ3
45kkUWviegO6mwEZjQvgBxjWib7090sCxkO847b8A93mfQNHnuy2ZEEJ+9xyk7nIWs
464RsiNR8pYc/SMvdocyAvQMH/qSvmn/IFJ+jHhtT8UJlXJ0bHvXTHjHMqBp6fP69z
47Jh1ERadWQdMaTkzQ+asl+kl/x3p6RZP8MEVbZIl/3pcV+xiFCYcFu2TETKMtbW+b
48NYOlrltFxFDvyu3WeNNp0g9k0nFpD/T1OXHRBRcbUDWE4QF6NWTm6NO9wy2UYHCi
497QTSecBWgMaw7cUdwvnW6chIVoov1pm69BI9D0PoV76zCI7KzpiDsTFxdilKwbQf
50K/PDnv9Adx3ERh0/F8llBHrj2UGsRs4aHSEBDBJIHDCp8+lqtsRcINQBKEU3qIjt
51wf5vizdaVIgQnsD2z8QmBQ7QCCipI0ur6GKl+YWDDOSDLDUs9dK4A6xo/4Q0bsnI
52rH63ti5HslGq6uArfFkewH2MWff/8Li3uGEqzpK5NhP5UpbArelK+QaQQP5SdsmW
53XFwUqDS4QTCKNJXw/5SQMl8UE10l2Xaav3TkiOYTcBcvPNDovYgnMyRff/tTeFa8
5483STkvpGtkULkCntp22fydv5rg6DZ7eJrYfC2oZXdM87hHhUALUO6Y/VtVmNdNYw
55F3Uim4PDuLIKt+mFqRtFqnWm+5X/AslC31qLkjH+Fbb83TY+mC9gbIn7CZGJRCjn
56zzzMX2h15V/VHzNUgx9V/h28T0/z25FxoozZiJxpmhOtqoxMHp+y6nXXfMoIAD1D
57963Pc7u1HS0ny54A7bqc6KKd4W9IF7HkXn3SoBwCyn0IOPoKQTDD8mW3lbBI6+h9
58vP+MAQpfD8s+3VZ9r7OKYCVmUv47ViTRlf428Co6WT7rTHjGM09tqz826fTOXA==
59=6Eu9
60-----END PGP MESSAGE-----
61
62--nextPart2814166.CHKktCGlQ3--
63
64--nextPart1939768.sIoLGH0PD8
65Content-Disposition: attachment; filename="image.png"
66Content-Transfer-Encoding: base64
67Content-Type: image/png; name="image.png"
68
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
112o/0d59+Lh3Y1HHcAAAAASUVORK5CYII=
113
114--nextPart1939768.sIoLGH0PD8--
115
diff --git a/framework/src/domain/mime/mimetreeparser/tests/data/openpgp-inline-charset-encrypted.mbox b/framework/src/domain/mime/mimetreeparser/tests/data/openpgp-inline-charset-encrypted.mbox
new file mode 100644
index 00000000..8bd06910
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/tests/data/openpgp-inline-charset-encrypted.mbox
@@ -0,0 +1,40 @@
1From test@example.com Thu, 17 Oct 2013 02:13:03 +0200
2Return-Path: <test@example.com>
3Delivered-To: you@you.com
4Received: from localhost (localhost [127.0.0.1])
5 by test@example.com (Postfix) with ESMTP id B30D8120030
6 for <you@you.com>; Thu, 17 Oct 2013 02:13:05 +0200 (CEST)
7From: test <test@example.com>
8To: you@you.com
9Subject: charset
10Date: Thu, 17 Oct 2013 02:13:03 +0200
11Message-ID: <4081645.yGjUJ4o4Se@example.local>
12User-Agent: KMail/4.12 pre (Linux/3.11-4.towo-siduction-amd64; KDE/4.11.2; x86_64; git-f7f14e3; 2013-10-15)
13MIME-Version: 1.0
14Content-Transfer-Encoding: 7Bit
15Content-Type: text/plain; charset="ISO-8859-15"
16
17-----BEGIN PGP MESSAGE-----
18Version: GnuPG v2.0.22 (GNU/Linux)
19
20hIwDGJlthTT7oq0BBACbaRZudMigMTetPZNRgkfEXv4QQowR1jborw0dcgKKqMQ1
216o67NkpxvmXKGJTfTVCLBX3nk6FKYo6NwlPCyU7X9X0DDk8hvaBdR9wGfrdm5YWX
22GKOzcqJY1EypiMsspXeZvjzEW7O8I956c3vBb/2pM3xqYEK1kh8+d9bVH+cjf4UB
23DAMMzkNap8zaOwEH/1rPShyYL8meJN+/GGgS8+Nf1BW5pSHdAPCg0dnX4QCLEx7u
24GkBU6N4JGYayaCBofibOLacQPhYZdnR5Xb/Pvrx03GrzyzyDp0WyeI9nGNfkani7
25sCRWbzlMPsEvGEvJVnMLNRSk4xhPIWumL4APkw+Mgi6mf+Br8z0RhfnGwyMA53Mr
26pG9VQKlq3v7/aaN40pMjAsxiytcHS515jXrb3Ko4pWbTlAr/eytOEfkLRJgSOpQT
27BY7lWs+UQJqiG8Yn65vS9LMDNJgX9EOGx77Z4u9wvv4ZieOxzgbHGg5kYCoae7ba
28hxZeNjYKscH+E6epbOxM/wlTdr4UTiiW9dMsH0zSwMUB891gToeXq+LDGEPTKVSX
29tsJm4HS/kISJBwrCI4EUqWZML6xQ427NkZGmF2z/sD3kmL66GjspIKnb4zHmXacp
3084n2KrI9s7p6AnKnQjsxvB/4/lpXPCIY5GH7KjySEJiMsHECzeN1dJSL6keykBsx
31DtmYDA+dhZ6UWbwzx/78+mjNREhyp/UiSAmLzlJh89OH/xelAPvKcIosYwz4cY9N
32wjralTmL+Y0aHKeZJOeqPLaXADcPFiZrCNPCH65Ey5GEtDpjLpEbjVbykPV9+YkK
337JKW6bwMraOl5zmAoR77PWMo3IoYb9q4GuqDr1V2ZGlb7eMH1gj1nfgfVintKC1X
343jFfy7aK6LIQDVKEwbi0SxVXTKStuliVUy5oX4woDOxmTEotJf1QlKZpn5oF20UP
35tumYrp0SPoP8Bo4EVRVaLupduI5cYce1q/kFj9Iho/wk56MoG9PxMMfsH7oKg3AA
36CqQ6/kM4oJNdN5xIf1EH5HeaNFkDy1jlLznnhwVAZKPo/9ffpg==
37=bPqu
38-----END PGP MESSAGE-----
39
40
diff --git a/framework/src/domain/mime/mimetreeparser/tests/data/openpgp-inline-encrypted+nonenc.mbox b/framework/src/domain/mime/mimetreeparser/tests/data/openpgp-inline-encrypted+nonenc.mbox
new file mode 100644
index 00000000..b98dc336
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/tests/data/openpgp-inline-encrypted+nonenc.mbox
@@ -0,0 +1,31 @@
1From test@kolab.org Wed, 25 May 2011 23:49:40 +0100
2From: OpenPGP Test <test@kolab.org>
3To: test@kolab.org
4Subject: inlinepgpencrypted + non enc text
5Date: Wed, 25 May 2011 23:49:40 +0100
6Message-ID: <1786696.yKXrOjjflF@herrwackelpudding.localhost>
7X-KMail-Transport: GMX
8X-KMail-Fcc: 28
9X-KMail-Drafts: 7
10X-KMail-Templates: 9
11User-Agent: KMail/4.6 beta5 (Linux/2.6.34.7-0.7-desktop; KDE/4.6.41; x86_64; git-0269848; 2011-04-19)
12MIME-Version: 1.0
13Content-Transfer-Encoding: 7Bit
14Content-Type: text/plain; charset="us-ascii"
15
16Not encrypted not signed :(
17
18-----BEGIN PGP MESSAGE-----
19Version: GnuPG v2.0.15 (GNU/Linux)
20
21hQEMAwzOQ1qnzNo7AQf/a3aNTLpQBfcUr+4AKsZQLj4h6z7e7a5AaCW8AG0wrbxN
22kBYB7E5jdZh45DX/99gvoZslthWryUCX2kKZ3LtIllxKVjqNuK5hSt+SAuKkwiMR
23Xcbf1KFKENKupgGSO9B2NJRbjoExdJ+fC3mGXnO3dT7xJJAo3oLE8Nivu+Bj1peY
24E1wCf+vcTwVHFrA7SV8eMRb9Z9wBXmU8Q8e9ekJ7ZsRX3tMeBs6jvscVvfMf6DYY
25N14snZBZuGNKT9a3DPny7IC1S0lHcaam34ogWwMi3FxPGJt/Lg52kARlkF5TDhcP
26N6H0EB/iqDRjOOUoEVm8um5XOSR1FpEiAdD0DON3y9JPATnrYq7sgYZz3BVImYY+
27N/jV8fEiN0a34pcOq8NQedMuOsJHNBS5MtbQH/kJLq0MXBpXekGlHo4MKw0trISc
28Rw3pW6/BFfhPJLni29g9tw==
29=fRFW
30-----END PGP MESSAGE-----
31
diff --git a/framework/src/domain/mime/mimetreeparser/tests/data/plaintext.mbox b/framework/src/domain/mime/mimetreeparser/tests/data/plaintext.mbox
new file mode 100644
index 00000000..d185b1c1
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/tests/data/plaintext.mbox
@@ -0,0 +1,13 @@
1Return-Path: <konqi@example.org>
2Date: Wed, 8 Jun 2016 20:34:44 -0700
3From: Konqi <konqi@example.org>
4To: konqi@kde.org
5Subject: A random subject with alternative contenttype
6MIME-Version: 1.0
7Content-Type: text/plain; charset=utf-8
8Content-Transfer-Encoding: quoted-printable
9
10If you can see this text it means that your email client couldn't display o=
11ur newsletter properly.
12Please visit this link to view the newsletter on our website: http://www.go=
13g.com/newsletter/
diff --git a/framework/src/domain/mime/mimetreeparser/tests/data/smime-encrypted.mbox b/framework/src/domain/mime/mimetreeparser/tests/data/smime-encrypted.mbox
new file mode 100644
index 00000000..6b6d6a0d
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/tests/data/smime-encrypted.mbox
@@ -0,0 +1,22 @@
1From test@example.com Sat, 13 Apr 2013 01:54:30 +0200
2From: test <test@example.com>
3To: you@you.com
4Subject: test
5Date: Sat, 13 Apr 2013 01:54:30 +0200
6Message-ID: <1576646.QQxzHWx8dA@tabin>
7X-KMail-Identity: 505942601
8User-Agent: KMail/4.10.2 (Linux/3.9.0-rc4-experimental-amd64; KDE/4.10.60; x86_64; git-fc9b82c; 2013-04-11)
9MIME-Version: 1.0
10Content-Type: application/pkcs7-mime; name="smime.p7m"; smime-type="enveloped-data"
11Content-Transfer-Encoding: base64
12Content-Disposition: attachment; filename="smime.p7m"
13
14MIAGCSqGSIb3DQEHA6CAMIACAQAxgfwwgfkCAQAwYjBVMQswCQYDVQQGEwJVUzENMAsGA1UECgwE
15S0RBQjEWMBQGA1UEAwwNdW5pdHRlc3QgY2VydDEfMB0GCSqGSIb3DQEJARYQdGVzdEBleGFtcGxl
16LmNvbQIJANNFIDoYY4XJMA0GCSqGSIb3DQEBAQUABIGAJwmmaOeidXUHSQGOf2OBIsPYafVqdORe
17y54pEXbXiAfSVUWgI4a9CsiWwcDX8vlaX9ZLLr+L2VmOfr6Yc5214yxzausZVvnUFjy6LUXotuEX
18tSar4EW7XI9DjaZc1l985naMsTx9JUa5GyQ9J6PGqhosAKpKMGgKkFAHaOwE1/IwgAYJKoZIhvcN
19AQcBMBQGCCqGSIb3DQMHBAieDfmz3WGbN6CABHgEpsLrNn0PAZTDUfNomDypvSCl5bQH+9cKm80m
20upMV2r8RBiXS7OaP4SpCxq18afDTTPatvboHIoEX92taTbq8soiAgEs6raSGtEYZNvFL0IYqm7MA
21o5HCOmjiEcInyPf14lL3HnPk10FaP3hh58qTHUh4LPYtL7UECOZELYnUfUVhAAAAAAAAAAAAAA==
22
diff --git a/framework/src/domain/mime/mimetreeparser/tests/gnupg_home/CMakeLists.txt b/framework/src/domain/mime/mimetreeparser/tests/gnupg_home/CMakeLists.txt
new file mode 100644
index 00000000..9c64a008
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/tests/gnupg_home/CMakeLists.txt
@@ -0,0 +1,10 @@
1configure_file( gpg-agent.conf.in
2 "${CMAKE_CURRENT_BINARY_DIR}/gpg-agent.conf" @ONLY )
3
4configure_file( gpgsm.conf.in
5 "${CMAKE_CURRENT_BINARY_DIR}/gpgsm.conf" @ONLY )
6
7file( COPY
8 ${CMAKE_CURRENT_SOURCE_DIR}
9 DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/../"
10)
diff --git a/framework/src/domain/mime/mimetreeparser/tests/gnupg_home/dirmngr-cache.d/DIR.txt b/framework/src/domain/mime/mimetreeparser/tests/gnupg_home/dirmngr-cache.d/DIR.txt
new file mode 100644
index 00000000..1a45a6b3
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/tests/gnupg_home/dirmngr-cache.d/DIR.txt
@@ -0,0 +1,3 @@
1v:1:
2c:4E31CEB57DDD4A7B9991AB05507B1ED4293FF952:CN=Test-ZS 7,O=Intevation GmbH,C=DE:ldap%3A//ca.intevation.org/cn=Test-ZS 7, o=Intevation GmbH, c=DE?certificateRevocationList:20100615T181523:20100707T181523:72FEF3FD88455A1D4C6796A6499D4422::::
3c:7F2A402CBB016A9146D613568C89D3596A4111AA:CN=Wurzel ZS 3,O=Intevation GmbH,C=DE:ldap%3A//ca.intevation.org/cn=Wurzel ZS 3, o=Intevation GmbH, c=DE?certificateRevocationList:20100625T102134:20100814T102134:44E60EEC02EF2FBF7A5C77E9BD565667::::
diff --git a/framework/src/domain/mime/mimetreeparser/tests/gnupg_home/dirmngr-cache.d/crl-4E31CEB57DDD4A7B9991AB05507B1ED4293FF952.db b/framework/src/domain/mime/mimetreeparser/tests/gnupg_home/dirmngr-cache.d/crl-4E31CEB57DDD4A7B9991AB05507B1ED4293FF952.db
new file mode 100644
index 00000000..0b7e2dd4
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/tests/gnupg_home/dirmngr-cache.d/crl-4E31CEB57DDD4A7B9991AB05507B1ED4293FF952.db
Binary files differ
diff --git a/framework/src/domain/mime/mimetreeparser/tests/gnupg_home/dirmngr-cache.d/crl-7F2A402CBB016A9146D613568C89D3596A4111AA.db b/framework/src/domain/mime/mimetreeparser/tests/gnupg_home/dirmngr-cache.d/crl-7F2A402CBB016A9146D613568C89D3596A4111AA.db
new file mode 100644
index 00000000..47474a26
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/tests/gnupg_home/dirmngr-cache.d/crl-7F2A402CBB016A9146D613568C89D3596A4111AA.db
Binary files differ
diff --git a/framework/src/domain/mime/mimetreeparser/tests/gnupg_home/dirmngr.conf b/framework/src/domain/mime/mimetreeparser/tests/gnupg_home/dirmngr.conf
new file mode 100644
index 00000000..a17a0354
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/tests/gnupg_home/dirmngr.conf
@@ -0,0 +1,8 @@
1
2###+++--- GPGConf ---+++###
3debug-level basic
4log-file socket:///home/leo/kde/src/kdepim/messagecomposer/tests/gnupg_home/log-socket
5###+++--- GPGConf ---+++### Tue 29 Jun 2010 10:23:13 AM EDT
6# GPGConf edited this configuration file.
7# It will disable options before this marked block, but it will
8# never change anything below these lines.
diff --git a/framework/src/domain/mime/mimetreeparser/tests/gnupg_home/gpg-agent.conf.in b/framework/src/domain/mime/mimetreeparser/tests/gnupg_home/gpg-agent.conf.in
new file mode 100644
index 00000000..ece69255
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/tests/gnupg_home/gpg-agent.conf.in
@@ -0,0 +1,10 @@
1pinentry-program @CMAKE_CURRENT_BINARY_DIR@/pinentry-fake.sh
2###+++--- GPGConf ---+++###
3allow-mark-trusted
4debug-level basic
5faked-system-time 20130110T154812
6log-file @CMAKE_CURRENT_BINARY_DIR@/gpg-agent.log
7###+++--- GPGConf ---+++### Tue 29 Jun 2010 10:23:13 AM EDT
8# GPGConf edited this configuration file.
9# It will disable options before this marked block, but it will
10# never change anything below these lines.
diff --git a/framework/src/domain/mime/mimetreeparser/tests/gnupg_home/gpg.conf b/framework/src/domain/mime/mimetreeparser/tests/gnupg_home/gpg.conf
new file mode 100644
index 00000000..f1760823
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/tests/gnupg_home/gpg.conf
@@ -0,0 +1,244 @@
1# Options for GnuPG
2# Copyright 1998, 1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
3#
4# This file is free software; as a special exception the author gives
5# unlimited permission to copy and/or distribute it, with or without
6# modifications, as long as this notice is preserved.
7#
8# This file is distributed in the hope that it will be useful, but
9# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
10# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11#
12# Unless you specify which option file to use (with the command line
13# option "--options filename"), GnuPG uses the file ~/.gnupg/gpg.conf
14# by default.
15#
16# An options file can contain any long options which are available in
17# GnuPG. If the first non white space character of a line is a '#',
18# this line is ignored. Empty lines are also ignored.
19#
20# See the man page for a list of options.
21
22# Uncomment the following option to get rid of the copyright notice
23
24#no-greeting
25
26# If you have more than 1 secret key in your keyring, you may want to
27# uncomment the following option and set your preferred keyid.
28
29#default-key 621CC013
30
31# If you do not pass a recipient to gpg, it will ask for one. Using
32# this option you can encrypt to a default key. Key validation will
33# not be done in this case. The second form uses the default key as
34# default recipient.
35
36#default-recipient some-user-id
37#default-recipient-self
38
39# Use --encrypt-to to add the specified key as a recipient to all
40# messages. This is useful, for example, when sending mail through a
41# mail client that does not automatically encrypt mail to your key.
42# In the example, this option allows you to read your local copy of
43# encrypted mail that you've sent to others.
44
45#encrypt-to some-key-id
46
47# By default GnuPG creates version 3 signatures for data files. This
48# is not strictly OpenPGP compliant but PGP 6 and most versions of PGP
49# 7 require them. To disable this behavior, you may use this option
50# or --openpgp.
51
52#no-force-v3-sigs
53
54# Because some mailers change lines starting with "From " to ">From "
55# it is good to handle such lines in a special way when creating
56# cleartext signatures; all other PGP versions do it this way too.
57
58#no-escape-from-lines
59
60# If you do not use the Latin-1 (ISO-8859-1) charset, you should tell
61# GnuPG which is the native character set. Please check the man page
62# for supported character sets. This character set is only used for
63# metadata and not for the actual message which does not undergo any
64# translation. Note that future version of GnuPG will change to UTF-8
65# as default character set. In most cases this option is not required
66# as GnuPG is able to figure out the correct charset at runtime.
67
68#charset utf-8
69
70# Group names may be defined like this:
71# group mynames = paige 0x12345678 joe patti
72#
73# Any time "mynames" is a recipient (-r or --recipient), it will be
74# expanded to the names "paige", "joe", and "patti", and the key ID
75# "0x12345678". Note there is only one level of expansion - you
76# cannot make an group that points to another group. Note also that
77# if there are spaces in the recipient name, this will appear as two
78# recipients. In these cases it is better to use the key ID.
79
80#group mynames = paige 0x12345678 joe patti
81
82# Lock the file only once for the lifetime of a process. If you do
83# not define this, the lock will be obtained and released every time
84# it is needed, which is usually preferable.
85
86#lock-once
87
88# GnuPG can send and receive keys to and from a keyserver. These
89# servers can be HKP, email, or LDAP (if GnuPG is built with LDAP
90# support).
91#
92# Example HKP keyserver:
93# hkp://keys.gnupg.net
94# hkp://subkeys.pgp.net
95#
96# Example email keyserver:
97# mailto:pgp-public-keys@keys.pgp.net
98#
99# Example LDAP keyservers:
100# ldap://keyserver.pgp.com
101#
102# Regular URL syntax applies, and you can set an alternate port
103# through the usual method:
104# hkp://keyserver.example.net:22742
105#
106# Most users just set the name and type of their preferred keyserver.
107# Note that most servers (with the notable exception of
108# ldap://keyserver.pgp.com) synchronize changes with each other. Note
109# also that a single server name may actually point to multiple
110# servers via DNS round-robin. hkp://keys.gnupg.net is an example of
111# such a "server", which spreads the load over a number of physical
112# servers. To see the IP address of the server actually used, you may use
113# the "--keyserver-options debug".
114
115keyserver hkp://keys.gnupg.net
116#keyserver mailto:pgp-public-keys@keys.nl.pgp.net
117#keyserver ldap://keyserver.pgp.com
118
119# Common options for keyserver functions:
120#
121# include-disabled : when searching, include keys marked as "disabled"
122# on the keyserver (not all keyservers support this).
123#
124# no-include-revoked : when searching, do not include keys marked as
125# "revoked" on the keyserver.
126#
127# verbose : show more information as the keys are fetched.
128# Can be used more than once to increase the amount
129# of information shown.
130#
131# use-temp-files : use temporary files instead of a pipe to talk to the
132# keyserver. Some platforms (Win32 for one) always
133# have this on.
134#
135# keep-temp-files : do not delete temporary files after using them
136# (really only useful for debugging)
137#
138# http-proxy="proxy" : set the proxy to use for HTTP and HKP keyservers.
139# This overrides the "http_proxy" environment variable,
140# if any.
141#
142# auto-key-retrieve : automatically fetch keys as needed from the keyserver
143# when verifying signatures or when importing keys that
144# have been revoked by a revocation key that is not
145# present on the keyring.
146#
147# no-include-attributes : do not include attribute IDs (aka "photo IDs")
148# when sending keys to the keyserver.
149
150#keyserver-options auto-key-retrieve
151
152# Display photo user IDs in key listings
153
154# list-options show-photos
155
156# Display photo user IDs when a signature from a key with a photo is
157# verified
158
159# verify-options show-photos
160
161# Use this program to display photo user IDs
162#
163# %i is expanded to a temporary file that contains the photo.
164# %I is the same as %i, but the file isn't deleted afterwards by GnuPG.
165# %k is expanded to the key ID of the key.
166# %K is expanded to the long OpenPGP key ID of the key.
167# %t is expanded to the extension of the image (e.g. "jpg").
168# %T is expanded to the MIME type of the image (e.g. "image/jpeg").
169# %f is expanded to the fingerprint of the key.
170# %% is %, of course.
171#
172# If %i or %I are not present, then the photo is supplied to the
173# viewer on standard input. If your platform supports it, standard
174# input is the best way to do this as it avoids the time and effort in
175# generating and then cleaning up a secure temp file.
176#
177# If no photo-viewer is provided, GnuPG will look for xloadimage, eog,
178# or display (ImageMagick). On Mac OS X and Windows, the default is
179# to use your regular JPEG image viewer.
180#
181# Some other viewers:
182# photo-viewer "qiv %i"
183# photo-viewer "ee %i"
184#
185# This one saves a copy of the photo ID in your home directory:
186# photo-viewer "cat > ~/photoid-for-key-%k.%t"
187#
188# Use your MIME handler to view photos:
189# photo-viewer "metamail -q -d -b -c %T -s 'KeyID 0x%k' -f GnuPG"
190
191# Passphrase agent
192#
193# We support the old experimental passphrase agent protocol as well as
194# the new Assuan based one (currently available in the "newpg" package
195# at ftp.gnupg.org/gcrypt/alpha/aegypten/). To make use of the agent,
196# you have to run an agent as daemon and use the option
197#
198# use-agent
199#
200# which tries to use the agent but will fallback to the regular mode
201# if there is a problem connecting to the agent. The normal way to
202# locate the agent is by looking at the environment variable
203# GPG_AGENT_INFO which should have been set during gpg-agent startup.
204# In certain situations the use of this variable is not possible, thus
205# the option
206#
207# --gpg-agent-info=<path>:<pid>:1
208#
209# may be used to override it.
210
211# Automatic key location
212#
213# GnuPG can automatically locate and retrieve keys as needed using the
214# auto-key-locate option. This happens when encrypting to an email
215# address (in the "user@example.com" form), and there are no
216# user@example.com keys on the local keyring. This option takes the
217# following arguments, in the order they are to be tried:
218#
219# cert = locate a key using DNS CERT, as specified in RFC-4398.
220# GnuPG can handle both the PGP (key) and IPGP (URL + fingerprint)
221# CERT methods.
222#
223# pka = locate a key using DNS PKA.
224#
225# ldap = locate a key using the PGP Universal method of checking
226# "ldap://keys.(thedomain)". For example, encrypting to
227# user@example.com will check ldap://keys.example.com.
228#
229# keyserver = locate a key using whatever keyserver is defined using
230# the keyserver option.
231#
232# You may also list arbitrary keyservers here by URL.
233#
234# Try CERT, then PKA, then LDAP, then hkp://subkeys.net:
235#auto-key-locate cert pka ldap hkp://subkeys.pgp.net
236
237###+++--- GPGConf ---+++###
238utf8-strings
239#debug-level basic
240#log-file socket:///home/leo/kde/src/kdepim/messagecomposer/tests/gnupg_home/log-socket
241###+++--- GPGConf ---+++### Tue 29 Jun 2010 10:23:13 AM EDT
242# GPGConf edited this configuration file.
243# It will disable options before this marked block, but it will
244# never change anything below these lines.
diff --git a/framework/src/domain/mime/mimetreeparser/tests/gnupg_home/gpgsm.conf.in b/framework/src/domain/mime/mimetreeparser/tests/gnupg_home/gpgsm.conf.in
new file mode 100644
index 00000000..92b6119d
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/tests/gnupg_home/gpgsm.conf.in
@@ -0,0 +1,10 @@
1
2###+++--- GPGConf ---+++###
3disable-crl-checks
4debug-level basic
5faked-system-time 20130110T154812
6log-file @CMAKE_CURRENT_BINARY_DIR@/gpgsm.log
7###+++--- GPGConf ---+++### Tue 29 Jun 2010 10:23:13 AM EDT
8# GPGConf edited this configuration file.
9# It will disable options before this marked block, but it will
10# never change anything below these lines.
diff --git a/framework/src/domain/mime/mimetreeparser/tests/gnupg_home/pinentry-fake.sh b/framework/src/domain/mime/mimetreeparser/tests/gnupg_home/pinentry-fake.sh
new file mode 100755
index 00000000..7135a942
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/tests/gnupg_home/pinentry-fake.sh
@@ -0,0 +1,9 @@
1#!/bin/sh
2
3echo "OK Your orders please"
4while :
5do
6 read cmd
7 echo "OK"
8 [ "$cmd" = "BYE" ] && break
9done
diff --git a/framework/src/domain/mime/mimetreeparser/tests/gnupg_home/private-keys-v1.d/1AA8BA52430E51AE249AF0DA97D59F869E4101A8.key b/framework/src/domain/mime/mimetreeparser/tests/gnupg_home/private-keys-v1.d/1AA8BA52430E51AE249AF0DA97D59F869E4101A8.key
new file mode 100644
index 00000000..39ac307b
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/tests/gnupg_home/private-keys-v1.d/1AA8BA52430E51AE249AF0DA97D59F869E4101A8.key
Binary files differ
diff --git a/framework/src/domain/mime/mimetreeparser/tests/gnupg_home/pubring.gpg b/framework/src/domain/mime/mimetreeparser/tests/gnupg_home/pubring.gpg
new file mode 100644
index 00000000..2e00fa24
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/tests/gnupg_home/pubring.gpg
Binary files differ
diff --git a/framework/src/domain/mime/mimetreeparser/tests/gnupg_home/pubring.kbx b/framework/src/domain/mime/mimetreeparser/tests/gnupg_home/pubring.kbx
new file mode 100644
index 00000000..0230f313
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/tests/gnupg_home/pubring.kbx
Binary files differ
diff --git a/framework/src/domain/mime/mimetreeparser/tests/gnupg_home/scdaemon.conf b/framework/src/domain/mime/mimetreeparser/tests/gnupg_home/scdaemon.conf
new file mode 100644
index 00000000..a17a0354
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/tests/gnupg_home/scdaemon.conf
@@ -0,0 +1,8 @@
1
2###+++--- GPGConf ---+++###
3debug-level basic
4log-file socket:///home/leo/kde/src/kdepim/messagecomposer/tests/gnupg_home/log-socket
5###+++--- GPGConf ---+++### Tue 29 Jun 2010 10:23:13 AM EDT
6# GPGConf edited this configuration file.
7# It will disable options before this marked block, but it will
8# never change anything below these lines.
diff --git a/framework/src/domain/mime/mimetreeparser/tests/gnupg_home/secring.gpg b/framework/src/domain/mime/mimetreeparser/tests/gnupg_home/secring.gpg
new file mode 100644
index 00000000..cfd3387d
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/tests/gnupg_home/secring.gpg
Binary files differ
diff --git a/framework/src/domain/mime/mimetreeparser/tests/gnupg_home/trustdb.gpg b/framework/src/domain/mime/mimetreeparser/tests/gnupg_home/trustdb.gpg
new file mode 100644
index 00000000..70089c15
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/tests/gnupg_home/trustdb.gpg
Binary files differ
diff --git a/framework/src/domain/mime/mimetreeparser/tests/gnupg_home/trustlist.txt b/framework/src/domain/mime/mimetreeparser/tests/gnupg_home/trustlist.txt
new file mode 100644
index 00000000..bbb0442d
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/tests/gnupg_home/trustlist.txt
@@ -0,0 +1,11 @@
15E:7C:B2:F4:9F:70:05:43:42:32:5D:75:74:70:00:09:B9:D8:08:61 S
2
3
4
5# CN=unittest cert
6# O=KDAB
7# C=US
8# EMail=test@example.com
924:D2:FC:A2:2E:B3:B8:0A:1E:37:71:D1:4C:C6:58:E3:21:2B:49:DC S
10
11
diff --git a/framework/src/domain/mime/mimetreeparser/tests/gpgerrortest.cpp b/framework/src/domain/mime/mimetreeparser/tests/gpgerrortest.cpp
new file mode 100644
index 00000000..4254d972
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/tests/gpgerrortest.cpp
@@ -0,0 +1,214 @@
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 <QGpgME/Protocol>
24#include <gpgme++/context.h>
25#include <gpgme++/engineinfo.h>
26#include <gpgme.h>
27
28#include <QDebug>
29#include <QDir>
30#include <QProcess>
31#include <QTest>
32
33QByteArray readMailFromFile(const QString &mailFile)
34{
35 QFile file(QLatin1String(MAIL_DATA_DIR) + QLatin1Char('/') + mailFile);
36 file.open(QIODevice::ReadOnly);
37 Q_ASSERT(file.isOpen());
38 return file.readAll();
39}
40
41void killAgent(const QString& dir)
42{
43 QProcess proc;
44 proc.setProgram(QStringLiteral("gpg-connect-agent"));
45 QStringList arguments;
46 arguments << "-S " << dir + "/S.gpg-agent";
47 proc.start();
48 proc.waitForStarted();
49 proc.write("KILLAGENT\n");
50 proc.write("BYE\n");
51 proc.closeWriteChannel();
52 proc.waitForFinished();
53}
54
55class GpgErrorTest : public QObject
56{
57 Q_OBJECT
58
59private slots:
60
61 void testGpgConfiguredCorrectly()
62 {
63 setEnv("GNUPGHOME", GNUPGHOME);
64
65 Parser parser(readMailFromFile("openpgp-inline-charset-encrypted.mbox"));
66
67 auto contentPartList = parser.collectContentParts();
68 QCOMPARE(contentPartList.size(), 1);
69 auto contentPart = contentPartList[0];
70 QCOMPARE(contentPart->availableContents(), QVector<QByteArray>() << "plaintext");
71 auto contentList = contentPart->content("plaintext");
72 QVERIFY(contentList[0]->content().startsWith("asdasd"));
73 QCOMPARE(contentList[0]->encryptions().size(), 1);
74 auto enc = contentList[0]->encryptions()[0];
75 QCOMPARE(enc->errorType(), Encryption::NoError);
76 QCOMPARE(enc->errorString(), QString());
77 QCOMPARE((int) enc->recipients().size(), 2);
78 }
79
80 void testNoGPGInstalled_data()
81 {
82 QTest::addColumn<QString>("mailFileName");
83
84 QTest::newRow("openpgp-inline-charset-encrypted") << "openpgp-inline-charset-encrypted.mbox";
85 QTest::newRow("openpgp-encrypted-attachment-and-non-encrypted-attachment") << "openpgp-encrypted-attachment-and-non-encrypted-attachment.mbox";
86 QTest::newRow("smime-encrypted") << "smime-encrypted.mbox";
87 }
88
89 void testNoGPGInstalled()
90 {
91 QFETCH(QString, mailFileName);
92
93 setEnv("PATH", "/nonexististing");
94 setGpgMEfname("/nonexisting/gpg", "");
95
96 Parser parser(readMailFromFile(mailFileName));
97 auto contentPartList = parser.collectContentParts();
98
99 QCOMPARE(contentPartList.size(), 1);
100 auto contentPart = contentPartList[0];
101 QCOMPARE(contentPart->availableContents(), QVector<QByteArray>() << "plaintext");
102 auto contentList = contentPart->content("plaintext");
103 QCOMPARE(contentList[0]->encryptions().size(), 1);
104 QVERIFY(contentList[0]->content().isEmpty());
105 auto enc = contentList[0]->encryptions()[0];
106 qDebug() << "HUHU"<< enc->errorType();
107 QCOMPARE(enc->errorType(), Encryption::UnknownError);
108 QCOMPARE(enc->errorString(), QString("Crypto plug-in \"OpenPGP\" could not decrypt the data.<br />Error: No data"));
109 QCOMPARE((int) enc->recipients().size(), 0);
110 }
111
112 void testGpgIncorrectGPGHOME_data()
113 {
114 QTest::addColumn<QString>("mailFileName");
115
116 QTest::newRow("openpgp-inline-charset-encrypted") << "openpgp-inline-charset-encrypted.mbox";
117 QTest::newRow("openpgp-encrypted-attachment-and-non-encrypted-attachment") << "openpgp-encrypted-attachment-and-non-encrypted-attachment.mbox";
118 QTest::newRow("smime-encrypted") << "smime-encrypted.mbox";
119 }
120
121 void testGpgIncorrectGPGHOME()
122 {
123 QFETCH(QString, mailFileName);
124 setEnv("GNUPGHOME", QByteArray(GNUPGHOME) + QByteArray("noexist"));
125
126 Parser parser(readMailFromFile(mailFileName));
127
128 auto contentPartList = parser.collectContentParts();
129 QCOMPARE(contentPartList.size(), 1);
130 auto contentPart = contentPartList[0];
131 QCOMPARE(contentPart->availableContents(), QVector<QByteArray>() << "plaintext");
132 auto contentList = contentPart->content("plaintext");
133 QCOMPARE(contentList[0]->encryptions().size(), 1);
134 QCOMPARE(contentList[0]->signatures().size(), 0);
135 QVERIFY(contentList[0]->content().isEmpty());
136 auto enc = contentList[0]->encryptions()[0];
137 qDebug() << enc->errorType();
138 QCOMPARE(enc->errorType(), Encryption::KeyMissing);
139 QCOMPARE(enc->errorString(), QString("Crypto plug-in \"OpenPGP\" could not decrypt the data.<br />Error: Decryption failed"));
140 QCOMPARE((int) enc->recipients().size(), 2);
141 }
142
143public Q_SLOTS:
144 void init()
145 {
146 mResetGpgmeEngine = false;
147 mModifiedEnv.clear();
148 {
149 QGpgME::openpgp(); // We need to intialize it, otherwise ctx will be a nullpointer
150 const GpgME::Context *ctx = GpgME::Context::createForProtocol(GpgME::Protocol::OpenPGP);
151 const auto engineinfo = ctx->engineInfo();
152 mGpgmeEngine_fname = engineinfo.fileName();
153 }
154 mEnv = QProcessEnvironment::systemEnvironment();
155 unsetEnv("GNUPGHOME");
156 }
157
158 void cleanup()
159 {
160 QCoreApplication::sendPostedEvents();
161
162 const QString &gnupghome = qgetenv("GNUPGHOME");
163 if (!gnupghome.isEmpty()) {
164 killAgent(gnupghome);
165 }
166
167 resetGpgMfname();
168 resetEnv();
169 }
170private:
171 void unsetEnv(const QByteArray &name)
172 {
173 mModifiedEnv << name;
174 unsetenv(name);
175 }
176
177 void setEnv(const QByteArray &name, const QByteArray &value)
178 {
179 mModifiedEnv << name;
180 setenv(name, value , 1);
181 }
182
183 void resetEnv()
184 {
185 foreach(const auto &i, mModifiedEnv) {
186 if (mEnv.contains(i)) {
187 setenv(i, mEnv.value(i).toUtf8(), 1);
188 } else {
189 unsetenv(i);
190 }
191 }
192 }
193
194 void resetGpgMfname()
195 {
196 if (mResetGpgmeEngine) {
197 gpgme_set_engine_info (GPGME_PROTOCOL_OpenPGP, mGpgmeEngine_fname, NULL);
198 }
199 }
200
201 void setGpgMEfname(const QByteArray &fname, const QByteArray &homedir)
202 {
203 mResetGpgmeEngine = true;
204 gpgme_set_engine_info (GPGME_PROTOCOL_OpenPGP, fname, homedir);
205 }
206
207 QSet<QByteArray> mModifiedEnv;
208 QProcessEnvironment mEnv;
209 bool mResetGpgmeEngine;
210 QByteArray mGpgmeEngine_fname;
211};
212
213QTEST_GUILESS_MAIN(GpgErrorTest)
214#include "gpgerrortest.moc"
diff --git a/framework/src/domain/mime/mimetreeparser/tests/interfacetest.cpp b/framework/src/domain/mime/mimetreeparser/tests/interfacetest.cpp
new file mode 100644
index 00000000..3ae32a4a
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/tests/interfacetest.cpp
@@ -0,0 +1,310 @@
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 <QTest>
24
25QByteArray readMailFromFile(const QString &mailFile)
26{
27 QFile file(QLatin1String(MAIL_DATA_DIR) + QLatin1Char('/') + mailFile);
28 file.open(QIODevice::ReadOnly);
29 Q_ASSERT(file.isOpen());
30 return file.readAll();
31}
32
33QByteArray join(QVector<QByteArray> vec, QByteArray sep)
34{
35 QByteArray ret;
36 bool bInit = true;
37 foreach(const auto &entry, vec) {
38 if (!bInit) {
39 ret += sep;
40 }
41 bInit = false;
42 ret += entry;
43 }
44 return ret;
45}
46
47class InterfaceTest : public QObject
48{
49 Q_OBJECT
50private:
51 void printTree(const Part::Ptr &start, QString pre)
52 {
53 foreach (const auto &part, start->subParts()) {
54 qWarning() << QStringLiteral("%1* %2(%3)")
55 .arg(pre)
56 .arg(QString::fromLatin1(part->type()))
57 .arg(QString::fromLatin1(join(part->availableContents(),", ")));
58 printTree(part,pre + QStringLiteral(" "));
59 }
60 }
61
62private slots:
63
64 void testTextMail()
65 {
66 Parser parser(readMailFromFile("plaintext.mbox"));
67 printTree(parser.d->mTree,QString());
68 auto contentPartList = parser.collectContentParts();
69 QCOMPARE(contentPartList.size(), 1);
70 auto contentPart = contentPartList[0];
71 QVERIFY((bool)contentPart);
72 QCOMPARE(contentPart->availableContents(), QVector<QByteArray>() << "plaintext");
73 auto contentList = contentPart->content("plaintext");
74 QCOMPARE(contentList.size(), 1);
75 QCOMPARE(contentList[0]->content(), QStringLiteral("If you can see this text it means that your email client couldn't display our newsletter properly.\nPlease visit this link to view the newsletter on our website: http://www.gog.com/newsletter/").toLocal8Bit());
76 QCOMPARE(contentList[0]->charset(), QStringLiteral("utf-8").toLocal8Bit());
77 QCOMPARE(contentList[0]->encryptions().size(), 0);
78 QCOMPARE(contentList[0]->signatures().size(), 0);
79
80 contentList = contentPart->content("html");
81 QCOMPARE(contentList.size(), 0);
82 auto contentAttachmentList = parser.collectAttachmentParts();
83 QCOMPARE(contentAttachmentList.size(), 0);
84 }
85
86 void testTextAlternative()
87 {
88 Parser parser(readMailFromFile("alternative.mbox"));
89 printTree(parser.d->mTree,QString());
90 auto contentPartList = parser.collectContentParts();
91 QCOMPARE(contentPartList.size(), 1);
92 auto contentPart = contentPartList[0];
93 QVERIFY((bool)contentPart);
94 QCOMPARE(contentPart->availableContents(), QVector<QByteArray>() << "html" << "plaintext");
95 auto contentList = contentPart->content("plaintext");
96 QCOMPARE(contentList.size(), 1);
97 QCOMPARE(contentList[0]->content(), QStringLiteral("If you can see this text it means that your email client couldn't display our newsletter properly.\nPlease visit this link to view the newsletter on our website: http://www.gog.com/newsletter/\n").toLocal8Bit());
98 QCOMPARE(contentList[0]->charset(), QStringLiteral("utf-8").toLocal8Bit());
99 QCOMPARE(contentList[0]->encryptions().size(), 0);
100 QCOMPARE(contentList[0]->signatures().size(), 0);
101
102 contentList = contentPart->content("html");
103 QCOMPARE(contentList.size(), 1);
104 QCOMPARE(contentList[0]->content(), QStringLiteral("<html><body><p><span>HTML</span> text</p></body></html>\n\n").toLocal8Bit());
105 QCOMPARE(contentList[0]->charset(), QStringLiteral("utf-8").toLocal8Bit());
106 QCOMPARE(contentList[0]->encryptions().size(), 0);
107 QCOMPARE(contentList[0]->signatures().size(), 0);
108 auto contentAttachmentList = parser.collectAttachmentParts();
109 QCOMPARE(contentAttachmentList.size(), 0);
110 }
111
112 void testTextHtml()
113 {
114 Parser parser(readMailFromFile("html.mbox"));
115 printTree(parser.d->mTree,QString());
116 auto contentPartList = parser.collectContentParts();
117 QCOMPARE(contentPartList.size(), 1);
118 auto contentPart = contentPartList[0];
119 QVERIFY((bool)contentPart);
120 QCOMPARE(contentPart->availableContents(), QVector<QByteArray>() << "html");
121
122 auto contentList = contentPart->content("plaintext");
123 QCOMPARE(contentList.size(), 0);
124
125 contentList = contentPart->content("html");
126 QCOMPARE(contentList.size(), 1);
127 QCOMPARE(contentList[0]->content(), QStringLiteral("<html><body><p><span>HTML</span> text</p></body></html>").toLocal8Bit());
128 QCOMPARE(contentList[0]->charset(), QStringLiteral("utf-8").toLocal8Bit());
129 QCOMPARE(contentList[0]->encryptions().size(), 0);
130 QCOMPARE(contentList[0]->signatures().size(), 0);
131 auto contentAttachmentList = parser.collectAttachmentParts();
132 QCOMPARE(contentAttachmentList.size(), 0);
133 }
134
135 void testSMimeEncrypted()
136 {
137 Parser parser(readMailFromFile("smime-encrypted.mbox"));
138 printTree(parser.d->mTree,QString());
139 auto contentPartList = parser.collectContentParts();
140 QCOMPARE(contentPartList.size(), 1);
141 auto contentPart = contentPartList[0];
142 QVERIFY((bool)contentPart);
143 QCOMPARE(contentPart->availableContents(), QVector<QByteArray>() << "plaintext");
144 auto contentList = contentPart->content("plaintext");
145 QCOMPARE(contentList.size(), 1);
146 QCOMPARE(contentList[0]->content(), QStringLiteral("The quick brown fox jumped over the lazy dog.").toLocal8Bit());
147 QCOMPARE(contentList[0]->charset(), QStringLiteral("us-ascii").toLocal8Bit());
148 QCOMPARE(contentList[0]->encryptions().size(), 1);
149 QCOMPARE(contentList[0]->signatures().size(), 0);
150 auto contentAttachmentList = parser.collectAttachmentParts();
151 QCOMPARE(contentAttachmentList.size(), 0);
152 }
153
154 void testOpenPGPEncryptedAttachment()
155 {
156 Parser parser(readMailFromFile("openpgp-encrypted-attachment-and-non-encrypted-attachment.mbox"));
157 printTree(parser.d->mTree,QString());
158 auto contentPartList = parser.collectContentParts();
159 QCOMPARE(contentPartList.size(), 1);
160 auto contentPart = contentPartList[0];
161 QVERIFY((bool)contentPart);
162 QCOMPARE(contentPart->availableContents(), QVector<QByteArray>() << "plaintext");
163 auto contentList = contentPart->content("plaintext");
164 QCOMPARE(contentList.size(), 1);
165 QCOMPARE(contentList[0]->content(), QStringLiteral("test text").toLocal8Bit());
166 QCOMPARE(contentList[0]->charset(), QStringLiteral("us-ascii").toLocal8Bit());
167 QCOMPARE(contentList[0]->encryptions().size(), 1);
168 QCOMPARE(contentList[0]->signatures().size(), 1);
169 auto contentAttachmentList = parser.collectAttachmentParts();
170 QCOMPARE(contentAttachmentList.size(), 2);
171 QCOMPARE(contentAttachmentList[0]->availableContents(), QVector<QByteArray>() << "text/plain");
172 QCOMPARE(contentAttachmentList[0]->content().size(), 1);
173 QCOMPARE(contentAttachmentList[0]->encryptions().size(), 1);
174 QCOMPARE(contentAttachmentList[0]->signatures().size(), 1);
175 QCOMPARE(contentAttachmentList[1]->availableContents(), QVector<QByteArray>() << "image/png");
176 QCOMPARE(contentAttachmentList[1]->content().size(), 1);
177 QCOMPARE(contentAttachmentList[1]->encryptions().size(), 0);
178 QCOMPARE(contentAttachmentList[1]->signatures().size(), 0);
179 }
180
181 void testOpenPGPInline()
182 {
183 Parser parser(readMailFromFile("openpgp-inline-charset-encrypted.mbox"));
184 printTree(parser.d->mTree,QString());
185 auto contentPartList = parser.collectContentParts();
186 QCOMPARE(contentPartList.size(), 1);
187 auto contentPart = contentPartList[0];
188 QVERIFY((bool)contentPart);
189 QCOMPARE(contentPart->availableContents(), QVector<QByteArray>() << "plaintext");
190 QCOMPARE(contentPart->encryptions().size(), 0);
191 QCOMPARE(contentPart->signatures().size(), 0);
192 auto contentList = contentPart->content("plaintext");
193 QCOMPARE(contentList.size(), 1);
194 QCOMPARE(contentList[0]->content(), QStringLiteral("asdasd asd asd asdf sadf sdaf sadf äöü").toLocal8Bit());
195 QCOMPARE(contentList[0]->charset(), QStringLiteral("ISO-8859-15").toLocal8Bit());
196 QCOMPARE(contentList[0]->encryptions().size(), 1);
197 QCOMPARE(contentList[0]->signatures().size(), 1);
198 auto contentAttachmentList = parser.collectAttachmentParts();
199 QCOMPARE(contentAttachmentList.size(), 0);
200 }
201
202 void testOpenPPGInlineWithNonEncText()
203 {
204 Parser parser(readMailFromFile("openpgp-inline-encrypted+nonenc.mbox"));
205 printTree(parser.d->mTree,QString());
206 auto contentPartList = parser.collectContentParts();
207 QCOMPARE(contentPartList.size(), 1);
208 auto contentPart = contentPartList[0];
209 QVERIFY((bool)contentPart);
210 QCOMPARE(contentPart->availableContents(), QVector<QByteArray>() << "plaintext");
211 QCOMPARE(contentPart->encryptions().size(), 0);
212 QCOMPARE(contentPart->signatures().size(), 0);
213 auto contentList = contentPart->content("plaintext");
214 QCOMPARE(contentList.size(), 2);
215 QCOMPARE(contentList[0]->content(), QStringLiteral("Not encrypted not signed :(\n\n").toLocal8Bit());
216 QCOMPARE(contentList[0]->charset(), QStringLiteral("us-ascii").toLocal8Bit());
217 QCOMPARE(contentList[0]->encryptions().size(), 0);
218 QCOMPARE(contentList[0]->signatures().size(), 0);
219 QCOMPARE(contentList[1]->content(), QStringLiteral("some random text").toLocal8Bit());
220 QCOMPARE(contentList[1]->charset(), QStringLiteral("us-ascii").toLocal8Bit());
221 QCOMPARE(contentList[1]->encryptions().size(), 1);
222 QCOMPARE(contentList[1]->signatures().size(), 0);
223 auto contentAttachmentList = parser.collectAttachmentParts();
224 QCOMPARE(contentAttachmentList.size(), 0);
225 }
226
227 void testEncryptionBlock()
228 {
229 Parser parser(readMailFromFile("openpgp-encrypted-attachment-and-non-encrypted-attachment.mbox"));
230 auto contentPartList = parser.collectContentParts();
231 auto contentPart = contentPartList[0];
232 auto contentList = contentPart->content("plaintext");
233 QCOMPARE(contentList.size(), 1);
234 QCOMPARE(contentList[0]->encryptions().size(), 1);
235 auto enc = contentList[0]->encryptions()[0];
236 QCOMPARE((int) enc->recipients().size(), 2);
237
238 auto r = enc->recipients()[0];
239 QCOMPARE(r->keyid(),QStringLiteral("14B79E26050467AA"));
240 QCOMPARE(r->name(),QStringLiteral("kdetest"));
241 QCOMPARE(r->email(),QStringLiteral("you@you.com"));
242 QCOMPARE(r->comment(),QStringLiteral(""));
243
244 r = enc->recipients()[1];
245 QCOMPARE(r->keyid(),QStringLiteral("8D9860C58F246DE6"));
246 QCOMPARE(r->name(),QStringLiteral("unittest key"));
247 QCOMPARE(r->email(),QStringLiteral("test@kolab.org"));
248 QCOMPARE(r->comment(),QStringLiteral("no password"));
249 }
250
251 void testSignatureBlock()
252 {
253 Parser parser(readMailFromFile("openpgp-encrypted-attachment-and-non-encrypted-attachment.mbox"));
254 auto contentPartList = parser.collectContentParts();
255 auto contentPart = contentPartList[0];
256 auto contentList = contentPart->content("plaintext");
257 QCOMPARE(contentList.size(), 1);
258 QCOMPARE(contentList[0]->signatures().size(), 1);
259 auto sig = contentList[0]->signatures()[0];
260 QCOMPARE(sig->creationDateTime(), QDateTime(QDate(2015,05,01),QTime(15,12,47)));
261 QCOMPARE(sig->expirationDateTime(), QDateTime());
262 QCOMPARE(sig->neverExpires(), true);
263
264 auto key = sig->key();
265 QCOMPARE(key->keyid(),QStringLiteral("8D9860C58F246DE6"));
266 QCOMPARE(key->name(),QStringLiteral("unittest key"));
267 QCOMPARE(key->email(),QStringLiteral("test@kolab.org"));
268 QCOMPARE(key->comment(),QStringLiteral("no password"));
269 }
270
271 void testRelatedAlternative()
272 {
273 Parser parser(readMailFromFile("cid-links.mbox"));
274 printTree(parser.d->mTree,QString());
275 auto contentPartList = parser.collectContentParts();
276 QCOMPARE(contentPartList.size(), 1);
277 auto contentPart = contentPartList[0];
278 QVERIFY((bool)contentPart);
279 QCOMPARE(contentPart->availableContents(), QVector<QByteArray>() << "html" << "plaintext");
280 QCOMPARE(contentPart->encryptions().size(), 0);
281 QCOMPARE(contentPart->signatures().size(), 0);
282 auto contentList = contentPart->content("plaintext");
283 QCOMPARE(contentList.size(), 1);
284 auto contentAttachmentList = parser.collectAttachmentParts();
285 QCOMPARE(contentAttachmentList.size(), 0);
286 }
287
288 void testAttachmentPart()
289 {
290 Parser parser(readMailFromFile("cid-links.mbox"));
291 const auto relatedImage = parser.d->mTree->subParts().at(1);
292 QCOMPARE(relatedImage->availableContents(), QVector<QByteArray>() << "image/jpeg");
293 auto contentList = relatedImage->content();
294 QCOMPARE(contentList.size(), 1);
295 contentList = relatedImage->content("image/jpeg");
296 QCOMPARE(contentList.size(), 1);
297 }
298
299 void testCidLink()
300 {
301 Parser parser(readMailFromFile("cid-links.mbox"));
302 printTree(parser.d->mTree,QString());
303 QCOMPARE(parser.getPart(QUrl("cid:9359201d15e53f31a68c307b3369b6@info")), parser.d->mTree->subParts().at(1));
304 QVERIFY(!parser.getPart(QUrl("cid:")));
305 QVERIFY(!parser.getPart(QUrl("cid:unknown")));
306 }
307};
308
309QTEST_GUILESS_MAIN(InterfaceTest)
310#include "interfacetest.moc"
diff --git a/framework/src/domain/mime/mimetreeparser/tests/kdepim_add_gpg_crypto_test.cmake b/framework/src/domain/mime/mimetreeparser/tests/kdepim_add_gpg_crypto_test.cmake
new file mode 100644
index 00000000..ea0ab8d2
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/tests/kdepim_add_gpg_crypto_test.cmake
@@ -0,0 +1,61 @@
1# Copyright (c) 2013 Sandro Knauß <mail@sandroknauss.de>
2#
3# Redistribution and use is allowed according to the terms of the BSD license.
4# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
5
6set( MIMETREEPARSERRELPATH framework/src/domain/mimetreeparser)
7set( GNUPGHOME ${CMAKE_BINARY_DIR}/${MIMETREEPARSERRELPATH}/tests/gnupg_home )
8add_definitions( -DGNUPGHOME="${GNUPGHOME}" )
9
10macro (ADD_GPG_CRYPTO_TEST _target _testname)
11 if (UNIX)
12 if (APPLE)
13 set(_library_path_variable "DYLD_LIBRARY_PATH")
14 elseif (CYGWIN)
15 set(_library_path_variable "PATH")
16 else (APPLE)
17 set(_library_path_variable "LD_LIBRARY_PATH")
18 endif (APPLE)
19
20 if (APPLE)
21 # DYLD_LIBRARY_PATH does not work like LD_LIBRARY_PATH
22 # OSX already has the RPATH in libraries and executables, putting runtime directories in
23 # DYLD_LIBRARY_PATH actually breaks things
24 set(_ld_library_path "${LIBRARY_OUTPUT_PATH}/${CMAKE_CFG_INTDIR}/")
25 else (APPLE)
26 set(_ld_library_path "${LIBRARY_OUTPUT_PATH}/${CMAKE_CFG_INTDIR}/:${LIB_INSTALL_DIR}:${QT_LIBRARY_DIR}")
27 endif (APPLE)
28 set(_executable "$<TARGET_FILE:${_target}>")
29
30 # use add_custom_target() to have the sh-wrapper generated during build time instead of cmake time
31 add_custom_command(TARGET ${_target} POST_BUILD
32 COMMAND ${CMAKE_COMMAND}
33 -D_filename=${_executable}.shell -D_library_path_variable=${_library_path_variable}
34 -D_ld_library_path="${_ld_library_path}" -D_executable=$<TARGET_FILE:${_target}>
35 -D_gnupghome="${GNUPGHOME}"
36 -P ${CMAKE_SOURCE_DIR}/${MIMETREEPARSERRELPATH}/tests/kdepim_generate_crypto_test_wrapper.cmake
37 )
38
39 set_property(DIRECTORY APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES "${_executable}.shell" )
40 add_test(NAME ${_testname} COMMAND ${_executable}.shell)
41
42 else (UNIX)
43 # under windows, set the property WRAPPER_SCRIPT just to the name of the executable
44 # maybe later this will change to a generated batch file (for setting the PATH so that the Qt libs are found)
45 set(_ld_library_path "${LIBRARY_OUTPUT_PATH}/${CMAKE_CFG_INTDIR}\;${LIB_INSTALL_DIR}\;${QT_LIBRARY_DIR}")
46 set(_executable "$<TARGET_FILE:${_target}>")
47
48 # use add_custom_target() to have the batch-file-wrapper generated during build time instead of cmake time
49 add_custom_command(TARGET ${_target} POST_BUILD
50 COMMAND ${CMAKE_COMMAND}
51 -D_filename="${_executable}.bat"
52 -D_ld_library_path="${_ld_library_path}" -D_executable="${_executable}"
53 -D_gnupghome="${GNUPGHOME}"
54 -P ${CMAKE_SOURCE_DIR}/${MIMETREEPARSERRELPATH}/tests/kdepim_generate_crypto_test_wrapper.cmake
55 )
56
57 add_test(NAME ${_testname} COMMAND ${_executable}.bat)
58
59 endif (UNIX)
60endmacro (ADD_GPG_CRYPTO_TEST)
61
diff --git a/framework/src/domain/mime/mimetreeparser/tests/kdepim_generate_crypto_test_wrapper.cmake b/framework/src/domain/mime/mimetreeparser/tests/kdepim_generate_crypto_test_wrapper.cmake
new file mode 100644
index 00000000..e1412f37
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/tests/kdepim_generate_crypto_test_wrapper.cmake
@@ -0,0 +1,45 @@
1# Copyright (c) 2006, Alexander Neundorf, <neundorf@kde.org>
2# Copyright (c) 2013, Sandro Knauß <mail@sandroknauss.de>
3#
4# Redistribution and use is allowed according to the terms of the BSD license.
5# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
6
7
8if (UNIX)
9
10file(WRITE "${_filename}"
11"#!/bin/sh
12# created by cmake, don't edit, changes will be lost
13
14# don't mess with a gpg-agent already running on the system
15unset GPG_AGENT_INFO
16
17${_library_path_variable}=${_ld_library_path}\${${_library_path_variable}:+:\$${_library_path_variable}} GNUPGHOME=${_gnupghome} gpg-agent --daemon \"${_executable}\" \"$@\"
18_result=$?
19_pid=`echo GETINFO pid | GNUPGHOME=${_gnupghome} gpg-connect-agent | grep 'D' | cut -d' ' -f2`
20if [ ! -z \"\$_pid\" ]; then
21 echo \"Waiting for gpg-agent to terminate (PID: $_pid)...\"
22 while kill -0 \"\$_pid\"; do
23 sleep 1
24 done
25fi
26exit \$_result
27")
28
29# make it executable
30# since this is only executed on UNIX, it is safe to call chmod
31exec_program(chmod ARGS ug+x \"${_filename}\" OUTPUT_VARIABLE _dummy )
32
33else (UNIX)
34
35file(TO_NATIVE_PATH "${_ld_library_path}" win_path)
36file(TO_NATIVE_PATH "${_gnupghome}" win_gnupghome)
37
38file(WRITE "${_filename}"
39"
40set PATH=${win_path};$ENV{PATH}
41set GNUPGHOME=${win_gnupghome};$ENV{GNUPGHOME}
42gpg-agent --daemon \"${_executable}\" %*
43")
44
45endif (UNIX)
diff --git a/framework/src/domain/mime/mimetreeparser/thoughts.txt b/framework/src/domain/mime/mimetreeparser/thoughts.txt
new file mode 100644
index 00000000..3340347a
--- /dev/null
+++ b/framework/src/domain/mime/mimetreeparser/thoughts.txt
@@ -0,0 +1,148 @@
1Usecases:
2
3# plaintext msg + attachment
4* ContentPart => cp1
5* AttachmentPart => ap1
6
7(cp1) == collect<ContentPart>(select=NoEncapsulatedMessages)
8(ap1) == collect<AttachmentParts>(select=NoEncapsulatedMessages)
9
10(PlainText) == cp1.availableContent()
11
12# html msg + related attachment + normal attachment
13* ContentPart => cp1
14* AttachmentPart(mimetype="*/related", cid="12345678") => ap1
15* AttachmentPart => ap2
16
17(cp1) == collect<ContentPart>(select=NoEncapsulatedMessages)
18(ap1, ap2) == collect<AttachmentParts>(select=NoEncapsulatedMessages)
19(ap2) == collect<AttachmentParts>(select=NoEncapsulatedMessages, filter=filterelated)
20
21ap1 == getPart("cid:12345678")
22
23(Html) == cp1.availableContent()
24
25# alternative msg + attachment
26* ContentPart(html=[Content("HTML"),], plaintext=[Content("Text"),]) => cp1
27* AttachmentPart => ap1
28
29(cp1) == collect<ContentPart>(select=NoEncapsulatedMessages)
30(ap1) == collect<AttachmentParts>(select=NoEncapsulatedMessages)
31
32(Html, PlainText) == cp1.availableContent()
33[Content("HTML"),] == cp1.content(Html)
34[Content("Text"),] == cp1.content(Plaintext)
35
36# alternative msg with GPGInlin
37* ContentPart(
38 plaintext=[Content("Text"), Content("foo", encryption=(enc1))],
39 html=[Content("HTML"),]
40 ) => cp1
41
42(Html, PlainText) == cp1.availableContent()
43
44[Content("HTML"),] == cp1.content(Html)
45[Content("Text"),Content("foo", encryption=(enc1))] == cp1.content(Plaintext)
46
47
48# encrypted msg (not encrypted/error) with unencrypted attachment
49* EncryptionErrorPart => cp1
50* AttachmentPart => ap1
51
52(cp1) == collect<ContentPart>(select=NoEncapsulatedMessages)
53(ap1) == collect<AttachmentParts>(select=NoEncapsulatedMessages)
54
55#encrypted msg (decrypted with attachment) + unencrypted attachment
56* encrytion=(rec1,rec2) => enc1
57 * ContentPart(encrytion = (enc1,)) => cp1
58 * AttachmentPart(encryption = (enc1,)) => ap1
59* AttachmentPart => ap2
60
61(cp1) == collect<ContentPart>(select=NoEncapsulatedMessages)
62(ap1, ap2) == collect<AttachmentParts>(select=NoEncapsulatedMessages)
63
64#INLINE GPG encrypted msg + attachment
65* ContentPart => cp1 with
66 plaintext=[Content, Content(encrytion = (enc1(rec1,rec2),)), Content(signed = (sig1,)), Content]
67* AttachmentPart => ap1
68
69(cp1) == collect<ContentPart>(select=NoEncapsulatedMessages)
70(ap1) == collect<AttachmentParts>(select=NoEncapsulatedMessages)
71
72[Content, Content(encrytion = (enc1(rec1,rec2),)), Content(signed = (sig1,)), Content] == cp1.content(Plaintext)
73
74#forwared encrypted msg + attachments
75* ContentPart => cp1
76* EncapsulatedPart => ep1
77 * Encrytion=(rec1,rec2) => enc1
78 * Signature => sig1
79 * ContentPart(encrytion = (enc1,), signature = (sig1,)) => cp2
80 * Content(encrytion = (enc1,), signature = (sig1,))
81 * Content(encrytion = (enc1, enc2(rec3,rec4),), signature = (sig1,))
82 * AttachmentPart(encrytion = (enc1,), signature = (sig1,)) => ap1
83* AttachmentPart => ap2
84
85(cp1) = collect<ContentPart>(select=NoEncapsulatedMessages)
86(ap2) = collect<AttachmentParts>(select=NoEncapsulatedMessages)
87
88(cp2) = collect<ContentPart>(ep1, select=NoEncapsulatedMessages)
89(ap1) = collect<AttachmentParts>(ep1, select=NoEncapsulatedMessages)
90
91(cp1, cp2) == collect<ContentPart>()
92(ap1, ap2) == collect<AttachmentParts>()[Content, Content(encrytion = (enc1(rec1,rec2),)), Content(signed = (sig1,)), Content]
93
94
95# plaintext msg + attachment + cert
96* ContentPart => cp1
97* AttachmentPart => ap1
98* CertPart => cep1
99
100(cp1) == collect<ContentPart>(select=NoEncapsulatedMessages)
101(ap1, cep1) == collect<AttachmentPart>(select=NoEncapsulatedMessages)
102(ap1) == collect<AttachmentPart>(select=NoEncapsulatedMessages, filter=filterSubAttachmentParts)
103
104(cep1) == collect<CertPart>(select=NoEncapsulatedMessages)
105
106
107collect function:
108
109bool noEncapsulatedMessages(Part part)
110{
111 if (is<EncapsulatedPart>(part)) {
112 return false;
113 }
114 return true;
115}
116
117bool filterRelated(T part)
118{
119 if (part.mimetype == related && !part.cid.isEmpty()) {
120 return false; //filter out related parts
121 }
122 return true;
123}
124
125bool filterSubAttachmentParts(AttachmentPart part)
126{
127 if (isSubPart<AttachmentPart>(part)) {
128 return false; // filter out CertPart f.ex.
129 }
130 return true;
131}
132
133List<T> collect<T>(Part start, std::function<bool(const Part &)> select, std::function<bool(const std::shared_ptr<T> &)> filter) {
134 List<T> col;
135 if (!select(start)) {
136 return col;
137 }
138
139 if(isOrSubTypeIs<T>(start) && filter(start.staticCast<T>)){
140 col.append(p);
141 }
142 foreach(childs as child) {
143 if (select(child)) {
144 col.expand(collect(child,select,filter);
145 }
146 }
147 return col;
148} \ No newline at end of file
diff --git a/framework/src/domain/mime/objecttreesource.cpp b/framework/src/domain/mime/objecttreesource.cpp
new file mode 100644
index 00000000..186fdf80
--- /dev/null
+++ b/framework/src/domain/mime/objecttreesource.cpp
@@ -0,0 +1,152 @@
1/*
2 Copyright (C) 2009 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net
3 Copyright (c) 2009 Andras Mantia <andras@kdab.net>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18*/
19
20#include "objecttreesource.h"
21
22#include <otp/attachmentstrategy.h>
23#include <otp/bodypartformatterbasefactory.h>
24#include <otp/messagepart.h>
25#include <otp/messagepartrenderer.h>
26
27class ObjectSourcePrivate
28{
29public:
30 ObjectSourcePrivate()
31 : mWriter(0)
32 , mAllowDecryption(true)
33 , mHtmlLoadExternal(true)
34 , mPreferredMode(MimeTreeParser::Util::Html)
35 {
36
37 }
38 MimeTreeParser::HtmlWriter *mWriter;
39 MimeTreeParser::BodyPartFormatterBaseFactory mBodyPartFormatterBaseFactory;
40 bool mAllowDecryption;
41 bool mHtmlLoadExternal;
42 MimeTreeParser::Util::HtmlMode mPreferredMode;
43};
44
45ObjectTreeSource::ObjectTreeSource(MimeTreeParser::HtmlWriter *writer)
46 : MimeTreeParser::Interface::ObjectTreeSource()
47 , d(new ObjectSourcePrivate)
48 {
49 d->mWriter = writer;
50 }
51
52ObjectTreeSource::~ObjectTreeSource()
53{
54 delete d;
55}
56
57void ObjectTreeSource::setAllowDecryption(bool allowDecryption)
58{
59 d->mAllowDecryption = allowDecryption;
60}
61
62MimeTreeParser::HtmlWriter *ObjectTreeSource::htmlWriter()
63{
64 return d->mWriter;
65}
66
67bool ObjectTreeSource::htmlLoadExternal() const
68{
69 return d->mHtmlLoadExternal;
70}
71
72void ObjectTreeSource::setHtmlLoadExternal(bool loadExternal)
73{
74 d->mHtmlLoadExternal = loadExternal;
75}
76
77bool ObjectTreeSource::decryptMessage() const
78{
79 return d->mAllowDecryption;
80}
81
82bool ObjectTreeSource::showSignatureDetails() const
83{
84 return true;
85}
86
87int ObjectTreeSource::levelQuote() const
88{
89 return 1;
90}
91
92const QTextCodec *ObjectTreeSource::overrideCodec()
93{
94 return Q_NULLPTR;
95}
96
97QString ObjectTreeSource::createMessageHeader(KMime::Message *message)
98{
99 return QString();
100}
101
102const MimeTreeParser::AttachmentStrategy *ObjectTreeSource::attachmentStrategy()
103{
104 return MimeTreeParser::AttachmentStrategy::smart();
105}
106
107QObject *ObjectTreeSource::sourceObject()
108{
109 return Q_NULLPTR;
110}
111
112void ObjectTreeSource::setHtmlMode(MimeTreeParser::Util::HtmlMode mode, const QList<MimeTreeParser::Util::HtmlMode> &availableModes)
113{
114 Q_UNUSED(mode);
115 Q_UNUSED(availableModes);
116}
117
118MimeTreeParser::Util::HtmlMode ObjectTreeSource::preferredMode() const
119{
120 return d->mPreferredMode;
121}
122
123bool ObjectTreeSource::autoImportKeys() const
124{
125 return false;
126}
127
128bool ObjectTreeSource::showEmoticons() const
129{
130 return false;
131}
132
133bool ObjectTreeSource::showExpandQuotesMark() const
134{
135 return false;
136}
137
138bool ObjectTreeSource::isPrinting() const
139{
140 return false;
141}
142
143const MimeTreeParser::BodyPartFormatterBaseFactory *ObjectTreeSource::bodyPartFormatterFactory()
144{
145 return &(d->mBodyPartFormatterBaseFactory);
146}
147
148MimeTreeParser::Interface::MessagePartRenderer::Ptr ObjectTreeSource::messagePartTheme(MimeTreeParser::Interface::MessagePart::Ptr msgPart)
149{
150 Q_UNUSED(msgPart);
151 return MimeTreeParser::Interface::MessagePartRenderer::Ptr();
152}
diff --git a/framework/src/domain/mime/objecttreesource.h b/framework/src/domain/mime/objecttreesource.h
new file mode 100644
index 00000000..2167e06f
--- /dev/null
+++ b/framework/src/domain/mime/objecttreesource.h
@@ -0,0 +1,57 @@
1/*
2 Copyright (C) 2009 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net
3 Copyright (c) 2009 Andras Mantia <andras@kdab.net>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18*/
19
20#ifndef MAILVIEWER_OBJECTTREEEMPTYSOURCE_H
21#define MAILVIEWER_OBJECTTREEEMPTYSOURCE_H
22
23#include <otp/objecttreesource.h>
24
25class QString;
26
27class ObjectSourcePrivate;
28class ObjectTreeSource : public MimeTreeParser::Interface::ObjectTreeSource
29{
30public:
31 ObjectTreeSource(MimeTreeParser::HtmlWriter *writer);
32 virtual ~ObjectTreeSource();
33 void setHtmlLoadExternal(bool loadExternal);
34 bool decryptMessage() const Q_DECL_OVERRIDE;
35 bool htmlLoadExternal() const Q_DECL_OVERRIDE;
36 bool showSignatureDetails() const Q_DECL_OVERRIDE;
37 void setHtmlMode(MimeTreeParser::Util::HtmlMode mode, const QList<MimeTreeParser::Util::HtmlMode> &availableModes) Q_DECL_OVERRIDE;
38 MimeTreeParser::Util::HtmlMode preferredMode() const Q_DECL_OVERRIDE;
39 void setAllowDecryption(bool allowDecryption);
40 int levelQuote() const Q_DECL_OVERRIDE;
41 const QTextCodec *overrideCodec() Q_DECL_OVERRIDE;
42 QString createMessageHeader(KMime::Message *message) Q_DECL_OVERRIDE;
43 const MimeTreeParser::AttachmentStrategy *attachmentStrategy() Q_DECL_OVERRIDE;
44 MimeTreeParser::HtmlWriter *htmlWriter() Q_DECL_OVERRIDE;
45 QObject *sourceObject() Q_DECL_OVERRIDE;
46 bool autoImportKeys() const Q_DECL_OVERRIDE;
47 bool showEmoticons() const Q_DECL_OVERRIDE;
48 bool showExpandQuotesMark() const Q_DECL_OVERRIDE;
49 bool isPrinting() const Q_DECL_OVERRIDE;
50 const MimeTreeParser::BodyPartFormatterBaseFactory *bodyPartFormatterFactory() Q_DECL_OVERRIDE;
51 MimeTreeParser::Interface::MessagePartRendererPtr messagePartTheme(MimeTreeParser::Interface::MessagePartPtr msgPart) Q_DECL_OVERRIDE;
52private:
53 ObjectSourcePrivate *const d;
54};
55
56#endif
57
diff --git a/framework/src/domain/mime/stringhtmlwriter.cpp b/framework/src/domain/mime/stringhtmlwriter.cpp
new file mode 100644
index 00000000..88034492
--- /dev/null
+++ b/framework/src/domain/mime/stringhtmlwriter.cpp
@@ -0,0 +1,150 @@
1/* -*- c++ -*-
2 filehtmlwriter.cpp
3
4 This file is part of KMail, the KDE mail client.
5 Copyright (c) 2003 Marc Mutz <mutz@kde.org>
6
7 KMail is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License, version 2, as
9 published by the Free Software Foundation.
10
11 KMail is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
20 In addition, as a special exception, the copyright holders give
21 permission to link the code of this program with any edition of
22 the Qt library by Trolltech AS, Norway (or with modified versions
23 of Qt that use the same license as Qt), and distribute linked
24 combinations including the two. You must obey the GNU General
25 Public License in all respects for all of the code used other than
26 Qt. If you modify this file, you may extend this exception to
27 your version of the file, but you are not obligated to do so. If
28 you do not wish to do so, delete this exception statement from
29 your version.
30*/
31
32#include "stringhtmlwriter.h"
33
34#include <QDebug>
35#include <QTextStream>
36#include <QUrl>
37
38StringHtmlWriter::StringHtmlWriter()
39 : MimeTreeParser::HtmlWriter()
40 , mState(Ended)
41{
42}
43
44StringHtmlWriter::~StringHtmlWriter()
45{
46}
47
48void StringHtmlWriter::begin(const QString &css)
49{
50 if (mState != Ended) {
51 qWarning() << "begin() called on non-ended session!";
52 reset();
53 }
54
55 mState = Begun;
56 mExtraHead.clear();
57 mHtml.clear();
58
59 if (!css.isEmpty()) {
60 write(QLatin1String("<!-- CSS Definitions \n") + css + QLatin1String("-->\n"));
61 }
62}
63
64void StringHtmlWriter::end()
65{
66 if (mState != Begun) {
67 qWarning() << "Called on non-begun or queued session!";
68 }
69
70 if (!mExtraHead.isEmpty()) {
71 insertExtraHead();
72 mExtraHead.clear();
73 }
74 resolveCidUrls();
75 mState = Ended;
76}
77
78void StringHtmlWriter::reset()
79{
80 if (mState != Ended) {
81 mHtml.clear();
82 mExtraHead.clear();
83 mState = Begun; // don't run into end()'s warning
84 end();
85 mState = Ended;
86 }
87}
88
89void StringHtmlWriter::write(const QString &str)
90{
91 if (mState != Begun) {
92 qWarning() << "Called in Ended or Queued state!";
93 }
94 mHtml.append(str);
95}
96
97void StringHtmlWriter::queue(const QString &str)
98{
99 write(str);
100}
101
102void StringHtmlWriter::flush()
103{
104 mState = Begun; // don't run into end()'s warning
105 end();
106}
107
108void StringHtmlWriter::embedPart(const QByteArray &contentId, const QString &url)
109{
110 write("<!-- embedPart(contentID=" + contentId + ", url=" + url + ") -->\n");
111 mEmbeddedPartMap.insert(contentId, url);
112}
113
114void StringHtmlWriter::resolveCidUrls()
115{
116 for (const auto &cid : mEmbeddedPartMap.keys()) {
117 mHtml.replace(QString("src=\"cid:%1\"").arg(QString(cid)), QString("src=\"%1\"").arg(mEmbeddedPartMap.value(cid).toString()));
118 }
119}
120
121void StringHtmlWriter::extraHead(const QString &extraHead)
122{
123 if (mState != Ended) {
124 qWarning() << "Called on non-started session!";
125 }
126 mExtraHead.append(extraHead);
127}
128
129
130void StringHtmlWriter::insertExtraHead()
131{
132 const QString headTag(QStringLiteral("<head>"));
133 const int index = mHtml.indexOf(headTag);
134 if (index != -1) {
135 mHtml.insert(index + headTag.length(), mExtraHead);
136 }
137}
138
139QMap<QByteArray, QUrl> StringHtmlWriter::embeddedParts() const
140{
141 return mEmbeddedPartMap;
142}
143
144QString StringHtmlWriter::html() const
145{
146 if (mState != Ended) {
147 qWarning() << "Called on non-ended session!";
148 }
149 return mHtml;
150}
diff --git a/framework/src/domain/mime/stringhtmlwriter.h b/framework/src/domain/mime/stringhtmlwriter.h
new file mode 100644
index 00000000..20f6763e
--- /dev/null
+++ b/framework/src/domain/mime/stringhtmlwriter.h
@@ -0,0 +1,71 @@
1/* -*- c++ -*-
2
3 Copyright (c) 2016 Sandro Knauß <sknauss@kde.org>
4
5 Kube is free software; you can redistribute it and/or modify it
6 under the terms of the GNU General Public License, version 2, as
7 published by the Free Software Foundation.
8
9 Kube is distributed in the hope that it will be useful, but
10 WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
18 In addition, as a special exception, the copyright holders give
19 permission to link the code of this program with any edition of
20 the Qt library by Trolltech AS, Norway (or with modified versions
21 of Qt that use the same license as Qt), and distribute linked
22 combinations including the two. You must obey the GNU General
23 Public License in all respects for all of the code used other than
24 Qt. If you modify this file, you may extend this exception to
25 your version of the file, but you are not obligated to do so. If
26 you do not wish to do so, delete this exception statement from
27 your version.
28*/
29
30#ifndef __KUBE_FRAMEWORK_MAIL_STRINGHTMLWRITER_H__
31#define __KUBE_FRAMEWORK_MAIL_STRINGHTMLWRITER_H__
32
33#include <otp/htmlwriter.h>
34
35#include <QFile>
36#include <QTextStream>
37
38class QString;
39
40class StringHtmlWriter : public MimeTreeParser::HtmlWriter
41{
42public:
43 explicit StringHtmlWriter();
44 virtual ~StringHtmlWriter();
45
46 void begin(const QString &cssDefs) Q_DECL_OVERRIDE;
47 void end() Q_DECL_OVERRIDE;
48 void reset() Q_DECL_OVERRIDE;
49 void write(const QString &str) Q_DECL_OVERRIDE;
50 void queue(const QString &str) Q_DECL_OVERRIDE;
51 void flush() Q_DECL_OVERRIDE;
52 void embedPart(const QByteArray &contentId, const QString &url) Q_DECL_OVERRIDE;
53 void extraHead(const QString &str) Q_DECL_OVERRIDE;
54
55 QString html() const;
56 QMap<QByteArray, QUrl> embeddedParts() const;
57private:
58 void insertExtraHead();
59 void resolveCidUrls();
60
61 QString mHtml;
62 QString mExtraHead;
63 enum State {
64 Begun,
65 Queued,
66 Ended
67 } mState;
68 QMap<QByteArray, QUrl> mEmbeddedPartMap;
69};
70
71#endif // __MESSAGEVIEWER_FILEHTMLWRITER_H__