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,204 @@
1
+ from __future__ import annotations
2
+
3
+
4
+ __all__ = [
5
+ 'openMsg',
6
+ 'openMsgBulk',
7
+ ]
8
+
9
+
10
+ import glob
11
+ import logging
12
+
13
+ from typing import List, Optional, Tuple, TYPE_CHECKING, Union
14
+
15
+ from . import constants
16
+ from .exceptions import (
17
+ InvalidFileFormatError, UnrecognizedMSGTypeError,
18
+ UnsupportedMSGTypeError
19
+ )
20
+
21
+
22
+ logger = logging.getLogger(__name__)
23
+ logger.addHandler(logging.NullHandler())
24
+
25
+ if TYPE_CHECKING:
26
+ from .msg_classes import MSGFile
27
+
28
+
29
+ def _getMsgClassInfo(classType: str) -> Tuple[bool, Optional[str]]:
30
+ """
31
+ Checks if the specified class type is recognized by the module.
32
+
33
+ Usually used for checking if a type is simply unsupported rather than
34
+ unknown.
35
+
36
+ Returns a tuple of two items. The first is whether it is known. If it is
37
+ known and support is refused, the second item will be a string of the
38
+ relevent issue number. Otherwise, it will be None.
39
+ """
40
+ classType = classType.lower()
41
+ if classType == 'ipm':
42
+ return (True, None)
43
+
44
+ for item in constants.KNOWN_CLASS_TYPES:
45
+ if classType.startswith(item):
46
+ # Check if the found class type has had support refused.
47
+ for tup in constants.REFUSED_CLASS_TYPES:
48
+ if tup[0] == item:
49
+ return (True, tup[1])
50
+ else:
51
+ return (True, None)
52
+
53
+ return (False, None)
54
+
55
+
56
+ def openMsg(path, **kwargs) -> MSGFile:
57
+ """
58
+ Function to automatically open an MSG file and detect what type it is.
59
+
60
+ Accepts all of the same arguments as the ``__init__`` method for the class
61
+ it creates. Extra options will be ignored if the class doesn't know what to
62
+ do with them, but child instances may end up using them if they understand
63
+ them. See ``MSGFile.__init__`` for a list of all globally recognized
64
+ options.
65
+
66
+ :param strict: If set to ``True``, this function will raise an exception
67
+ when it cannot identify what ``MSGFile`` derivitive to use. Otherwise,
68
+ it will log the error and return a basic ``MSGFile`` instance. Default
69
+ is ``True``.
70
+
71
+ :raises UnsupportedMSGTypeError: The type is recognized but not suppoted.
72
+ :raises UnrecognizedMSGTypeError: The type is not recognized.
73
+ """
74
+ from .msg_classes import (
75
+ AppointmentMeeting, Contact, Journal, MeetingCancellation,
76
+ MeetingException, MeetingForwardNotification, MeetingRequest,
77
+ MeetingResponse, Message, MSGFile, MessageSigned, Post, StickyNote,
78
+ Task, TaskRequest
79
+ )
80
+
81
+ # When the initial MSG file is opened, it should *always* delay attachments
82
+ # so it can get the main class type. We only need to load them after that
83
+ # if we are directly returning the MSGFile instance *and* delayAttachments
84
+ # is False.
85
+ #
86
+ # So first let's store the original value.
87
+ delayAttachments = kwargs.get('delayAttachments', False)
88
+ kwargs['delayAttachments'] = True
89
+
90
+ msg = MSGFile(path, **kwargs)
91
+
92
+ # Restore the option in the kwargs so we don't have to worry about it.
93
+ kwargs['delayAttachments'] = delayAttachments
94
+
95
+ # After rechecking the docs, all comparisons should be case-insensitive, not
96
+ # case-sensitive. My reading ability is great.
97
+ #
98
+ # Also after consideration, I realized we need to be very careful here, as
99
+ # other file types (like doc, ppt, etc.) might open but not return a class
100
+ # type. If the stream is not found, classType returns None, which has no
101
+ # lower function. So let's make sure we got a good return first.
102
+ if not msg.classType:
103
+ if kwargs.get('strict', True):
104
+ raise InvalidFileFormatError('File was confirmed to be an olefile, but was not an MSG file.')
105
+ else:
106
+ # If strict mode is off, we'll just return an MSGFile anyways.
107
+ logger.critical('Received file that was an olefile but was not an MSG file. Returning MSGFile anyways because strict mode is off.')
108
+ return msg
109
+ classType = msg.classType.lower()
110
+ # Put the message class first as it is most common.
111
+ if classType.startswith('ipm.note') or classType.startswith('report'):
112
+ msg.close()
113
+ if classType.endswith('smime.multipartsigned') or classType.endswith('smime'):
114
+ return MessageSigned(path, **kwargs)
115
+ else:
116
+ return Message(path, **kwargs)
117
+ elif classType.startswith('ipm.activity'):
118
+ msg.close()
119
+ return Journal(path, **kwargs)
120
+ elif classType.startswith('ipm.appointment'):
121
+ msg.close()
122
+ return AppointmentMeeting(path, **kwargs)
123
+ elif classType.startswith('ipm.contact') or classType.startswith('ipm.distlist'):
124
+ msg.close()
125
+ return Contact(path, **kwargs)
126
+ elif classType.startswith('ipm.post'):
127
+ msg.close()
128
+ return Post(path, **kwargs)
129
+ elif classType.startswith('ipm.schedule.meeting.request'):
130
+ msg.close()
131
+ return MeetingRequest(path, **kwargs)
132
+ elif classType.startswith('ipm.schedule.meeting.canceled'):
133
+ msg.close()
134
+ return MeetingCancellation(path, **kwargs)
135
+ elif classType.startswith('ipm.schedule.meeting.notification.forward'):
136
+ msg.close()
137
+ return MeetingForwardNotification(path, **kwargs)
138
+ elif classType.startswith('ipm.schedule.meeting.resp'):
139
+ msg.close()
140
+ return MeetingResponse(path, **kwargs)
141
+ elif classType.startswith('ipm.stickynote'):
142
+ msg.close()
143
+ return StickyNote(path, **kwargs)
144
+ elif classType.startswith('ipm.taskrequest'):
145
+ msg.close()
146
+ return TaskRequest(path, **kwargs)
147
+ elif classType.startswith('ipm.task'):
148
+ msg.close()
149
+ return Task(path, **kwargs)
150
+ elif classType.startswith('ipm.ole.class.{00061055-0000-0000-c000-000000000046}'):
151
+ # Exception objects have a weird class type.
152
+ msg.close()
153
+ return MeetingException(path, **kwargs)
154
+ elif classType == 'ipm':
155
+ # Unspecified format. It should be equal to this and not just start with
156
+ # it.
157
+ if not delayAttachments:
158
+ msg.attachments
159
+ return msg
160
+ elif kwargs.get('strict', True):
161
+ # Because we are closing it, we need to store it in a variable first.
162
+ ct = msg.classType
163
+ msg.close()
164
+ # Now we need to figure out exactly what we are going to be reporting to
165
+ # the user.
166
+ if (info := _getMsgClassInfo(classType))[0]:
167
+ if info[1]:
168
+ raise UnsupportedMSGTypeError(f'Support for MSG type "{ct}" has been refused. See {constants.REPOSITORY_URL}/issues/{info[1]} for more information.')
169
+ raise UnsupportedMSGTypeError(f'MSG type "{ct}" currently is not supported by the module. If you would like support, please make a feature request.')
170
+ raise UnrecognizedMSGTypeError(f'Could not recognize MSG class type "{ct}". As such, there is a high chance that support may be impossible, but you should contact the developers to find out more.')
171
+ else:
172
+ if (info := _getMsgClassInfo(classType))[0]:
173
+ if info[1]:
174
+ logger.error(f'Support for MSG type "{msg.classType}" has been refused. See {constants.REPOSITORY_URL}/issues/{info[1]} for more information.')
175
+ else:
176
+ logger.error(f'MSG type "{msg.classType}" currently is not supported by the module. If you would like support, please make a feature request.')
177
+ logger.error(f'Could not recognize MSG class type "{msg.classType}". As such, there is a high chance that support may be impossible, but you should contact the developers to find out more.')
178
+ if not delayAttachments:
179
+ msg.attachments
180
+ return msg
181
+
182
+
183
+ def openMsgBulk(path, **kwargs) -> Union[List[MSGFile], Tuple[Exception, Union[str, bytes]]]:
184
+ """
185
+ Takes the same arguments as openMsg, but opens a collection of MSG files
186
+ based on a wild card. Returns a list if successful, otherwise returns a
187
+ tuple.
188
+
189
+ :param ignoreFailures: If this is ``True``, will return a list of all
190
+ successful files, ignoring any failures. Otherwise, will close all that
191
+ successfully opened, and return a tuple of the exception and the path of
192
+ the file that failed.
193
+ """
194
+ files = []
195
+ for x in glob.glob(str(path)):
196
+ try:
197
+ files.append(openMsg(x, **kwargs))
198
+ except Exception as e:
199
+ if not kwargs.get('ignoreFailures', False):
200
+ for msg in files:
201
+ msg.close()
202
+ return (e, x)
203
+
204
+ return files
@@ -0,0 +1,23 @@
1
+ """
2
+ Classes and functions involved with managing properties.
3
+ """
4
+
5
+ __all__ = [
6
+ 'FixedLengthProp',
7
+ 'Named',
8
+ 'NamedProperties',
9
+ 'NamedPropertyBase',
10
+ 'NumericalNamedProperty',
11
+ 'PropBase',
12
+ 'PropertiesStore',
13
+ 'StringNamedProperty',
14
+ 'VariableLengthProp',
15
+ ]
16
+
17
+
18
+ from .named import (
19
+ Named, NamedProperties, NamedPropertyBase, NumericalNamedProperty,
20
+ StringNamedProperty
21
+ )
22
+ from .prop import FixedLengthProp, PropBase, VariableLengthProp
23
+ from .properties_store import PropertiesStore