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.
- extract_msg/__init__.py +71 -0
- extract_msg/__main__.py +115 -0
- extract_msg/_rtf/__init__.py +21 -0
- extract_msg/_rtf/create_doc.py +29 -0
- extract_msg/_rtf/inject_rtf.py +174 -0
- extract_msg/_rtf/token.py +35 -0
- extract_msg/_rtf/tokenize_rtf.py +253 -0
- extract_msg/attachments/__init__.py +144 -0
- extract_msg/attachments/attachment.py +188 -0
- extract_msg/attachments/attachment_base.py +730 -0
- extract_msg/attachments/broken_att.py +42 -0
- extract_msg/attachments/custom_att.py +173 -0
- extract_msg/attachments/custom_att_handler/__init__.py +80 -0
- extract_msg/attachments/custom_att_handler/custom_handler.py +134 -0
- extract_msg/attachments/custom_att_handler/lnk_obj_att.py +109 -0
- extract_msg/attachments/custom_att_handler/outlook_image_dib.py +141 -0
- extract_msg/attachments/emb_msg_att.py +134 -0
- extract_msg/attachments/signed_att.py +234 -0
- extract_msg/attachments/unsupported_att.py +40 -0
- extract_msg/attachments/web_att.py +74 -0
- extract_msg/constants/__init__.py +294 -0
- extract_msg/constants/ps.py +46 -0
- extract_msg/constants/re.py +42 -0
- extract_msg/constants/st.py +110 -0
- extract_msg/data/logging-config/logging-nt.json +64 -0
- extract_msg/data/logging-config/logging-posix.json +64 -0
- extract_msg/encoding/__init__.py +277 -0
- extract_msg/encoding/_dt/__init__.py +4 -0
- extract_msg/encoding/_dt/_mac_ce.py +8 -0
- extract_msg/encoding/_dt/_mac_cyrillic.py +8 -0
- extract_msg/encoding/_dt/_mac_greek.py +8 -0
- extract_msg/encoding/_dt/_mac_iceland.py +8 -0
- extract_msg/encoding/_dt/_mac_turkish.py +8 -0
- extract_msg/encoding/_dt/_win874_dec.py +8 -0
- extract_msg/encoding/_dt/_win950_dec.py +6 -0
- extract_msg/encoding/utils.py +329 -0
- extract_msg/enums.py +2067 -0
- extract_msg/exceptions.py +170 -0
- extract_msg/msg_classes/__init__.py +49 -0
- extract_msg/msg_classes/appointment.py +195 -0
- extract_msg/msg_classes/calendar.py +92 -0
- extract_msg/msg_classes/calendar_base.py +496 -0
- extract_msg/msg_classes/contact.py +1335 -0
- extract_msg/msg_classes/journal.py +131 -0
- extract_msg/msg_classes/meeting_cancellation.py +108 -0
- extract_msg/msg_classes/meeting_exception.py +55 -0
- extract_msg/msg_classes/meeting_forward.py +115 -0
- extract_msg/msg_classes/meeting_related.py +65 -0
- extract_msg/msg_classes/meeting_request.py +189 -0
- extract_msg/msg_classes/meeting_response.py +74 -0
- extract_msg/msg_classes/message.py +12 -0
- extract_msg/msg_classes/message_base.py +1382 -0
- extract_msg/msg_classes/message_signed.py +17 -0
- extract_msg/msg_classes/message_signed_base.py +168 -0
- extract_msg/msg_classes/msg.py +1172 -0
- extract_msg/msg_classes/post.py +53 -0
- extract_msg/msg_classes/sticky_note.py +67 -0
- extract_msg/msg_classes/task.py +363 -0
- extract_msg/msg_classes/task_request.py +86 -0
- extract_msg/null_date.py +34 -0
- extract_msg/ole_writer.py +1086 -0
- extract_msg/open_msg.py +204 -0
- extract_msg/properties/__init__.py +23 -0
- extract_msg/properties/named.py +481 -0
- extract_msg/properties/prop.py +506 -0
- extract_msg/properties/properties_store.py +476 -0
- extract_msg/py.typed +0 -0
- extract_msg/recipient.py +362 -0
- extract_msg/structures/__init__.py +34 -0
- extract_msg/structures/_helpers.py +345 -0
- extract_msg/structures/business_card.py +422 -0
- extract_msg/structures/cfoas.py +108 -0
- extract_msg/structures/contact_link_entry.py +51 -0
- extract_msg/structures/dev_mode_a.py +621 -0
- extract_msg/structures/dv_target_device.py +168 -0
- extract_msg/structures/entry_id.py +697 -0
- extract_msg/structures/misc_id.py +222 -0
- extract_msg/structures/mon_stream.py +55 -0
- extract_msg/structures/odt.py +74 -0
- extract_msg/structures/ole_pres.py +302 -0
- extract_msg/structures/ole_stream_struct.py +117 -0
- extract_msg/structures/recurrence_pattern.py +199 -0
- extract_msg/structures/report_tag.py +127 -0
- extract_msg/structures/system_time.py +158 -0
- extract_msg/structures/time_zone_definition.py +111 -0
- extract_msg/structures/time_zone_struct.py +102 -0
- extract_msg/structures/toc_entry.py +137 -0
- extract_msg/structures/tz_rule.py +148 -0
- extract_msg/utils.py +1270 -0
- extract_msg-0.48.2.dist-info/LICENSE.txt +674 -0
- extract_msg-0.48.2.dist-info/METADATA +321 -0
- extract_msg-0.48.2.dist-info/RECORD +95 -0
- extract_msg-0.48.2.dist-info/WHEEL +5 -0
- extract_msg-0.48.2.dist-info/entry_points.txt +2 -0
- extract_msg-0.48.2.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
__all__ = [
|
|
2
|
+
'OleStreamStruct',
|
|
3
|
+
]
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
from typing import final, Optional
|
|
7
|
+
|
|
8
|
+
from ..constants import st
|
|
9
|
+
from ._helpers import BytesReader
|
|
10
|
+
from .mon_stream import MonikerStream
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@final
|
|
14
|
+
class OleStreamStruct:
|
|
15
|
+
"""
|
|
16
|
+
The OLEStream structure, as specified in [MS-OLEDS].
|
|
17
|
+
|
|
18
|
+
Specifically, this is *only* the version that is used for embedded objects.
|
|
19
|
+
As such, only some of the fields are ever present.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def __init__(self, data: Optional[bytes] = None):
|
|
23
|
+
"""
|
|
24
|
+
:raises TypeError: The data given is not for an embedded object.
|
|
25
|
+
"""
|
|
26
|
+
self.__rms = None
|
|
27
|
+
if not data:
|
|
28
|
+
self.__flags = 0
|
|
29
|
+
self.__linkUpdateOption = 0
|
|
30
|
+
return
|
|
31
|
+
reader = BytesReader(data)
|
|
32
|
+
# Assert the version.
|
|
33
|
+
reader.assertRead(b'\x01\x00\x00\x02', 'Ole stream had invalid version (expected {expected}, got {actual}).')
|
|
34
|
+
self.__flags = reader.readUnsignedInt()
|
|
35
|
+
if self.__flags & 1:
|
|
36
|
+
raise TypeError('Cannot parse an OLEStream structure for a linked object.')
|
|
37
|
+
self.__linkUpdateOption = reader.readUnsignedInt()
|
|
38
|
+
reader.assertNull(4, 'Ole stream reserved was not null (got {actual}).')
|
|
39
|
+
rmsSize = reader.readUnsignedInt()
|
|
40
|
+
if rmsSize > 0:
|
|
41
|
+
self.__rms = MonikerStream(reader.read(rmsSize - 4))
|
|
42
|
+
|
|
43
|
+
def __bytes__(self) -> bytes:
|
|
44
|
+
return self.toBytes()
|
|
45
|
+
|
|
46
|
+
def toBytes(self) -> bytes:
|
|
47
|
+
ret = b'\x01\x00\x00\x02'
|
|
48
|
+
ret += st.ST_LE_UI32.pack(self.__flags)
|
|
49
|
+
ret += st.ST_LE_UI32.pack(self.__linkUpdateOption)
|
|
50
|
+
ret += b'\x00\x00\x00\x00'
|
|
51
|
+
rmsBytes = b'' if self.__rms is None else bytes(self.__rms)
|
|
52
|
+
rmsLen = (len(rmsBytes) + 4) if rmsBytes else 0
|
|
53
|
+
ret += st.ST_LE_UI32.pack(rmsLen) + rmsBytes
|
|
54
|
+
|
|
55
|
+
return ret
|
|
56
|
+
|
|
57
|
+
@property
|
|
58
|
+
def flags(self) -> int:
|
|
59
|
+
"""
|
|
60
|
+
The flags for the OLEStream.
|
|
61
|
+
|
|
62
|
+
The bit with mask ``0x00001000`` is an implementation-specific hint
|
|
63
|
+
supplied by the application or by a higher-level protocol that creates
|
|
64
|
+
the data structure. It MAY be ignored on processing. A server
|
|
65
|
+
implementation which does not ignore this bit MAY cache the storage
|
|
66
|
+
when the bit is set.
|
|
67
|
+
|
|
68
|
+
:raises ValueError: The property was set with a bit other than the
|
|
69
|
+
implementation specific bit set.
|
|
70
|
+
"""
|
|
71
|
+
return self.__flags
|
|
72
|
+
|
|
73
|
+
@flags.setter
|
|
74
|
+
def flags(self, value: int) -> None:
|
|
75
|
+
if not isinstance(value, int):
|
|
76
|
+
raise TypeError(':property flags: MUST be an int.')
|
|
77
|
+
if value != 0 and value != 0x1000:
|
|
78
|
+
raise ValueError('Cannot set bits other than the implementation specific one.')
|
|
79
|
+
|
|
80
|
+
self.__flags = value
|
|
81
|
+
|
|
82
|
+
@property
|
|
83
|
+
def linkUpdateOption(self) -> int:
|
|
84
|
+
"""
|
|
85
|
+
An implementation-specific hint.
|
|
86
|
+
|
|
87
|
+
This hint MAY be ignored. On Windows, this field contains values from
|
|
88
|
+
the OLEUPDATE enumeration.
|
|
89
|
+
"""
|
|
90
|
+
return self.__linkUpdateOption
|
|
91
|
+
|
|
92
|
+
@linkUpdateOption.setter
|
|
93
|
+
def linkUpdateOption(self, value: int) -> None:
|
|
94
|
+
if not isinstance(value, int):
|
|
95
|
+
raise TypeError(':property linkUpdateOption: MUST be an int.')
|
|
96
|
+
if value < 0:
|
|
97
|
+
raise ValueError(':property linkUpdateOption: MUST be positive.')
|
|
98
|
+
if value > 0xFFFFFFFF:
|
|
99
|
+
raise ValueError(':property linkUpdateOption: MUST be less than 0x100000000.')
|
|
100
|
+
|
|
101
|
+
self.__linkUpdateOption = value
|
|
102
|
+
|
|
103
|
+
@property
|
|
104
|
+
def reservedMonikerStream(self) -> Optional[MonikerStream]:
|
|
105
|
+
"""
|
|
106
|
+
A MonikerStream structure that can contain any arbitrary value.
|
|
107
|
+
"""
|
|
108
|
+
return self.__rms
|
|
109
|
+
|
|
110
|
+
@reservedMonikerStream.setter
|
|
111
|
+
def reservedMonikerStream(self, data: Optional[MonikerStream]) -> None:
|
|
112
|
+
if data is not None and not isinstance(data, MonikerStream):
|
|
113
|
+
raise TypeError('Reserved moniker stream must be a MonikerStream instance or None.')
|
|
114
|
+
|
|
115
|
+
self.__rms = data
|
|
116
|
+
|
|
117
|
+
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
__all__ = [
|
|
2
|
+
'RecurrencePattern',
|
|
3
|
+
]
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
from typing import Any, Tuple
|
|
7
|
+
|
|
8
|
+
from ..enums import RecurCalendarType, RecurDOW, RecurEndType, RecurFrequency, RecurMonthNthWeek, RecurPatternType, RecurPatternTypeSpecificWeekday
|
|
9
|
+
from ._helpers import BytesReader
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class RecurrencePattern:
|
|
13
|
+
"""
|
|
14
|
+
A RecurrencePattern structure, as specified in [MS-OXOCAL].
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def __init__(self, data: bytes):
|
|
18
|
+
self.__rawData = data
|
|
19
|
+
reader = BytesReader(data)
|
|
20
|
+
self.__readerVersion = reader.readUnsignedShort()
|
|
21
|
+
self.__writerVersion = reader.readUnsignedShort()
|
|
22
|
+
if not (self.__readerVersion == self.__writerVersion == 0x3004):
|
|
23
|
+
raise ValueError('Reader version or writer version was not set to 0x3004.')
|
|
24
|
+
|
|
25
|
+
self.__recurFrequency = RecurFrequency(reader.readUnsignedShort())
|
|
26
|
+
self.__patternType = RecurPatternType(reader.readUnsignedShort())
|
|
27
|
+
self.__calendarType = RecurCalendarType(reader.readUnsignedShort())
|
|
28
|
+
self.__firstDateTime = reader.readUnsignedInt()
|
|
29
|
+
self.__period = reader.readUnsignedInt()
|
|
30
|
+
self.__slidingFlag = reader.readUnsignedInt()
|
|
31
|
+
# This is just here to help shorten lines.
|
|
32
|
+
RPTSW = RecurPatternTypeSpecificWeekday
|
|
33
|
+
# This field changes depending on the recurrence type.
|
|
34
|
+
if self.__patternType == RecurPatternType.DAY:
|
|
35
|
+
self.__patternTypeSpecific = None
|
|
36
|
+
elif self.__patternType == RecurPatternType.WEEK:
|
|
37
|
+
self.__patternTypeSpecific = RPTSW(reader.readUnsignedInt())
|
|
38
|
+
elif self.__patternType in (RecurPatternType.MONTH_NTH, RecurPatternType.HJ_MONTH_NTH):
|
|
39
|
+
self.__patternTypeSpecific = reader.readUnsignedInt()
|
|
40
|
+
else:
|
|
41
|
+
self.__patternTypeSpecific = (RPTSW(reader.readUnsignedInt()),
|
|
42
|
+
RecurMonthNthWeek(reader.readUnsignedInt()))
|
|
43
|
+
|
|
44
|
+
self.__endType = RecurEndType.fromInt(reader.readUnsignedInt())
|
|
45
|
+
self.__occurrenceCount = reader.readUnsignedInt()
|
|
46
|
+
self.__firstDOW = RecurDOW(reader.readUnsignedInt())
|
|
47
|
+
deletedInstanceCount = reader.readUnsignedInt()
|
|
48
|
+
self.__deletedInstanceDates = tuple(reader.readUnsignedInt() for _ in range(deletedInstanceCount))
|
|
49
|
+
modifiedInstanceCount = reader.readUnsignedInt()
|
|
50
|
+
self.__modifiedInstanceDates = tuple(reader.readUnsignedInt() for _ in range(modifiedInstanceCount))
|
|
51
|
+
self.__startDate = reader.readUnsignedInt()
|
|
52
|
+
self.__endDate = reader.readUnsignedInt()
|
|
53
|
+
|
|
54
|
+
def __bytes__(self) -> bytes:
|
|
55
|
+
return self.toBytes()
|
|
56
|
+
|
|
57
|
+
def toBytes(self) -> bytes:
|
|
58
|
+
return self.__rawData
|
|
59
|
+
|
|
60
|
+
@property
|
|
61
|
+
def calendarType(self) -> RecurCalendarType:
|
|
62
|
+
"""
|
|
63
|
+
The type of calendar that is used.
|
|
64
|
+
"""
|
|
65
|
+
return self.__calendarType
|
|
66
|
+
|
|
67
|
+
@property
|
|
68
|
+
def deletedInstanceDates(self) -> Tuple[int, ...]:
|
|
69
|
+
"""
|
|
70
|
+
A tuple of the dates (stored as number of minutes between midnight,
|
|
71
|
+
January 1, 1601, and midnight on the specified day in the timezone
|
|
72
|
+
specified in the calendar object), ordered from earliest to latest, of
|
|
73
|
+
either a deleted instance or a modified instance for this recurrence.
|
|
74
|
+
"""
|
|
75
|
+
return self.__deletedInstanceDates
|
|
76
|
+
|
|
77
|
+
@property
|
|
78
|
+
def endDate(self) -> int:
|
|
79
|
+
"""
|
|
80
|
+
An integer that specifies the ending date for the recurrence.
|
|
81
|
+
|
|
82
|
+
The value is the number of minutes between midnight, January 1, 1601,
|
|
83
|
+
and midnight of the date of the last occurrence. When the value of the
|
|
84
|
+
endType field is ``END_AFTER_N_OCCURRENCES``, this value is calculated
|
|
85
|
+
based on the number of occurrences. If the recurrence does not have an
|
|
86
|
+
end date, the value of the endDate field MUST be set to ``0x5AE980DF``.
|
|
87
|
+
"""
|
|
88
|
+
return self.__endDate
|
|
89
|
+
|
|
90
|
+
@property
|
|
91
|
+
def endType(self) -> RecurEndType:
|
|
92
|
+
"""
|
|
93
|
+
The ending type for the recurrence.
|
|
94
|
+
"""
|
|
95
|
+
return self.__endType
|
|
96
|
+
|
|
97
|
+
@property
|
|
98
|
+
def firstDateTime(self) -> int:
|
|
99
|
+
"""
|
|
100
|
+
The first ever dat, week, or month of a recurring series, dating back to
|
|
101
|
+
a reference date, which is January 1, 1601, for a Gregorian calendar.
|
|
102
|
+
"""
|
|
103
|
+
return self.__firstDateTime
|
|
104
|
+
|
|
105
|
+
@property
|
|
106
|
+
def firstDayOfWeek(self) -> RecurDOW:
|
|
107
|
+
"""
|
|
108
|
+
The day on which the calendar week begins.
|
|
109
|
+
"""
|
|
110
|
+
return self.__firstDOW
|
|
111
|
+
|
|
112
|
+
@property
|
|
113
|
+
def modifiedInstanceDates(self) -> Tuple[int, ...]:
|
|
114
|
+
"""
|
|
115
|
+
A tuple of the dates (stored as number of minutes between midnight,
|
|
116
|
+
January 1, 1601, and midnight on the specified day in the timezone
|
|
117
|
+
specified in the calendar object), ordered from earliest to latest, of
|
|
118
|
+
a modified instance.
|
|
119
|
+
"""
|
|
120
|
+
return self.__modifiedInstanceDates
|
|
121
|
+
|
|
122
|
+
@property
|
|
123
|
+
def occurrenceCount(self) -> int:
|
|
124
|
+
"""
|
|
125
|
+
Number of occurrences for a recurrence that ends after N occurrences.
|
|
126
|
+
"""
|
|
127
|
+
return self.__occurrenceCount
|
|
128
|
+
|
|
129
|
+
@property
|
|
130
|
+
def patternType(self) -> RecurPatternType:
|
|
131
|
+
"""
|
|
132
|
+
The type of recurrence pattern.
|
|
133
|
+
"""
|
|
134
|
+
return self.__patternType
|
|
135
|
+
|
|
136
|
+
@property
|
|
137
|
+
def patternTypeSpecific(self) -> Any:
|
|
138
|
+
"""
|
|
139
|
+
The specifics for the pattern type.
|
|
140
|
+
|
|
141
|
+
Return is different depending on what type of pattern is being used.
|
|
142
|
+
|
|
143
|
+
* RecurPatternType.DAY: No value is returned.
|
|
144
|
+
* RecurPatternType.WEEK: A set of RecurPatternTypeSpecificWeekday bits.
|
|
145
|
+
* RecurPatternType.MONTH: The day of the month on which the recurrence
|
|
146
|
+
falls.
|
|
147
|
+
* RecurPatternType.MONTH_NTH: A tuple containing information from
|
|
148
|
+
[MS-OXOCAL] PatternTypeSpecific MonthNth.
|
|
149
|
+
* RecurPatternType.MONTH_END: The day of the month on which the
|
|
150
|
+
recurrence falls.
|
|
151
|
+
* RecurPatternType.HJ_MONTH: The day of the month on which the
|
|
152
|
+
recurrence falls.
|
|
153
|
+
* RecurPatternType.HJ_MONTH_NTH: A tuple containing information from
|
|
154
|
+
[MS-OXOCAL] PatternTypeSpecific MonthNth.
|
|
155
|
+
* RecurPatternType.HJ_MONTH_END: The day of the month on which the
|
|
156
|
+
recurrence falls.
|
|
157
|
+
"""
|
|
158
|
+
return self.__patternTypeSpecific
|
|
159
|
+
|
|
160
|
+
@property
|
|
161
|
+
def period(self) -> int:
|
|
162
|
+
"""
|
|
163
|
+
An integer that specifies the interval at which the meeting pattern
|
|
164
|
+
specified in PatternTypeSpecific field repeats.
|
|
165
|
+
|
|
166
|
+
The Period value MUST be between 1 and the maximum recurrence interval,
|
|
167
|
+
which is 999 days for daily recurrences, 99 weeks for weekly
|
|
168
|
+
recurrences, and 99 months for monthly recurrences.
|
|
169
|
+
"""
|
|
170
|
+
return self.__period
|
|
171
|
+
|
|
172
|
+
@property
|
|
173
|
+
def readerVersion(self) -> int:
|
|
174
|
+
return self.__readerVersion
|
|
175
|
+
|
|
176
|
+
@property
|
|
177
|
+
def recurFrequency(self) -> RecurFrequency:
|
|
178
|
+
"""
|
|
179
|
+
The frequency of the recurring series.
|
|
180
|
+
"""
|
|
181
|
+
return self.__recurFrequency
|
|
182
|
+
|
|
183
|
+
@property
|
|
184
|
+
def slidingFlag(self) -> int:
|
|
185
|
+
return self.__slidingFlag
|
|
186
|
+
|
|
187
|
+
@property
|
|
188
|
+
def startDate(self) -> int:
|
|
189
|
+
"""
|
|
190
|
+
An integer that specifies the date of the first occurrence.
|
|
191
|
+
|
|
192
|
+
The value is the number of minutes between midnight, January 1, 1601,
|
|
193
|
+
and midnight of the date of the first occurrence.
|
|
194
|
+
"""
|
|
195
|
+
return self.__startDate
|
|
196
|
+
|
|
197
|
+
@property
|
|
198
|
+
def writerVersion(self) -> int:
|
|
199
|
+
return self.__writerVersion
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
__all__ = [
|
|
2
|
+
'ReportTag',
|
|
3
|
+
]
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
from typing import Optional
|
|
7
|
+
|
|
8
|
+
from ._helpers import BytesReader
|
|
9
|
+
from .entry_id import EntryID, FolderEntryID, MessageEntryID, StoreObjectEntryID
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ReportTag:
|
|
13
|
+
"""
|
|
14
|
+
A Report Tag structure, as defined in [MS-OXOMSG].
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def __init__(self, data: bytes):
|
|
18
|
+
self.__rawData = data
|
|
19
|
+
reader = BytesReader(data)
|
|
20
|
+
|
|
21
|
+
self.__cookie = reader.assertRead(b'PCDFEB09\x00')
|
|
22
|
+
|
|
23
|
+
self.__version = reader.readUnsignedInt()
|
|
24
|
+
entrySize = reader.readInt()
|
|
25
|
+
if entrySize:
|
|
26
|
+
self.__storeEntryID = StoreObjectEntryID(reader.read(entrySize))
|
|
27
|
+
else:
|
|
28
|
+
self.__storeEntryID = None
|
|
29
|
+
|
|
30
|
+
entrySize = reader.readInt()
|
|
31
|
+
if entrySize:
|
|
32
|
+
self.__folderEntryID = FolderEntryID(reader.read(entrySize))
|
|
33
|
+
else:
|
|
34
|
+
self.__folderEntryID = None
|
|
35
|
+
|
|
36
|
+
entrySize = reader.readInt()
|
|
37
|
+
if entrySize:
|
|
38
|
+
self.__messageEntryID = MessageEntryID(reader.read(entrySize))
|
|
39
|
+
else:
|
|
40
|
+
self.__messageEntryID = None
|
|
41
|
+
|
|
42
|
+
entrySize = reader.readInt()
|
|
43
|
+
if entrySize:
|
|
44
|
+
self.__searchFolderEntryID = FolderEntryID(reader.read(entrySize))
|
|
45
|
+
else:
|
|
46
|
+
self.__searchFolderEntryID = None
|
|
47
|
+
|
|
48
|
+
entrySize = reader.readInt()
|
|
49
|
+
if entrySize:
|
|
50
|
+
self.__messageSearchKey = reader.read(entrySize)
|
|
51
|
+
else:
|
|
52
|
+
self.__messageSearchKey = None
|
|
53
|
+
|
|
54
|
+
entrySize = reader.readInt()
|
|
55
|
+
if entrySize:
|
|
56
|
+
self.__ansiText = reader.read(entrySize)
|
|
57
|
+
else:
|
|
58
|
+
self.__ansiText = None
|
|
59
|
+
|
|
60
|
+
def __bytes__(self) -> bytes:
|
|
61
|
+
return self.toBytes()
|
|
62
|
+
|
|
63
|
+
def toBytes(self) -> bytes:
|
|
64
|
+
return self.__rawData
|
|
65
|
+
|
|
66
|
+
@property
|
|
67
|
+
def ansiText(self) -> Optional[bytes]:
|
|
68
|
+
"""
|
|
69
|
+
The subject of the original message.
|
|
70
|
+
|
|
71
|
+
Set to None if not present.
|
|
72
|
+
"""
|
|
73
|
+
return self.__ansiText
|
|
74
|
+
|
|
75
|
+
@property
|
|
76
|
+
def cookie(self) -> bytes:
|
|
77
|
+
"""
|
|
78
|
+
String used for validation.
|
|
79
|
+
|
|
80
|
+
Set to ``b'PCDFEB09\x00'``.
|
|
81
|
+
"""
|
|
82
|
+
return self.__cookie
|
|
83
|
+
|
|
84
|
+
@property
|
|
85
|
+
def folderEntryID(self) -> Optional[EntryID]:
|
|
86
|
+
"""
|
|
87
|
+
The EntryID of the folder than contains the original message.
|
|
88
|
+
"""
|
|
89
|
+
return self.__folderEntryID
|
|
90
|
+
|
|
91
|
+
@property
|
|
92
|
+
def messageEntryID(self) -> Optional[EntryID]:
|
|
93
|
+
"""
|
|
94
|
+
The EntryID of the original message.
|
|
95
|
+
"""
|
|
96
|
+
return self.__messageEntryID
|
|
97
|
+
|
|
98
|
+
@property
|
|
99
|
+
def messageSearchKey(self) -> Optional[bytes]:
|
|
100
|
+
"""
|
|
101
|
+
The search key of the original message.
|
|
102
|
+
"""
|
|
103
|
+
return self.__messageSearchKey
|
|
104
|
+
|
|
105
|
+
@property
|
|
106
|
+
def searchFolderEntryID(self) -> Optional[EntryID]:
|
|
107
|
+
"""
|
|
108
|
+
The EntryID of an alternate folder that contains the original message.
|
|
109
|
+
"""
|
|
110
|
+
return self.__searchFolderEntryID
|
|
111
|
+
|
|
112
|
+
@property
|
|
113
|
+
def storeEntryID(self) -> Optional[EntryID]:
|
|
114
|
+
"""
|
|
115
|
+
The EntryID of the mailbox that contains the original message.
|
|
116
|
+
"""
|
|
117
|
+
return self.__storeEntryID
|
|
118
|
+
|
|
119
|
+
@property
|
|
120
|
+
def version(self) -> int:
|
|
121
|
+
"""
|
|
122
|
+
The version used.
|
|
123
|
+
|
|
124
|
+
If SearchFolderEntryID is present, this MUST be ``0x00020001``,
|
|
125
|
+
otherwise it MUST be ``0x00010001``.
|
|
126
|
+
"""
|
|
127
|
+
return self.__version
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
__all__ = [
|
|
2
|
+
'SystemTime',
|
|
3
|
+
]
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
from typing import Any, Optional
|
|
7
|
+
|
|
8
|
+
from .. import constants
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class SystemTime:
|
|
12
|
+
"""
|
|
13
|
+
A SYSTEMTIME struct, as defined in [MS-DTYP].
|
|
14
|
+
"""
|
|
15
|
+
def __init__(self, data: Optional[bytes] = None):
|
|
16
|
+
data = data or (b'\x00' * 16)
|
|
17
|
+
self.unpack(data)
|
|
18
|
+
|
|
19
|
+
def __eq__(self, other: Any) -> bool:
|
|
20
|
+
return isinstance(other, SystemTime) and bytes(self) == bytes(other)
|
|
21
|
+
|
|
22
|
+
def __ne__(self, other: Any) -> bool:
|
|
23
|
+
return not self.__eq__(other)
|
|
24
|
+
|
|
25
|
+
def __bytes__(self) -> bytes:
|
|
26
|
+
return self.toBytes()
|
|
27
|
+
|
|
28
|
+
def toBytes(self) -> bytes:
|
|
29
|
+
"""
|
|
30
|
+
Packs the current data into bytes.
|
|
31
|
+
"""
|
|
32
|
+
return constants.st.ST_SYSTEMTIME.pack(self.__year,
|
|
33
|
+
self.__month,
|
|
34
|
+
self.__dayOfWeek,
|
|
35
|
+
self.__day,
|
|
36
|
+
self.__hour,
|
|
37
|
+
self.__minute,
|
|
38
|
+
self.__second,
|
|
39
|
+
self.__milliseconds)
|
|
40
|
+
|
|
41
|
+
def unpack(self, data: bytes) -> None:
|
|
42
|
+
"""
|
|
43
|
+
Fills out the fields of this instance by unpacking the bytes.
|
|
44
|
+
"""
|
|
45
|
+
unpacked = constants.st.ST_SYSTEMTIME.unpack(data)
|
|
46
|
+
self.__year = unpacked[0]
|
|
47
|
+
self.__month = unpacked[1]
|
|
48
|
+
self.__dayOfWeek = unpacked[2]
|
|
49
|
+
self.__day = unpacked[3]
|
|
50
|
+
self.__hour = unpacked[4]
|
|
51
|
+
self.__minute = unpacked[5]
|
|
52
|
+
self.__second = unpacked[6]
|
|
53
|
+
self.__milliseconds = unpacked[7]
|
|
54
|
+
|
|
55
|
+
@property
|
|
56
|
+
def day(self) -> int:
|
|
57
|
+
return self.__day
|
|
58
|
+
|
|
59
|
+
@day.setter
|
|
60
|
+
def day(self, value: int) -> None:
|
|
61
|
+
if value < 0:
|
|
62
|
+
raise ValueError('Day must be positive.')
|
|
63
|
+
if value > 0xFFFF:
|
|
64
|
+
raise ValueError('Day cannot be greater than 0xFFFF.')
|
|
65
|
+
|
|
66
|
+
self.__day = value
|
|
67
|
+
|
|
68
|
+
@property
|
|
69
|
+
def dayOfWeek(self) -> int:
|
|
70
|
+
return self.__dayOfWeek
|
|
71
|
+
|
|
72
|
+
@dayOfWeek.setter
|
|
73
|
+
def dayOfWeek(self, value: int) -> None:
|
|
74
|
+
if value < 0:
|
|
75
|
+
raise ValueError('Day of week must be positive.')
|
|
76
|
+
if value > 0xFFFF:
|
|
77
|
+
raise ValueError('Day of week cannot be greater than 0xFFFF.')
|
|
78
|
+
|
|
79
|
+
self.__dayOfWeek = value
|
|
80
|
+
|
|
81
|
+
@property
|
|
82
|
+
def hour(self) -> int:
|
|
83
|
+
return self.__hour
|
|
84
|
+
|
|
85
|
+
@hour.setter
|
|
86
|
+
def hour(self, value: int) -> None:
|
|
87
|
+
if value < 0:
|
|
88
|
+
raise ValueError('Hour must be positive.')
|
|
89
|
+
if value > 0xFFFF:
|
|
90
|
+
raise ValueError('Hour cannot be greater than 0xFFFF.')
|
|
91
|
+
|
|
92
|
+
self.__hour = value
|
|
93
|
+
|
|
94
|
+
@property
|
|
95
|
+
def milliseconds(self) -> int:
|
|
96
|
+
return self.__milliseconds
|
|
97
|
+
|
|
98
|
+
@milliseconds.setter
|
|
99
|
+
def milliseconds(self, value: int) -> None:
|
|
100
|
+
if value < 0:
|
|
101
|
+
raise ValueError('Milliseconds must be positive.')
|
|
102
|
+
if value > 0xFFFF:
|
|
103
|
+
raise ValueError('Milliseconds cannot be greater than 0xFFFF.')
|
|
104
|
+
|
|
105
|
+
self.__milliseconds = value
|
|
106
|
+
|
|
107
|
+
@property
|
|
108
|
+
def minute(self) -> int:
|
|
109
|
+
return self.__minute
|
|
110
|
+
|
|
111
|
+
@minute.setter
|
|
112
|
+
def minute(self, value: int) -> None:
|
|
113
|
+
if value < 0:
|
|
114
|
+
raise ValueError('Minute must be positive.')
|
|
115
|
+
if value > 0xFFFF:
|
|
116
|
+
raise ValueError('Minute cannot be greater than 0xFFFF.')
|
|
117
|
+
|
|
118
|
+
self.__minute = value
|
|
119
|
+
|
|
120
|
+
@property
|
|
121
|
+
def month(self) -> int:
|
|
122
|
+
return self.__month
|
|
123
|
+
|
|
124
|
+
@month.setter
|
|
125
|
+
def month(self, value: int) -> None:
|
|
126
|
+
if value < 0:
|
|
127
|
+
raise ValueError('Month must be positive.')
|
|
128
|
+
if value > 0xFFFF:
|
|
129
|
+
raise ValueError('Month cannot be greater than 0xFFFF.')
|
|
130
|
+
|
|
131
|
+
self.__month = value
|
|
132
|
+
|
|
133
|
+
@property
|
|
134
|
+
def second(self) -> int:
|
|
135
|
+
return self.__second
|
|
136
|
+
|
|
137
|
+
@second.setter
|
|
138
|
+
def second(self, value: int) -> None:
|
|
139
|
+
if value < 0:
|
|
140
|
+
raise ValueError('Second must be positive.')
|
|
141
|
+
if value > 0xFFFF:
|
|
142
|
+
raise ValueError('Second cannot be greater than 0xFFFF.')
|
|
143
|
+
|
|
144
|
+
self.__second = value
|
|
145
|
+
|
|
146
|
+
@property
|
|
147
|
+
def year(self) -> int:
|
|
148
|
+
return self.__year
|
|
149
|
+
|
|
150
|
+
@year.setter
|
|
151
|
+
def year(self, value: int) -> None:
|
|
152
|
+
if value < 0:
|
|
153
|
+
raise ValueError('Year must be positive.')
|
|
154
|
+
if value > 0xFFFF:
|
|
155
|
+
raise ValueError('Year cannot be greater than 0xFFFF.')
|
|
156
|
+
|
|
157
|
+
self.__year = value
|
|
158
|
+
|