extract-msg 0.48.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. extract_msg/__init__.py +71 -0
  2. extract_msg/__main__.py +115 -0
  3. extract_msg/_rtf/__init__.py +21 -0
  4. extract_msg/_rtf/create_doc.py +29 -0
  5. extract_msg/_rtf/inject_rtf.py +174 -0
  6. extract_msg/_rtf/token.py +35 -0
  7. extract_msg/_rtf/tokenize_rtf.py +253 -0
  8. extract_msg/attachments/__init__.py +144 -0
  9. extract_msg/attachments/attachment.py +188 -0
  10. extract_msg/attachments/attachment_base.py +730 -0
  11. extract_msg/attachments/broken_att.py +42 -0
  12. extract_msg/attachments/custom_att.py +173 -0
  13. extract_msg/attachments/custom_att_handler/__init__.py +80 -0
  14. extract_msg/attachments/custom_att_handler/custom_handler.py +134 -0
  15. extract_msg/attachments/custom_att_handler/lnk_obj_att.py +109 -0
  16. extract_msg/attachments/custom_att_handler/outlook_image_dib.py +141 -0
  17. extract_msg/attachments/emb_msg_att.py +134 -0
  18. extract_msg/attachments/signed_att.py +234 -0
  19. extract_msg/attachments/unsupported_att.py +40 -0
  20. extract_msg/attachments/web_att.py +74 -0
  21. extract_msg/constants/__init__.py +294 -0
  22. extract_msg/constants/ps.py +46 -0
  23. extract_msg/constants/re.py +42 -0
  24. extract_msg/constants/st.py +110 -0
  25. extract_msg/data/logging-config/logging-nt.json +64 -0
  26. extract_msg/data/logging-config/logging-posix.json +64 -0
  27. extract_msg/encoding/__init__.py +277 -0
  28. extract_msg/encoding/_dt/__init__.py +4 -0
  29. extract_msg/encoding/_dt/_mac_ce.py +8 -0
  30. extract_msg/encoding/_dt/_mac_cyrillic.py +8 -0
  31. extract_msg/encoding/_dt/_mac_greek.py +8 -0
  32. extract_msg/encoding/_dt/_mac_iceland.py +8 -0
  33. extract_msg/encoding/_dt/_mac_turkish.py +8 -0
  34. extract_msg/encoding/_dt/_win874_dec.py +8 -0
  35. extract_msg/encoding/_dt/_win950_dec.py +6 -0
  36. extract_msg/encoding/utils.py +329 -0
  37. extract_msg/enums.py +2067 -0
  38. extract_msg/exceptions.py +170 -0
  39. extract_msg/msg_classes/__init__.py +49 -0
  40. extract_msg/msg_classes/appointment.py +195 -0
  41. extract_msg/msg_classes/calendar.py +92 -0
  42. extract_msg/msg_classes/calendar_base.py +496 -0
  43. extract_msg/msg_classes/contact.py +1335 -0
  44. extract_msg/msg_classes/journal.py +131 -0
  45. extract_msg/msg_classes/meeting_cancellation.py +108 -0
  46. extract_msg/msg_classes/meeting_exception.py +55 -0
  47. extract_msg/msg_classes/meeting_forward.py +115 -0
  48. extract_msg/msg_classes/meeting_related.py +65 -0
  49. extract_msg/msg_classes/meeting_request.py +189 -0
  50. extract_msg/msg_classes/meeting_response.py +74 -0
  51. extract_msg/msg_classes/message.py +12 -0
  52. extract_msg/msg_classes/message_base.py +1382 -0
  53. extract_msg/msg_classes/message_signed.py +17 -0
  54. extract_msg/msg_classes/message_signed_base.py +168 -0
  55. extract_msg/msg_classes/msg.py +1172 -0
  56. extract_msg/msg_classes/post.py +53 -0
  57. extract_msg/msg_classes/sticky_note.py +67 -0
  58. extract_msg/msg_classes/task.py +363 -0
  59. extract_msg/msg_classes/task_request.py +86 -0
  60. extract_msg/null_date.py +34 -0
  61. extract_msg/ole_writer.py +1086 -0
  62. extract_msg/open_msg.py +204 -0
  63. extract_msg/properties/__init__.py +23 -0
  64. extract_msg/properties/named.py +481 -0
  65. extract_msg/properties/prop.py +506 -0
  66. extract_msg/properties/properties_store.py +476 -0
  67. extract_msg/py.typed +0 -0
  68. extract_msg/recipient.py +362 -0
  69. extract_msg/structures/__init__.py +34 -0
  70. extract_msg/structures/_helpers.py +345 -0
  71. extract_msg/structures/business_card.py +422 -0
  72. extract_msg/structures/cfoas.py +108 -0
  73. extract_msg/structures/contact_link_entry.py +51 -0
  74. extract_msg/structures/dev_mode_a.py +621 -0
  75. extract_msg/structures/dv_target_device.py +168 -0
  76. extract_msg/structures/entry_id.py +697 -0
  77. extract_msg/structures/misc_id.py +222 -0
  78. extract_msg/structures/mon_stream.py +55 -0
  79. extract_msg/structures/odt.py +74 -0
  80. extract_msg/structures/ole_pres.py +302 -0
  81. extract_msg/structures/ole_stream_struct.py +117 -0
  82. extract_msg/structures/recurrence_pattern.py +199 -0
  83. extract_msg/structures/report_tag.py +127 -0
  84. extract_msg/structures/system_time.py +158 -0
  85. extract_msg/structures/time_zone_definition.py +111 -0
  86. extract_msg/structures/time_zone_struct.py +102 -0
  87. extract_msg/structures/toc_entry.py +137 -0
  88. extract_msg/structures/tz_rule.py +148 -0
  89. extract_msg/utils.py +1270 -0
  90. extract_msg-0.48.2.dist-info/LICENSE.txt +674 -0
  91. extract_msg-0.48.2.dist-info/METADATA +321 -0
  92. extract_msg-0.48.2.dist-info/RECORD +95 -0
  93. extract_msg-0.48.2.dist-info/WHEEL +5 -0
  94. extract_msg-0.48.2.dist-info/entry_points.txt +2 -0
  95. extract_msg-0.48.2.dist-info/top_level.txt +1 -0
