@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,31 @@
1
+ import type { BufferReader, BufferWriter } from "../codec/BufferCursor.js";
2
+ import type { RopContext, RopHandler } from "./RopHandler.js";
3
+ /**
4
+ * `RopDeleteMessages` (`[MS-OXCMSG]`/`[MS-OXCROPS]`, RopId `0x1E`): deletes one or more messages by `MessageId`
5
+ * from an already-open folder handle. Reuses `RecoverableRepoUtils.delete()` exactly as-built in Phase 2 - the
6
+ * same soft-delete-with-watermark-bump machinery EAS's own `Sync`/`FolderSync` incremental-delete detection
7
+ * already relies on (see `BaseMapiEmsmdbRoute.ts`'s own doc comment for why `folderRepo`/`messageRepo`/
8
+ * `calendarEventRepo` are built as `RecoverableRepoUtils`, not plain `RepoUtils`) - no new deletion-tracking
9
+ * code, zero duplication.
10
+ *
11
+ * A `MessageId` resolves via `session.messageIds` (`MessageTarget.assignOrGetMid`'s own registry, already
12
+ * generic over both `"message:<uid>"` and `"calendarEvent:<uid>"` target strings, since a Calendar item is
13
+ * itself a Message object on the wire - see `CalendarEventTarget.ts`'s own doc comment) directly to the entity
14
+ * to delete, the same "the session already knows what this small integer refers to" design every other MID-
15
+ * consuming ROP in this pragmatic subset already uses (`RopOpenMessage`, `RopSaveChangesMessage`) - the
16
+ * request's own `InputHandleIndex` (nominally the messages' shared parent folder) is validated to be a real,
17
+ * open folder handle but not cross-checked against each message's actual `folderUid`, the same pragmatic
18
+ * simplification `RopOpenMessageHandler`'s own doc comment documents for its analogous `FolderId` field.
19
+ *
20
+ * `WantAsynchronous` (`RopProgress`-reported async completion) and `NotifyNonRead` (non-read-receipt generation
21
+ * to the original sender) are decoded to advance the reader correctly but not honored - this pragmatic subset
22
+ * has no async-ROP or non-read-receipt machinery. `PartialCompletion` is reported `true` if any requested
23
+ * `MessageId` didn't resolve to a real message/calendar-event target - the rest are still deleted, matching
24
+ * this pragmatic subset's "don't fail the whole ROP over one stale reference" principle used throughout.
25
+ *
26
+ * @author Jean-Philippe Steinmetz
27
+ */
28
+ export declare class RopDeleteMessagesHandler implements RopHandler {
29
+ readonly ropId = 30;
30
+ handle(reader: BufferReader, writer: BufferWriter, context: RopContext): Promise<void>;
31
+ }
@@ -0,0 +1,24 @@
1
+ import type { BufferReader, BufferWriter } from "../codec/BufferCursor.js";
2
+ import type { RopContext, RopHandler } from "./RopHandler.js";
3
+ /**
4
+ * `RopFastTransferSourceCopyProperties` (`[MS-OXCFXICS]`/`[MS-OXCROPS]`, RopId `0x69`): the explicit-column
5
+ * sibling of `RopFastTransferSourceCopyTo` - `PropertyTags` here is the properties to **copy** (an include
6
+ * list, per `[MS-OXCROPS]`'s own wording, the opposite of `CopyTo`'s exclude list), applied uniformly at both
7
+ * folder- and message-level wherever `FastTransferStream.ts` builds a `propList` - see that file's own doc
8
+ * comment for this pragmatic subset's overall ICS scope. An empty `PropertyTags` list is honored literally
9
+ * (copies zero properties, i.e. empty `propList`s) rather than falling back to `CopyTo`'s own default column
10
+ * set - that fallback belongs to `CopyTo` alone, since an explicit "copy nothing" request here is spec-valid
11
+ * and distinguishable from "use the default set" (`CopyTo` simply has no `PropertyTags`-supplied alternative to
12
+ * fall back from).
13
+ *
14
+ * `Level`/`CopyFlags`/`SendOptions` are decoded to advance the reader correctly but not honored - see
15
+ * `RopFastTransferSourceCopyToHandler`'s own doc comment for the identical reasoning. Note `CopyFlags` here is
16
+ * **1 byte**, not 4 like `CopyTo`'s own field of the same name - confirmed against each ROP's own request-buffer
17
+ * page separately, not assumed identical.
18
+ *
19
+ * @author Jean-Philippe Steinmetz
20
+ */
21
+ export declare class RopFastTransferSourceCopyPropertiesHandler implements RopHandler {
22
+ readonly ropId = 105;
23
+ handle(reader: BufferReader, writer: BufferWriter, context: RopContext): Promise<void>;
24
+ }
@@ -0,0 +1,24 @@
1
+ import type { BufferReader, BufferWriter } from "../codec/BufferCursor.js";
2
+ import type { RopContext, RopHandler } from "./RopHandler.js";
3
+ /**
4
+ * `RopFastTransferSourceCopyTo` (`[MS-OXCFXICS]`/`[MS-OXCROPS]`, RopId `0x4D`): begins a FastTransfer download
5
+ * of an already-open Folder or Message (or Calendar item, itself a Message object on the wire) object,
6
+ * producing a new `"fastTransfer"` Server object handle `RopFastTransferSourceGetBuffer` pages the built stream
7
+ * out of. See `FastTransferStream.ts`'s own doc comment for this pragmatic subset's "full, non-differential
8
+ * dump" ICS scope and its default property columns.
9
+ *
10
+ * `Level` (whether to recurse into subfolders) is decoded to advance the reader correctly but not honored -
11
+ * this pragmatic subset never recurses into subfolders regardless. `CopyFlags`/`SendOptions` (Move mode, best-
12
+ * body preference, Unicode preference, recoverable-mode, ...) are likewise decoded but not honored - this
13
+ * pragmatic subset always copies (never moves) and always encodes the same way regardless of client
14
+ * preference, matching `RopGetPropertiesSpecificHandler`'s own treatment of its analogous `WantUnicode` field.
15
+ * `PropertyTags` is the properties to *exclude* from the default column set (`[MS-OXCROPS]`'s own wording) -
16
+ * see `FastTransferStream.ts` for why an explicit include list instead is `RopFastTransferSourceCopyProperties`'s
17
+ * own job, not this ROP's.
18
+ *
19
+ * @author Jean-Philippe Steinmetz
20
+ */
21
+ export declare class RopFastTransferSourceCopyToHandler implements RopHandler {
22
+ readonly ropId = 77;
23
+ handle(reader: BufferReader, writer: BufferWriter, context: RopContext): Promise<void>;
24
+ }
@@ -0,0 +1,23 @@
1
+ import type { BufferReader, BufferWriter } from "../codec/BufferCursor.js";
2
+ import type { RopContext, RopHandler } from "./RopHandler.js";
3
+ /**
4
+ * `RopFastTransferSourceGetBuffer` (`[MS-OXCFXICS]`/`[MS-OXCROPS]`, RopId `0x4E`): pages the FastTransfer
5
+ * stream a prior `RopFastTransferSourceCopyTo`/`CopyProperties` built (`FastTransferStream.ts`) out of its
6
+ * `"fastTransfer"` handle, `BufferSize` bytes at a time (or the entire remaining buffer, capped to
7
+ * `MAX_TRANSFER_BUFFER_SIZE`, for the `0xBABE` "server-determined" sentinel - `MaximumBufferSize`, present only
8
+ * in that case, is decoded to advance the reader correctly but not honored, since honoring it would mean
9
+ * returning *more* than one chunk can carry, not less). Reports `Done` once the whole buffer has been returned
10
+ * across one or more calls, `Partial` otherwise - `NoRoom` and `Error` are never produced (a `"fastTransfer"`
11
+ * handle, once created, always has a complete, already-valid buffer to page from).
12
+ *
13
+ * `BackoffTime` is never emitted (this pragmatic subset never returns the one `ReturnValue` that field is
14
+ * conditional on), and the failure path (`ERROR_INVALID_OBJECT`) omits every field after `ReturnValue` entirely
15
+ * - the same "just the fixed header, no success-only tail" shape every other failing handler in this pragmatic
16
+ * subset already uses, not a specific claim about the real spec's own error-path byte layout.
17
+ *
18
+ * @author Jean-Philippe Steinmetz
19
+ */
20
+ export declare class RopFastTransferSourceGetBufferHandler implements RopHandler {
21
+ readonly ropId = 78;
22
+ handle(reader: BufferReader, writer: BufferWriter, context: RopContext): void;
23
+ }
@@ -0,0 +1,31 @@
1
+ import type { BufferReader, BufferWriter } from "../codec/BufferCursor.js";
2
+ import type { RopContext, RopHandler } from "./RopHandler.js";
3
+ /**
4
+ * `RopGetContentsTable` (`[MS-OXCFOLD]`/`[MS-OXCROPS]`): opens a table listing an already-opened folder's
5
+ * messages, producing a new `"table"` Server object handle `RopSetColumns`/`RopQueryRows` reference by
6
+ * `InputHandleIndex` - the exact same request/response shape as `RopGetHierarchyTable` (confirmed identical
7
+ * field-by-field against `[MS-OXCROPS]`'s own request-buffer page, differing only in `RopId`), just listing
8
+ * messages instead of child folders. `RopSetColumns`/`RopQueryRows` themselves need no changes at all to
9
+ * support this - they already operate generically on whatever `rows`/`columns` a table handle carries,
10
+ * dispatching to `FolderTarget`'s or `MessageTarget`'s resolvers by row-target prefix (`"folder:"`/`"virtual:"`
11
+ * vs `"message:"`).
12
+ *
13
+ * A folder's contents table is only ever non-empty for a real folder (`"folder:<uid>"`) - the virtual special
14
+ * folders (`RopLogonHandler`'s own doc comment) have no real backing row to hold messages under, so opening a
15
+ * contents table on one always yields an empty table rather than an error.
16
+ *
17
+ * **Calendar folders** (`Folder.type === FolderType.CALENDAR`) list `"calendarEvent:<uid>"` rows resolved via
18
+ * `calendarEventRepo` instead of `"message:<uid>"` rows - a single table handle only ever holds one kind of
19
+ * row, decided once here by checking the folder's own `type`, exactly the same "resolve by target-string
20
+ * prefix downstream" design `MessageTarget`/`FolderTarget` already use for their own rows. **Contacts and
21
+ * Tasks** folders (`FolderType.CONTACTS`/`TASKS`) list `"contact:<uid>"`/`"task:<uid>"` rows the same way, via
22
+ * `contactRepo`/`taskRepo` - empty (rather than an error) when the context has neither wired up, the same
23
+ * optional-repo degradation `RopContext`'s own doc comment describes.
24
+ *
25
+ * @author Jean-Philippe Steinmetz
26
+ */
27
+ export declare class RopGetContentsTableHandler implements RopHandler {
28
+ readonly ropId = 5;
29
+ handle(reader: BufferReader, writer: BufferWriter, context: RopContext): Promise<void>;
30
+ private resolveRows;
31
+ }
@@ -0,0 +1,17 @@
1
+ import type { BufferReader, BufferWriter } from "../codec/BufferCursor.js";
2
+ import type { RopContext, RopHandler } from "./RopHandler.js";
3
+ /**
4
+ * `RopGetHierarchyTable` (`[MS-OXCFOLD]`/`[MS-OXCROPS]`): opens a table listing an already-opened folder's
5
+ * direct child folders, producing a new `"table"` Server object handle `RopSetColumns`/`RopQueryRows`
6
+ * reference by `InputHandleIndex`. Only `TableFlags = 0x00` ("Standard") is supported - this pragmatic subset
7
+ * has no deferred/async table population, so the immediate-row-count-returning variants some `TableFlags`
8
+ * values request are moot, and the response omits the row-count field those variants add (confirmed absent
9
+ * for the Standard case against `[MS-OXCROPS]`'s own request-buffer page, which explicitly lists `TableFlags`
10
+ * as this ROP's only request payload beyond the common handle-index header).
11
+ *
12
+ * @author Jean-Philippe Steinmetz
13
+ */
14
+ export declare class RopGetHierarchyTableHandler implements RopHandler {
15
+ readonly ropId = 4;
16
+ handle(reader: BufferReader, writer: BufferWriter, context: RopContext): Promise<void>;
17
+ }
@@ -0,0 +1,23 @@
1
+ import type { BufferReader, BufferWriter } from "../codec/BufferCursor.js";
2
+ import type { RopContext, RopHandler } from "./RopHandler.js";
3
+ /**
4
+ * `RopGetPropertiesSpecific` (`[MS-OXCPRPT]`/`[MS-OXCROPS]`): fetches named property values for a single
5
+ * already-opened Folder or Message object (`RopOpenFolder`/`RopOpenMessage`), by an explicit `PropertyTags`
6
+ * list - the single-object analog of `RopQueryRows`' per-row column fetch, reusing the exact same
7
+ * `PropertyResolvers` value-resolution logic (a folder/message handle's `entityUid` is already the same
8
+ * `"folder:"`/`"virtual:"`/`"message:"` target-string format a table row uses). Like `RopQueryRows`, this
9
+ * pragmatic subset always emits a `StandardPropertyRow` (`Flags = 0x00`, confirmed as the response's `RowData`
10
+ * format via `[MS-OXCROPS]`'s own "Success Response Buffer" page, which names `[MS-OXCDATA]` §2.8's
11
+ * `PropertyRow` structure directly) - an unsupported property falls back to a type-appropriate default rather
12
+ * than a `FlaggedPropertyRow`'s per-column error signaling, exactly as `RopQueryRows` already does.
13
+ *
14
+ * `PropertySizeLimit`/`WantUnicode` are decoded (to advance the reader correctly) but not honored - this
15
+ * pragmatic subset never truncates a property value and always encodes strings the same way regardless of the
16
+ * client's Unicode preference (see `PropertyValue.ts`'s own string-encoding doc comments).
17
+ *
18
+ * @author Jean-Philippe Steinmetz
19
+ */
20
+ export declare class RopGetPropertiesSpecificHandler implements RopHandler {
21
+ readonly ropId = 7;
22
+ handle(reader: BufferReader, writer: BufferWriter, context: RopContext): Promise<void>;
23
+ }
@@ -0,0 +1,23 @@
1
+ import type { BufferReader, BufferWriter } from "../codec/BufferCursor.js";
2
+ import type { RopContext, RopHandler } from "./RopHandler.js";
3
+ /**
4
+ * `RopGetPropertyIdsFromNames` (`[MS-OXCPRPT]`/`[MS-OXCROPS]` §2.2.8.1): resolves a list of named properties
5
+ * (`PropertyName` structures - a property-set `GUID` plus a numeric `LID` or string `Name`) into numeric
6
+ * property IDs a subsequent `RopSetProperties`/`RopGetPropertiesSpecific`/`RopQueryRows` call can use directly.
7
+ * This is the mechanism almost every Calendar-specific property (`PidLidAppointmentStartWhole`,
8
+ * `PidLidBusyStatus`, `PidLidAppointmentRecur`, ...) requires, since none of them have a fixed numeric
9
+ * `PidTag*`-style ID the way `PidTagSubject` does - see `NamedPropertyRegistry.ts`'s own doc comment for the
10
+ * session-scoped registry this delegates to.
11
+ *
12
+ * An unresolvable `PropertyName` (`Kind = 0xFF`) maps to `0x0000` in the response, per spec - this pragmatic
13
+ * subset has no other unmappable case (no permission/quota/hard-limit checks), so every `Kind = 0x00`/`0x01`
14
+ * entry always succeeds. `Flags` is decoded to advance past it correctly but not honored - see
15
+ * `assignOrGetNamedPropertyId`'s own doc comment for why "probe" and "assign" collapse to the same behavior
16
+ * for a purely session-scoped registry.
17
+ *
18
+ * @author Jean-Philippe Steinmetz
19
+ */
20
+ export declare class RopGetPropertyIdsFromNamesHandler implements RopHandler {
21
+ readonly ropId = 86;
22
+ handle(reader: BufferReader, writer: BufferWriter, context: RopContext): void;
23
+ }
@@ -0,0 +1,67 @@
1
+ import type { RepoUtils } from "@rapidrest/service-core";
2
+ import type { BlobStore } from "@rapidmx/restapi";
3
+ import type { ScanPipeline } from "@rapidmx/restapi/scan";
4
+ import type { BufferReader, BufferWriter } from "../codec/BufferCursor.js";
5
+ import type { MapiSessionContext } from "../MapiSessionManager.js";
6
+ /**
7
+ * Everything a `RopHandler` needs beyond the raw ROP bytes it decodes itself. `folderRepo`/`mailboxRepo` are
8
+ * built once by `BaseMapiEmsmdbRoute` (not by each handler, unlike EAS's per-command repo pattern - MAPI's ROPs
9
+ * are far more tightly interdependent around the same few entity types than EAS's per-command-collection-type
10
+ * structure was, so centralizing the common repos here avoids every handler rebuilding its own). Untyped as
11
+ * `RepoUtils<any>` rather than a generic `F extends Folder` parameter threaded through every handler file -
12
+ * the same pragmatic `any` this codebase already uses throughout for `xxxClass`/`xxxRepo` DI fields, since
13
+ * `RepoUtils<F>`'s method contravariance makes a generic version genuinely awkward for no real benefit here
14
+ * (every handler only ever reads `Folder`-shaped rows, never constructs backend-specific entities itself).
15
+ *
16
+ * `folderClass`/`messageClass` (the concrete Mongo/SQL entity constructors) are threaded through here rather
17
+ * than held as a field on each handler - unlike EAS's `EasCommandHandler`s, a single shared `RopHandler`
18
+ * instance (e.g. `RopSubmitMessageHandler`) is reused for *both* the Mongo and SQL route (`ropHandlerClasses`
19
+ * lists the exact same classes in `MapiEmsmdbRouteMongo`/`SQL`), so the concrete class can only ever be known
20
+ * per-request, via this context - needed by `RopSubmitMessageHandler`'s `findOrCreateWellKnownFolder()`/
21
+ * `messageRepo.create()` calls when persisting a submitted message's Sent Items copy. `scanPipeline`/
22
+ * `mailTransport` back the same `scanAndRelay()` compose/send pipeline `BaseMessageRoute.send()`/EAS's
23
+ * `ComposeMailCommand` already share via `MailSendUtils.ts`.
24
+ *
25
+ * `contactRepo`/`taskRepo`/`contactClass`/`taskClass`/`labelRepo`/`labelClass` are **optional**, unlike every
26
+ * other repo/class pair here - `BaseMapiEmsmdbRoute` always populates them in real use (see its own `@Init`),
27
+ * but making them required would force every one of this codebase's many existing `RopContext`-literal test
28
+ * fixtures (most predating Contacts/Tasks folder support, and all predating Label support) to grow new fields
29
+ * each for no behavioral reason. Every consumer (`PropertyResolvers.ts`, `RopGetContentsTableHandler.ts`,
30
+ * `RopOpenMessageHandler.ts`) already degrades a `"contact:"`/`"task:"` target to empty/no-rows when the
31
+ * corresponding repo is absent, the same "missing data, not a crash" stance `resolveContactInfo`/
32
+ * `resolveTaskInfo` take for a vanished row; `labelRepo` absent degrades `PidNameKeywords` (Outlook Categories)
33
+ * to an empty list the same way.
34
+ */
35
+ export interface RopContext {
36
+ mailboxUid: string;
37
+ userUid: string;
38
+ session: MapiSessionContext;
39
+ mailboxRepo: RepoUtils<any>;
40
+ folderRepo: RepoUtils<any>;
41
+ messageRepo: RepoUtils<any>;
42
+ calendarEventRepo: RepoUtils<any>;
43
+ contactRepo?: RepoUtils<any>;
44
+ taskRepo?: RepoUtils<any>;
45
+ labelRepo?: RepoUtils<any>;
46
+ folderClass: any;
47
+ messageClass: any;
48
+ calendarEventClass: any;
49
+ contactClass?: any;
50
+ taskClass?: any;
51
+ labelClass?: any;
52
+ blobStore: BlobStore;
53
+ scanPipeline: ScanPipeline;
54
+ mailTransport: any;
55
+ }
56
+ /**
57
+ * One class per ROP, mirroring `EasCommandHandler`'s exact one-class-per-command convention. A handler owns
58
+ * its own request/response wire format entirely - decoding whatever fields follow its own `RopId` byte from
59
+ * `reader` (already consumed by the dispatcher) and appending its response bytes (if any - `RopRelease`
60
+ * intentionally appends none) to the shared `writer`. See `RopBuffer.ts`'s doc comment for why a generic
61
+ * dispatcher can't do this decoding itself.
62
+ */
63
+ export interface RopHandler {
64
+ /** The `RopId` byte this handler processes (e.g. `0xFE` for `RopLogon`). */
65
+ readonly ropId: number;
66
+ handle(reader: BufferReader, writer: BufferWriter, context: RopContext): Promise<void> | void;
67
+ }
@@ -0,0 +1,31 @@
1
+ import { BufferReader, BufferWriter } from "../codec/BufferCursor.js";
2
+ import type { RopContext, RopHandler } from "./RopHandler.js";
3
+ /**
4
+ * `RopLogon` (`[MS-OXCSTOR]`/`[MS-OXCROPS]`): the first ROP of any session, a prerequisite for every other
5
+ * ROP. This pragmatic subset always performs a private-mailbox logon against the mailbox `BaseMapiEmsmdbRoute`
6
+ * already resolved and authorized at `Connect` time - the request's own `Essdn` field is decoded (to advance
7
+ * past it correctly) but never trusted for mailbox identity, the same principle `BaseMapiEmsmdbRoute.Connect`
8
+ * already applies to that request's `UserDn` field. Because mailbox resolution already happened upstream,
9
+ * this handler has no real failure path to model (`ecUnknownUser`/`ecLoginFailure`/... are all deferred).
10
+ *
11
+ * @author Jean-Philippe Steinmetz
12
+ */
13
+ export declare class RopLogonHandler implements RopHandler {
14
+ readonly ropId = 254;
15
+ handle(reader: BufferReader, writer: BufferWriter, context: RopContext): Promise<void>;
16
+ /**
17
+ * Assigns this session's FIDs for the 13 special folders, remembering each one's real-`Folder`-or-virtual-
18
+ * placeholder target in `session.folderIds` so a later `RopOpenFolder` can resolve it back to something
19
+ * real. Delegates to `FolderTarget.assignOrGetFid` - the same "reuse an existing FID for this target if one
20
+ * was already assigned, otherwise mint the next free one" logic `RopGetHierarchyTable`/`RopQueryRows` use
21
+ * for child folders - rather than wholesale-replacing `session.folderIds` with a freshly-numbered map
22
+ * starting back at FID 1. A second `RopLogon` on a live session (spec-legal - a real client can reconnect
23
+ * or re-logon within one session context) previously invalidated every FID a client had already learned
24
+ * for a child folder via `RopGetHierarchyTable`/`RopQueryRows` (all assigned starting at FID 14, since the
25
+ * old code always reset the counter to 1-13 for just these specials) and could even cause FIDs 14+ to be
26
+ * silently re-issued to a *different* folder than the one the client last associated with that number.
27
+ * Reusing `assignOrGetFid` means a re-`RopLogon` is now purely additive: these 13 targets get their
28
+ * existing FIDs back if this is a second logon, and any child-folder FIDs already assigned are left alone.
29
+ */
30
+ private assignFolderIds;
31
+ }
@@ -0,0 +1,19 @@
1
+ import type { BufferReader, BufferWriter } from "../codec/BufferCursor.js";
2
+ import type { RopContext, RopHandler } from "./RopHandler.js";
3
+ /**
4
+ * `RopOpenFolder` (`[MS-OXCFOLD]`/`[MS-OXCROPS]`): opens a folder by FID, producing a new `"folder"` Server
5
+ * object handle later ROPs (`RopGetHierarchyTable`, a future `RopGetContentsTable`) reference by
6
+ * `InputHandleIndex`. The request's own `OpenModeFlags` (`OpenSoftDeleted`, ...) is decoded to advance past
7
+ * it correctly but not acted on - this pragmatic subset has no soft-deleted-folder recovery flow.
8
+ *
9
+ * Private-mailbox-only response shape: `IsGhosted` is always `0` and its conditional
10
+ * `ServerCount`/`CheapServerCount`/`Servers` fields are never present, since `IsGhosted` and its trailing
11
+ * fields are documented as public-folder-only (`[MS-OXCFOLD]`'s own "RopOpenFolder ROP Response Buffer"
12
+ * page). `HasRules` is always `0` - this library has no `[MS-OXORULE]` rules-engine support.
13
+ *
14
+ * @author Jean-Philippe Steinmetz
15
+ */
16
+ export declare class RopOpenFolderHandler implements RopHandler {
17
+ readonly ropId = 2;
18
+ handle(reader: BufferReader, writer: BufferWriter, context: RopContext): void;
19
+ }
@@ -0,0 +1,27 @@
1
+ import type { BufferReader, BufferWriter } from "../codec/BufferCursor.js";
2
+ import type { RopContext, RopHandler } from "./RopHandler.js";
3
+ /**
4
+ * `RopOpenMessage` (`[MS-OXCMSG]`/`[MS-OXCROPS]`): opens a message by MID, producing a new `"message"` Server
5
+ * object handle later ROPs (`RopGetPropertiesSpecific`, `RopOpenStream`) reference by `InputHandleIndex`. The
6
+ * request's own `FolderId` (the message's expected parent folder) and `CodePageId` are decoded to advance past
7
+ * them correctly but not otherwise acted on - a MID is resolved directly via `session.messageIds`
8
+ * (`MessageTarget.assignOrGetMid`), the same "the session already knows what this small integer refers to"
9
+ * design `RopOpenFolderHandler` uses for FIDs, so cross-checking the caller-supplied `FolderId` would be pure
10
+ * redundancy. `OpenModeFlags` is likewise decoded but not honored - this pragmatic subset only ever opens a
11
+ * message for reading (no `RopSetProperties`/`RopSaveChangesMessage` write-back support yet).
12
+ *
13
+ * **Pragmatic success response**: `HasNamedProperties` is always `false` - a conservative hint value only, not
14
+ * a claim that named properties are unsupported (`RopGetPropertyIdsFromNames`/`PropertyResolvers.ts` do resolve
15
+ * them for a subsequently-opened Calendar item; a real client calls `RopGetPropertyIdsFromNames` unconditionally
16
+ * for a known Calendar message class regardless of this flag, so leaving it `false` doesn't block that path).
17
+ * The `SubjectPrefix`/`NormalizedSubject` `TypedString`s are "no prefix" + the item's plain subject/title (this
18
+ * data model has no separate prefix/normalized-subject split the way real Exchange does), and the recipient
19
+ * table (`RecipientCount`/`ColumnCount`/`RecipientColumns`/`RowCount`/`RecipientRows`) is always empty - reading
20
+ * a message's `To`/`Cc` list is a documented gap in this pass, not silently wrong data.
21
+ *
22
+ * @author Jean-Philippe Steinmetz
23
+ */
24
+ export declare class RopOpenMessageHandler implements RopHandler {
25
+ readonly ropId = 3;
26
+ handle(reader: BufferReader, writer: BufferWriter, context: RopContext): Promise<void>;
27
+ }
@@ -0,0 +1,27 @@
1
+ import type { BufferReader, BufferWriter } from "../codec/BufferCursor.js";
2
+ import type { RopContext, RopHandler } from "./RopHandler.js";
3
+ /**
4
+ * `RopOpenStream` (`[MS-OXCPRPT]`/`[MS-OXCROPS]`): opens a property for streaming access, producing a new
5
+ * `"stream"` Server object handle `RopReadStream`/`RopWriteStream` reads/writes by `InputHandleIndex`.
6
+ * **Pragmatic subset**: only `PidTagBody` (`PtypString`, the plain-text message body) on a `"message"` handle
7
+ * is supported - a real client also streams `PidTagHtml`/`PidTagRtfCompressed` (rich body formats) and
8
+ * folder/attachment binary properties, none of which this pass implements; requesting any other `PropertyTag`,
9
+ * or opening a stream against a non-message handle, fails with `MAPI_E_NOT_FOUND` rather than succeeding with
10
+ * wrong data.
11
+ *
12
+ * `OpenModeFlags` (`[MS-OXCPRPT]` §2.2.14.1) selects which of two independent code paths runs:
13
+ * - `ReadOnly` (`0x00`): the step-7 behavior - resolves the real, already-saved message's body via
14
+ * `MessageBodyStream.resolveMessageBodyBytes()` (`BlobStore` + `mailparser`) and reports its length as
15
+ * `StreamSize`, for `RopReadStream` to read back.
16
+ * - `ReadWrite`/`Create` (`0x01`/`0x02`): a **write-mode** stream, the step-8 addition compose/send needs to
17
+ * accept a message body via `RopWriteStream` - `StreamSize` is always `0` (matching `Create`'s "discards the
18
+ * existing value" semantics; this pragmatic subset never opens `ReadWrite` against a body that already has
19
+ * content) and the new handle's `writeTargetHandleIndex` remembers which message handle to write back into,
20
+ * the link `RopWriteStream`/`RopSaveChangesMessageHandler`/`RopSubmitMessageHandler` use to find it again.
21
+ *
22
+ * @author Jean-Philippe Steinmetz
23
+ */
24
+ export declare class RopOpenStreamHandler implements RopHandler {
25
+ readonly ropId = 43;
26
+ handle(reader: BufferReader, writer: BufferWriter, context: RopContext): Promise<void>;
27
+ }
@@ -0,0 +1,36 @@
1
+ import { BufferReader, BufferWriter } from "../codec/BufferCursor.js";
2
+ import type { RopContext, RopHandler } from "./RopHandler.js";
3
+ /**
4
+ * `RopQueryRows` (`[MS-OXCTABL]`/`[MS-OXCROPS]`): fetches up to `RowCount` rows from an already-configured
5
+ * table (`RopGetHierarchyTable` + `RopSetColumns`), advancing the table's cursor. Confirmed field-by-field
6
+ * against `[MS-OXCTABL]`'s own real captured request/response byte example - including the response's
7
+ * `RowData` encoding (`PropertyRow`: a leading `Flags` byte, `0x00` for a `StandardPropertyRow` where every
8
+ * column is a plain `PropertyValue`, `0x01` for a `FlaggedPropertyRow` where each column gets its own
9
+ * `FlaggedPropertyValue` flag+optional-value). This pragmatic subset always emits `StandardPropertyRow`
10
+ * (`Flags = 0x00`) - every configured column always resolves to *some* value here (falling back to a
11
+ * type-appropriate zero/empty default for an unsupported property, never an error), so `FlaggedPropertyRow`'s
12
+ * per-column error signaling is never needed.
13
+ *
14
+ * Backward reads (`ForwardRead = FALSE`) and the `NoAdvance` flag are decoded (to advance the reader
15
+ * correctly) but not honored - this pragmatic subset's tables are simple forward-only cursors. Works generically
16
+ * against either a `RopGetHierarchyTable` (folder rows, `"folder:"`/`"virtual:"` targets) or a
17
+ * `RopGetContentsTable` (message rows, `"message:"` targets) handle - dispatching to `FolderTarget`'s or
18
+ * `MessageTarget`'s resolver and property-value mapping by row-target prefix, since a single table handle only
19
+ * ever holds one kind of row.
20
+ *
21
+ * **Known, deliberate performance limitation**: `resolvePropertyValues` still does one `repo.findOne()` per
22
+ * row (message/calendarEvent/contact/task) rather than a single batched fetch for the whole page - a genuine
23
+ * N+1 for a large `RowCount`. A real fix needs a batched "fetch all these uids" query, which this codebase's
24
+ * `RepoUtils<T>` abstraction doesn't expose a backend-agnostic way to express (Mongo's `$in` and TypeORM's
25
+ * `In()` aren't interchangeable at this call site, which has no idea which backend it's running against) - see
26
+ * `ResolutionCache` for the narrower, safe win this pass *did* make (the redundant *identical* whole-mailbox-
27
+ * folder-list and calendar-mailbox fetches that were happening once per row regardless of which distinct
28
+ * entity each row was, now happen at most once per call).
29
+ *
30
+ * @author Jean-Philippe Steinmetz
31
+ */
32
+ export declare class RopQueryRowsHandler implements RopHandler {
33
+ readonly ropId = 21;
34
+ handle(reader: BufferReader, writer: BufferWriter, context: RopContext): Promise<void>;
35
+ private buildRow;
36
+ }
@@ -0,0 +1,18 @@
1
+ import type { BufferReader, BufferWriter } from "../codec/BufferCursor.js";
2
+ import type { RopContext, RopHandler } from "./RopHandler.js";
3
+ /**
4
+ * `RopReadStream` (`[MS-OXCPRPT]`/`[MS-OXCROPS]`): reads up to `ByteCount` (or `MaximumByteCount`, if
5
+ * `ByteCount` is the `0xBABE` sentinel) bytes from an already-opened stream (`RopOpenStream`), advancing the
6
+ * stream's read position - clamped to `MAX_DATA_SIZE` (`0xFFFF`) regardless of what the client asked for, since
7
+ * the response's own `DataSize` field can't carry more than that; a client wanting more of a large body simply
8
+ * calls `RopReadStream` again from the advanced `streamPosition`, the same paging behavior real Exchange uses.
9
+ * Unlike most other ROPs in this pragmatic subset, `[MS-OXCROPS]` documents only one combined response-buffer
10
+ * shape for this ROP (no separate Success/Failure pages) - `DataSize`/`Data` are always present, `DataSize = 0`
11
+ * (empty `Data`) standing in for the failure case rather than the fields being omitted entirely.
12
+ *
13
+ * @author Jean-Philippe Steinmetz
14
+ */
15
+ export declare class RopReadStreamHandler implements RopHandler {
16
+ readonly ropId = 44;
17
+ handle(reader: BufferReader, writer: BufferWriter, context: RopContext): Promise<void>;
18
+ }
@@ -0,0 +1,14 @@
1
+ import type { BufferReader, BufferWriter } from "../codec/BufferCursor.js";
2
+ import type { RopContext, RopHandler } from "./RopHandler.js";
3
+ /**
4
+ * `RopRelease` (`[MS-OXCROPS]`): releases a Server object handle. Confirmed against the spec's own real
5
+ * captured example (`08 00 01 00 00 01 00 01 6F 00 00 00 6E 00 00 00`) that the request is exactly 3 bytes
6
+ * (`RopId`/`LogonId`/`InputHandleIndex`) and, uniquely among ROPs, **produces no response entry at all** - the
7
+ * server MUST NOT write anything to the output buffer for it, so `handle()` never touches `writer`.
8
+ *
9
+ * @author Jean-Philippe Steinmetz
10
+ */
11
+ export declare class RopReleaseHandler implements RopHandler {
12
+ readonly ropId = 1;
13
+ handle(reader: BufferReader, _writer: BufferWriter, context: RopContext): void;
14
+ }
@@ -0,0 +1,44 @@
1
+ import { BufferReader, type BufferWriter } from "../codec/BufferCursor.js";
2
+ import type { RopContext, RopHandler } from "./RopHandler.js";
3
+ /**
4
+ * `RopSaveChangesMessage` (`[MS-OXCMSG]`/`[MS-OXCROPS]`): persists a message's pending changes, returning the
5
+ * `MID` a client can reference it by afterwards. **Pragmatic subset**: this library has no separate Drafts-
6
+ * folder persistence step (matching EAS's own `ComposeMailCommand`, which never creates a standalone draft
7
+ * `Message` row either) - saving a `RopCreateMessage` draft does not yet write anything to a database. It only
8
+ * assigns a session-scoped MID (`MessageTarget.assignOrGetMid`, the exact mechanism a `RopQueryRows` row's
9
+ * `PidTagMid` column already uses) so the wire contract's `MessageId` field is well-formed, keyed by this
10
+ * handle's own index (`` `draft:${inputHandleIndex}` ``) since an unsaved draft has no real `"message:<uid>"`
11
+ * target yet. The real work - actually building and sending a MIME message from the properties/body
12
+ * accumulated so far - happens at `RopSubmitMessage` time, not here; calling Save without ever calling Submit
13
+ * silently discards the draft when the session expires, a real, deliberate limitation (this pragmatic subset
14
+ * has no autosave-recovery story) rather than a silently-wrong one.
15
+ *
16
+ * Re-saving an already-real message (a `RopOpenMessage` handle, not a fresh `RopCreateMessage` draft) is
17
+ * accepted but is a no-op beyond echoing that message's own already-assigned MID - editing an existing
18
+ * message's properties is out of scope for this pragmatic subset's compose/send-only ROP coverage.
19
+ *
20
+ * `SaveFlags` is decoded to advance past it correctly but not honored - this pragmatic subset has no
21
+ * conflict-resolution/force-save semantics to vary by flag.
22
+ *
23
+ * **Calendar branch**: unlike mail, a draft whose `PidTagMessageClass` starts with `"IPM.Appointment"` *is*
24
+ * durably persisted here (`saveAppointment()`) - creating (or updating, if this handle already refers to a real
25
+ * `"calendarEvent:<uid>"`) a real `CalendarEvent` row, so a subsequent `RopGetPropertiesSpecific` can read it
26
+ * back before `RopSubmitMessage` is ever called (a calendar item must exist as soon as it's saved, the same way
27
+ * a real Exchange server behaves - unlike a mail draft, which this pragmatic subset never persists at all until
28
+ * Submit).
29
+ *
30
+ * @author Jean-Philippe Steinmetz
31
+ */
32
+ export declare class RopSaveChangesMessageHandler implements RopHandler {
33
+ readonly ropId = 12;
34
+ handle(reader: BufferReader, writer: BufferWriter, context: RopContext): Promise<void>;
35
+ /**
36
+ * Persists (creating, or updating an already-real one) a `CalendarEvent` from the draft's accumulated
37
+ * `RopSetProperties` values, and returns the MID a client can reference it by. Every field this pragmatic
38
+ * subset understands (per `CalendarNamedProperties.ts`) is decoded via `decodeCalendarFieldsFromDraft`
39
+ * below - the write-side mirror of `PropertyResolvers.calendarEventValueFor`'s read-side switch. A field the
40
+ * client never set (e.g. no `RopSetProperties` call touched `PidLidBusyStatus`) falls back to the existing
41
+ * row's own value on an update, or the entity class's own default on a fresh create.
42
+ */
43
+ private saveAppointment;
44
+ }
@@ -0,0 +1,21 @@
1
+ import type { BufferReader, BufferWriter } from "../codec/BufferCursor.js";
2
+ import type { RopContext, RopHandler } from "./RopHandler.js";
3
+ /**
4
+ * `RopSetColumns` (`[MS-OXCTABL]`/`[MS-OXCROPS]`): configures which properties a table's subsequent
5
+ * `RopQueryRows` calls return, and in what order. Unlike `RopOpenFolder`/`RopGetHierarchyTable`, this ROP
6
+ * doesn't create a new handle - it mutates the existing table handle referenced by `InputHandleIndex` in
7
+ * place, so its response echoes `InputHandleIndex` rather than an `OutputHandleIndex`, the same convention
8
+ * `RopQueryRows`' own response uses (confirmed against `[MS-OXCTABL]`'s real captured response example).
9
+ *
10
+ * Validates that `InputHandleIndex` actually refers to a `"table"` handle before mutating it, the same check
11
+ * every other handle-consuming ROP in this pragmatic subset already makes - a missing or wrong-type handle
12
+ * previously fell through silently (no `else` branch) to a bare success response, meaning a client that set
13
+ * columns against a stale/released/wrong-kind handle index only discovered the real problem one ROP later,
14
+ * when `RopQueryRows` on that same index returned `MAPI_E_INVALID_OBJECT` instead.
15
+ *
16
+ * @author Jean-Philippe Steinmetz
17
+ */
18
+ export declare class RopSetColumnsHandler implements RopHandler {
19
+ readonly ropId = 18;
20
+ handle(reader: BufferReader, writer: BufferWriter, context: RopContext): void;
21
+ }
@@ -0,0 +1,38 @@
1
+ import type { BufferReader, BufferWriter } from "../codec/BufferCursor.js";
2
+ import type { RopContext, RopHandler } from "./RopHandler.js";
3
+ /** `PidTagReadReceiptRequested` (`PT_BOOLEAN`) - a client checking "Request a read receipt" before Submit sets
4
+ * this; `RopSubmitMessageHandler` reads it back to attach a real `Disposition-Notification-To` header, mirroring
5
+ * `BaseMessageRoute.send()`'s own explicit-`Message.requestReceipt` handling (see `util/ReceiptUtils.ts`). */
6
+ export declare const PID_TAG_READ_RECEIPT_REQUESTED = 41;
7
+ /** `PidTagDeferredSendTime` (`PT_SYSTIME`) - real Outlook's "Do not deliver before" compose option;
8
+ * `RopSubmitMessageHandler` reads it back to defer relay exactly like `BaseMessageRoute.send()`'s own
9
+ * `Message.scheduledSendTime` branch does (moving the message to Outbox for `ScheduledSendJob` to relay later)
10
+ * instead of sending immediately. */
11
+ export declare const PID_TAG_DEFERRED_SEND_TIME = 16367;
12
+ /**
13
+ * `RopSetProperties` (`[MS-OXCPRPT]`/`[MS-OXCROPS]`): sets an explicit, client-chosen list of properties
14
+ * (`TaggedPropertyValue`s, each carrying its own `PropertyTag`) on an already-open object. This pragmatic
15
+ * subset supports only `"message"` handles (a `RopCreateMessage` draft, or an already-open `RopOpenMessage`
16
+ * handle) and only tracks the small property set `TRACKED_PROPERTY_IDS`/`TRACKED_APPOINTMENT_LIDS`/
17
+ * `TRACKED_COMMON_LIDS` name - see those constants' own comments for why. A named property (ID `>= 0x8000`) is
18
+ * resolved back to its `(PropertySet GUID, LID)` identity via `resolveNamedProperty` before it can be tracked,
19
+ * since MAPI has no fixed numeric ID for any Calendar property the way `PidTagSubject` has one.
20
+ *
21
+ * Every value is coerced to a plain string via `stringifyValue` before being stored in `handle.draftProperties`
22
+ * (itself a `Record<string, string>` for `MapiSessionContext`'s own JSON-safety reasons, see
23
+ * `MapiSessionManager.ts`) - a lossless, type-aware encoding (ISO-8601 for `PtypTime`, base64 for `PtypBinary`,
24
+ * plain `String()` otherwise) so `RopSaveChangesMessageHandler` can decode a Calendar item's properties back
25
+ * into a real `CalendarEvent`'s typed fields, not just display strings.
26
+ *
27
+ * Always reports success with zero `PropertyProblems`, even for a property this pragmatic subset doesn't
28
+ * track - the same "never fail over an unsupported property" pragmatic stance `RopQueryRows`/
29
+ * `RopGetPropertiesSpecific` already take (falling back to a default value there; simply not tracking the
30
+ * property here, since there is no row to fill in for a *set* operation).
31
+ *
32
+ * @author Jean-Philippe Steinmetz
33
+ */
34
+ export declare class RopSetPropertiesHandler implements RopHandler {
35
+ readonly ropId = 10;
36
+ handle(reader: BufferReader, writer: BufferWriter, context: RopContext): void;
37
+ private isTrackedNamedProperty;
38
+ }