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,345 @@
1
+ """
2
+ Module for helper classes to different data structures.
3
+ """
4
+
5
+ __all__ = [
6
+ 'BytesReader',
7
+ ]
8
+
9
+
10
+ import io
11
+ import struct
12
+
13
+ from typing import Any, Optional, Tuple, Type, TypeVar, Union
14
+
15
+ from .. import constants
16
+
17
+ _T = TypeVar('_T')
18
+
19
+
20
+ class BytesReader(io.BytesIO):
21
+ """
22
+ Extension of io.BytesIO that allows you to read specific data types from the
23
+ stream.
24
+ """
25
+
26
+ def __init__(self, *args, littleEndian: bool = True, **kwargs):
27
+ super().__init__(*args, **kwargs)
28
+ self.__le = bool(littleEndian)
29
+ if self.__le:
30
+ self.__int8_t = constants.st.ST_LE_I8
31
+ self.__int16_t = constants.st.ST_LE_I16
32
+ self.__int32_t = constants.st.ST_LE_I32
33
+ self.__int64_t = constants.st.ST_LE_I64
34
+ self.__uint8_t = constants.st.ST_LE_UI8
35
+ self.__uint16_t = constants.st.ST_LE_UI16
36
+ self.__uint32_t = constants.st.ST_LE_UI32
37
+ self.__uint64_t = constants.st.ST_LE_UI64
38
+ self.__float_t = constants.st.ST_LE_F32
39
+ self.__double_t = constants.st.ST_LE_F64
40
+ else:
41
+ self.__int8_t = constants.st.ST_BE_I8
42
+ self.__int16_t = constants.st.ST_BE_I16
43
+ self.__int32_t = constants.st.ST_BE_I32
44
+ self.__int64_t = constants.st.ST_BE_I64
45
+ self.__uint8_t = constants.st.ST_BE_UI8
46
+ self.__uint16_t = constants.st.ST_BE_UI16
47
+ self.__uint32_t = constants.st.ST_BE_UI32
48
+ self.__uint64_t = constants.st.ST_BE_UI64
49
+ self.__float_t = constants.st.ST_BE_F32
50
+ self.__double_t = constants.st.ST_BE_F64
51
+
52
+ def _readDecodedString(self, encoding: str, width: int = 1) -> str:
53
+ """
54
+ Reads a null terminated string with the specified character width
55
+ decoded using the specified encoding. If it cannot be read or cannot be
56
+ decoded then the position of the read pointer will not be changed.
57
+ """
58
+ position = self.tell()
59
+ try:
60
+ return self.readByteString(width).decode(encoding)
61
+ except Exception:
62
+ while self.tell() != position:
63
+ self.seek(position)
64
+ raise
65
+
66
+ def assertNull(self, length: int, errorMsg: Optional[str] = None) -> bytes:
67
+ """
68
+ Reads the number of bytes specified and ensures they are all null.
69
+
70
+ Ensures the reader returns back to the spot before attempting to read if
71
+ there are not enough bytes to read.
72
+
73
+ :param length: The amount of bytes to read.
74
+ :param errorMsg: Optional, the error message to use if the bytes are not
75
+ all null.
76
+
77
+ :returns: The bytes read, if you need them.
78
+
79
+ :raise IOError: Not enough bytes left to read.
80
+ :raises ValueError: Assertion failed.
81
+ """
82
+ # Quick return for reading 0 bytes.
83
+ if length == 0:
84
+ return b''
85
+
86
+ valueRead = self.tryReadBytes(length)
87
+ if valueRead:
88
+ if sum(valueRead) != 0:
89
+ errorMsg = errorMsg or 'Bytes read were not all null.'
90
+ raise ValueError(errorMsg)
91
+ else:
92
+ raise IOError('Not enough bytes left in buffer.')
93
+
94
+ return valueRead
95
+
96
+ def assertRead(self, value: bytes, errorMsg: Optional[str] = None) -> bytes:
97
+ """
98
+ Reads the number of bytes and compares them to the value provided. If it
99
+ does not match, throws a value error.
100
+
101
+ Ensures the reader returns back to the spot before attempting to read if
102
+ there are not enough bytes to read.
103
+
104
+ :param value: Value to compare read bytes to.
105
+ :param errorMsg: Optional, an error message to emit on mismatch. Does
106
+ not apply to the buffer being too small. Allows for a format string
107
+ with the keyword values "expected" and "actual", representing the
108
+ value given to the function and the actual value read, respectively.
109
+
110
+ :returns: The bytes read, if you need them.
111
+
112
+ :raises TypeError: The value given was not bytes.
113
+ :raises ValueError: Assertion failed.
114
+ """
115
+ # Quick return for a value being empty.
116
+ if len(value) == 0:
117
+ return b''
118
+
119
+ if not isinstance(value, bytes): # pyright: ignore
120
+ raise TypeError(':param value: was not bytes.')
121
+
122
+ valueRead = self.tryReadBytes(len(value))
123
+ if valueRead:
124
+ if valueRead != value:
125
+ errorMsg = errorMsg or 'Value did not match (expected {expected}, got {actual}).'
126
+ raise ValueError(errorMsg.format(expected = value, actual = valueRead))
127
+ else:
128
+ raise IOError('Not enough bytes left in buffer.')
129
+
130
+ return valueRead
131
+
132
+ def readAnsiString(self) -> str:
133
+ """
134
+ Reads a null-terminated string in ANSI format.
135
+ """
136
+ return self._readDecodedString('ansi')
137
+
138
+ def readAsciiString(self) -> str:
139
+ """
140
+ Reads a null-terminated string in ASCII format.
141
+ """
142
+ return self._readDecodedString('ascii')
143
+
144
+ def readByte(self) -> int:
145
+ """
146
+ Reads a signed byte from the stream.
147
+ """
148
+ value = self.tryReadBytes(1)
149
+ if value:
150
+ return self.__int8_t.unpack(value)[0]
151
+ else:
152
+ raise IOError('Not enough bytes left in buffer.')
153
+
154
+ def readByteString(self, width: int = 1) -> bytes:
155
+ """
156
+ Reads a string of bytes until it finds the null character, returning
157
+ everything before that and consuming the null. Unlike other string
158
+ functions, this will not decode the data into a string.
159
+
160
+ :param width: tells how big a character is (in bytes), so this function
161
+ can be used for strings whose characters are multiple bytes.
162
+ """
163
+ if width < 1:
164
+ raise ValueError('Character width must be at least 1.')
165
+
166
+ position = self.tell()
167
+ string = b''
168
+ null = b'\x00' * width
169
+
170
+ while True:
171
+ nextChar = self.read(width)
172
+ if nextChar == b'':
173
+ # We reached the end of the buffer without finding the null. We
174
+ # need to seek back to where we started and raise an exception.
175
+ while self.tell() != position:
176
+ self.seek(position)
177
+ raise IOError('Could not find null character.')
178
+ elif nextChar == null:
179
+ # If we find the null character, return what we have read.
180
+ return string
181
+ else:
182
+ # Otherwise add the character to our string.
183
+ string += nextChar
184
+
185
+ def readClass(self, _class: Type[_T]) -> _T:
186
+ """
187
+ Takes anything with a __SIZE__ property and a call function that takes
188
+ a single bytes argument and returns the result of that function.
189
+
190
+ Generally, this is intended to take a fixed-size class and return an
191
+ instance of the class created with that amount of bytes. However, there
192
+ is little reason to truly limit it to only that.
193
+ """
194
+ if not hasattr(_class, '__SIZE__'):
195
+ raise TypeError('Argument to readClass MUST have a __SIZE__ attribute.')
196
+ value = self.tryReadBytes(_class.__SIZE__)
197
+ if value:
198
+ return _class(value)
199
+ else:
200
+ raise IOError('Not enough bytes left in buffer.')
201
+
202
+ def readDouble(self) -> float:
203
+ """
204
+ Reads a double from the stream.
205
+ """
206
+ value = self.tryReadBytes(8)
207
+ if value:
208
+ return self.__double_t.unpack(value)[0]
209
+ else:
210
+ raise IOError('Not enough bytes left in buffer.')
211
+
212
+ def readFloat(self) -> float:
213
+ """
214
+ Reads a float from the stream.
215
+ """
216
+ value = self.tryReadBytes(4)
217
+ if value:
218
+ return self.__float_t.unpack(value)[0]
219
+ else:
220
+ raise IOError('Not enough bytes left in buffer.')
221
+
222
+ def readInt(self) -> int:
223
+ """
224
+ Reads a signed int from the stream.
225
+ """
226
+ value = self.tryReadBytes(4)
227
+ if value:
228
+ return self.__int32_t.unpack(value)[0]
229
+ else:
230
+ raise IOError('Not enough bytes left in buffer.')
231
+
232
+ def readLong(self) -> int:
233
+ """
234
+ Reads a signed byte from the stream.
235
+ """
236
+ value = self.tryReadBytes(8)
237
+ if value:
238
+ return self.__int64_t.unpack(value)[0]
239
+ else:
240
+ raise IOError('Not enough bytes left in buffer.')
241
+
242
+ def readShort(self) -> int:
243
+ """
244
+ Reads a signed short from the stream.
245
+ """
246
+ value = self.tryReadBytes(2)
247
+ if value:
248
+ return self.__int16_t.unpack(value)[0]
249
+ else:
250
+ raise IOError('Not enough bytes left in buffer.')
251
+
252
+ def readStruct(self, _struct: Union[struct.Struct, Any]) -> Tuple[Any, ...]:
253
+ """
254
+ Read enough bytes for a struct and unpack it, returning the tuple of
255
+ values.
256
+
257
+ :param _struct: A struct or struct-like object using duck-typing. Only
258
+ requires the object have an unpack method that takes a single
259
+ argument and a size property to tell how many bytes to read.
260
+
261
+ :raises IOError: If there are not enough bytes left to read.
262
+ """
263
+ value = self.tryReadBytes(_struct.size)
264
+ if value:
265
+ return _struct.unpack(value)
266
+ else:
267
+ raise IOError('Not enough bytes left in buffer.')
268
+
269
+ def readUnsignedByte(self) -> int:
270
+ """
271
+ Reads an unsigned byte from the stream.
272
+ """
273
+ value = self.tryReadBytes(1)
274
+ if value:
275
+ return self.__uint8_t.unpack(value)[0]
276
+ else:
277
+ raise IOError('Not enough bytes left in buffer.')
278
+
279
+ def readUnsignedInt(self) -> int:
280
+ """
281
+ Reads an unsigned int from the stream.
282
+ """
283
+ value = self.tryReadBytes(4)
284
+ if value:
285
+ return self.__uint32_t.unpack(value)[0]
286
+ else:
287
+ raise IOError('Not enough bytes left in buffer.')
288
+
289
+ def readUnsignedLong(self) -> int:
290
+ """
291
+ Reads an unsigned long from the stream.
292
+ """
293
+ value = self.tryReadBytes(8)
294
+ if value:
295
+ return self.__uint64_t.unpack(value)[0]
296
+ else:
297
+ raise IOError('Not enough bytes left in buffer.')
298
+
299
+ def readUnsignedShort(self) -> int:
300
+ """
301
+ Reads an unsigned short from the stream.
302
+ """
303
+ value = self.tryReadBytes(2)
304
+ if value:
305
+ return self.__uint16_t.unpack(value)[0]
306
+ else:
307
+ raise IOError('Not enough bytes left in buffer.')
308
+
309
+ def readUtf8String(self) -> str:
310
+ """
311
+ Reads a null-terminated string in UTF-8 format.
312
+ """
313
+ return self._readDecodedString('utf-8')
314
+
315
+ def readUtf16String(self) -> str:
316
+ """
317
+ Reads a null terminated string in UTF-16 format using the endienness of
318
+ the reader to determine which one to use.
319
+ """
320
+ return self._readDecodedString('utf-16-le' if self.__le else 'utf-16-be', 2)
321
+
322
+ def readUtf32String(self) -> str:
323
+ """
324
+ Reads a null terminated string in UTF-32 format using the endienness of
325
+ the reader to determine which one to use.
326
+ """
327
+ return self._readDecodedString('utf-32-le' if self.__le else 'utf-32-be', 4)
328
+
329
+ def tryReadBytes(self, size: int) -> bytes:
330
+ """
331
+ Tries to read the specified number of bytes, returning b'' if not
332
+ possible. Will only change the position of the read pointer if reading
333
+ was possible.
334
+ """
335
+ if size < 1:
336
+ raise ValueError(':param size: must be at least 1.')
337
+ position = self.tell()
338
+ value = self.read(size)
339
+ if len(value) == size:
340
+ return value
341
+
342
+ # Ensure that we seek back to where we started if we could not read.
343
+ while self.tell() != position:
344
+ self.seek(position)
345
+ return b''