@rapidmx/mapi-plugin 1.0.0-beta.3

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 (180) hide show
  1. package/LICENSE +382 -0
  2. package/README.md +60 -0
  3. package/dist/lib/BaseMapiEmsmdbRoute.js +297 -0
  4. package/dist/lib/BaseMapiEmsmdbRoute.js.map +1 -0
  5. package/dist/lib/BaseMapiNspiRoute.js +114 -0
  6. package/dist/lib/BaseMapiNspiRoute.js.map +1 -0
  7. package/dist/lib/MapiSessionManager.js +119 -0
  8. package/dist/lib/MapiSessionManager.js.map +1 -0
  9. package/dist/lib/RopDispatcher.js +34 -0
  10. package/dist/lib/RopDispatcher.js.map +1 -0
  11. package/dist/lib/codec/AppointmentRecurrence.js +314 -0
  12. package/dist/lib/codec/AppointmentRecurrence.js.map +1 -0
  13. package/dist/lib/codec/BufferCursor.js +213 -0
  14. package/dist/lib/codec/BufferCursor.js.map +1 -0
  15. package/dist/lib/codec/GlobalObjectId.js +92 -0
  16. package/dist/lib/codec/GlobalObjectId.js.map +1 -0
  17. package/dist/lib/codec/MapiGuid.js +41 -0
  18. package/dist/lib/codec/MapiGuid.js.map +1 -0
  19. package/dist/lib/codec/MapiTimeZone.js +93 -0
  20. package/dist/lib/codec/MapiTimeZone.js.map +1 -0
  21. package/dist/lib/codec/PropertyValue.js +214 -0
  22. package/dist/lib/codec/PropertyValue.js.map +1 -0
  23. package/dist/lib/codec/RopBuffer.js +26 -0
  24. package/dist/lib/codec/RopBuffer.js.map +1 -0
  25. package/dist/lib/codec/TypedString.js +54 -0
  26. package/dist/lib/codec/TypedString.js.map +1 -0
  27. package/dist/lib/index.js +99 -0
  28. package/dist/lib/index.js.map +1 -0
  29. package/dist/lib/mongo/MapiEmsmdbRouteMongo.js +84 -0
  30. package/dist/lib/mongo/MapiEmsmdbRouteMongo.js.map +1 -0
  31. package/dist/lib/mongo/MapiNspiRouteMongo.js +33 -0
  32. package/dist/lib/mongo/MapiNspiRouteMongo.js.map +1 -0
  33. package/dist/lib/mongo/index.js +7 -0
  34. package/dist/lib/mongo/index.js.map +1 -0
  35. package/dist/lib/mongo.js +6 -0
  36. package/dist/lib/mongo.js.map +1 -0
  37. package/dist/lib/nspi/NspiBindHandler.js +54 -0
  38. package/dist/lib/nspi/NspiBindHandler.js.map +1 -0
  39. package/dist/lib/nspi/NspiCodec.js +106 -0
  40. package/dist/lib/nspi/NspiCodec.js.map +1 -0
  41. package/dist/lib/nspi/NspiGetMatchesHandler.js +142 -0
  42. package/dist/lib/nspi/NspiGetMatchesHandler.js.map +1 -0
  43. package/dist/lib/rop/CalendarEventTarget.js +27 -0
  44. package/dist/lib/rop/CalendarEventTarget.js.map +1 -0
  45. package/dist/lib/rop/CalendarNamedProperties.js +57 -0
  46. package/dist/lib/rop/CalendarNamedProperties.js.map +1 -0
  47. package/dist/lib/rop/ContactTarget.js +23 -0
  48. package/dist/lib/rop/ContactTarget.js.map +1 -0
  49. package/dist/lib/rop/FastTransferStream.js +98 -0
  50. package/dist/lib/rop/FastTransferStream.js.map +1 -0
  51. package/dist/lib/rop/FolderTarget.js +111 -0
  52. package/dist/lib/rop/FolderTarget.js.map +1 -0
  53. package/dist/lib/rop/MeetingMessageClassHandler.js +95 -0
  54. package/dist/lib/rop/MeetingMessageClassHandler.js.map +1 -0
  55. package/dist/lib/rop/MessageBodyStream.js +42 -0
  56. package/dist/lib/rop/MessageBodyStream.js.map +1 -0
  57. package/dist/lib/rop/MessageTarget.js +44 -0
  58. package/dist/lib/rop/MessageTarget.js.map +1 -0
  59. package/dist/lib/rop/NamedPropertyRegistry.js +66 -0
  60. package/dist/lib/rop/NamedPropertyRegistry.js.map +1 -0
  61. package/dist/lib/rop/PropertyResolvers.js +313 -0
  62. package/dist/lib/rop/PropertyResolvers.js.map +1 -0
  63. package/dist/lib/rop/RopCreateMessageHandler.js +53 -0
  64. package/dist/lib/rop/RopCreateMessageHandler.js.map +1 -0
  65. package/dist/lib/rop/RopDeleteFolderHandler.js +133 -0
  66. package/dist/lib/rop/RopDeleteFolderHandler.js.map +1 -0
  67. package/dist/lib/rop/RopDeleteMessagesHandler.js +70 -0
  68. package/dist/lib/rop/RopDeleteMessagesHandler.js.map +1 -0
  69. package/dist/lib/rop/RopFastTransferSourceCopyPropertiesHandler.js +61 -0
  70. package/dist/lib/rop/RopFastTransferSourceCopyPropertiesHandler.js.map +1 -0
  71. package/dist/lib/rop/RopFastTransferSourceCopyToHandler.js +61 -0
  72. package/dist/lib/rop/RopFastTransferSourceCopyToHandler.js.map +1 -0
  73. package/dist/lib/rop/RopFastTransferSourceGetBufferHandler.js +75 -0
  74. package/dist/lib/rop/RopFastTransferSourceGetBufferHandler.js.map +1 -0
  75. package/dist/lib/rop/RopGetContentsTableHandler.js +76 -0
  76. package/dist/lib/rop/RopGetContentsTableHandler.js.map +1 -0
  77. package/dist/lib/rop/RopGetHierarchyTableHandler.js +40 -0
  78. package/dist/lib/rop/RopGetHierarchyTableHandler.js.map +1 -0
  79. package/dist/lib/rop/RopGetPropertiesSpecificHandler.js +55 -0
  80. package/dist/lib/rop/RopGetPropertiesSpecificHandler.js.map +1 -0
  81. package/dist/lib/rop/RopGetPropertyIdsFromNamesHandler.js +92 -0
  82. package/dist/lib/rop/RopGetPropertyIdsFromNamesHandler.js.map +1 -0
  83. package/dist/lib/rop/RopHandler.js +2 -0
  84. package/dist/lib/rop/RopHandler.js.map +1 -0
  85. package/dist/lib/rop/RopLogonHandler.js +132 -0
  86. package/dist/lib/rop/RopLogonHandler.js.map +1 -0
  87. package/dist/lib/rop/RopOpenFolderHandler.js +46 -0
  88. package/dist/lib/rop/RopOpenFolderHandler.js.map +1 -0
  89. package/dist/lib/rop/RopOpenMessageHandler.js +81 -0
  90. package/dist/lib/rop/RopOpenMessageHandler.js.map +1 -0
  91. package/dist/lib/rop/RopOpenStreamHandler.js +81 -0
  92. package/dist/lib/rop/RopOpenStreamHandler.js.map +1 -0
  93. package/dist/lib/rop/RopQueryRowsHandler.js +91 -0
  94. package/dist/lib/rop/RopQueryRowsHandler.js.map +1 -0
  95. package/dist/lib/rop/RopReadStreamHandler.js +55 -0
  96. package/dist/lib/rop/RopReadStreamHandler.js.map +1 -0
  97. package/dist/lib/rop/RopReleaseHandler.js +20 -0
  98. package/dist/lib/rop/RopReleaseHandler.js.map +1 -0
  99. package/dist/lib/rop/RopSaveChangesMessageHandler.js +210 -0
  100. package/dist/lib/rop/RopSaveChangesMessageHandler.js.map +1 -0
  101. package/dist/lib/rop/RopSetColumnsHandler.js +52 -0
  102. package/dist/lib/rop/RopSetColumnsHandler.js.map +1 -0
  103. package/dist/lib/rop/RopSetPropertiesHandler.js +162 -0
  104. package/dist/lib/rop/RopSetPropertiesHandler.js.map +1 -0
  105. package/dist/lib/rop/RopSubmitMessageHandler.js +314 -0
  106. package/dist/lib/rop/RopSubmitMessageHandler.js.map +1 -0
  107. package/dist/lib/rop/RopWriteStreamHandler.js +45 -0
  108. package/dist/lib/rop/RopWriteStreamHandler.js.map +1 -0
  109. package/dist/lib/rop/TaskNamedProperties.js +29 -0
  110. package/dist/lib/rop/TaskNamedProperties.js.map +1 -0
  111. package/dist/lib/rop/TaskTarget.js +19 -0
  112. package/dist/lib/rop/TaskTarget.js.map +1 -0
  113. package/dist/lib/sql/MapiEmsmdbRouteSQL.js +84 -0
  114. package/dist/lib/sql/MapiEmsmdbRouteSQL.js.map +1 -0
  115. package/dist/lib/sql/MapiNspiRouteSQL.js +33 -0
  116. package/dist/lib/sql/MapiNspiRouteSQL.js.map +1 -0
  117. package/dist/lib/sql/index.js +7 -0
  118. package/dist/lib/sql/index.js.map +1 -0
  119. package/dist/lib/sql.js +6 -0
  120. package/dist/lib/sql.js.map +1 -0
  121. package/dist/types/BaseMapiEmsmdbRoute.d.ts +82 -0
  122. package/dist/types/BaseMapiNspiRoute.d.ts +38 -0
  123. package/dist/types/MapiSessionManager.d.ts +130 -0
  124. package/dist/types/RopDispatcher.d.ts +17 -0
  125. package/dist/types/codec/AppointmentRecurrence.d.ts +14 -0
  126. package/dist/types/codec/BufferCursor.d.ts +78 -0
  127. package/dist/types/codec/GlobalObjectId.d.ts +11 -0
  128. package/dist/types/codec/MapiGuid.d.ts +6 -0
  129. package/dist/types/codec/MapiTimeZone.d.ts +7 -0
  130. package/dist/types/codec/PropertyValue.d.ts +77 -0
  131. package/dist/types/codec/RopBuffer.d.ts +25 -0
  132. package/dist/types/codec/TypedString.d.ts +17 -0
  133. package/dist/types/index.d.ts +94 -0
  134. package/dist/types/mongo/MapiEmsmdbRouteMongo.d.ts +19 -0
  135. package/dist/types/mongo/MapiNspiRouteMongo.d.ts +13 -0
  136. package/dist/types/mongo/index.d.ts +2 -0
  137. package/dist/types/mongo.d.ts +1 -0
  138. package/dist/types/nspi/NspiBindHandler.d.ts +22 -0
  139. package/dist/types/nspi/NspiCodec.d.ts +59 -0
  140. package/dist/types/nspi/NspiGetMatchesHandler.d.ts +24 -0
  141. package/dist/types/rop/CalendarEventTarget.d.ts +29 -0
  142. package/dist/types/rop/CalendarNamedProperties.d.ts +37 -0
  143. package/dist/types/rop/ContactTarget.d.ts +29 -0
  144. package/dist/types/rop/FastTransferStream.d.ts +18 -0
  145. package/dist/types/rop/FolderTarget.d.ts +63 -0
  146. package/dist/types/rop/MeetingMessageClassHandler.d.ts +36 -0
  147. package/dist/types/rop/MessageBodyStream.d.ts +26 -0
  148. package/dist/types/rop/MessageTarget.d.ts +38 -0
  149. package/dist/types/rop/NamedPropertyRegistry.d.ts +46 -0
  150. package/dist/types/rop/PropertyResolvers.d.ts +60 -0
  151. package/dist/types/rop/RopCreateMessageHandler.d.ts +23 -0
  152. package/dist/types/rop/RopDeleteFolderHandler.d.ts +38 -0
  153. package/dist/types/rop/RopDeleteMessagesHandler.d.ts +31 -0
  154. package/dist/types/rop/RopFastTransferSourceCopyPropertiesHandler.d.ts +24 -0
  155. package/dist/types/rop/RopFastTransferSourceCopyToHandler.d.ts +24 -0
  156. package/dist/types/rop/RopFastTransferSourceGetBufferHandler.d.ts +23 -0
  157. package/dist/types/rop/RopGetContentsTableHandler.d.ts +31 -0
  158. package/dist/types/rop/RopGetHierarchyTableHandler.d.ts +17 -0
  159. package/dist/types/rop/RopGetPropertiesSpecificHandler.d.ts +23 -0
  160. package/dist/types/rop/RopGetPropertyIdsFromNamesHandler.d.ts +23 -0
  161. package/dist/types/rop/RopHandler.d.ts +67 -0
  162. package/dist/types/rop/RopLogonHandler.d.ts +31 -0
  163. package/dist/types/rop/RopOpenFolderHandler.d.ts +19 -0
  164. package/dist/types/rop/RopOpenMessageHandler.d.ts +27 -0
  165. package/dist/types/rop/RopOpenStreamHandler.d.ts +27 -0
  166. package/dist/types/rop/RopQueryRowsHandler.d.ts +36 -0
  167. package/dist/types/rop/RopReadStreamHandler.d.ts +18 -0
  168. package/dist/types/rop/RopReleaseHandler.d.ts +14 -0
  169. package/dist/types/rop/RopSaveChangesMessageHandler.d.ts +44 -0
  170. package/dist/types/rop/RopSetColumnsHandler.d.ts +21 -0
  171. package/dist/types/rop/RopSetPropertiesHandler.d.ts +38 -0
  172. package/dist/types/rop/RopSubmitMessageHandler.d.ts +70 -0
  173. package/dist/types/rop/RopWriteStreamHandler.d.ts +21 -0
  174. package/dist/types/rop/TaskNamedProperties.d.ts +24 -0
  175. package/dist/types/rop/TaskTarget.d.ts +24 -0
  176. package/dist/types/sql/MapiEmsmdbRouteSQL.d.ts +19 -0
  177. package/dist/types/sql/MapiNspiRouteSQL.d.ts +13 -0
  178. package/dist/types/sql/index.d.ts +2 -0
  179. package/dist/types/sql.d.ts +1 -0
  180. package/package.json +104 -0