@@ -0,0 +1,17 @@
1
+ __all__ = [
2
+ 'MessageSigned',
3
+ ]
4
+
5
+
6
+ from typing import TypeVar
7
+
8
+ from .message_signed_base import MessageSignedBase
9
+
10
+
11
+ _T = TypeVar('_T')
12
+
13
+
14
+ class MessageSigned(MessageSignedBase[_T]):
15
+ """
16
+ Parser for Signed Microsoft Outlook message files.
17
+ """
@@ -0,0 +1,168 @@
1
+ __all__ = [
2
+ 'MessageSignedBase',
3
+ ]
4
+
5
+
6
+ import functools
7
+ import html
8
+ import logging
9
+ import re
10
+
11
+ from typing import Generic, List, Optional, Type, TypeVar
12
+
13
+ from ..attachments import AttachmentBase, SignedAttachment
14
+ from ..enums import AttachmentType, DeencapType, ErrorBehavior
15
+ from ..exceptions import StandardViolationError
16
+ from .message_base import MessageBase
17
+ from ..utils import inputToBytes, inputToString, unwrapMultipart
18
+
19
+
20
+ logger = logging.getLogger(__name__)
21
+ logger.addHandler(logging.NullHandler())
22
+
23
+ _T = TypeVar('_T')
24
+
25
+
26
+ class MessageSignedBase(MessageBase, Generic[_T]):
27
+ """
28
+ Base class for Message-like MSG files that are signed.
29
+ """
30
+
31
+ def __init__(self, path, signedAttachmentClass: Type[_T] = SignedAttachment, **kwargs):
32
+ """
33
+ Supports all of the options from :meth:`MessageBase.__init__` with some
34
+ additional ones.
35
+
36
+ :param signedAttachmentClass: optional, the class the object will use
37
+ for signed attachments.
38
+ """
39
+ self.__sAttCls = signedAttachmentClass
40
+ super().__init__(path, **kwargs)
41
+
42
+ @functools.cached_property
43
+ def attachments(self) -> List[_T]:
44
+ """
45
+ A list of all attachments.
46
+
47
+ :raises StandardViolationError: The standard for signed messages was
48
+ blatantly violated.
49
+ """
50
+ # Set these to None to make error handling easier.
51
+ self._signedBody = None
52
+ self._signedHtmlBody = None
53
+
54
+ atts = self.rawAttachments
55
+
56
+ if len(atts) != 1:
57
+ if ErrorBehavior.STANDARDS_VIOLATION in self.errorBehavior:
58
+ if len(atts) == 0:
59
+ logger.error('Standards Violation: Signed message has no attachments.')
60
+ return []
61
+ # If there is at least one attachment, log an error about it
62
+ # and then try to use the first one.
63
+ logger.error('Standards Violation: Signed message has more than one attachment. Attempting to use first one.')
64
+ else:
65
+ raise StandardViolationError('Signed messages without exactly 1 (regular) attachment constitute a violation of the standard.')
66
+
67
+ # If we are here, validate that the found attachment is acceptable.
68
+ if atts[0].type is not AttachmentType.DATA:
69
+ # This is unacceptable and the attachment cannot be used.
70
+ if ErrorBehavior.STANDARDS_VIOLATION in self.errorBehavior:
71
+ logger.error('Standards Violation: Attachment on signed message is unacceptable (not binary).')
72
+ return []
73
+ raise StandardViolationError('Signed messages *must* have a binary attachment. Signed attachments cannot be parsed otherwise.')
74
+
75
+ # Check the mimetype *directly*. Detection of mimetype through data is
76
+ # unacceptable for this check.
77
+ #temp = atts[0].getStringStream('__substg1.0_370E')
78
+ #if temp == 'multipart/signed':
79
+ ## Do nothing, but this is how we detect that it is clear signed.
80
+ #pass
81
+ #else:
82
+ ## Do nothing, but this is how we detect that it is *not* clear
83
+ # signed.
84
+ #pass
85
+
86
+ # We need to unwrap the multipart stream.
87
+ unwrapped = unwrapMultipart(atts[0].data)
88
+
89
+ # Now store everything where it needs to be and make the
90
+ # attachments.
91
+ self._signedBody = unwrapped['plain_body']
92
+ self._signedHtmlBody = inputToBytes(unwrapped['html_body'], 'utf-8')
93
+
94
+ return [self.__sAttCls(self, **att) for att in unwrapped['attachments']]
95
+
96
+ @functools.cached_property
97
+ def body(self) -> Optional[str]:
98
+ """
99
+ The message body, if it exists.
100
+ """
101
+ if (body := self.getStringStream('__substg1.0_1000')) is not None:
102
+ pass
103
+ elif self.signedBody:
104
+ body = self.signedBody
105
+ elif self.rtfBody:
106
+ # If the body doesn't exist, see if we can get it from the RTF
107
+ # body.
108
+ body = self.deencapsulateBody(self.rtfBody, DeencapType.PLAIN)
109
+
110
+ if body:
111
+ body = inputToString(body, 'utf-8')
112
+ if re.search('\n', body):
113
+ if re.search('\r\n', body):
114
+ self._crlf = '\r\n'
115
+
116
+ return body
117
+
118
+ @functools.cached_property
119
+ def htmlBody(self) -> Optional[bytes]:
120
+ """
121
+ The html body, if it exists.
122
+ """
123
+ if (htmlBody := self.getStream('__substg1.0_10130102')) is not None:
124
+ pass
125
+ elif self.signedHtmlBody:
126
+ htmlBody = self.signedHtmlBody
127
+ elif self.rtfBody:
128
+ logger.info('HTML body was not found, attempting to generate from RTF.')
129
+ htmlBody = self.deencapsulateBody(self.rtfBody, DeencapType.HTML)
130
+ elif self.body:
131
+ # Convert the plain text body to html.
132
+ logger.info('HTML body was not found, attempting to generate from plain text body.')
133
+ correctedBody = html.escpae(self.body).replace('\r', '').replace('\n', '<br />')
134
+ htmlBody = f'<html><body>{correctedBody}</body></head>'.encode('utf-8')
135
+ else:
136
+ logger.info('HTML body could not be found nor generated.')
137
+
138
+ return htmlBody
139
+
140
+ @functools.cached_property
141
+ def rawAttachments(self) -> List[AttachmentBase]:
142
+ """
143
+ A property to allow access to the non-signed attachments.
144
+ """
145
+ return super().attachments
146
+
147
+ @property
148
+ def signedAttachmentClass(self) -> Type[_T]:
149
+ """
150
+ The attachment class used for signed attachments.
151
+ """
152
+ return self.__sAttCls
153
+
154
+ @functools.cached_property
155
+ def signedBody(self) -> Optional[str]:
156
+ """
157
+ The body from the signed message if it exists.
158
+ """
159
+ self.attachments
160
+ return self._signedBody
161
+
162
+ @functools.cached_property
163
+ def signedHtmlBody(self) -> Optional[bytes]:
164
+ """
165
+ The HTML body from the signed message if it exists.
166
+ """
167
+ self.attachments
168
+ return self._signedHtmlBody