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,131 @@
|
|
|
1
|
+
__all__ = [
|
|
2
|
+
'Journal',
|
|
3
|
+
]
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
import base64
|
|
7
|
+
import datetime
|
|
8
|
+
import functools
|
|
9
|
+
import json
|
|
10
|
+
|
|
11
|
+
from typing import List, Optional
|
|
12
|
+
|
|
13
|
+
from ..constants import HEADER_FORMAT_TYPE, ps
|
|
14
|
+
from ..enums import LogFlags
|
|
15
|
+
from .message_base import MessageBase
|
|
16
|
+
from ..utils import minutesToDurationStr
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class Journal(MessageBase):
|
|
20
|
+
"""
|
|
21
|
+
Class for parsing Journal messages.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def getJson(self) -> str:
|
|
25
|
+
return json.dumps({
|
|
26
|
+
'subject': self.subject,
|
|
27
|
+
'entryType': self.logTypeDesc,
|
|
28
|
+
'company': self.companies[0] if self.companies else None,
|
|
29
|
+
'start': self.logStart.__format__(self.datetimeFormat) if self.logStart else None,
|
|
30
|
+
'end': self.logEnd.__format__(self.datetimeFormat) if self.logEnd else None,
|
|
31
|
+
'duration': minutesToDurationStr(self.duration),
|
|
32
|
+
'body': self.body,
|
|
33
|
+
# There is a good chance the body property won't exist, so this is a
|
|
34
|
+
# backup.
|
|
35
|
+
'rtfBodyB64': base64.b64encode(self.rtfBody).decode('ascii') if self.rtfBody else None,
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
@functools.cached_property
|
|
39
|
+
def companies(self) -> Optional[List[str]]:
|
|
40
|
+
"""
|
|
41
|
+
Contains a list of company names, each of which is accociated with a
|
|
42
|
+
contact this is precified in the contacts property.
|
|
43
|
+
"""
|
|
44
|
+
return self.getNamedProp('8539', ps.PSETID_COMMON)
|
|
45
|
+
|
|
46
|
+
@functools.cached_property
|
|
47
|
+
def logDocumentPosted(self) -> bool:
|
|
48
|
+
"""
|
|
49
|
+
Indicates whether the document was sent by email of posted to a server
|
|
50
|
+
folder during journaling.
|
|
51
|
+
"""
|
|
52
|
+
return bool(self.getNamedProp('8711', ps.PSETID_LOG))
|
|
53
|
+
|
|
54
|
+
@functools.cached_property
|
|
55
|
+
def logDocumentPrinted(self) -> bool:
|
|
56
|
+
"""
|
|
57
|
+
Indicates whether the document was printed during journaling.
|
|
58
|
+
"""
|
|
59
|
+
return bool(self.getNamedProp('870E', ps.PSETID_LOG))
|
|
60
|
+
|
|
61
|
+
@functools.cached_property
|
|
62
|
+
def logDocumentRouted(self) -> bool:
|
|
63
|
+
"""
|
|
64
|
+
Indicates whether the document was sent to a routing recipient during
|
|
65
|
+
journaling.
|
|
66
|
+
"""
|
|
67
|
+
return bool(self.getNamedProp('8710', ps.PSETID_LOG))
|
|
68
|
+
|
|
69
|
+
@functools.cached_property
|
|
70
|
+
def logDocumentSaved(self) -> bool:
|
|
71
|
+
"""
|
|
72
|
+
Indicates whether the document was saved during journaling.
|
|
73
|
+
"""
|
|
74
|
+
return bool(self.getNamedProp('870F', ps.PSETID_LOG))
|
|
75
|
+
|
|
76
|
+
@functools.cached_property
|
|
77
|
+
def logDuration(self) -> int:
|
|
78
|
+
"""
|
|
79
|
+
The duration, in minutes, of the activity.
|
|
80
|
+
"""
|
|
81
|
+
return self.getNamedProp('8707', ps.PSETID_LOG, 0)
|
|
82
|
+
|
|
83
|
+
@functools.cached_property
|
|
84
|
+
def logEnd(self) -> Optional[datetime.datetime]:
|
|
85
|
+
"""
|
|
86
|
+
The name of the activity that is being recorded.
|
|
87
|
+
"""
|
|
88
|
+
return self.getNamedProp('8708', ps.PSETID_LOG)
|
|
89
|
+
|
|
90
|
+
@functools.cached_property
|
|
91
|
+
def logFlags(self) -> LogFlags:
|
|
92
|
+
"""
|
|
93
|
+
The name of the activity that is being recorded.
|
|
94
|
+
"""
|
|
95
|
+
return LogFlags(self.getNamedProp('870C', ps.PSETID_LOG, 0))
|
|
96
|
+
|
|
97
|
+
@functools.cached_property
|
|
98
|
+
def logStart(self) -> Optional[datetime.datetime]:
|
|
99
|
+
"""
|
|
100
|
+
The name of the activity that is being recorded.
|
|
101
|
+
"""
|
|
102
|
+
return self.getNamedProp('8706', ps.PSETID_LOG)
|
|
103
|
+
|
|
104
|
+
@functools.cached_property
|
|
105
|
+
def logType(self) -> Optional[str]:
|
|
106
|
+
"""
|
|
107
|
+
The name of the activity that is being recorded.
|
|
108
|
+
"""
|
|
109
|
+
return self.getNamedProp('8700', ps.PSETID_LOG)
|
|
110
|
+
|
|
111
|
+
@functools.cached_property
|
|
112
|
+
def logTypeDesc(self) -> Optional[str]:
|
|
113
|
+
"""
|
|
114
|
+
The description of the activity that is being recorded.
|
|
115
|
+
"""
|
|
116
|
+
return self.getNamedProp('8712', ps.PSETID_LOG)
|
|
117
|
+
|
|
118
|
+
@property
|
|
119
|
+
def headerFormatProperties(self) -> HEADER_FORMAT_TYPE:
|
|
120
|
+
return {
|
|
121
|
+
'-main details-': {
|
|
122
|
+
'Subject': self.subject,
|
|
123
|
+
'Entry Type': self.logTypeDesc,
|
|
124
|
+
'Company': self.companies[0] if self.companies else None,
|
|
125
|
+
},
|
|
126
|
+
'-time-': {
|
|
127
|
+
'Start': self.logStart.__format__(self.datetimeFormat) if self.logStart else None,
|
|
128
|
+
'End': self.logEnd.__format__(self.datetimeFormat) if self.logEnd else None,
|
|
129
|
+
'Duration': minutesToDurationStr(self.duration),
|
|
130
|
+
},
|
|
131
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
__all__ = [
|
|
2
|
+
'MeetingCancellation',
|
|
3
|
+
]
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
import json
|
|
7
|
+
|
|
8
|
+
from .. import constants
|
|
9
|
+
from ..enums import RecurPatternType, ResponseStatus
|
|
10
|
+
from .meeting_related import MeetingRelated
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
# The documentation for this only specifies restrictions on existing properties,
|
|
14
|
+
# so we just mostly leave this alone.
|
|
15
|
+
class MeetingCancellation(MeetingRelated):
|
|
16
|
+
"""
|
|
17
|
+
Class for a Meeting Cancellation object.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def getJson(self) -> str:
|
|
21
|
+
meetingStatusString = {
|
|
22
|
+
ResponseStatus.NONE: None,
|
|
23
|
+
ResponseStatus.ORGANIZED: 'Meeting organizer',
|
|
24
|
+
ResponseStatus.TENTATIVE: 'Tentatively accepted',
|
|
25
|
+
ResponseStatus.ACCEPTED: 'Accepted',
|
|
26
|
+
ResponseStatus.DECLINED: 'Declined',
|
|
27
|
+
ResponseStatus.NOT_RESPONDED: 'Not yet responded',
|
|
28
|
+
}[self.responseStatus]
|
|
29
|
+
|
|
30
|
+
# Get the recurrence string.
|
|
31
|
+
recur = '(none)'
|
|
32
|
+
if self.appointmentRecur:
|
|
33
|
+
recur = {
|
|
34
|
+
RecurPatternType.DAY: 'Daily',
|
|
35
|
+
RecurPatternType.WEEK: 'Weekly',
|
|
36
|
+
RecurPatternType.MONTH: 'Monthly',
|
|
37
|
+
RecurPatternType.MONTH_NTH: 'Monthly',
|
|
38
|
+
RecurPatternType.MONTH_END: 'Monthly',
|
|
39
|
+
RecurPatternType.HJ_MONTH: 'Monthly',
|
|
40
|
+
RecurPatternType.HJ_MONTH_NTH: 'Monthly',
|
|
41
|
+
RecurPatternType.HJ_MONTH_END: 'Monthly',
|
|
42
|
+
}[self.appointmentRecur.patternType]
|
|
43
|
+
|
|
44
|
+
return json.dumps({
|
|
45
|
+
'recurrence': recur,
|
|
46
|
+
'recurrencePattern': self.recurrencePattern,
|
|
47
|
+
'body': self.body,
|
|
48
|
+
'meetingStatus': meetingStatusString,
|
|
49
|
+
'organizer': self.organizer,
|
|
50
|
+
'requiredAttendees': self.to,
|
|
51
|
+
'optionalAttendees': self.cc,
|
|
52
|
+
'resources': self.bcc,
|
|
53
|
+
'start': self.startDate.__format__(self.datetimeFormat) if self.endDate else None,
|
|
54
|
+
'end': self.endDate.__format__(self.datetimeFormat) if self.endDate else None,
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
@property
|
|
58
|
+
def headerFormatProperties(self) -> constants.HEADER_FORMAT_TYPE:
|
|
59
|
+
meetingStatusString = {
|
|
60
|
+
ResponseStatus.NONE: None,
|
|
61
|
+
ResponseStatus.ORGANIZED: 'Meeting organizer',
|
|
62
|
+
ResponseStatus.TENTATIVE: 'Tentatively accepted',
|
|
63
|
+
ResponseStatus.ACCEPTED: 'Accepted',
|
|
64
|
+
ResponseStatus.DECLINED: 'Declined',
|
|
65
|
+
ResponseStatus.NOT_RESPONDED: 'Not yet responded',
|
|
66
|
+
}[self.responseStatus]
|
|
67
|
+
|
|
68
|
+
# Get the recurrence string.
|
|
69
|
+
recur = '(none)'
|
|
70
|
+
if self.appointmentRecur:
|
|
71
|
+
recur = {
|
|
72
|
+
RecurPatternType.DAY: 'Daily',
|
|
73
|
+
RecurPatternType.WEEK: 'Weekly',
|
|
74
|
+
RecurPatternType.MONTH: 'Monthly',
|
|
75
|
+
RecurPatternType.MONTH_NTH: 'Monthly',
|
|
76
|
+
RecurPatternType.MONTH_END: 'Monthly',
|
|
77
|
+
RecurPatternType.HJ_MONTH: 'Monthly',
|
|
78
|
+
RecurPatternType.HJ_MONTH_NTH: 'Monthly',
|
|
79
|
+
RecurPatternType.HJ_MONTH_END: 'Monthly',
|
|
80
|
+
}[self.appointmentRecur.patternType]
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
'-main info-': {
|
|
84
|
+
'Subject': self.subject,
|
|
85
|
+
'Location': self.location,
|
|
86
|
+
},
|
|
87
|
+
'-date-': {
|
|
88
|
+
'Start': self.startDate.__format__(self.datetimeFormat) if self.startDate else None,
|
|
89
|
+
'End': self.endDate.__format__(self.datetimeFormat) if self.endDate else None,
|
|
90
|
+
'Show Time As': 'Free',
|
|
91
|
+
},
|
|
92
|
+
'-recurrence-': {
|
|
93
|
+
'Recurrance': recur,
|
|
94
|
+
'Recurrence Pattern': self.recurrencePattern,
|
|
95
|
+
},
|
|
96
|
+
'-status-': {
|
|
97
|
+
'Meeting Status': meetingStatusString,
|
|
98
|
+
},
|
|
99
|
+
'-attendees-': {
|
|
100
|
+
'Organizer': self.organizer,
|
|
101
|
+
'Required Attendees': self.to,
|
|
102
|
+
'Optional Attendees': self.cc,
|
|
103
|
+
'Resources': self.bcc,
|
|
104
|
+
},
|
|
105
|
+
'-importance-': {
|
|
106
|
+
'Importance': self.importanceString,
|
|
107
|
+
},
|
|
108
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
__all__ = [
|
|
2
|
+
'MeetingException',
|
|
3
|
+
]
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
import datetime
|
|
7
|
+
import functools
|
|
8
|
+
|
|
9
|
+
from typing import Optional
|
|
10
|
+
|
|
11
|
+
from .. import constants
|
|
12
|
+
from ..enums import SaveType
|
|
13
|
+
from .meeting_related import MeetingRelated
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class MeetingException(MeetingRelated):
|
|
17
|
+
"""
|
|
18
|
+
Class for handling Meeting Exceptions.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def save(self, **_) -> constants.SAVE_TYPE:
|
|
22
|
+
"""
|
|
23
|
+
Meeting Exceptions are hidden attachments with no save behaviors.
|
|
24
|
+
|
|
25
|
+
If you want something to happen for saving, you can call the save of a
|
|
26
|
+
parent class or write your own code.
|
|
27
|
+
"""
|
|
28
|
+
return (SaveType.NONE, None)
|
|
29
|
+
|
|
30
|
+
@functools.cached_property
|
|
31
|
+
def exceptionReplaceTime(self) -> Optional[datetime.datetime]:
|
|
32
|
+
"""
|
|
33
|
+
The date and time within the recurrence pattern that the exception will
|
|
34
|
+
replace.
|
|
35
|
+
|
|
36
|
+
The value is specified in UTC.
|
|
37
|
+
"""
|
|
38
|
+
return self.getNamedProp('8228', constants.ps.PSETID_APPOINTMENT)
|
|
39
|
+
|
|
40
|
+
@functools.cached_property
|
|
41
|
+
def fExceptionalBody(self) -> bool:
|
|
42
|
+
"""
|
|
43
|
+
Indicates that the Exception Embedded Message object has a body that
|
|
44
|
+
differs from the Recurring Calendar object.
|
|
45
|
+
|
|
46
|
+
If ``True``, the Exception MUST have a body.
|
|
47
|
+
"""
|
|
48
|
+
return bool(self.getNamedProp('8206', constants.ps.PSETID_APPOINTMENT))
|
|
49
|
+
|
|
50
|
+
@functools.cached_property
|
|
51
|
+
def fInvited(self) -> bool:
|
|
52
|
+
"""
|
|
53
|
+
Indicates if invitations have been sent for this exception.
|
|
54
|
+
"""
|
|
55
|
+
return bool(self.getNamedProp('8229', constants.ps.PSETID_APPOINTMENT))
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
__all__ = [
|
|
2
|
+
'MeetingForwardNotification',
|
|
3
|
+
]
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
import functools
|
|
7
|
+
import json
|
|
8
|
+
|
|
9
|
+
from typing import Optional
|
|
10
|
+
|
|
11
|
+
from .. import constants
|
|
12
|
+
from .meeting_related import MeetingRelated
|
|
13
|
+
from ..enums import RecurPatternType, ResponseStatus
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class MeetingForwardNotification(MeetingRelated):
|
|
17
|
+
"""
|
|
18
|
+
Class for handling Meeting Forward Notification objects.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def getJson(self) -> str:
|
|
22
|
+
meetingStatusString = {
|
|
23
|
+
ResponseStatus.NONE: None,
|
|
24
|
+
ResponseStatus.ORGANIZED: 'Meeting organizer',
|
|
25
|
+
ResponseStatus.TENTATIVE: 'Tentatively accepted',
|
|
26
|
+
ResponseStatus.ACCEPTED: 'Accepted',
|
|
27
|
+
ResponseStatus.DECLINED: 'Declined',
|
|
28
|
+
ResponseStatus.NOT_RESPONDED: 'Not yet responded',
|
|
29
|
+
}[self.responseStatus]
|
|
30
|
+
|
|
31
|
+
# Get the recurrence string.
|
|
32
|
+
recur = '(none)'
|
|
33
|
+
if self.appointmentRecur:
|
|
34
|
+
recur = {
|
|
35
|
+
RecurPatternType.DAY: 'Daily',
|
|
36
|
+
RecurPatternType.WEEK: 'Weekly',
|
|
37
|
+
RecurPatternType.MONTH: 'Monthly',
|
|
38
|
+
RecurPatternType.MONTH_NTH: 'Monthly',
|
|
39
|
+
RecurPatternType.MONTH_END: 'Monthly',
|
|
40
|
+
RecurPatternType.HJ_MONTH: 'Monthly',
|
|
41
|
+
RecurPatternType.HJ_MONTH_NTH: 'Monthly',
|
|
42
|
+
RecurPatternType.HJ_MONTH_END: 'Monthly',
|
|
43
|
+
}[self.appointmentRecur.patternType]
|
|
44
|
+
|
|
45
|
+
return json.dumps({
|
|
46
|
+
'recurrence': recur,
|
|
47
|
+
'recurrencePattern': self.recurrencePattern,
|
|
48
|
+
'body': self.body,
|
|
49
|
+
'meetingStatus': meetingStatusString,
|
|
50
|
+
'organizer': self.organizer,
|
|
51
|
+
'requiredAttendees': self.to,
|
|
52
|
+
'optionalAttendees': self.cc,
|
|
53
|
+
'resources': self.bcc,
|
|
54
|
+
'start': self.startDate.__format__(self.datetimeFormat) if self.endDate else None,
|
|
55
|
+
'end': self.endDate.__format__(self.datetimeFormat) if self.endDate else None,
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
@functools.cached_property
|
|
59
|
+
def forwardNotificationRecipients(self) -> Optional[bytes]:
|
|
60
|
+
"""
|
|
61
|
+
Bytes containing a list of RecipientRow structures that indicate the
|
|
62
|
+
recipients of a meeting forward.
|
|
63
|
+
|
|
64
|
+
Incomplete, looks to be the same structure as
|
|
65
|
+
appointmentUnsendableRecipients, so we need more examples of this.
|
|
66
|
+
"""
|
|
67
|
+
return self.getNamedProp('8261', constants.ps.PSETID_APPOINTMENT)
|
|
68
|
+
|
|
69
|
+
@property
|
|
70
|
+
def headerFormatProperties(self) -> constants.HEADER_FORMAT_TYPE:
|
|
71
|
+
# Get the recurrence string.
|
|
72
|
+
recur = '(none)'
|
|
73
|
+
if self.appointmentRecur:
|
|
74
|
+
recur = {
|
|
75
|
+
RecurPatternType.DAY: 'Daily',
|
|
76
|
+
RecurPatternType.WEEK: 'Weekly',
|
|
77
|
+
RecurPatternType.MONTH: 'Monthly',
|
|
78
|
+
RecurPatternType.MONTH_NTH: 'Monthly',
|
|
79
|
+
RecurPatternType.MONTH_END: 'Monthly',
|
|
80
|
+
RecurPatternType.HJ_MONTH: 'Monthly',
|
|
81
|
+
RecurPatternType.HJ_MONTH_NTH: 'Monthly',
|
|
82
|
+
RecurPatternType.HJ_MONTH_END: 'Monthly',
|
|
83
|
+
}[self.appointmentRecur.patternType]
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
'-main info-': {
|
|
87
|
+
'Subject': self.subject,
|
|
88
|
+
'Location': self.location,
|
|
89
|
+
},
|
|
90
|
+
'-date-': {
|
|
91
|
+
'Start': self.startDate.__format__(self.datetimeFormat) if self.startDate else None,
|
|
92
|
+
'End': self.endDate.__format__(self.datetimeFormat) if self.endDate else None,
|
|
93
|
+
},
|
|
94
|
+
'-recurrence-': {
|
|
95
|
+
'Recurrance': recur,
|
|
96
|
+
'Recurrence Pattern': self.recurrencePattern,
|
|
97
|
+
},
|
|
98
|
+
'-attendees-': {
|
|
99
|
+
'Organizer': self.organizer,
|
|
100
|
+
'Required Attendees': self.to,
|
|
101
|
+
'Optional Attendees': self.cc,
|
|
102
|
+
'Resources': self.bcc,
|
|
103
|
+
},
|
|
104
|
+
'-importance-': {
|
|
105
|
+
'Importance': self.importanceString,
|
|
106
|
+
},
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
@functools.cached_property
|
|
110
|
+
def promptSendUpdate(self) -> bool:
|
|
111
|
+
"""
|
|
112
|
+
Indicates that the Meeting Forward Notification object was out-of-date
|
|
113
|
+
when it was received.
|
|
114
|
+
"""
|
|
115
|
+
return bool(self.getNamedProp('8045', constants.ps.PSETID_COMMON))
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
__all__ = [
|
|
2
|
+
'MeetingRelated',
|
|
3
|
+
]
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
import datetime
|
|
7
|
+
import functools
|
|
8
|
+
|
|
9
|
+
from typing import Optional
|
|
10
|
+
|
|
11
|
+
from ..constants import ps
|
|
12
|
+
from .calendar_base import CalendarBase
|
|
13
|
+
from ..enums import ServerProcessingAction
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class MeetingRelated(CalendarBase):
|
|
17
|
+
"""
|
|
18
|
+
Base class for meeting-related objects.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
@functools.cached_property
|
|
22
|
+
def attendeeCriticalChange(self) -> Optional[datetime.datetime]:
|
|
23
|
+
"""
|
|
24
|
+
The date and time at which the meeting-related object was sent.
|
|
25
|
+
"""
|
|
26
|
+
return self.getNamedProp('0001', ps.PSETID_MEETING)
|
|
27
|
+
|
|
28
|
+
@functools.cached_property
|
|
29
|
+
def processed(self) -> bool:
|
|
30
|
+
"""
|
|
31
|
+
Indicates whether a client has processed a meeting-related object.
|
|
32
|
+
"""
|
|
33
|
+
return bool(self.getPropertyVal('7D01000B'))
|
|
34
|
+
|
|
35
|
+
@functools.cached_property
|
|
36
|
+
def serverProcessed(self) -> bool:
|
|
37
|
+
"""
|
|
38
|
+
Indicates that the Meeting Request object or Meeting Update object has
|
|
39
|
+
been processed.
|
|
40
|
+
"""
|
|
41
|
+
return bool(self.getNamedProp('85CC', ps.PSETID_CALENDAR_ASSISTANT))
|
|
42
|
+
|
|
43
|
+
@functools.cached_property
|
|
44
|
+
def serverProcessingActions(self) -> Optional[ServerProcessingAction]:
|
|
45
|
+
"""
|
|
46
|
+
A union of which actions have been taken on the Meeting Request object
|
|
47
|
+
or Meeting Update object.
|
|
48
|
+
"""
|
|
49
|
+
return self.getNamedAs('85CD', ps.PSETID_CALENDAR_ASSISTANT, ServerProcessingAction)
|
|
50
|
+
|
|
51
|
+
@functools.cached_property
|
|
52
|
+
def timeZone(self) -> Optional[int]:
|
|
53
|
+
"""
|
|
54
|
+
Specifies information about the time zone of a recurring meeting.
|
|
55
|
+
|
|
56
|
+
See PidLidTimeZone in [MS-OXOCAL] for details.
|
|
57
|
+
"""
|
|
58
|
+
return self.getNamedProp('000C', ps.PSETID_MEETING)
|
|
59
|
+
|
|
60
|
+
@functools.cached_property
|
|
61
|
+
def where(self) -> Optional[str]:
|
|
62
|
+
"""
|
|
63
|
+
PidLidWhere. Should be the same as location.
|
|
64
|
+
"""
|
|
65
|
+
return self.getNamedProp('0002', ps.PSETID_MEETING)
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
__all__ = [
|
|
2
|
+
'MeetingRequest',
|
|
3
|
+
]
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
import datetime
|
|
7
|
+
import functools
|
|
8
|
+
import json
|
|
9
|
+
|
|
10
|
+
from typing import Optional
|
|
11
|
+
|
|
12
|
+
from ..constants import HEADER_FORMAT_TYPE, ps
|
|
13
|
+
from .meeting_related import MeetingRelated
|
|
14
|
+
from ..enums import BusyStatus, MeetingObjectChange, MeetingType, RecurCalendarType, RecurPatternType, ResponseStatus
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class MeetingRequest(MeetingRelated):
|
|
18
|
+
"""
|
|
19
|
+
Class for handling Meeting Request and Meeting Update objects.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def getJson(self) -> str:
|
|
23
|
+
meetingStatusString = {
|
|
24
|
+
ResponseStatus.NONE: None,
|
|
25
|
+
ResponseStatus.ORGANIZED: 'Meeting organizer',
|
|
26
|
+
ResponseStatus.TENTATIVE: 'Tentatively accepted',
|
|
27
|
+
ResponseStatus.ACCEPTED: 'Accepted',
|
|
28
|
+
ResponseStatus.DECLINED: 'Declined',
|
|
29
|
+
ResponseStatus.NOT_RESPONDED: 'Not yet responded',
|
|
30
|
+
}[self.responseStatus]
|
|
31
|
+
|
|
32
|
+
# Get the recurrence string.
|
|
33
|
+
recur = '(none)'
|
|
34
|
+
if self.appointmentRecur:
|
|
35
|
+
recur = {
|
|
36
|
+
RecurPatternType.DAY: 'Daily',
|
|
37
|
+
RecurPatternType.WEEK: 'Weekly',
|
|
38
|
+
RecurPatternType.MONTH: 'Monthly',
|
|
39
|
+
RecurPatternType.MONTH_NTH: 'Monthly',
|
|
40
|
+
RecurPatternType.MONTH_END: 'Monthly',
|
|
41
|
+
RecurPatternType.HJ_MONTH: 'Monthly',
|
|
42
|
+
RecurPatternType.HJ_MONTH_NTH: 'Monthly',
|
|
43
|
+
RecurPatternType.HJ_MONTH_END: 'Monthly',
|
|
44
|
+
}[self.appointmentRecur.patternType]
|
|
45
|
+
|
|
46
|
+
return json.dumps({
|
|
47
|
+
'recurrence': recur,
|
|
48
|
+
'recurrencePattern': self.recurrencePattern,
|
|
49
|
+
'body': self.body,
|
|
50
|
+
'meetingStatus': meetingStatusString,
|
|
51
|
+
'organizer': self.organizer,
|
|
52
|
+
'requiredAttendees': self.to,
|
|
53
|
+
'optionalAttendees': self.cc,
|
|
54
|
+
'resources': self.bcc,
|
|
55
|
+
'start': self.startDate.__format__(self.datetimeFormat) if self.endDate else None,
|
|
56
|
+
'end': self.endDate.__format__(self.datetimeFormat) if self.endDate else None,
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
@functools.cached_property
|
|
60
|
+
def appointmentMessageClass(self) -> Optional[str]:
|
|
61
|
+
"""
|
|
62
|
+
Indicates the value of the PidTagMessageClass property of the Meeting
|
|
63
|
+
object that is to be generated from the Meeting Request object. MUST
|
|
64
|
+
start with "IPM.Appointment".
|
|
65
|
+
"""
|
|
66
|
+
return self.getNamedProp('0024', ps.PSETID_MEETING)
|
|
67
|
+
|
|
68
|
+
@functools.cached_property
|
|
69
|
+
def calendarType(self) -> Optional[RecurCalendarType]:
|
|
70
|
+
"""
|
|
71
|
+
The value of the CalendarType field from the PidLidAppointmentRecur
|
|
72
|
+
property if the Meeting Request object represents a recurring series or
|
|
73
|
+
an exception.
|
|
74
|
+
"""
|
|
75
|
+
return self.getNamedAs('001C', ps.PSETID_MEETING, RecurCalendarType)
|
|
76
|
+
|
|
77
|
+
@functools.cached_property
|
|
78
|
+
def changeHighlight(self) -> Optional[MeetingObjectChange]:
|
|
79
|
+
"""
|
|
80
|
+
Soecifies a bit field that indicates how the Meeting object has been
|
|
81
|
+
changed.
|
|
82
|
+
|
|
83
|
+
Returns a union of the set flags.
|
|
84
|
+
"""
|
|
85
|
+
return self.getNamedAs('8204', ps.PSETID_APPOINTMENT, MeetingObjectChange)
|
|
86
|
+
|
|
87
|
+
@functools.cached_property
|
|
88
|
+
def forwardInstance(self) -> bool:
|
|
89
|
+
"""
|
|
90
|
+
Indicates that the Meeting Request object represents an exception to a
|
|
91
|
+
recurring series, and it was forwarded (even when forwarded by the
|
|
92
|
+
organizer) rather than being an invitation sent by the organizer.
|
|
93
|
+
"""
|
|
94
|
+
return bool(self.getNamedProp('820A', ps.PSETID_APPOINTMENT))
|
|
95
|
+
|
|
96
|
+
@property
|
|
97
|
+
def headerFormatProperties(self) -> HEADER_FORMAT_TYPE:
|
|
98
|
+
meetingStatusString = {
|
|
99
|
+
ResponseStatus.NONE: None,
|
|
100
|
+
ResponseStatus.ORGANIZED: 'Meeting organizer',
|
|
101
|
+
ResponseStatus.TENTATIVE: 'Tentatively accepted',
|
|
102
|
+
ResponseStatus.ACCEPTED: 'Accepted',
|
|
103
|
+
ResponseStatus.DECLINED: 'Declined',
|
|
104
|
+
ResponseStatus.NOT_RESPONDED: 'Not yet responded',
|
|
105
|
+
}[self.responseStatus]
|
|
106
|
+
|
|
107
|
+
# Get the recurrence string.
|
|
108
|
+
recur = '(none)'
|
|
109
|
+
if self.appointmentRecur:
|
|
110
|
+
recur = {
|
|
111
|
+
RecurPatternType.DAY: 'Daily',
|
|
112
|
+
RecurPatternType.WEEK: 'Weekly',
|
|
113
|
+
RecurPatternType.MONTH: 'Monthly',
|
|
114
|
+
RecurPatternType.MONTH_NTH: 'Monthly',
|
|
115
|
+
RecurPatternType.MONTH_END: 'Monthly',
|
|
116
|
+
RecurPatternType.HJ_MONTH: 'Monthly',
|
|
117
|
+
RecurPatternType.HJ_MONTH_NTH: 'Monthly',
|
|
118
|
+
RecurPatternType.HJ_MONTH_END: 'Monthly',
|
|
119
|
+
}[self.appointmentRecur.patternType]
|
|
120
|
+
|
|
121
|
+
showTime = None if self.appointmentNotAllowPropose else 'Tentative'
|
|
122
|
+
|
|
123
|
+
return {
|
|
124
|
+
'-main info-': {
|
|
125
|
+
'Subject': self.subject,
|
|
126
|
+
'Location': self.location,
|
|
127
|
+
},
|
|
128
|
+
'-date-': {
|
|
129
|
+
'Start': self.startDate.__format__(self.datetimeFormat) if self.startDate else None,
|
|
130
|
+
'End': self.endDate.__format__(self.datetimeFormat) if self.endDate else None,
|
|
131
|
+
'Show Time As': showTime,
|
|
132
|
+
},
|
|
133
|
+
'-recurrence-': {
|
|
134
|
+
'Recurrance': recur,
|
|
135
|
+
'Recurrence Pattern': self.recurrencePattern,
|
|
136
|
+
},
|
|
137
|
+
'-status-': {
|
|
138
|
+
'Meeting Status': meetingStatusString,
|
|
139
|
+
},
|
|
140
|
+
'-attendees-': {
|
|
141
|
+
'Organizer': self.organizer,
|
|
142
|
+
'Required Attendees': self.to,
|
|
143
|
+
'Optional Attendees': self.cc,
|
|
144
|
+
'Resources': self.bcc,
|
|
145
|
+
},
|
|
146
|
+
'-importance-': {
|
|
147
|
+
'Importance': self.importanceString,
|
|
148
|
+
},
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
@functools.cached_property
|
|
153
|
+
def intendedBusyStatus(self) -> Optional[BusyStatus]:
|
|
154
|
+
"""
|
|
155
|
+
The value of the busyStatus on the Meeting object in the organizer's
|
|
156
|
+
calendar at the time the Meeting Request object or Meeting Update object
|
|
157
|
+
was sent.
|
|
158
|
+
"""
|
|
159
|
+
return self.getNamedAs('8224', ps.PSETID_APPOINTMENT, BusyStatus)
|
|
160
|
+
|
|
161
|
+
@functools.cached_property
|
|
162
|
+
def meetingType(self) -> Optional[MeetingType]:
|
|
163
|
+
"""
|
|
164
|
+
The type of Meeting Request object or Meeting Update object.
|
|
165
|
+
"""
|
|
166
|
+
return self.getNamedAs('0026', ps.PSETID_MEETING, MeetingType)
|
|
167
|
+
|
|
168
|
+
@functools.cached_property
|
|
169
|
+
def oldLocation(self) -> Optional[str]:
|
|
170
|
+
"""
|
|
171
|
+
The original value of the location property before a meeting update.
|
|
172
|
+
"""
|
|
173
|
+
return self.getNamedProp('0028', ps.PSETID_MEETING)
|
|
174
|
+
|
|
175
|
+
@functools.cached_property
|
|
176
|
+
def oldWhenEndWhole(self) -> Optional[datetime.datetime]:
|
|
177
|
+
"""
|
|
178
|
+
The original value of the appointmentEndWhole property before a meeting
|
|
179
|
+
update.
|
|
180
|
+
"""
|
|
181
|
+
return self.getNamedProp('002A', ps.PSETID_MEETING)
|
|
182
|
+
|
|
183
|
+
@functools.cached_property
|
|
184
|
+
def oldWhenStartWhole(self) -> Optional[datetime.datetime]:
|
|
185
|
+
"""
|
|
186
|
+
The original value of the appointmentStartWhole property before a
|
|
187
|
+
meeting update.
|
|
188
|
+
"""
|
|
189
|
+
return self.getNamedProp('0029', ps.PSETID_MEETING)
|