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,111 @@
|
|
|
1
|
+
__all__ = [
|
|
2
|
+
'TimeZoneDefinition',
|
|
3
|
+
]
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
from typing import List, Optional
|
|
7
|
+
|
|
8
|
+
from ..constants import st
|
|
9
|
+
from ._helpers import BytesReader
|
|
10
|
+
from .tz_rule import TZRule
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class TimeZoneDefinition:
|
|
14
|
+
"""
|
|
15
|
+
Structure for PidLidAppointmentTimeZoneDefinitionRecur from [MS-OXOCAL].
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def __init__(self, data: Optional[bytes] = None):
|
|
19
|
+
if not data:
|
|
20
|
+
self.__majorVersion = 2
|
|
21
|
+
self.__minorVersion = 1
|
|
22
|
+
self.__keyName = ''
|
|
23
|
+
self.__rules = [TZRule()]
|
|
24
|
+
return
|
|
25
|
+
reader = BytesReader(data)
|
|
26
|
+
self.__majorVersion = reader.readUnsignedByte()
|
|
27
|
+
self.__minorVersion = reader.readUnsignedByte()
|
|
28
|
+
cbHeader = reader.readUnsignedShort()
|
|
29
|
+
reader.assertRead(b'\x02\x00')
|
|
30
|
+
cchKeyName = reader.readUnsignedShort()
|
|
31
|
+
self.__keyName = reader.read(2 * cchKeyName).decode('utf-16-le')
|
|
32
|
+
cRules = reader.readUnsignedShort()
|
|
33
|
+
if cRules < 1 or cRules > 1024:
|
|
34
|
+
raise ValueError('Value for cRules was out of range.')
|
|
35
|
+
self.__rules = [reader.readClass(TZRule) for _ in range(cRules)]
|
|
36
|
+
|
|
37
|
+
def __bytes__(self) -> bytes:
|
|
38
|
+
return self.toBytes()
|
|
39
|
+
|
|
40
|
+
def toBytes(self) -> bytes:
|
|
41
|
+
# Validate some of the data.
|
|
42
|
+
if len(self.__rules) < 1:
|
|
43
|
+
raise ValueError('Cannot pack a TimeZoneDefinition with no rules.')
|
|
44
|
+
if len(self.__rules) > 1024:
|
|
45
|
+
raise ValueError('TimeZoneDefintion can only have up to 1024 rules.')
|
|
46
|
+
|
|
47
|
+
ret = bytes((self.__majorVersion, self.__minorVersion))
|
|
48
|
+
ret += st.ST_LE_UI16.pack(6 + 2 * len(self.__keyName))
|
|
49
|
+
ret += b'\x02\x00'
|
|
50
|
+
ret += st.ST_LE_UI16.pack(2* len(self.__keyName))
|
|
51
|
+
ret += st.ST_LE_UI16.pack(len(self.__rules))
|
|
52
|
+
ret += b''.join(bytes(x) for x in self.__rules)
|
|
53
|
+
|
|
54
|
+
return ret
|
|
55
|
+
|
|
56
|
+
@property
|
|
57
|
+
def keyName(self) -> str:
|
|
58
|
+
"""
|
|
59
|
+
The name of the associated time zone.
|
|
60
|
+
|
|
61
|
+
Not localized but instead set to the unique name of the desired time
|
|
62
|
+
zone.
|
|
63
|
+
"""
|
|
64
|
+
return self.__keyName
|
|
65
|
+
|
|
66
|
+
@keyName.setter
|
|
67
|
+
def keyName(self, value: str) -> None:
|
|
68
|
+
value = str(value)
|
|
69
|
+
if len(value) > 260:
|
|
70
|
+
raise ValueError('Key name must be a string less than 261 characters.')
|
|
71
|
+
|
|
72
|
+
self.__keyName = value
|
|
73
|
+
|
|
74
|
+
@property
|
|
75
|
+
def majorVersion(self) -> int:
|
|
76
|
+
"""
|
|
77
|
+
The major version.
|
|
78
|
+
"""
|
|
79
|
+
return self.__majorVersion
|
|
80
|
+
|
|
81
|
+
@majorVersion.setter
|
|
82
|
+
def majorVersion(self, value: int) -> None:
|
|
83
|
+
if value > 255:
|
|
84
|
+
raise ValueError('Major version cannot be greater than 255')
|
|
85
|
+
if value < 0:
|
|
86
|
+
raise ValueError('Major version must be positive.')
|
|
87
|
+
|
|
88
|
+
self.__minorVersion = value
|
|
89
|
+
|
|
90
|
+
@property
|
|
91
|
+
def minorVersion(self) -> int:
|
|
92
|
+
"""
|
|
93
|
+
The minor version.
|
|
94
|
+
"""
|
|
95
|
+
return self.__minorVersion
|
|
96
|
+
|
|
97
|
+
@minorVersion.setter
|
|
98
|
+
def minorVersion(self, value: int) -> None:
|
|
99
|
+
if value > 255:
|
|
100
|
+
raise ValueError('Minor version cannot be greater than 255')
|
|
101
|
+
if value < 0:
|
|
102
|
+
raise ValueError('Minor version must be positive.')
|
|
103
|
+
|
|
104
|
+
self.__minorVersion = value
|
|
105
|
+
|
|
106
|
+
@property
|
|
107
|
+
def rules(self) -> List[TZRule]:
|
|
108
|
+
"""
|
|
109
|
+
A tuple of TZRule structures that specifies a time zone.
|
|
110
|
+
"""
|
|
111
|
+
return self.__rules
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
__all__ = [
|
|
2
|
+
'TimeZoneStruct',
|
|
3
|
+
]
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
from typing import Optional
|
|
7
|
+
|
|
8
|
+
from .. import constants
|
|
9
|
+
from .system_time import SystemTime
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class TimeZoneStruct:
|
|
13
|
+
"""
|
|
14
|
+
A TimeZoneStruct, as specified in [MS-OXOCAL].
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def __init__(self, data: Optional[bytes] = None):
|
|
18
|
+
if not data:
|
|
19
|
+
self.__bias = 0
|
|
20
|
+
self.__standardBias = 0
|
|
21
|
+
self.__daylightBias = 0
|
|
22
|
+
self.__standardDate = SystemTime()
|
|
23
|
+
self.__daylightDate = SystemTime()
|
|
24
|
+
|
|
25
|
+
return
|
|
26
|
+
unpacked = constants.st.ST_TZ.unpack(data)
|
|
27
|
+
self.__bias = unpacked[0]
|
|
28
|
+
self.__standardBias = unpacked[1]
|
|
29
|
+
self.__daylightBias = unpacked[2]
|
|
30
|
+
self.__standardDate = SystemTime(unpacked[4])
|
|
31
|
+
self.__daylightDate = SystemTime(unpacked[6])
|
|
32
|
+
|
|
33
|
+
def __bytes__(self) -> bytes:
|
|
34
|
+
return self.toBytes()
|
|
35
|
+
|
|
36
|
+
def toBytes(self) -> bytes:
|
|
37
|
+
return constants.st.ST_TZ.pack(self.__bias,
|
|
38
|
+
self.__standardBias,
|
|
39
|
+
self.__daylightBias,
|
|
40
|
+
self.standardYear,
|
|
41
|
+
bytes(self.__standardDate),
|
|
42
|
+
self.daylightYear,
|
|
43
|
+
bytes(self.__daylightDate))
|
|
44
|
+
|
|
45
|
+
@property
|
|
46
|
+
def bias(self) -> int:
|
|
47
|
+
"""
|
|
48
|
+
The time zone's offset in minutes from UTC.
|
|
49
|
+
"""
|
|
50
|
+
return self.__bias
|
|
51
|
+
|
|
52
|
+
@property
|
|
53
|
+
def daylightBias(self) -> int:
|
|
54
|
+
"""
|
|
55
|
+
The offset in minutes from the value of the bias field during daylight
|
|
56
|
+
saving time.
|
|
57
|
+
"""
|
|
58
|
+
return self.__daylightBias
|
|
59
|
+
|
|
60
|
+
@property
|
|
61
|
+
def daylightDate(self) -> SystemTime:
|
|
62
|
+
"""
|
|
63
|
+
The date and local time that indicate when to begin using the value
|
|
64
|
+
specified in the daylightBias field. Uses the same format as
|
|
65
|
+
standardDate.
|
|
66
|
+
"""
|
|
67
|
+
return self.__daylightDate
|
|
68
|
+
|
|
69
|
+
@property
|
|
70
|
+
def daylightYear(self) -> int:
|
|
71
|
+
"""
|
|
72
|
+
The value of the daylightDate field's year.
|
|
73
|
+
"""
|
|
74
|
+
return self.__daylightDate.year
|
|
75
|
+
|
|
76
|
+
@property
|
|
77
|
+
def standardBias(self) -> int:
|
|
78
|
+
"""
|
|
79
|
+
The offset in minutes from the value of the bias field during standard
|
|
80
|
+
time.
|
|
81
|
+
"""
|
|
82
|
+
return self.__standardBias
|
|
83
|
+
|
|
84
|
+
@property
|
|
85
|
+
def standardDate(self) -> SystemTime:
|
|
86
|
+
"""
|
|
87
|
+
The date and local time that indicate when to begin using the value
|
|
88
|
+
specified in the standardBias field. If the time zone does not support
|
|
89
|
+
daylight's savings time, the month member must be 0. If the year is not
|
|
90
|
+
0, then it is an absolute date than only occurs once, otherwise it is a
|
|
91
|
+
relative date that occurs yearly.
|
|
92
|
+
|
|
93
|
+
See [MS-OXOCAL] for details.
|
|
94
|
+
"""
|
|
95
|
+
return self.__standardDate
|
|
96
|
+
|
|
97
|
+
@property
|
|
98
|
+
def standardYear(self) -> int:
|
|
99
|
+
"""
|
|
100
|
+
The value of the standardDate field's year.
|
|
101
|
+
"""
|
|
102
|
+
return self.__standardDate.year
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
__all__ = [
|
|
2
|
+
'TOCEntry',
|
|
3
|
+
]
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
from typing import Optional, Union
|
|
7
|
+
|
|
8
|
+
from ._helpers import BytesReader
|
|
9
|
+
from .cfoas import ClipboardFormatOrAnsiString
|
|
10
|
+
from ..constants import st
|
|
11
|
+
from .dv_target_device import DVTargetDevice
|
|
12
|
+
from ..enums import ADVF, DVAspect
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class TOCEntry:
|
|
16
|
+
def __init__(self, reader: Optional[Union[bytes, BytesReader]] = None):
|
|
17
|
+
if reader is None:
|
|
18
|
+
self.__clipFormat = ClipboardFormatOrAnsiString()
|
|
19
|
+
self.__aspect = 0
|
|
20
|
+
self.__lindex = 0
|
|
21
|
+
self.__tymed = 0
|
|
22
|
+
self.__advf = 0
|
|
23
|
+
self.__targetDevice = DVTargetDevice()
|
|
24
|
+
return
|
|
25
|
+
|
|
26
|
+
if isinstance(reader, bytes):
|
|
27
|
+
reader = BytesReader(reader)
|
|
28
|
+
|
|
29
|
+
self.__clipFormat = ClipboardFormatOrAnsiString(reader)
|
|
30
|
+
targetDeviceSize = reader.readUnsignedInt()
|
|
31
|
+
self.__aspect = reader.readUnsignedInt()
|
|
32
|
+
self.__lindex = reader.readUnsignedInt()
|
|
33
|
+
self.__tymed = reader.readUnsignedInt()
|
|
34
|
+
reader.read(12)
|
|
35
|
+
self.__advf = reader.readUnsignedInt()
|
|
36
|
+
|
|
37
|
+
# Based off the wording of the documentation, it seems like this can't
|
|
38
|
+
# actually be 0 bytes, so this should be fine.
|
|
39
|
+
self.__targetDevice = DVTargetDevice(reader.read(targetDeviceSize))
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def __bytes__(self) -> bytes:
|
|
43
|
+
return self.toBytes()
|
|
44
|
+
|
|
45
|
+
def toBytes(self) -> bytes:
|
|
46
|
+
ret = bytes(self.__clipFormat)
|
|
47
|
+
td = bytes(self.__targetDevice)
|
|
48
|
+
ret += st.ST_LE_UI32.pack(len(td))
|
|
49
|
+
ret += st.ST_LE_UI32.pack(self.__aspect)
|
|
50
|
+
ret += st.ST_LE_UI32.pack(self.__lindex)
|
|
51
|
+
ret += st.ST_LE_UI32.pack(self.__tymed)
|
|
52
|
+
ret += b'\x00' * 12
|
|
53
|
+
ret += st.ST_LE_UI32.pack(self.__advf)
|
|
54
|
+
ret += b'\x00' * 4
|
|
55
|
+
ret += td
|
|
56
|
+
|
|
57
|
+
return ret
|
|
58
|
+
|
|
59
|
+
@property
|
|
60
|
+
def advf(self) -> Union[int, ADVF]:
|
|
61
|
+
"""
|
|
62
|
+
An implementation specific hint on how to render the presentation data
|
|
63
|
+
on screen. May be ignored on processing.
|
|
64
|
+
"""
|
|
65
|
+
return self.__advf
|
|
66
|
+
|
|
67
|
+
@advf.setter
|
|
68
|
+
def advf(self, val: Union[int, ADVF]) -> None:
|
|
69
|
+
if not isinstance(val, int):
|
|
70
|
+
raise TypeError(':property advf: must be an int.')
|
|
71
|
+
if val < 0:
|
|
72
|
+
raise ValueError(':property advf: must be positive.')
|
|
73
|
+
if val > 0xFFFFFFFF:
|
|
74
|
+
raise ValueError(':property advf: cannot be greater than 0xFFFFFFFF.')
|
|
75
|
+
|
|
76
|
+
self.__advf = val
|
|
77
|
+
|
|
78
|
+
@property
|
|
79
|
+
def ansiClipboardFormat(self) -> ClipboardFormatOrAnsiString:
|
|
80
|
+
return self.__clipFormat
|
|
81
|
+
|
|
82
|
+
@property
|
|
83
|
+
def aspect(self) -> Union[int, DVAspect]:
|
|
84
|
+
"""
|
|
85
|
+
An implementation specific hint on how to render the presentation data
|
|
86
|
+
on screen. May be ignored on processing.
|
|
87
|
+
"""
|
|
88
|
+
return self.__aspect
|
|
89
|
+
|
|
90
|
+
@aspect.setter
|
|
91
|
+
def aspect(self, val: Union[int, DVAspect]) -> None:
|
|
92
|
+
if not isinstance(val, int):
|
|
93
|
+
raise TypeError(':property aspect: must be an int.')
|
|
94
|
+
if val < 0:
|
|
95
|
+
raise ValueError(':property aspect: must be positive.')
|
|
96
|
+
if val > 0xFFFFFFFF:
|
|
97
|
+
raise ValueError(':property aspect: cannot be greater than 0xFFFFFFFF.')
|
|
98
|
+
|
|
99
|
+
self.__aspect = val
|
|
100
|
+
|
|
101
|
+
@property
|
|
102
|
+
def lindex(self) -> int:
|
|
103
|
+
"""
|
|
104
|
+
An implementation specific hint on how to render the presentation data
|
|
105
|
+
on screen. May be ignored on processing.
|
|
106
|
+
"""
|
|
107
|
+
return self.__lindex
|
|
108
|
+
|
|
109
|
+
@lindex.setter
|
|
110
|
+
def lindex(self, val: int) -> None:
|
|
111
|
+
if not isinstance(val, int):
|
|
112
|
+
raise TypeError(':property lindex: must be an int.')
|
|
113
|
+
if val < 0:
|
|
114
|
+
raise ValueError(':property lindex: must be positive.')
|
|
115
|
+
if val > 0xFFFFFFFF:
|
|
116
|
+
raise ValueError(':property lindex: cannot be greater than 0xFFFFFFFF.')
|
|
117
|
+
|
|
118
|
+
self.__lindex = val
|
|
119
|
+
|
|
120
|
+
@property
|
|
121
|
+
def targetDevice(self) -> DVTargetDevice:
|
|
122
|
+
return self.__targetDevice
|
|
123
|
+
|
|
124
|
+
@property
|
|
125
|
+
def tymed(self) -> int:
|
|
126
|
+
return self.__tymed
|
|
127
|
+
|
|
128
|
+
@tymed.setter
|
|
129
|
+
def tymed(self, val: int) -> None:
|
|
130
|
+
if not isinstance(val, int):
|
|
131
|
+
raise TypeError(':property lindex: must be an int.')
|
|
132
|
+
if val < 0:
|
|
133
|
+
raise ValueError(':property lindex: must be positive.')
|
|
134
|
+
if val > 0xFFFFFFFF:
|
|
135
|
+
raise ValueError(':property lindex: cannot be greater than 0xFFFFFFFF.')
|
|
136
|
+
|
|
137
|
+
self.__tymed = val
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
__all__ = [
|
|
2
|
+
'TZRule',
|
|
3
|
+
]
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
import logging
|
|
7
|
+
|
|
8
|
+
from struct import Struct
|
|
9
|
+
from typing import Final, final, Optional
|
|
10
|
+
|
|
11
|
+
from ..enums import TZFlag
|
|
12
|
+
from ._helpers import BytesReader
|
|
13
|
+
from .system_time import SystemTime
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
logger.addHandler(logging.NullHandler())
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@final
|
|
21
|
+
class TZRule:
|
|
22
|
+
"""
|
|
23
|
+
A TZRule structure, as defined in [MS-OXOCAL].
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
__SIZE__: int = 66
|
|
27
|
+
__struct: Final[Struct] = Struct('4B2H14x3i16s16s')
|
|
28
|
+
|
|
29
|
+
def __init__(self, data: Optional[bytes] = None):
|
|
30
|
+
if not data:
|
|
31
|
+
self.__majorVersion = 2
|
|
32
|
+
self.__minorVersion = 1
|
|
33
|
+
self.__flags = TZFlag(0)
|
|
34
|
+
self.__year = 0
|
|
35
|
+
self.__bias = 0
|
|
36
|
+
self.__standardBias = 0
|
|
37
|
+
self.__daylightBias = 0
|
|
38
|
+
self.__standardDate = SystemTime()
|
|
39
|
+
self.__daylightDate = SystemTime()
|
|
40
|
+
return
|
|
41
|
+
|
|
42
|
+
reader = BytesReader(data)
|
|
43
|
+
self.__majorVersion = reader.readUnsignedByte()
|
|
44
|
+
self.__minorVersion = reader.readUnsignedByte()
|
|
45
|
+
reader.assertRead(b'\x3E\x00')
|
|
46
|
+
self.__flags = TZFlag(reader.readUnsignedShort())
|
|
47
|
+
self.__year = reader.readUnsignedShort()
|
|
48
|
+
# This *MUST* be null, however I've seen Outlook not follow that. Simply
|
|
49
|
+
# log a warning about it even though it's a violation.
|
|
50
|
+
if any(b := reader.read(14)):
|
|
51
|
+
logger.warning(f'Read TZRule with non-null X section (got {b}).')
|
|
52
|
+
self.__bias = reader.readInt()
|
|
53
|
+
self.__standardBias = reader.readInt()
|
|
54
|
+
self.__daylightBias = reader.readInt()
|
|
55
|
+
self.__standardDate = SystemTime(reader.read(16))
|
|
56
|
+
self.__daylightDate = SystemTime(reader.read(16))
|
|
57
|
+
|
|
58
|
+
def __bytes__(self) -> bytes:
|
|
59
|
+
return self.toBytes()
|
|
60
|
+
|
|
61
|
+
def toBytes(self) -> bytes:
|
|
62
|
+
return self.__struct.pack(self.__majorVersion,
|
|
63
|
+
self.__minorVersion,
|
|
64
|
+
62,
|
|
65
|
+
0,
|
|
66
|
+
self.__flags,
|
|
67
|
+
self.__year,
|
|
68
|
+
self.__bias,
|
|
69
|
+
self.__standardBias,
|
|
70
|
+
self.__daylightBias,
|
|
71
|
+
bytes(self.__standardDate),
|
|
72
|
+
bytes(self.__daylightDate))
|
|
73
|
+
|
|
74
|
+
@property
|
|
75
|
+
def bias(self) -> int:
|
|
76
|
+
"""
|
|
77
|
+
The time zone's offset in minutes from UTC.
|
|
78
|
+
"""
|
|
79
|
+
return self.__bias
|
|
80
|
+
|
|
81
|
+
@property
|
|
82
|
+
def daylightBias(self) -> int:
|
|
83
|
+
"""
|
|
84
|
+
The offset in minutes from the value of the bias field during daylight
|
|
85
|
+
saving time.
|
|
86
|
+
"""
|
|
87
|
+
return self.__daylightBias
|
|
88
|
+
|
|
89
|
+
@property
|
|
90
|
+
def daylightDate(self) -> SystemTime:
|
|
91
|
+
"""
|
|
92
|
+
The date and local time that indicate when to begin using the value
|
|
93
|
+
specified in the daylightBias field. Uses the same format as
|
|
94
|
+
standardDate.
|
|
95
|
+
"""
|
|
96
|
+
return self.__daylightDate
|
|
97
|
+
|
|
98
|
+
@property
|
|
99
|
+
def flags(self) -> TZFlag:
|
|
100
|
+
"""
|
|
101
|
+
The flags for this rule.
|
|
102
|
+
"""
|
|
103
|
+
return self.__flags
|
|
104
|
+
|
|
105
|
+
@property
|
|
106
|
+
def majorVersion(self) -> int:
|
|
107
|
+
"""
|
|
108
|
+
The major version.
|
|
109
|
+
"""
|
|
110
|
+
return self.__majorVersion
|
|
111
|
+
|
|
112
|
+
@property
|
|
113
|
+
def minorVersion(self) -> int:
|
|
114
|
+
"""
|
|
115
|
+
The minor version.
|
|
116
|
+
"""
|
|
117
|
+
return self.__minorVersion
|
|
118
|
+
|
|
119
|
+
@property
|
|
120
|
+
def standardBias(self) -> int:
|
|
121
|
+
"""
|
|
122
|
+
The offset in minutes from the value of the bias field during standard
|
|
123
|
+
time.
|
|
124
|
+
"""
|
|
125
|
+
return self.__standardBias
|
|
126
|
+
|
|
127
|
+
@property
|
|
128
|
+
def standardDate(self) -> SystemTime:
|
|
129
|
+
"""
|
|
130
|
+
The date and local time that indicate when to begin using the value
|
|
131
|
+
specified in the standardBias field. If the time zone does not support
|
|
132
|
+
daylight's savings time, the month member must be 0. If the year is not
|
|
133
|
+
0, then it is an absolute date than only occurs once, otherwise it is a
|
|
134
|
+
relative date that occurs yearly.
|
|
135
|
+
|
|
136
|
+
See [MS-OXOCAL] for details.
|
|
137
|
+
"""
|
|
138
|
+
return self.__standardDate
|
|
139
|
+
|
|
140
|
+
@property
|
|
141
|
+
def year(self) -> int:
|
|
142
|
+
"""
|
|
143
|
+
The year this rule is scheduled to take place. A rule will remain in
|
|
144
|
+
effect from January 1 of it's year until January 1 of the next rule's
|
|
145
|
+
year field. If no rules exist for subsequent years, this rule will
|
|
146
|
+
remain in effect indefinately.
|
|
147
|
+
"""
|
|
148
|
+
return self.__year
|