@@ -0,0 +1,313 @@
1
+ ///////////////////////////////////////////////////////////////////////////////
2
+ // Copyright (C) 2026 Jean-Philippe Steinmetz. All rights reserved.
3
+ // SPDX-License-Identifier: MPL-2.0
4
+ ///////////////////////////////////////////////////////////////////////////////
5
+ import { encodeAppointmentRecurrence } from "../codec/AppointmentRecurrence.js";
6
+ import { encodeTimeZoneStruct } from "../codec/MapiTimeZone.js";
7
+ import { PropertyType } from "../codec/PropertyValue.js";
8
+ import { BUSY_STATUS_CODES, LID_APPOINTMENT_END_WHOLE, LID_APPOINTMENT_RECUR, LID_APPOINTMENT_START_WHOLE, LID_BUSY_STATUS, LID_LOCATION, LID_RECURRING, LID_REMINDER_DELTA, LID_REMINDER_SET, LID_RESPONSE_STATUS, LID_TIME_ZONE_STRUCT, PSETID_APPOINTMENT, PSETID_COMMON, RESPONSE_STATUS_CODES, RESPONSE_STATUS_NONE, RESPONSE_STATUS_ORGANIZED, } from "./CalendarNamedProperties.js";
9
+ import { resolveCalendarEventInfo } from "./CalendarEventTarget.js";
10
+ import { resolveContactInfo } from "./ContactTarget.js";
11
+ import { assignOrGetFid, resolveFolderInfo } from "./FolderTarget.js";
12
+ import { assignOrGetMid, resolveMessageInfo } from "./MessageTarget.js";
13
+ import { resolveNamedProperty } from "./NamedPropertyRegistry.js";
14
+ import { LID_PERCENT_COMPLETE, LID_TASK_COMPLETE, LID_TASK_DUE_DATE, LID_TASK_STATUS, PSETID_TASK, TASK_STATUS_COMPLETE, TASK_STATUS_NOT_STARTED, } from "./TaskNamedProperties.js";
15
+ import { resolveTaskInfo } from "./TaskTarget.js";
16
+ // Well-known folder property IDs this pragmatic subset supports - the small set a real client needs to render
17
+ // a folder-hierarchy view. Add more as a real need arises, not speculatively.
18
+ const PID_TAG_DISPLAY_NAME = 0x3001;
19
+ const PID_TAG_FOLDER_ID = 0x6748;
20
+ const PID_TAG_CONTENT_COUNT = 0x3602;
21
+ const PID_TAG_CONTENT_UNREAD_COUNT = 0x3603;
22
+ const PID_TAG_SUBFOLDERS = 0x360a;
23
+ // Well-known message property IDs this pragmatic subset supports - the small set a real client needs to render
24
+ // a message list or a single message's metadata.
25
+ const PID_TAG_SUBJECT = 0x0037;
26
+ const PID_TAG_MESSAGE_FLAGS = 0x0e07;
27
+ const PID_TAG_HAS_ATTACHMENTS = 0x0e1b;
28
+ const PID_TAG_MESSAGE_DELIVERY_TIME = 0x0e06;
29
+ const PID_TAG_MID = 0x674a;
30
+ /** `MSGFLAG_READ`, the one `PidTagMessageFlags` bit this pragmatic subset ever sets. */
31
+ const MSGFLAG_READ = 0x01;
32
+ /** `PidNameKeywords` ([MS-OXPROPS] "Keywords") - Outlook's Categories feature, a `Kind=name` named property
33
+ * (unlike every other named property this pragmatic subset resolves, all `Kind=lid` - see
34
+ * `NamedPropertyRegistry.ts`'s own `PropertyName` shape for why both kinds are supported). Maps directly onto
35
+ * this data model's Gmail-style `Message.labelUids`: Outlook only understands free-text category names, not a
36
+ * `Label.uid` reference, so `resolvePropertyValues` resolves each uid to its `Label.name` before this property
37
+ * is ever read. `PT_MV_UNICODE` (`PtypMultipleString` in this codebase's own enum naming). */
38
+ const PSETID_PUBLIC_STRINGS = "00020329-0000-0000-c000-000000000046";
39
+ const NAME_KEYWORDS = "Keywords";
40
+ // Well-known Contact property IDs this pragmatic subset supports - all plain (non-named) PidTags, per
41
+ // [MS-OXOCNTC], since a Contact needs no PSETID/LID lookup the way Calendar/Task properties do.
42
+ const PID_TAG_GIVEN_NAME = 0x3a06;
43
+ const PID_TAG_SURNAME = 0x3a11;
44
+ const PID_TAG_EMAIL_ADDRESS = 0x3003;
45
+ const PID_TAG_BUSINESS_TELEPHONE_NUMBER = 0x3a08;
46
+ const PID_TAG_COMPANY_NAME = 0x3a16;
47
+ const PID_TAG_TITLE = 0x3a17;
48
+ // Calendar named-property identity (`(PropertySet GUID, LID)` pairs) this pragmatic subset supports - see
49
+ // CalendarNamedProperties.ts's own doc comment. A calendar item's property IDs `>= 0x8000` are resolved back
50
+ // to one of these via `NamedPropertyRegistry.resolveNamedProperty` before this switch can run, since MAPI has
51
+ // no fixed numeric ID for any of them the way `PidTagSubject` has one.
52
+ /** A type-appropriate zero/empty value for a requested property this handler has no real data for - keeps
53
+ * `StandardPropertyRow` encoding valid (a real value of the right type, per `writePropertyValue`'s
54
+ * expectations) without needing to model every property a client could ever ask for. Shared by
55
+ * `RopQueryRowsHandler` (table rows) and `RopGetPropertiesSpecificHandler` (single-object property fetch). */
56
+ export function defaultValueForType(propertyType) {
57
+ switch (propertyType) {
58
+ case PropertyType.PtypBoolean:
59
+ return false;
60
+ case PropertyType.PtypInteger16:
61
+ case PropertyType.PtypInteger32:
62
+ case PropertyType.PtypFloating32:
63
+ case PropertyType.PtypFloating64:
64
+ return 0;
65
+ case PropertyType.PtypInteger64:
66
+ return 0n;
67
+ case PropertyType.PtypTime:
68
+ return new Date(0);
69
+ case PropertyType.PtypGuid:
70
+ return "00000000-0000-0000-0000-000000000000";
71
+ case PropertyType.PtypBinary:
72
+ return Buffer.alloc(0);
73
+ case PropertyType.PtypMultipleInteger32:
74
+ case PropertyType.PtypMultipleString:
75
+ case PropertyType.PtypMultipleString8:
76
+ case PropertyType.PtypMultipleBinary:
77
+ return [];
78
+ case PropertyType.PtypString:
79
+ case PropertyType.PtypString8:
80
+ default:
81
+ return "";
82
+ }
83
+ }
84
+ /** Resolves one requested property's value for a `"folder:<uid>"`/`"virtual:<name>"` target. */
85
+ export function folderValueFor(session, propertyId, propertyType, target, info) {
86
+ switch (propertyId) {
87
+ case PID_TAG_DISPLAY_NAME:
88
+ return info.displayName;
89
+ case PID_TAG_FOLDER_ID:
90
+ return BigInt(assignOrGetFid(session, target));
91
+ case PID_TAG_CONTENT_COUNT:
92
+ return info.totalCount;
93
+ case PID_TAG_CONTENT_UNREAD_COUNT:
94
+ return info.unreadCount;
95
+ case PID_TAG_SUBFOLDERS:
96
+ return info.hasChildren;
97
+ default:
98
+ return defaultValueForType(propertyType);
99
+ }
100
+ }
101
+ /** Resolves one requested property's value for a `"message:<uid>"` target. `labelNames` is this message's own
102
+ * `labelUids` already resolved to display names (see `resolvePropertyValues`'s own doc comment) - a plain
103
+ * parameter rather than a repo lookup here, since this function (like every other `xxxValueFor`) is
104
+ * synchronous and can't do its own async DB access. */
105
+ export function messageValueFor(session, propertyId, propertyType, target, info, labelNames = []) {
106
+ switch (propertyId) {
107
+ case PID_TAG_SUBJECT:
108
+ return info.subject;
109
+ case PID_TAG_MESSAGE_FLAGS:
110
+ return info.read ? MSGFLAG_READ : 0;
111
+ case PID_TAG_HAS_ATTACHMENTS:
112
+ return info.hasAttachments;
113
+ case PID_TAG_MESSAGE_DELIVERY_TIME:
114
+ return info.receivedDate;
115
+ case PID_TAG_MID:
116
+ return BigInt(assignOrGetMid(session, target));
117
+ default: {
118
+ if (propertyId >= 0x8000) {
119
+ const named = resolveNamedProperty(session, propertyId);
120
+ if (named && named.kind === "name" && named.name === NAME_KEYWORDS && named.guid.toLowerCase() === PSETID_PUBLIC_STRINGS) {
121
+ return labelNames;
122
+ }
123
+ }
124
+ return defaultValueForType(propertyType);
125
+ }
126
+ }
127
+ }
128
+ /** Resolves one requested property's value for a `"calendarEvent:<uid>"` target. Almost every Appointment
129
+ * property is a *named* property (`PidLid*`, no fixed numeric ID) rather than a plain `PidTag*` - a property ID
130
+ * `>= 0x8000` is resolved back to its `(PropertySet GUID, LID)` identity via `resolveNamedProperty` before this
131
+ * can dispatch on it (see `NamedPropertyRegistry.ts`'s own doc comment and the architecture plan's "Calendar
132
+ * support" section for the full named-property table this switch implements). */
133
+ export function calendarEventValueFor(session, propertyId, propertyType, target, info, callerAddress) {
134
+ if (propertyId === PID_TAG_SUBJECT) {
135
+ return info.title;
136
+ }
137
+ if (propertyId === PID_TAG_MID) {
138
+ return BigInt(assignOrGetMid(session, target));
139
+ }
140
+ if (propertyId < 0x8000) {
141
+ return defaultValueForType(propertyType);
142
+ }
143
+ const namedProperty = resolveNamedProperty(session, propertyId);
144
+ if (!namedProperty || namedProperty.kind !== "lid") {
145
+ return defaultValueForType(propertyType);
146
+ }
147
+ const guid = namedProperty.guid.toLowerCase();
148
+ const isAppointmentProperty = guid === PSETID_APPOINTMENT;
149
+ const isCommonProperty = guid === PSETID_COMMON;
150
+ switch (namedProperty.lid) {
151
+ case LID_LOCATION:
152
+ return isAppointmentProperty ? (info.location ?? "") : defaultValueForType(propertyType);
153
+ case LID_APPOINTMENT_START_WHOLE:
154
+ return isAppointmentProperty ? info.startDate : defaultValueForType(propertyType);
155
+ case LID_APPOINTMENT_END_WHOLE:
156
+ return isAppointmentProperty ? info.endDate : defaultValueForType(propertyType);
157
+ case LID_BUSY_STATUS:
158
+ return isAppointmentProperty ? BUSY_STATUS_CODES[info.busyStatus] : defaultValueForType(propertyType);
159
+ case LID_RECURRING:
160
+ return isAppointmentProperty ? info.recurrenceRule !== undefined : defaultValueForType(propertyType);
161
+ case LID_APPOINTMENT_RECUR:
162
+ return isAppointmentProperty && info.recurrenceRule
163
+ ? encodeAppointmentRecurrence(info.recurrenceRule, info.startDate, info.endDate)
164
+ : defaultValueForType(propertyType);
165
+ case LID_TIME_ZONE_STRUCT:
166
+ return isAppointmentProperty ? encodeTimeZoneStruct(info.timezone, info.startDate) : defaultValueForType(propertyType);
167
+ case LID_RESPONSE_STATUS:
168
+ return isAppointmentProperty ? responseStatusFor(info, callerAddress) : defaultValueForType(propertyType);
169
+ case LID_REMINDER_SET:
170
+ return isCommonProperty ? info.reminderMinutesBeforeStart != null : defaultValueForType(propertyType);
171
+ case LID_REMINDER_DELTA:
172
+ return isCommonProperty ? (info.reminderMinutesBeforeStart ?? 0) : defaultValueForType(propertyType);
173
+ default:
174
+ return defaultValueForType(propertyType);
175
+ }
176
+ }
177
+ /** The caller's own `PidLidResponseStatus` value: `respOrganized` if the caller mailbox *is* this event's
178
+ * organizer, else the matching `Attendee.responseStatus` (by address, case-insensitive), else `respNone` if the
179
+ * caller isn't party to this event at all (e.g. a shared/delegate calendar view). */
180
+ function responseStatusFor(info, callerAddress) {
181
+ if (callerAddress && info.organizerAddress.toLowerCase() === callerAddress.toLowerCase()) {
182
+ return RESPONSE_STATUS_ORGANIZED;
183
+ }
184
+ const attendee = info.attendees.find((a) => a.address.toLowerCase() === callerAddress.toLowerCase());
185
+ return attendee ? RESPONSE_STATUS_CODES[attendee.responseStatus] : RESPONSE_STATUS_NONE;
186
+ }
187
+ /** Resolves one requested property's value for a `"contact:<uid>"` target - a `Folder` of type `CONTACTS`'s
188
+ * content-table rows/opened items. Every field here is a plain fixed `PidTag` (`[MS-OXOCNTC]`), unlike Calendar/
189
+ * Task's named-property tables, since Contact's own well-known fields all have real numeric IDs. */
190
+ export function contactValueFor(session, propertyId, propertyType, target, info) {
191
+ switch (propertyId) {
192
+ case PID_TAG_SUBJECT:
193
+ return info.displayName;
194
+ case PID_TAG_MID:
195
+ return BigInt(assignOrGetMid(session, target));
196
+ case PID_TAG_GIVEN_NAME:
197
+ return info.givenName ?? "";
198
+ case PID_TAG_SURNAME:
199
+ return info.surname ?? "";
200
+ case PID_TAG_EMAIL_ADDRESS:
201
+ return info.email ?? "";
202
+ case PID_TAG_BUSINESS_TELEPHONE_NUMBER:
203
+ return info.businessPhone ?? "";
204
+ case PID_TAG_COMPANY_NAME:
205
+ return info.companyName ?? "";
206
+ case PID_TAG_TITLE:
207
+ return info.jobTitle ?? "";
208
+ default:
209
+ return defaultValueForType(propertyType);
210
+ }
211
+ }
212
+ /** Resolves one requested property's value for a `"task:<uid>"` target - a `Folder` of type `TASKS`'s
213
+ * content-table rows/opened items. Almost every Task-specific property is a *named* property (`PidLid*`) under
214
+ * `PSETID_Task`, resolved back to its `(PropertySet GUID, LID)` identity via `resolveNamedProperty` exactly the
215
+ * same way `calendarEventValueFor` handles `PSETID_Appointment`/`PSETID_Common` - see `TaskNamedProperties.ts`'s
216
+ * own doc comment for the LID table this switch implements. */
217
+ export function taskValueFor(session, propertyId, propertyType, target, info) {
218
+ if (propertyId === PID_TAG_SUBJECT) {
219
+ return info.title;
220
+ }
221
+ if (propertyId === PID_TAG_MID) {
222
+ return BigInt(assignOrGetMid(session, target));
223
+ }
224
+ if (propertyId < 0x8000) {
225
+ return defaultValueForType(propertyType);
226
+ }
227
+ const namedProperty = resolveNamedProperty(session, propertyId);
228
+ if (!namedProperty || namedProperty.kind !== "lid" || namedProperty.guid.toLowerCase() !== PSETID_TASK) {
229
+ return defaultValueForType(propertyType);
230
+ }
231
+ switch (namedProperty.lid) {
232
+ case LID_TASK_STATUS:
233
+ return info.completed ? TASK_STATUS_COMPLETE : TASK_STATUS_NOT_STARTED;
234
+ case LID_PERCENT_COMPLETE:
235
+ return info.completed ? 1.0 : 0.0;
236
+ case LID_TASK_DUE_DATE:
237
+ return info.dueDate ?? defaultValueForType(propertyType);
238
+ case LID_TASK_COMPLETE:
239
+ return info.completed;
240
+ default:
241
+ return defaultValueForType(propertyType);
242
+ }
243
+ }
244
+ /** Resolves every column in `columns` for a single `target` (a `"folder:"`/`"virtual:"`/`"message:"`/
245
+ * `"calendarEvent:"`/`"contact:"`/`"task:"` target string), in order - the shared implementation behind both
246
+ * `RopQueryRowsHandler` (one call per table row) and `RopGetPropertiesSpecificHandler` (one call for the single
247
+ * object a handle refers to). */
248
+ export async function resolvePropertyValues(target, columns, context, cache) {
249
+ const isMessage = target.startsWith("message:");
250
+ const isCalendarEvent = target.startsWith("calendarEvent:");
251
+ const isContact = target.startsWith("contact:");
252
+ const isTask = target.startsWith("task:");
253
+ const folderInfo = isMessage || isCalendarEvent || isContact || isTask
254
+ ? undefined
255
+ : await resolveFolderInfo(context.mailboxUid, target, context.folderRepo, cache);
256
+ const messageInfo = isMessage ? await resolveMessageInfo(target, context.messageRepo) : undefined;
257
+ const calendarEventInfo = isCalendarEvent ? await resolveCalendarEventInfo(target, context.calendarEventRepo) : undefined;
258
+ const contactInfo = isContact && context.contactRepo ? await resolveContactInfo(target, context.contactRepo) : undefined;
259
+ const taskInfo = isTask && context.taskRepo ? await resolveTaskInfo(target, context.taskRepo) : undefined;
260
+ let labelNames = [];
261
+ if (messageInfo && messageInfo.labelUids.length > 0 && context.labelRepo) {
262
+ let labelNamesByUid;
263
+ if (cache) {
264
+ if (!cache.labelNamesByUid) {
265
+ const labels = await context.labelRepo.find({ mailboxUid: context.mailboxUid }, { ignoreACL: true });
266
+ cache.labelNamesByUid = new Map(labels.map((l) => [l.uid, l.name]));
267
+ }
268
+ labelNamesByUid = cache.labelNamesByUid;
269
+ }
270
+ else {
271
+ const labels = await context.labelRepo.find({ mailboxUid: context.mailboxUid }, { ignoreACL: true });
272
+ labelNamesByUid = new Map(labels.map((l) => [l.uid, l.name]));
273
+ }
274
+ labelNames = messageInfo.labelUids.map((uid) => labelNamesByUid.get(uid)).filter((name) => name !== undefined);
275
+ }
276
+ let callerAddress = "";
277
+ if (isCalendarEvent) {
278
+ if (cache) {
279
+ if (cache.callerAddress === undefined) {
280
+ const mailbox = await context.mailboxRepo.findOne(context.mailboxUid, { ignoreACL: true });
281
+ cache.callerAddress = mailbox?.primarySmtpAddress ?? "";
282
+ }
283
+ callerAddress = cache.callerAddress ?? "";
284
+ }
285
+ else {
286
+ const mailbox = await context.mailboxRepo.findOne(context.mailboxUid, { ignoreACL: true });
287
+ callerAddress = mailbox?.primarySmtpAddress ?? "";
288
+ }
289
+ }
290
+ return columns.map((column) => {
291
+ if (calendarEventInfo) {
292
+ return calendarEventValueFor(context.session, column.propertyId, column.propertyType, target, calendarEventInfo, callerAddress);
293
+ }
294
+ if (contactInfo) {
295
+ return contactValueFor(context.session, column.propertyId, column.propertyType, target, contactInfo);
296
+ }
297
+ if (taskInfo) {
298
+ return taskValueFor(context.session, column.propertyId, column.propertyType, target, taskInfo);
299
+ }
300
+ if (messageInfo) {
301
+ return messageValueFor(context.session, column.propertyId, column.propertyType, target, messageInfo, labelNames);
302
+ }
303
+ if (isContact || isTask) {
304
+ // contactRepo/taskRepo absent (see RopHandler.ts's own doc comment on why they're optional) - no
305
+ // real data to resolve against, so every column degrades to its type-appropriate default rather
306
+ // than falling through to the folder-info branch below (folderInfo is never populated for these
307
+ // target prefixes, see above - forcing that branch would throw on the `!` assertion).
308
+ return defaultValueForType(column.propertyType);
309
+ }
310
+ return folderValueFor(context.session, column.propertyId, column.propertyType, target, folderInfo);
311
+ });
312
+ }
313
+ //# sourceMappingURL=PropertyResolvers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PropertyResolvers.js","sourceRoot":"","sources":["../../../src/rop/PropertyResolvers.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ const ROP_ID_CREATE_MESSAGE = 0x06;
2
+ /** The well-known MAPI HRESULT `MAPI_E_NOT_FOUND`, reused as this handler's only failure `ReturnValue` - an
3
+ * unrecognized `FolderId`, the same constant `RopOpenFolderHandler` uses for its own analogous lookup miss. */
4
+ const ERROR_NOT_FOUND = 0x8004010f;
5
+ /**
6
+ * `RopCreateMessage` (`[MS-OXCMSG]`/`[MS-OXCROPS]`): begins composing a new message in a folder, producing a
7
+ * new `"message"` Server object handle later ROPs (`RopSetProperties`, `RopOpenStream`, `RopSaveChangesMessage`)
8
+ * reference by `InputHandleIndex`. Unlike `RopOpenMessage`'s handle (an already-real, already-saved message),
9
+ * this handle's `entityUid` is deliberately left `""` - there is no real backing `Message` row, and won't be
10
+ * one until `RopSubmitMessage` actually sends it (this pragmatic subset has no separate Drafts-folder
11
+ * persistence step; see `RopSaveChangesMessageHandler`'s own doc comment). `draftFolderUid` remembers the
12
+ * target folder for that eventual send, and `draftProperties` starts empty, filled in by later
13
+ * `RopSetProperties`/`RopWriteStream` calls.
14
+ *
15
+ * `CodePageId` is decoded to advance past it correctly but not otherwise acted on (this pragmatic subset always
16
+ * encodes strings as UTF-16LE/UTF-8 directly, the same treatment `RopOpenMessageHandler` gives it).
17
+ * `AssociatedFlag` (FAI messages - hidden configuration/rules objects, not real mail) is decoded but not
18
+ * honored - this pragmatic subset's compose/send path only ever produces ordinary mail messages.
19
+ *
20
+ * @author Jean-Philippe Steinmetz
21
+ */
22
+ export class RopCreateMessageHandler {
23
+ constructor() {
24
+ this.ropId = ROP_ID_CREATE_MESSAGE;
25
+ }
26
+ handle(reader, writer, context) {
27
+ reader.readUInt8(); // LogonId - this pragmatic subset doesn't track multiple concurrent logons per session
28
+ reader.readUInt8(); // InputHandleIndex - the logon handle this create is performed against; not
29
+ // separately validated, matching RopOpenFolderHandler's own treatment of its input handle.
30
+ const outputHandleIndex = reader.readUInt8();
31
+ reader.readUInt16LE(); // CodePageId - see class doc comment
32
+ const folderId = reader.readBigUInt64LE();
33
+ reader.readUInt8(); // AssociatedFlag - see class doc comment
34
+ const target = context.session.folderIds[folderId.toString()];
35
+ if (!target) {
36
+ writer.writeUInt8(ROP_ID_CREATE_MESSAGE);
37
+ writer.writeUInt8(outputHandleIndex);
38
+ writer.writeUInt32LE(ERROR_NOT_FOUND);
39
+ return;
40
+ }
41
+ context.session.handles[outputHandleIndex] = {
42
+ type: "message",
43
+ entityUid: "",
44
+ draftFolderUid: target,
45
+ draftProperties: {},
46
+ };
47
+ writer.writeUInt8(ROP_ID_CREATE_MESSAGE);
48
+ writer.writeUInt8(outputHandleIndex);
49
+ writer.writeUInt32LE(0); // ReturnValue - success
50
+ writer.writeUInt8(0); // HasMessageId - no MID is assigned until RopSaveChangesMessage
51
+ }
52
+ }
53
+ //# sourceMappingURL=RopCreateMessageHandler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RopCreateMessageHandler.js","sourceRoot":"","sources":["../../../src/rop/RopCreateMessageHandler.ts"],"names":[],"mappings":"AAOA,MAAM,qBAAqB,GAAG,IAAI,CAAC;AAEnC;+GAC+G;AAC/G,MAAM,eAAe,GAAG,UAAU,CAAC;AAEnC;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,OAAO,uBAAuB;IAApC;QACoB,UAAK,GAAG,qBAAqB,CAAC;IA+BlD,CAAC;IA7BU,MAAM,CAAC,MAAoB,EAAE,MAAoB,EAAE,OAAmB;QACzE,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,uFAAuF;QAC3G,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,4EAA4E;QAChG,2FAA2F;QAC3F,MAAM,iBAAiB,GAAW,MAAM,CAAC,SAAS,EAAE,CAAC;QACrD,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC,qCAAqC;QAC5D,MAAM,QAAQ,GAAW,MAAM,CAAC,eAAe,EAAE,CAAC;QAClD,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,yCAAyC;QAE7D,MAAM,MAAM,GAAuB,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC;QAClF,IAAI,CAAC,MAAM,EAAE,CAAC;YACV,MAAM,CAAC,UAAU,CAAC,qBAAqB,CAAC,CAAC;YACzC,MAAM,CAAC,UAAU,CAAC,iBAAiB,CAAC,CAAC;YACrC,MAAM,CAAC,aAAa,CAAC,eAAe,CAAC,CAAC;YACtC,OAAO;QACX,CAAC;QAED,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,iBAAiB,CAAC,GAAG;YACzC,IAAI,EAAE,SAAS;YACf,SAAS,EAAE,EAAE;YACb,cAAc,EAAE,MAAM;YACtB,eAAe,EAAE,EAAE;SACtB,CAAC;QAEF,MAAM,CAAC,UAAU,CAAC,qBAAqB,CAAC,CAAC;QACzC,MAAM,CAAC,UAAU,CAAC,iBAAiB,CAAC,CAAC;QACrC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,wBAAwB;QACjD,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,gEAAgE;IAC1F,CAAC;CACJ"}
@@ -0,0 +1,133 @@
1
+ const ROP_ID_DELETE_FOLDER = 0x1d;
2
+ /** The well-known MAPI HRESULT `MAPI_E_NOT_FOUND`, reused for an unrecognized `FolderId` - the same constant
3
+ * `RopOpenFolderHandler` uses for its own analogous lookup miss. */
4
+ const ERROR_NOT_FOUND = 0x8004010f;
5
+ /** The well-known MAPI HRESULT `MAPI_E_INVALID_OBJECT`, reused for both "the referenced handle isn't a folder"
6
+ * and "the folder has content/subfolders but the matching cascade flag wasn't set" - the same constant reused
7
+ * throughout this pragmatic subset as a generic "can't do this" signal, not a claim of exact per-condition
8
+ * `[MS-OXCFOLD]` return-value-table parity (the real spec's own `ecFolderNotEmpty` isn't separately modeled). */
9
+ const ERROR_INVALID_OBJECT = 0x80070005;
10
+ /** `DeleteFolderFlags` bits (`[MS-OXCFOLD]` §2.2.1.3.1, confirmed this session). */
11
+ const DEL_MESSAGES = 0x01;
12
+ const DEL_FOLDERS = 0x04;
13
+ const DELETE_HARD_DELETE = 0x10;
14
+ /**
15
+ * `RopDeleteFolder` (`[MS-OXCFOLD]`/`[MS-OXCROPS]`, RopId `0x1D`): deletes a folder by `FolderId`. Reuses
16
+ * `RecoverableRepoUtils.delete()` exactly as-built in Phase 2 for `Folder`/`Message`/`CalendarEvent` - see
17
+ * `RopDeleteMessagesHandler.ts`'s own doc comment for why that matters (EAS's watermark-based incremental
18
+ * delete detection depends on it).
19
+ *
20
+ * **Real spec semantics honored, not simplified away**: per `[MS-OXCFOLD]`, `RopDeleteFolder` only operates on
21
+ * an empty folder by default - `DEL_MESSAGES` must be set to also delete the folder's own messages/calendar
22
+ * events/contacts/tasks, and `DEL_FOLDERS` to also delete (recursively) its subfolders; a non-empty folder
23
+ * deleted without the matching flag fails rather than silently cascading. `DELETE_HARD_DELETE` maps directly
24
+ * onto `RepoDeleteOptions.purge` - real, exact semantic overlap with this library's own soft-delete model, not
25
+ * a coincidence this codec papers over.
26
+ *
27
+ * **Contacts/Tasks folders**: emptiness is checked against `contactRepo`/`taskRepo` alongside `messageRepo`/
28
+ * `calendarEventRepo`, and both are included in the delete cascade - a `CONTACTS`/`TASKS` folder's real content
29
+ * lives in those repos (see `ContactTarget.ts`/`TaskTarget.ts`), not `messageRepo`, so checking only the latter
30
+ * would let a non-empty Contacts/Tasks folder be deleted without `DEL_MESSAGES` and orphan its rows. Guarded
31
+ * with `context.contactRepo`/`taskRepo` presence checks the same way `RopGetContentsTableHandler` does, since
32
+ * `RopContext`'s doc comment documents both as optional.
33
+ *
34
+ * The request's own `InputHandleIndex` (nominally the *parent* folder of the one being deleted) is validated to
35
+ * be a real, open folder handle but not cross-checked against the target folder's actual `parentFolderUid` -
36
+ * the same pragmatic simplification `RopOpenMessageHandler`'s own doc comment documents for its analogous
37
+ * `FolderId` field.
38
+ *
39
+ * @author Jean-Philippe Steinmetz
40
+ */
41
+ export class RopDeleteFolderHandler {
42
+ constructor() {
43
+ this.ropId = ROP_ID_DELETE_FOLDER;
44
+ }
45
+ async handle(reader, writer, context) {
46
+ reader.readUInt8(); // LogonId - this pragmatic subset doesn't track multiple concurrent logons per session
47
+ const inputHandleIndex = reader.readUInt8();
48
+ const deleteFolderFlags = reader.readUInt8();
49
+ const folderId = reader.readBigUInt64LE();
50
+ const handle = context.session.handles[inputHandleIndex];
51
+ if (!handle || handle.type !== "folder") {
52
+ writer.writeUInt8(ROP_ID_DELETE_FOLDER);
53
+ writer.writeUInt8(inputHandleIndex);
54
+ writer.writeUInt32LE(ERROR_INVALID_OBJECT);
55
+ return;
56
+ }
57
+ const target = context.session.folderIds[folderId.toString()];
58
+ if (!target?.startsWith("folder:")) {
59
+ writer.writeUInt8(ROP_ID_DELETE_FOLDER);
60
+ writer.writeUInt8(inputHandleIndex);
61
+ writer.writeUInt32LE(ERROR_NOT_FOUND);
62
+ return;
63
+ }
64
+ const uid = target.slice("folder:".length);
65
+ const purge = (deleteFolderFlags & DELETE_HARD_DELETE) !== 0;
66
+ const messages = await context.messageRepo.find({ folderUid: uid }, { ignoreACL: true });
67
+ const events = await context.calendarEventRepo.find({ folderUid: uid }, { ignoreACL: true });
68
+ const contacts = context.contactRepo ? await context.contactRepo.find({ folderUid: uid }, { ignoreACL: true }) : [];
69
+ const tasks = context.taskRepo ? await context.taskRepo.find({ folderUid: uid }, { ignoreACL: true }) : [];
70
+ const childFolders = await context.folderRepo.find({ parentFolderUid: uid }, { ignoreACL: true });
71
+ if ((messages.length > 0 || events.length > 0 || contacts.length > 0 || tasks.length > 0) &&
72
+ (deleteFolderFlags & DEL_MESSAGES) === 0) {
73
+ writer.writeUInt8(ROP_ID_DELETE_FOLDER);
74
+ writer.writeUInt8(inputHandleIndex);
75
+ writer.writeUInt32LE(ERROR_INVALID_OBJECT);
76
+ return;
77
+ }
78
+ if (childFolders.length > 0 && (deleteFolderFlags & DEL_FOLDERS) === 0) {
79
+ writer.writeUInt8(ROP_ID_DELETE_FOLDER);
80
+ writer.writeUInt8(inputHandleIndex);
81
+ writer.writeUInt32LE(ERROR_INVALID_OBJECT);
82
+ return;
83
+ }
84
+ for (const message of messages) {
85
+ await context.messageRepo.delete(message.uid, { ignoreACL: true, purge });
86
+ }
87
+ for (const event of events) {
88
+ await context.calendarEventRepo.delete(event.uid, { ignoreACL: true, purge });
89
+ }
90
+ for (const contact of contacts) {
91
+ await context.contactRepo.delete(contact.uid, { ignoreACL: true, purge });
92
+ }
93
+ for (const task of tasks) {
94
+ await context.taskRepo.delete(task.uid, { ignoreACL: true, purge });
95
+ }
96
+ for (const childFolder of childFolders) {
97
+ await this.deleteFolderRecursive(childFolder.uid, context, purge);
98
+ }
99
+ await context.folderRepo.delete(uid, { ignoreACL: true, purge });
100
+ writer.writeUInt8(ROP_ID_DELETE_FOLDER);
101
+ writer.writeUInt8(inputHandleIndex);
102
+ writer.writeUInt32LE(0); // ReturnValue - success
103
+ writer.writeUInt8(0); // PartialCompletion - always false; a partial failure above already returned early
104
+ }
105
+ /** Cascades a `DEL_FOLDERS` delete into `folderUid`'s own messages/calendar events/subfolders before
106
+ * deleting it - this data model's folder hierarchy is a plain parent-pointer tree (never a graph), so
107
+ * unbounded recursion here needs no cycle protection, the same assumption `FolderTarget.resolveFolderChildren`
108
+ * already makes. */
109
+ async deleteFolderRecursive(folderUid, context, purge) {
110
+ const messages = await context.messageRepo.find({ folderUid }, { ignoreACL: true });
111
+ const events = await context.calendarEventRepo.find({ folderUid }, { ignoreACL: true });
112
+ const contacts = context.contactRepo ? await context.contactRepo.find({ folderUid }, { ignoreACL: true }) : [];
113
+ const tasks = context.taskRepo ? await context.taskRepo.find({ folderUid }, { ignoreACL: true }) : [];
114
+ const childFolders = await context.folderRepo.find({ parentFolderUid: folderUid }, { ignoreACL: true });
115
+ for (const message of messages) {
116
+ await context.messageRepo.delete(message.uid, { ignoreACL: true, purge });
117
+ }
118
+ for (const event of events) {
119
+ await context.calendarEventRepo.delete(event.uid, { ignoreACL: true, purge });
120
+ }
121
+ for (const contact of contacts) {
122
+ await context.contactRepo.delete(contact.uid, { ignoreACL: true, purge });
123
+ }
124
+ for (const task of tasks) {
125
+ await context.taskRepo.delete(task.uid, { ignoreACL: true, purge });
126
+ }
127
+ for (const childFolder of childFolders) {
128
+ await this.deleteFolderRecursive(childFolder.uid, context, purge);
129
+ }
130
+ await context.folderRepo.delete(folderUid, { ignoreACL: true, purge });
131
+ }
132
+ }
133
+ //# sourceMappingURL=RopDeleteFolderHandler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RopDeleteFolderHandler.js","sourceRoot":"","sources":["../../../src/rop/RopDeleteFolderHandler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,70 @@
1
+ const ROP_ID_DELETE_MESSAGES = 0x1e;
2
+ /** The well-known MAPI HRESULT `MAPI_E_INVALID_OBJECT`, reused for "the referenced handle isn't a folder (or
3
+ * doesn't exist)" - the same constant `RopGetContentsTableHandler` uses for its own analogous check. */
4
+ const ERROR_INVALID_OBJECT = 0x80070005;
5
+ /**
6
+ * `RopDeleteMessages` (`[MS-OXCMSG]`/`[MS-OXCROPS]`, RopId `0x1E`): deletes one or more messages by `MessageId`
7
+ * from an already-open folder handle. Reuses `RecoverableRepoUtils.delete()` exactly as-built in Phase 2 - the
8
+ * same soft-delete-with-watermark-bump machinery EAS's own `Sync`/`FolderSync` incremental-delete detection
9
+ * already relies on (see `BaseMapiEmsmdbRoute.ts`'s own doc comment for why `folderRepo`/`messageRepo`/
10
+ * `calendarEventRepo` are built as `RecoverableRepoUtils`, not plain `RepoUtils`) - no new deletion-tracking
11
+ * code, zero duplication.
12
+ *
13
+ * A `MessageId` resolves via `session.messageIds` (`MessageTarget.assignOrGetMid`'s own registry, already
14
+ * generic over both `"message:<uid>"` and `"calendarEvent:<uid>"` target strings, since a Calendar item is
15
+ * itself a Message object on the wire - see `CalendarEventTarget.ts`'s own doc comment) directly to the entity
16
+ * to delete, the same "the session already knows what this small integer refers to" design every other MID-
17
+ * consuming ROP in this pragmatic subset already uses (`RopOpenMessage`, `RopSaveChangesMessage`) - the
18
+ * request's own `InputHandleIndex` (nominally the messages' shared parent folder) is validated to be a real,
19
+ * open folder handle but not cross-checked against each message's actual `folderUid`, the same pragmatic
20
+ * simplification `RopOpenMessageHandler`'s own doc comment documents for its analogous `FolderId` field.
21
+ *
22
+ * `WantAsynchronous` (`RopProgress`-reported async completion) and `NotifyNonRead` (non-read-receipt generation
23
+ * to the original sender) are decoded to advance the reader correctly but not honored - this pragmatic subset
24
+ * has no async-ROP or non-read-receipt machinery. `PartialCompletion` is reported `true` if any requested
25
+ * `MessageId` didn't resolve to a real message/calendar-event target - the rest are still deleted, matching
26
+ * this pragmatic subset's "don't fail the whole ROP over one stale reference" principle used throughout.
27
+ *
28
+ * @author Jean-Philippe Steinmetz
29
+ */
30
+ export class RopDeleteMessagesHandler {
31
+ constructor() {
32
+ this.ropId = ROP_ID_DELETE_MESSAGES;
33
+ }
34
+ async handle(reader, writer, context) {
35
+ reader.readUInt8(); // LogonId - this pragmatic subset doesn't track multiple concurrent logons per session
36
+ const inputHandleIndex = reader.readUInt8();
37
+ reader.readUInt8(); // WantAsynchronous - no RopProgress/async-ROP support in this pragmatic subset
38
+ reader.readUInt8(); // NotifyNonRead - no non-read-receipt generation support
39
+ const messageIdCount = reader.readUInt16LE();
40
+ const messageIds = [];
41
+ for (let i = 0; i < messageIdCount; i++) {
42
+ messageIds.push(reader.readBigUInt64LE());
43
+ }
44
+ const handle = context.session.handles[inputHandleIndex];
45
+ if (!handle || handle.type !== "folder") {
46
+ writer.writeUInt8(ROP_ID_DELETE_MESSAGES);
47
+ writer.writeUInt8(inputHandleIndex);
48
+ writer.writeUInt32LE(ERROR_INVALID_OBJECT);
49
+ return;
50
+ }
51
+ let partialCompletion = false;
52
+ for (const messageId of messageIds) {
53
+ const target = context.session.messageIds[messageId.toString()];
54
+ if (target?.startsWith("message:")) {
55
+ await context.messageRepo.delete(target.slice("message:".length), { ignoreACL: true });
56
+ }
57
+ else if (target?.startsWith("calendarEvent:")) {
58
+ await context.calendarEventRepo.delete(target.slice("calendarEvent:".length), { ignoreACL: true });
59
+ }
60
+ else {
61
+ partialCompletion = true;
62
+ }
63
+ }
64
+ writer.writeUInt8(ROP_ID_DELETE_MESSAGES);
65
+ writer.writeUInt8(inputHandleIndex);
66
+ writer.writeUInt32LE(0); // ReturnValue - success
67
+ writer.writeUInt8(partialCompletion ? 1 : 0); // PartialCompletion
68
+ }
69
+ }
70
+ //# sourceMappingURL=RopDeleteMessagesHandler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RopDeleteMessagesHandler.js","sourceRoot":"","sources":["../../../src/rop/RopDeleteMessagesHandler.ts"],"names":[],"mappings":"AAOA,MAAM,sBAAsB,GAAG,IAAI,CAAC;AAEpC;wGACwG;AACxG,MAAM,oBAAoB,GAAG,UAAU,CAAC;AAExC;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,OAAO,wBAAwB;IAArC;QACoB,UAAK,GAAG,sBAAsB,CAAC;IAsCnD,CAAC;IApCU,KAAK,CAAC,MAAM,CAAC,MAAoB,EAAE,MAAoB,EAAE,OAAmB;QAC/E,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,uFAAuF;QAC3G,MAAM,gBAAgB,GAAW,MAAM,CAAC,SAAS,EAAE,CAAC;QACpD,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,+EAA+E;QACnG,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,yDAAyD;QAC7E,MAAM,cAAc,GAAW,MAAM,CAAC,YAAY,EAAE,CAAC;QACrD,MAAM,UAAU,GAAa,EAAE,CAAC;QAChC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,cAAc,EAAE,CAAC,EAAE,EAAE,CAAC;YACtC,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,eAAe,EAAE,CAAC,CAAC;QAC9C,CAAC;QAED,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;QACzD,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACtC,MAAM,CAAC,UAAU,CAAC,sBAAsB,CAAC,CAAC;YAC1C,MAAM,CAAC,UAAU,CAAC,gBAAgB,CAAC,CAAC;YACpC,MAAM,CAAC,aAAa,CAAC,oBAAoB,CAAC,CAAC;YAC3C,OAAO;QACX,CAAC;QAED,IAAI,iBAAiB,GAAG,KAAK,CAAC;QAC9B,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;YACjC,MAAM,MAAM,GAAuB,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,SAAS,CAAC,QAAQ,EAAE,CAAC,CAAC;YACpF,IAAI,MAAM,EAAE,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;gBACjC,MAAM,OAAO,CAAC,WAAW,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAC3F,CAAC;iBAAM,IAAI,MAAM,EAAE,UAAU,CAAC,gBAAgB,CAAC,EAAE,CAAC;gBAC9C,MAAM,OAAO,CAAC,iBAAiB,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,MAAM,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YACvG,CAAC;iBAAM,CAAC;gBACJ,iBAAiB,GAAG,IAAI,CAAC;YAC7B,CAAC;QACL,CAAC;QAED,MAAM,CAAC,UAAU,CAAC,sBAAsB,CAAC,CAAC;QAC1C,MAAM,CAAC,UAAU,CAAC,gBAAgB,CAAC,CAAC;QACpC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,wBAAwB;QACjD,MAAM,CAAC,UAAU,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,oBAAoB;IACtE,CAAC;CACJ"}