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,362 @@
1
+ from __future__ import annotations
2
+
3
+
4
+ __all__ = [
5
+ 'Recipient',
6
+ ]
7
+
8
+
9
+ import enum
10
+ import functools
11
+ import logging
12
+ import weakref
13
+
14
+ from typing import (
15
+ Any, Generic, List, Optional, Tuple, TYPE_CHECKING, Type, TypeVar, Union
16
+ )
17
+
18
+ from .constants import MSG_PATH, OVERRIDE_CLASS
19
+ from .enums import ErrorBehavior, PropertiesType
20
+ from .exceptions import StandardViolationError
21
+ from .properties.properties_store import PropertiesStore
22
+ from .structures.entry_id import PermanentEntryID
23
+ from .utils import msgPathToString
24
+
25
+
26
+ if TYPE_CHECKING:
27
+ from .msg_classes.msg import MSGFile
28
+
29
+ logger = logging.getLogger(__name__)
30
+ logger.addHandler(logging.NullHandler())
31
+
32
+ _T = TypeVar('_T')
33
+ _RT = TypeVar('_RT', bound = enum.IntEnum)
34
+
35
+
36
+ class Recipient(Generic[_RT]):
37
+ """
38
+ Contains the data of one of the recipients in an MSG file.
39
+ """
40
+
41
+ def __init__(self, _dir: str, msg: MSGFile, recipientTypeClass: Type[_RT]):
42
+ self.__msg = weakref.ref(msg) # Allows calls to original msg file.
43
+ self.__dir = _dir
44
+ if not self.exists('__properties_version1.0'):
45
+ if ErrorBehavior.STANDARDS_VIOLATION in msg.errorBehavior:
46
+ logger.error('Recipients MUST have a property stream.')
47
+ else:
48
+ raise StandardViolationError('Recipients MUST have a property stream.') from None
49
+ self.__props = PropertiesStore(self.getStream('__properties_version1.0'), PropertiesType.RECIPIENT)
50
+ self.__email = self.getStringStream('__substg1.0_39FE')
51
+ if not self.__email:
52
+ self.__email = self.getStringStream('__substg1.0_3003')
53
+ self.__name = self.getStringStream('__substg1.0_3001')
54
+ self.__typeFlags = self.__props.getValue('0C150003', 0)
55
+ self.__type = recipientTypeClass(0xF & self.__typeFlags)
56
+ self.__formatted = f'{self.__name} <{self.__email}>'
57
+
58
+ def exists(self, filename: MSG_PATH) -> bool:
59
+ """
60
+ Checks if stream exists inside the recipient folder.
61
+
62
+ :raises ReferenceError: The associated ``MSGFile`` instance has been
63
+ garbage collected.
64
+ """
65
+ if (msg := self.__msg()) is None:
66
+ raise ReferenceError('The MSGFile for this Recipient instance has been garbage collected.')
67
+ return msg.exists([self.__dir, msgPathToString(filename)])
68
+
69
+ def sExists(self, filename: MSG_PATH) -> bool:
70
+ """
71
+ Checks if the string stream exists inside the recipient folder.
72
+
73
+ :raises ReferenceError: The associated ``MSGFile`` instance has been
74
+ garbage collected.
75
+ """
76
+ if (msg := self.__msg()) is None:
77
+ raise ReferenceError('The MSGFile for this Recipient instance has been garbage collected.')
78
+ return msg.sExists([self.__dir, msgPathToString(filename)])
79
+
80
+ def existsTypedProperty(self, id, _type = None) -> Tuple[bool, int]:
81
+ """
82
+ Determines if the stream with the provided id exists.
83
+
84
+ The return of this function is 2 values, the first being a boolean for if anything was found, and the second being how many were found.
85
+
86
+ :raises ReferenceError: The associated ``MSGFile`` instance has been
87
+ garbage collected.
88
+ """
89
+ if (msg := self.__msg()) is None:
90
+ raise ReferenceError('The MSGFile for this Recipient instance has been garbage collected.')
91
+ return msg.existsTypedProperty(id, self.__dir, _type, True, self.__props)
92
+
93
+ def getMultipleBinary(self, filename: MSG_PATH) -> Optional[List[bytes]]:
94
+ """
95
+ Gets a multiple binary property as a list of bytes objects.
96
+
97
+ Like :meth:`getStringStream`, the 4 character type suffix should be
98
+ omitted. So if you want the stream "__substg1.0_00011102" then the
99
+ filename would simply be "__substg1.0_0001".
100
+
101
+ :raises ReferenceError: The associated ``MSGFile`` instance has been
102
+ garbage collected.
103
+ """
104
+ if (msg := self.__msg()) is None:
105
+ raise ReferenceError('The MSGFile for this Recipient instance has been garbage collected.')
106
+ return msg.getMultipleBinary([self.__dir, msgPathToString(filename)])
107
+
108
+ def getMultipleString(self, filename: MSG_PATH) -> Optional[List[str]]:
109
+ """
110
+ Gets a multiple string property as a list of str objects.
111
+
112
+ Like :meth:`getStringStream`, the 4 character type suffix should be
113
+ omitted. So if you want the stream "__substg1.0_00011102" then the
114
+ filename would simply be "__substg1.0_0001".
115
+
116
+ :raises ReferenceError: The associated ``MSGFile`` instance has been
117
+ garbage collected.
118
+ """
119
+ if (msg := self.__msg()) is None:
120
+ raise ReferenceError('The MSGFile for this Recipient instance has been garbage collected.')
121
+ return msg.getMultipleString([self.__dir, msgPathToString(filename)])
122
+
123
+ def getPropertyAs(self, propertyName: Union[int, str], overrideClass: OVERRIDE_CLASS[_T]) -> Optional[_T]:
124
+ """
125
+ Returns the property, setting the class if found.
126
+
127
+ :param overrideClass: Class/function to use to morph the data that was
128
+ read. The data will be the first argument to the class's
129
+ ``__init__`` method or the function itself, if that is what is
130
+ provided. If the value is ``None``, this function is not called. If
131
+ you want it to be called regardless, you should handle the data
132
+ directly.
133
+ """
134
+ value = self.getPropertyVal(propertyName)
135
+
136
+ if value is not None:
137
+ value = overrideClass(value)
138
+
139
+ return value
140
+
141
+ def getPropertyVal(self, name: Union[int, str], default: _T = None) -> Union[Any, _T]:
142
+ """
143
+ ``instance.props.getValue(name, default)``
144
+
145
+ Can be overridden to create new behavior.
146
+ """
147
+ return self.props.getValue(name, default)
148
+
149
+ def getSingleOrMultipleBinary(self, filename: MSG_PATH) -> Optional[Union[List[bytes], bytes]]:
150
+ """
151
+ A combination of :meth:`getStringStream` and :meth:`getMultipleString`.
152
+
153
+ Checks to see if a single binary stream exists to return, otherwise
154
+ tries to return the multiple binary stream of the same ID.
155
+
156
+ Like :meth:`getStringStream`, the 4 character type suffix should be
157
+ omitted. So if you want the stream "__substg1.0_00010102" then the
158
+ filename would simply be "__substg1.0_0001".
159
+
160
+ :raises ReferenceError: The associated ``MSGFile`` instance has been
161
+ garbage collected.
162
+ """
163
+ if (msg := self.__msg()) is None:
164
+ raise ReferenceError('The MSGFile for this Recipient instance has been garbage collected.')
165
+ return msg.getSingleOrMultipleBinary([self.__dir, msgPathToString(filename)])
166
+
167
+ def getSingleOrMultipleString(self, filename: MSG_PATH) -> Optional[Union[List[str], str]]:
168
+ """
169
+ A combination of :meth:`getStringStream` and :meth:`getMultipleString`.
170
+
171
+ Checks to see if a single string stream exists to return, otherwise
172
+ tries to return the multiple string stream of the same ID.
173
+
174
+ Like :meth:`getStringStream`, the 4 character type suffix should be
175
+ omitted. So if you want the stream "__substg1.0_0001001F" then the
176
+ filename would simply be "__substg1.0_0001".
177
+
178
+ :raises ReferenceError: The associated ``MSGFile`` instance has been
179
+ garbage collected.
180
+ """
181
+ if (msg := self.__msg()) is None:
182
+ raise ReferenceError('The MSGFile for this Recipient instance has been garbage collected.')
183
+ return msg.getSingleOrMultipleString([self.__dir, msgPathToString(filename)])
184
+
185
+ def getStream(self, filename: MSG_PATH) -> Optional[bytes]:
186
+ """
187
+ Gets a binary representation of the requested stream.
188
+
189
+ This should ALWAYS return a ``bytes`` object if it was found, otherwise
190
+ returns ``None``.
191
+
192
+ :raises ReferenceError: The associated ``MSGFile`` instance has been
193
+ garbage collected.
194
+ """
195
+ if (msg := self.__msg()) is None:
196
+ raise ReferenceError('The MSGFile for this Recipient instance has been garbage collected.')
197
+ return msg.getStream([self.__dir, msgPathToString(filename)])
198
+
199
+ def getStreamAs(self, streamID: MSG_PATH, overrideClass: OVERRIDE_CLASS[_T]) -> Optional[_T]:
200
+ """
201
+ Returns the specified stream, modifying it to the specified class if it
202
+ is found.
203
+
204
+ :param overrideClass: Class/function to use to morph the data that was
205
+ read. The data will be the first argument to the class's
206
+ ``__init__`` method or the function itself, if that is what is
207
+ provided. If the value is ``None``, this function is not called. If
208
+ you want it to be called regardless, you should handle the data
209
+ directly.
210
+ """
211
+ value = self.getStream(streamID)
212
+
213
+ if value is not None:
214
+ value = overrideClass(value)
215
+
216
+ return value
217
+
218
+ def getStringStream(self, filename: MSG_PATH) -> Optional[str]:
219
+ """
220
+ Gets a string representation of the requested stream.
221
+
222
+ Rather than the full filename, you should only feed this function the
223
+ filename sans the type. So if the full name is "__substg1.0_001A001F",
224
+ the filename this function should receive should be "__substg1.0_001A".
225
+
226
+ This should ALWAYS return a string if it was found, otherwise returns
227
+ None.
228
+
229
+ :raises ReferenceError: The associated ``MSGFile`` instance has been
230
+ garbage collected.
231
+ """
232
+ if (msg := self.__msg()) is None:
233
+ raise ReferenceError('The MSGFile for this Recipient instance has been garbage collected.')
234
+ return msg.getStringStream([self.__dir, msgPathToString(filename)])
235
+
236
+ def getStringStreamAs(self, streamID: MSG_PATH, overrideClass: OVERRIDE_CLASS[_T]) -> Optional[_T]:
237
+ """
238
+ Returns the specified string stream, modifying it to the specified
239
+ class if it is found.
240
+
241
+ :param overrideClass: Class/function to use to morph the data that was
242
+ read. The data will be the first argument to the class's
243
+ ``__init__`` method or the function itself, if that is what is
244
+ provided. If the value is ``None``, this function is not called. If
245
+ you want it to be called regardless, you should handle the data
246
+ directly.
247
+ """
248
+ value = self.getStream(streamID)
249
+
250
+ if value is not None:
251
+ value = overrideClass(value)
252
+
253
+ return value
254
+
255
+ def listDir(self, streams: bool = True, storages: bool = False) -> List[List[str]]:
256
+ """
257
+ Lists the streams and or storages that exist in the recipient directory.
258
+
259
+ :returns: The paths *excluding* the recipient directory, allowing the
260
+ paths to be directly used for accessing a file.
261
+ """
262
+ if (msg := self.__msg()) is None:
263
+ raise ReferenceError('The MSGFile for this Recipient instance has been garbage collected.')
264
+ return [path[1:] for path in msg.listDir(streams, storages, False)
265
+ if len(path) > 1 and path[0] == self.__dir]
266
+
267
+ def slistDir(self, streams: bool = True, storages: bool = False) -> List[str]:
268
+ """
269
+ Like :meth:`listDir`, except it returns the paths as strings.
270
+ """
271
+ return ['/'.join(path) for path in self.listDir(streams, storages)]
272
+
273
+ @functools.cached_property
274
+ def account(self) -> Optional[str]:
275
+ """
276
+ The account of this recipient.
277
+ """
278
+ return self.getStringStream('__substg1.0_3A00')
279
+
280
+ @property
281
+ def email(self) -> Optional[str]:
282
+ """
283
+ The recipient's email.
284
+ """
285
+ return self.__email
286
+
287
+ @functools.cached_property
288
+ def entryID(self) -> Optional[PermanentEntryID]:
289
+ """
290
+ The recipient's Entry ID.
291
+ """
292
+ return self.getStreamAs('__substg1.0_0FFF0102', PermanentEntryID)
293
+
294
+ @property
295
+ def formatted(self) -> str:
296
+ """
297
+ The formatted recipient string.
298
+ """
299
+ return self.__formatted
300
+
301
+ @functools.cached_property
302
+ def instanceKey(self) -> Optional[bytes]:
303
+ """
304
+ The instance key of this recipient.
305
+ """
306
+ return self.getStream('__substg1.0_0FF60102')
307
+
308
+ @property
309
+ def name(self) -> Optional[str]:
310
+ """
311
+ The recipient's name.
312
+ """
313
+ return self.__name
314
+
315
+ @property
316
+ def props(self) -> PropertiesStore:
317
+ """
318
+ The Properties instance of the recipient.
319
+ """
320
+ return self.__props
321
+
322
+ @functools.cached_property
323
+ def recordKey(self) -> Optional[bytes]:
324
+ """
325
+ The record key of this recipient.
326
+ """
327
+ return self.getStream('__substg1.0_0FF90102')
328
+
329
+ @functools.cached_property
330
+ def searchKey(self) -> Optional[bytes]:
331
+ """
332
+ The search key of this recipient.
333
+ """
334
+ return self.getStream('__substg1.0_300B0102')
335
+
336
+ @functools.cached_property
337
+ def smtpAddress(self) -> Optional[str]:
338
+ """
339
+ The SMTP address of this recipient.
340
+ """
341
+ return self.getStringStream('__substg1.0_39FE')
342
+
343
+ @functools.cached_property
344
+ def transmittableDisplayName(self) -> Optional[str]:
345
+ """
346
+ The transmittable display name of this recipient.
347
+ """
348
+ return self.getStringStream('__substg1.0_3A20')
349
+
350
+ @property
351
+ def type(self) -> _RT:
352
+ """
353
+ The recipient type.
354
+ """
355
+ return self.__type
356
+
357
+ @property
358
+ def typeFlags(self) -> int:
359
+ """
360
+ The raw recipient type value and all the flags it includes.
361
+ """
362
+ return self.__typeFlags
@@ -0,0 +1,34 @@
1
+ """
2
+ extract_msg.structures - Submodule to help with parsing data structures in MSG
3
+ files. Broken up by structure type.
4
+ """
5
+
6
+ __all__ = [
7
+ '_helpers',
8
+ 'contact_link_entry',
9
+ 'business_card',
10
+ 'cfoas',
11
+ 'contact_link_entry',
12
+ 'dev_mode_a',
13
+ 'dv_target_device',
14
+ 'entry_id',
15
+ 'misc_id',
16
+ 'mon_stream',
17
+ 'odt',
18
+ 'ole_pres',
19
+ 'ole_stream_struct',
20
+ 'recurrence_pattern',
21
+ 'report_tag',
22
+ 'system_time',
23
+ 'time_zone_definition',
24
+ 'time_zone_struct',
25
+ 'toc_entry',
26
+ 'tz_rule',
27
+ ]
28
+
29
+ from . import (
30
+ _helpers, business_card, cfoas, contact_link_entry, dev_mode_a,
31
+ dv_target_device, entry_id, misc_id, mon_stream, odt, ole_pres,
32
+ ole_stream_struct, recurrence_pattern, report_tag, system_time,
33
+ time_zone_definition, time_zone_struct, toc_entry, tz_rule
34
+ )