@rapidmx/mapi-plugin 1.0.0-beta.10

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 (204) hide show
  1. package/LICENSE +382 -0
  2. package/README.md +60 -0
  3. package/dist/lib/BaseMapiEmsmdbRoute.js +489 -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 +475 -0
  8. package/dist/lib/MapiSessionManager.js.map +1 -0
  9. package/dist/lib/RegexPatternUtils.js +30 -0
  10. package/dist/lib/RegexPatternUtils.js.map +1 -0
  11. package/dist/lib/RopDispatcher.js +145 -0
  12. package/dist/lib/RopDispatcher.js.map +1 -0
  13. package/dist/lib/codec/AppointmentRecurrence.js +315 -0
  14. package/dist/lib/codec/AppointmentRecurrence.js.map +1 -0
  15. package/dist/lib/codec/BufferCursor.js +242 -0
  16. package/dist/lib/codec/BufferCursor.js.map +1 -0
  17. package/dist/lib/codec/GlobalObjectId.js +98 -0
  18. package/dist/lib/codec/GlobalObjectId.js.map +1 -0
  19. package/dist/lib/codec/MapiGuid.js +41 -0
  20. package/dist/lib/codec/MapiGuid.js.map +1 -0
  21. package/dist/lib/codec/MapiTimeZone.js +126 -0
  22. package/dist/lib/codec/MapiTimeZone.js.map +1 -0
  23. package/dist/lib/codec/PropertyValue.js +240 -0
  24. package/dist/lib/codec/PropertyValue.js.map +1 -0
  25. package/dist/lib/codec/RopBuffer.js +38 -0
  26. package/dist/lib/codec/RopBuffer.js.map +1 -0
  27. package/dist/lib/codec/TypedString.js +54 -0
  28. package/dist/lib/codec/TypedString.js.map +1 -0
  29. package/dist/lib/index.js +99 -0
  30. package/dist/lib/index.js.map +1 -0
  31. package/dist/lib/mongo/MapiEmsmdbRouteMongo.js +85 -0
  32. package/dist/lib/mongo/MapiEmsmdbRouteMongo.js.map +1 -0
  33. package/dist/lib/mongo/MapiNspiRouteMongo.js +33 -0
  34. package/dist/lib/mongo/MapiNspiRouteMongo.js.map +1 -0
  35. package/dist/lib/mongo/index.js +7 -0
  36. package/dist/lib/mongo/index.js.map +1 -0
  37. package/dist/lib/mongo.js +6 -0
  38. package/dist/lib/mongo.js.map +1 -0
  39. package/dist/lib/nspi/NspiBindHandler.js +54 -0
  40. package/dist/lib/nspi/NspiBindHandler.js.map +1 -0
  41. package/dist/lib/nspi/NspiCodec.js +106 -0
  42. package/dist/lib/nspi/NspiCodec.js.map +1 -0
  43. package/dist/lib/nspi/NspiGetMatchesHandler.js +142 -0
  44. package/dist/lib/nspi/NspiGetMatchesHandler.js.map +1 -0
  45. package/dist/lib/rop/AddressList.js +114 -0
  46. package/dist/lib/rop/AddressList.js.map +1 -0
  47. package/dist/lib/rop/CalendarEventTarget.js +30 -0
  48. package/dist/lib/rop/CalendarEventTarget.js.map +1 -0
  49. package/dist/lib/rop/CalendarNamedProperties.js +57 -0
  50. package/dist/lib/rop/CalendarNamedProperties.js.map +1 -0
  51. package/dist/lib/rop/ContactTarget.js +19 -0
  52. package/dist/lib/rop/ContactTarget.js.map +1 -0
  53. package/dist/lib/rop/ContentsTable.js +60 -0
  54. package/dist/lib/rop/ContentsTable.js.map +1 -0
  55. package/dist/lib/rop/ExecuteBudget.js +82 -0
  56. package/dist/lib/rop/ExecuteBudget.js.map +1 -0
  57. package/dist/lib/rop/FastTransferStream.js +195 -0
  58. package/dist/lib/rop/FastTransferStream.js.map +1 -0
  59. package/dist/lib/rop/FolderTarget.js +118 -0
  60. package/dist/lib/rop/FolderTarget.js.map +1 -0
  61. package/dist/lib/rop/HandleDataCache.js +412 -0
  62. package/dist/lib/rop/HandleDataCache.js.map +1 -0
  63. package/dist/lib/rop/MeetingMessageClassHandler.js +241 -0
  64. package/dist/lib/rop/MeetingMessageClassHandler.js.map +1 -0
  65. package/dist/lib/rop/MessageBodyStream.js +68 -0
  66. package/dist/lib/rop/MessageBodyStream.js.map +1 -0
  67. package/dist/lib/rop/MessageTarget.js +57 -0
  68. package/dist/lib/rop/MessageTarget.js.map +1 -0
  69. package/dist/lib/rop/NamedPropertyRegistry.js +69 -0
  70. package/dist/lib/rop/NamedPropertyRegistry.js.map +1 -0
  71. package/dist/lib/rop/PropertyResolvers.js +353 -0
  72. package/dist/lib/rop/PropertyResolvers.js.map +1 -0
  73. package/dist/lib/rop/RepoPaging.js +54 -0
  74. package/dist/lib/rop/RepoPaging.js.map +1 -0
  75. package/dist/lib/rop/RestapiRules.js +52 -0
  76. package/dist/lib/rop/RestapiRules.js.map +1 -0
  77. package/dist/lib/rop/RopCreateMessageHandler.js +55 -0
  78. package/dist/lib/rop/RopCreateMessageHandler.js.map +1 -0
  79. package/dist/lib/rop/RopDeleteFolderHandler.js +174 -0
  80. package/dist/lib/rop/RopDeleteFolderHandler.js.map +1 -0
  81. package/dist/lib/rop/RopDeleteMessagesHandler.js +110 -0
  82. package/dist/lib/rop/RopDeleteMessagesHandler.js.map +1 -0
  83. package/dist/lib/rop/RopFastTransferSourceCopyPropertiesHandler.js +61 -0
  84. package/dist/lib/rop/RopFastTransferSourceCopyPropertiesHandler.js.map +1 -0
  85. package/dist/lib/rop/RopFastTransferSourceCopyToHandler.js +60 -0
  86. package/dist/lib/rop/RopFastTransferSourceCopyToHandler.js.map +1 -0
  87. package/dist/lib/rop/RopFastTransferSourceGetBufferHandler.js +76 -0
  88. package/dist/lib/rop/RopFastTransferSourceGetBufferHandler.js.map +1 -0
  89. package/dist/lib/rop/RopGetContentsTableHandler.js +55 -0
  90. package/dist/lib/rop/RopGetContentsTableHandler.js.map +1 -0
  91. package/dist/lib/rop/RopGetHierarchyTableHandler.js +42 -0
  92. package/dist/lib/rop/RopGetHierarchyTableHandler.js.map +1 -0
  93. package/dist/lib/rop/RopGetPropertiesSpecificHandler.js +56 -0
  94. package/dist/lib/rop/RopGetPropertiesSpecificHandler.js.map +1 -0
  95. package/dist/lib/rop/RopGetPropertyIdsFromNamesHandler.js +92 -0
  96. package/dist/lib/rop/RopGetPropertyIdsFromNamesHandler.js.map +1 -0
  97. package/dist/lib/rop/RopHandler.js +6 -0
  98. package/dist/lib/rop/RopHandler.js.map +1 -0
  99. package/dist/lib/rop/RopLogonHandler.js +135 -0
  100. package/dist/lib/rop/RopLogonHandler.js.map +1 -0
  101. package/dist/lib/rop/RopOpenFolderHandler.js +48 -0
  102. package/dist/lib/rop/RopOpenFolderHandler.js.map +1 -0
  103. package/dist/lib/rop/RopOpenMessageHandler.js +83 -0
  104. package/dist/lib/rop/RopOpenMessageHandler.js.map +1 -0
  105. package/dist/lib/rop/RopOpenStreamHandler.js +84 -0
  106. package/dist/lib/rop/RopOpenStreamHandler.js.map +1 -0
  107. package/dist/lib/rop/RopQueryRowsHandler.js +120 -0
  108. package/dist/lib/rop/RopQueryRowsHandler.js.map +1 -0
  109. package/dist/lib/rop/RopReadStreamHandler.js +73 -0
  110. package/dist/lib/rop/RopReadStreamHandler.js.map +1 -0
  111. package/dist/lib/rop/RopReleaseHandler.js +23 -0
  112. package/dist/lib/rop/RopReleaseHandler.js.map +1 -0
  113. package/dist/lib/rop/RopSaveChangesMessageHandler.js +269 -0
  114. package/dist/lib/rop/RopSaveChangesMessageHandler.js.map +1 -0
  115. package/dist/lib/rop/RopSetColumnsHandler.js +50 -0
  116. package/dist/lib/rop/RopSetColumnsHandler.js.map +1 -0
  117. package/dist/lib/rop/RopSetPropertiesHandler.js +173 -0
  118. package/dist/lib/rop/RopSetPropertiesHandler.js.map +1 -0
  119. package/dist/lib/rop/RopSubmitMessageHandler.js +342 -0
  120. package/dist/lib/rop/RopSubmitMessageHandler.js.map +1 -0
  121. package/dist/lib/rop/RopWriteStreamHandler.js +72 -0
  122. package/dist/lib/rop/RopWriteStreamHandler.js.map +1 -0
  123. package/dist/lib/rop/TaskNamedProperties.js +29 -0
  124. package/dist/lib/rop/TaskNamedProperties.js.map +1 -0
  125. package/dist/lib/rop/TaskTarget.js +15 -0
  126. package/dist/lib/rop/TaskTarget.js.map +1 -0
  127. package/dist/lib/rop/TransportSend.js +16 -0
  128. package/dist/lib/rop/TransportSend.js.map +1 -0
  129. package/dist/lib/sql/MapiEmsmdbRouteSQL.js +85 -0
  130. package/dist/lib/sql/MapiEmsmdbRouteSQL.js.map +1 -0
  131. package/dist/lib/sql/MapiNspiRouteSQL.js +33 -0
  132. package/dist/lib/sql/MapiNspiRouteSQL.js.map +1 -0
  133. package/dist/lib/sql/index.js +7 -0
  134. package/dist/lib/sql/index.js.map +1 -0
  135. package/dist/lib/sql.js +6 -0
  136. package/dist/lib/sql.js.map +1 -0
  137. package/dist/types/BaseMapiEmsmdbRoute.d.ts +139 -0
  138. package/dist/types/BaseMapiNspiRoute.d.ts +38 -0
  139. package/dist/types/MapiSessionManager.d.ts +285 -0
  140. package/dist/types/RegexPatternUtils.d.ts +14 -0
  141. package/dist/types/RopDispatcher.d.ts +53 -0
  142. package/dist/types/codec/AppointmentRecurrence.d.ts +14 -0
  143. package/dist/types/codec/BufferCursor.d.ts +92 -0
  144. package/dist/types/codec/GlobalObjectId.d.ts +15 -0
  145. package/dist/types/codec/MapiGuid.d.ts +6 -0
  146. package/dist/types/codec/MapiTimeZone.d.ts +7 -0
  147. package/dist/types/codec/PropertyValue.d.ts +87 -0
  148. package/dist/types/codec/RopBuffer.d.ts +32 -0
  149. package/dist/types/codec/TypedString.d.ts +17 -0
  150. package/dist/types/index.d.ts +94 -0
  151. package/dist/types/mongo/MapiEmsmdbRouteMongo.d.ts +20 -0
  152. package/dist/types/mongo/MapiNspiRouteMongo.d.ts +13 -0
  153. package/dist/types/mongo/index.d.ts +2 -0
  154. package/dist/types/mongo.d.ts +1 -0
  155. package/dist/types/nspi/NspiBindHandler.d.ts +22 -0
  156. package/dist/types/nspi/NspiCodec.d.ts +59 -0
  157. package/dist/types/nspi/NspiGetMatchesHandler.d.ts +26 -0
  158. package/dist/types/rop/AddressList.d.ts +48 -0
  159. package/dist/types/rop/CalendarEventTarget.d.ts +33 -0
  160. package/dist/types/rop/CalendarNamedProperties.d.ts +37 -0
  161. package/dist/types/rop/ContactTarget.d.ts +29 -0
  162. package/dist/types/rop/ContentsTable.d.ts +16 -0
  163. package/dist/types/rop/ExecuteBudget.d.ts +48 -0
  164. package/dist/types/rop/FastTransferStream.d.ts +57 -0
  165. package/dist/types/rop/FolderTarget.d.ts +64 -0
  166. package/dist/types/rop/HandleDataCache.d.ts +145 -0
  167. package/dist/types/rop/MeetingMessageClassHandler.d.ts +30 -0
  168. package/dist/types/rop/MessageBodyStream.d.ts +35 -0
  169. package/dist/types/rop/MessageTarget.d.ts +45 -0
  170. package/dist/types/rop/NamedPropertyRegistry.d.ts +50 -0
  171. package/dist/types/rop/PropertyResolvers.d.ts +86 -0
  172. package/dist/types/rop/RepoPaging.d.ts +30 -0
  173. package/dist/types/rop/RestapiRules.d.ts +32 -0
  174. package/dist/types/rop/RopCreateMessageHandler.d.ts +24 -0
  175. package/dist/types/rop/RopDeleteFolderHandler.d.ts +53 -0
  176. package/dist/types/rop/RopDeleteMessagesHandler.d.ts +43 -0
  177. package/dist/types/rop/RopFastTransferSourceCopyPropertiesHandler.d.ts +25 -0
  178. package/dist/types/rop/RopFastTransferSourceCopyToHandler.d.ts +25 -0
  179. package/dist/types/rop/RopFastTransferSourceGetBufferHandler.d.ts +22 -0
  180. package/dist/types/rop/RopGetContentsTableHandler.d.ts +25 -0
  181. package/dist/types/rop/RopGetHierarchyTableHandler.d.ts +18 -0
  182. package/dist/types/rop/RopGetPropertiesSpecificHandler.d.ts +26 -0
  183. package/dist/types/rop/RopGetPropertyIdsFromNamesHandler.d.ts +23 -0
  184. package/dist/types/rop/RopHandler.d.ts +106 -0
  185. package/dist/types/rop/RopLogonHandler.d.ts +31 -0
  186. package/dist/types/rop/RopOpenFolderHandler.d.ts +20 -0
  187. package/dist/types/rop/RopOpenMessageHandler.d.ts +28 -0
  188. package/dist/types/rop/RopOpenStreamHandler.d.ts +28 -0
  189. package/dist/types/rop/RopQueryRowsHandler.d.ts +43 -0
  190. package/dist/types/rop/RopReadStreamHandler.d.ts +20 -0
  191. package/dist/types/rop/RopReleaseHandler.d.ts +15 -0
  192. package/dist/types/rop/RopSaveChangesMessageHandler.d.ts +59 -0
  193. package/dist/types/rop/RopSetColumnsHandler.d.ts +21 -0
  194. package/dist/types/rop/RopSetPropertiesHandler.d.ts +38 -0
  195. package/dist/types/rop/RopSubmitMessageHandler.d.ts +80 -0
  196. package/dist/types/rop/RopWriteStreamHandler.d.ts +31 -0
  197. package/dist/types/rop/TaskNamedProperties.d.ts +24 -0
  198. package/dist/types/rop/TaskTarget.d.ts +24 -0
  199. package/dist/types/rop/TransportSend.d.ts +10 -0
  200. package/dist/types/sql/MapiEmsmdbRouteSQL.d.ts +20 -0
  201. package/dist/types/sql/MapiNspiRouteSQL.d.ts +13 -0
  202. package/dist/types/sql/index.d.ts +2 -0
  203. package/dist/types/sql.d.ts +1 -0
  204. package/package.json +104 -0
@@ -0,0 +1,489 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
11
+ return function (target, key) { decorator(target, key, paramIndex); }
12
+ };
13
+ ///////////////////////////////////////////////////////////////////////////////
14
+ // Copyright (C) 2026 Jean-Philippe Steinmetz. All rights reserved.
15
+ // SPDX-License-Identifier: MPL-2.0
16
+ ///////////////////////////////////////////////////////////////////////////////
17
+ import { ApiError, ObjectDecorators } from "@rapidrest/core";
18
+ import { ApiErrorMessages, ApiErrors, NotificationUtils, RepoUtils, RouteDecorators, } from "@rapidrest/service-core";
19
+ import { BufferReader, BufferWriter, DecodeError } from "./codec/BufferCursor.js";
20
+ import { decodeRopBuffer, encodeRopBuffer } from "./codec/RopBuffer.js";
21
+ import { MapiSessionManager, SESSION_LOCK_RENEW_MS, takeReleasedHandleData } from "./MapiSessionManager.js";
22
+ import { dispatchRops, ExecuteBufferTooSmallError, MAX_ROPS_LIST_BYTES } from "./RopDispatcher.js";
23
+ import { handleDataOwners } from "./rop/HandleDataCache.js";
24
+ import { handleDataStoreOf } from "./rop/RopHandler.js";
25
+ import { ScanPipeline } from "@rapidmx/restapi/scan";
26
+ import { RecoverableRepoUtils, recordAuditLog, refreshFolderCounts, resolveCallerMailboxUid, } from "@rapidmx/restapi";
27
+ const { Config, Init, Inject, Logger } = ObjectDecorators;
28
+ const { Auth, Post, Request, Response, User: AuthUser } = RouteDecorators;
29
+ /** `[MS-OXCMAPIHTTP]` `X-ResponseCode` 10, "Context Not Found": the session context named by the `MapiContext`
30
+ * cookie doesn't exist (expired, ended, or not the caller's). The client `Connect`s again. `X-ResponseCode` only takes
31
+ * the spec's small status values; the MAPI HRESULT goes in the body's `ErrorCode` (`ERROR_SESSION_NOT_FOUND`). */
32
+ const RESPONSE_CODE_CONTEXT_NOT_FOUND = 10;
33
+ /** The well-known MAPI HRESULT `MAPI_E_LOGON_FAILED`, reused as the body `ErrorCode` for "no such session -
34
+ * reconnect". Not a claim of exact `[MS-OXCRPC]` return-value-table parity for this specific condition. */
35
+ const ERROR_SESSION_NOT_FOUND = 0x80040111;
36
+ /** `MAPI_E_TOO_BIG`, the body `ErrorCode` when a request would grow the session past `MAX_SESSION_BYTES`. */
37
+ const ERROR_SESSION_TOO_BIG = 0x80040305;
38
+ /** `[MS-OXCMAPIHTTP]` `X-ResponseCode` 15, "Invalid Sequence": the request overlapped another one on the same
39
+ * session context. Sent when the session changed underneath this request (see `MapiSessionManager.save()`). */
40
+ const RESPONSE_CODE_INVALID_SEQUENCE = 15;
41
+ /** `ecBufferTooSmall` ([MS-OXCRPC]), the body `ErrorCode` when the client's `MaxRopOut` can't even hold a
42
+ * `RopBufferTooSmall` carrying the whole request. No ROP has run by then. */
43
+ const ERROR_BUFFER_TOO_SMALL = 0x0000047d;
44
+ /** The largest `RopBufferSize` accepted - the `[MS-OXCRPC]` ROP input buffer limit. */
45
+ export const MAX_ROP_BUFFER_SIZE = 32767;
46
+ /**
47
+ * Decodes an `Execute` request body's `Flags`/`RopBufferSize`/`RopBuffer`/`MaxRopOut` and the ROP buffer framing
48
+ * inside it. Throws a 400 `ApiError` for a malformed body - a `RopBufferSize` over `MAX_ROP_BUFFER_SIZE` or past the
49
+ * end of the body, or a ROP buffer `decodeRopBuffer` rejects - instead of letting a bare `RangeError` surface as a
50
+ * 500. A body that ends before `MaxRopOut` gets the largest response a `RopBuffer` can carry.
51
+ * `AuxiliaryBufferSize`/`AuxiliaryBuffer` are left unread - no auxiliary-payload support in this pragmatic subset.
52
+ */
53
+ export function decodeExecuteRequest(rawBody) {
54
+ try {
55
+ const reader = new BufferReader(rawBody);
56
+ reader.readUInt32LE(); // Flags - unused by this pragmatic subset (no client ROP-response hints honored)
57
+ const ropBufferSize = reader.readUInt32LE();
58
+ if (ropBufferSize > MAX_ROP_BUFFER_SIZE || ropBufferSize > reader.remaining) {
59
+ throw new RangeError(`Execute: invalid RopBufferSize ${ropBufferSize}.`);
60
+ }
61
+ const ropBuffer = decodeRopBuffer(reader.readBytes(ropBufferSize));
62
+ const maxRopOut = reader.remaining >= 4 ? reader.readUInt32LE() : MAX_ROPS_LIST_BYTES + 2;
63
+ return { ...ropBuffer, maxRopOut };
64
+ }
65
+ catch {
66
+ throw new ApiError(ApiErrors.INVALID_REQUEST, 400, ApiErrorMessages.INVALID_REQUEST);
67
+ }
68
+ }
69
+ function firstHeader(req, name) {
70
+ const value = req.headers[name];
71
+ return Array.isArray(value) ? value[0] : value;
72
+ }
73
+ /**
74
+ * Abstract base for the single fixed EMSMDB endpoint (`POST /mapi/emsmdb` by `[MS-OXCMAPIHTTP]` convention,
75
+ * though the concrete path is left to the consuming application to mount via `@Route(...)` - see
76
+ * `BaseEasRoute.ts` for the identical undecorated-base-class pattern this follows). Like EAS, MAPI/HTTP
77
+ * multiplexes several request types against one URL - here via the `X-RequestType` **header**
78
+ * (`Connect`/`Execute`/`Disconnect`/`NotificationWait`) rather than a query parameter - so there is exactly
79
+ * one `@Post()` method, not one per request type.
80
+ *
81
+ * **Auth**: `@Auth(["jwt"])` - unchanged from every other route in this app. Real, modern Exchange Server's
82
+ * own MAPI virtual directory genuinely supports `OAuth` as a configured `IISAuthenticationMethods` value
83
+ * alongside `NTLM`/`Negotiate`, so Bearer-token auth on this exact endpoint is real current Exchange
84
+ * behavior, not a deviation this library invents - see the architecture plan's "Auth" section.
85
+ *
86
+ * **Connect/Disconnect are complete**: `Connect` establishes a real `MapiSessionContext` (via
87
+ * `MapiSessionManager`) and sets the two spec-fixed session cookies (`MapiContext`/`MapiSequence`);
88
+ * `Disconnect` releases the session. **`Execute` dispatches real ROPs** (starting with `RopLogon`/`RopRelease`
89
+ * - build-order step 4) via `RopDispatcher`, using whichever `RopHandler`s `ropHandlerClasses` registers;
90
+ * an unrecognized `RopId` simply stops processing (see `RopDispatcher`'s own doc comment for why), the same
91
+ * "real, functional, no commands implemented yet" stance `BaseEasRoute`'s own skeleton step took for its
92
+ * empty `commandHandlerClasses` before `ProvisionCommand` landed - here scoped per-ROP instead of per-request.
93
+ *
94
+ * **Always non-chunked**: every response here uses `Content-Length` (via `res.status(200).send(buffer)`),
95
+ * never `Transfer-Encoding: chunked` - both are equally spec-valid per `[MS-OXCMAPIHTTP]`'s own "Common
96
+ * Response Format", and skipping the `PROCESSING`/`PENDING`/`DONE` keep-alive meta-tag streaming matches
97
+ * every other route in this library's own response idiom. See the architecture plan's "Protocol facts"
98
+ * section for the full reasoning and its documented limitation.
99
+ *
100
+ * `mailboxClass`/`folderClass` are supplied by the Mongo/SQL concrete subclasses, following the exact
101
+ * one-line-per-backend pattern used throughout this library's other routes/jobs. `folderRepo` is built once
102
+ * here (not per-handler, unlike EAS's per-command repo pattern) and threaded through `RopContext` to every
103
+ * `RopHandler` - see `RopHandler.ts`'s own doc comment for why.
104
+ *
105
+ * @author Jean-Philippe Steinmetz
106
+ */
107
+ export class BaseMapiEmsmdbRoute {
108
+ constructor() {
109
+ /** ROP handler classes to instantiate (one each) in `@Init`, keyed by their own `ropId`. Empty until a
110
+ * concrete `RopHandler` lands - every ROP is then simply left unprocessed (see `RopDispatcher`'s own doc
111
+ * comment), the correct, honest behavior for a transport skeleton with no ROPs implemented yet. */
112
+ this.ropHandlerClasses = [];
113
+ this.ropHandlers = new Map();
114
+ }
115
+ async init() {
116
+ this.mailboxRepo = await this._objectFactory.newInstance(RepoUtils, {
117
+ name: this.mailboxClass.name,
118
+ args: [this.mailboxClass],
119
+ });
120
+ this.folderRepo = await this._objectFactory.newInstance(RecoverableRepoUtils, {
121
+ name: this.folderClass.name,
122
+ args: [this.folderClass],
123
+ });
124
+ this.messageRepo = await this._objectFactory.newInstance(RecoverableRepoUtils, {
125
+ name: this.messageClass.name,
126
+ args: [this.messageClass],
127
+ });
128
+ this.calendarEventRepo = await this._objectFactory.newInstance(RecoverableRepoUtils, {
129
+ name: this.calendarEventClass.name,
130
+ args: [this.calendarEventClass],
131
+ });
132
+ this.contactRepo = await this._objectFactory.newInstance(RecoverableRepoUtils, {
133
+ name: this.contactClass.name,
134
+ args: [this.contactClass],
135
+ });
136
+ this.taskRepo = await this._objectFactory.newInstance(RecoverableRepoUtils, {
137
+ name: this.taskClass.name,
138
+ args: [this.taskClass],
139
+ });
140
+ // Label extends plain BaseEntity, not RecoverableBaseEntity (see restapi's own model doc comment) - no
141
+ // soft-delete support to preserve, so a plain RepoUtils is correct here, matching mailboxRepo's own
142
+ // choice for the identical reason.
143
+ this.labelRepo = await this._objectFactory.newInstance(RepoUtils, {
144
+ name: this.labelClass.name,
145
+ args: [this.labelClass],
146
+ });
147
+ this.sessionManager = await this._objectFactory.newInstance(MapiSessionManager);
148
+ for (const HandlerClass of this.ropHandlerClasses) {
149
+ const handler = await this._objectFactory.newInstance(HandlerClass);
150
+ this.ropHandlers.set(handler.ropId, handler);
151
+ }
152
+ }
153
+ async dispatch(req, res, user) {
154
+ if (!this.mailboxRepo ||
155
+ !this.folderRepo ||
156
+ !this.messageRepo ||
157
+ !this.calendarEventRepo ||
158
+ !this.contactRepo ||
159
+ !this.taskRepo ||
160
+ !this.labelRepo ||
161
+ !this.sessionManager ||
162
+ !this.blobStore ||
163
+ !this.scanPipeline ||
164
+ !this.mailTransport) {
165
+ throw new ApiError(ApiErrors.INTERNAL_ERROR, 500, ApiErrorMessages.INTERNAL_ERROR);
166
+ }
167
+ if (!user) {
168
+ throw new ApiError(ApiErrors.AUTH_PERMISSION_FAILURE, 403, ApiErrorMessages.AUTH_PERMISSION_FAILURE);
169
+ }
170
+ const requestType = firstHeader(req, "x-requesttype");
171
+ const clientInfo = firstHeader(req, "x-clientinfo") ?? "";
172
+ const requestId = firstHeader(req, "x-requestid") ?? "";
173
+ res.setHeader("Content-Type", "application/mapi-http")
174
+ .setHeader("X-RequestType", requestType ?? "")
175
+ .setHeader("X-RequestId", requestId)
176
+ .setHeader("X-ResponseCode", "0")
177
+ .setHeader("X-ClientInfo", clientInfo)
178
+ .setHeader("X-ServerApplication", "RapidREST-Mail");
179
+ switch (requestType) {
180
+ case "Connect":
181
+ await this.handleConnect(req, res, user);
182
+ return;
183
+ case "Execute":
184
+ await this.handleExecute(req, res, user);
185
+ return;
186
+ case "Disconnect":
187
+ await this.handleDisconnect(req, res, user);
188
+ return;
189
+ case "NotificationWait":
190
+ // Real, spec-defined request type, deliberately deferred - no push-notification support in
191
+ // this pass (see the architecture plan's ROP scope table). Distinct from an unrecognized
192
+ // X-RequestType value (400 below), the same distinction BaseEasRoute draws between 501
193
+ // ("recognized but deferred") and 400 ("malformed request").
194
+ res.status(501).send();
195
+ return;
196
+ default:
197
+ throw new ApiError(ApiErrors.INVALID_REQUEST, 400, ApiErrorMessages.INVALID_REQUEST);
198
+ }
199
+ }
200
+ /**
201
+ * `Connect` establishes a new Session Context - never trusts the request body's own `UserDn` field for
202
+ * mailbox identity (the same "never a client-supplied mailbox, always resolved from the authenticated
203
+ * JWT" principle `BaseEasRoute`/`BaseSearchRoute` already apply via `resolveCallerMailboxUid`) - so the
204
+ * request body is never decoded at all here.
205
+ */
206
+ async handleConnect(req, res, user) {
207
+ const mailboxUid = await resolveCallerMailboxUid(this.mailboxRepo, user);
208
+ if (!mailboxUid) {
209
+ throw new ApiError(ApiErrors.NOT_FOUND, 404, ApiErrorMessages.NOT_FOUND);
210
+ }
211
+ const mailbox = await this.mailboxRepo.findOne(mailboxUid, { ignoreACL: true });
212
+ const session = await this.sessionManager.create(mailboxUid, user.uid);
213
+ res.appendHeader("Set-Cookie", `MapiContext=${session.uid}`);
214
+ res.appendHeader("Set-Cookie", `MapiSequence=0`);
215
+ const body = new BufferWriter();
216
+ body.writeUInt32LE(0); // StatusCode
217
+ body.writeUInt32LE(0); // ErrorCode
218
+ body.writeUInt32LE(60000); // PollsMax (ms) - pragmatic constant, not a tuned server policy value
219
+ body.writeUInt32LE(3); // RetryCount - pragmatic constant
220
+ body.writeUInt32LE(5000); // RetryDelay (ms) - pragmatic constant
221
+ // DnPrefix: this library addresses mailboxes by UID/SMTP-address, never real X.500 DNs, so this is a
222
+ // fixed, unused placeholder rather than a real DN prefix a client could build recipients from.
223
+ body.writeNullTerminatedString8("/");
224
+ body.writeNullTerminatedUtf16LE(mailbox?.displayName ?? "");
225
+ body.writeUInt32LE(0); // AuxiliaryBufferSize
226
+ res.status(200).send(body.toBuffer());
227
+ }
228
+ /**
229
+ * `Execute` decodes the outer envelope (`Flags`/`RopBufferSize`/`RopBuffer`/`MaxRopOut`/...) and the inner ROP
230
+ * buffer framing (`RopBuffer.ts`), dispatches every contained ROP via `RopDispatcher`, and re-encodes the
231
+ * collected responses - preserving the incoming `handleTable` unchanged (this pragmatic subset never
232
+ * allocates/frees table-wide handle slots at the framing level; individual `RopHandler`s manage their own
233
+ * entries within `session.handles` instead).
234
+ *
235
+ * - **One request at a time per session.** Only the caller's own session (whose mailbox is still the caller's) is
236
+ * locked. The lock is taken before any ROP runs and renewed every `SESSION_LOCK_RENEW_MS` while the request runs,
237
+ * so a slow request keeps it; an overlapping request gets `X-ResponseCode` 15 (Invalid Sequence) without any side
238
+ * effects. The save stays compare-and-set. Each `Execute` also refreshes the session's place in the per-user cap.
239
+ * - **Retries.** While a request with an `X-RequestId` runs, an in-progress marker for that id is kept (renewed with
240
+ * the lock); a retry of it is answered with code 15 instead of running again. Its response then replaces the
241
+ * marker, and a request repeating the last id (a client retrying after a lost response) gets that response again
242
+ * instead of running its ROPs twice. A request that ends without a stored response drops its marker.
243
+ * `MapiSequence` isn't validated - the lock and the replay cache cover what it guards against here.
244
+ * - **Output size.** ROP responses are held to the client's `MaxRopOut`, less the `RopSize` field and handle
245
+ * table, and to what a 16-bit `RopSize` can describe (see `dispatchRops`). Room for a `RopBufferTooSmall` covering
246
+ * the rest of the request is kept free as ROPs run, so a response that doesn't fit is always answered that way (a
247
+ * normal, stored response). Only a `MaxRopOut` too small for a `RopBufferTooSmall` of the whole request answers
248
+ * `ecBufferTooSmall` in the body `ErrorCode`, before any ROP runs, so nothing is stored and a retry changes nothing.
249
+ * - **Released handles.** Once the session is saved, the FastTransfer streams and write-stream chunks of handles
250
+ * the request released are deleted from the shared store; `Disconnect` and session eviction delete a session's
251
+ * whole set.
252
+ * - **Malformed ROPs.** A ROP that can't be decoded answers 400; the ROPs before it already ran, so the session is
253
+ * still saved first.
254
+ */
255
+ async handleExecute(req, res, user) {
256
+ const sessionId = req.cookies["MapiContext"];
257
+ if (!sessionId) {
258
+ this.sendExecuteFailure(res, RESPONSE_CODE_CONTEXT_NOT_FOUND, ERROR_SESSION_NOT_FOUND);
259
+ return;
260
+ }
261
+ // Ownership first: someone else's session (or a mailbox reassigned since Connect) is never locked by this caller.
262
+ const owned = await this.loadOwnSession(req, user);
263
+ if (!owned || (await resolveCallerMailboxUid(this.mailboxRepo, user)) !== owned.mailboxUid) {
264
+ this.sendExecuteFailure(res, RESPONSE_CODE_CONTEXT_NOT_FOUND, ERROR_SESSION_NOT_FOUND);
265
+ return;
266
+ }
267
+ const lockToken = await this.sessionManager.acquireLock(sessionId);
268
+ if (!lockToken) {
269
+ this.sendExecuteFailure(res, RESPONSE_CODE_INVALID_SEQUENCE, RESPONSE_CODE_INVALID_SEQUENCE);
270
+ return;
271
+ }
272
+ const running = {};
273
+ // Renewed while the request runs, so a slow Execute never loses its lock (or its in-progress marker) to a retry.
274
+ // Renewals run one after another, and stop once the lock is lost. The marker is only extended while it is still
275
+ // this request's marker, so a renewal landing after the response was stored never turns it back into a marker.
276
+ let renewing = Promise.resolve();
277
+ const renewal = setInterval(() => {
278
+ renewing = renewing.then(async () => {
279
+ const held = await this.sessionManager.renewLock(sessionId, lockToken).catch(() => true);
280
+ if (!held) {
281
+ clearInterval(renewal);
282
+ return;
283
+ }
284
+ if (running.requestId) {
285
+ await this.sessionManager.renewInProgress(sessionId, running.requestId).catch(() => undefined);
286
+ }
287
+ });
288
+ }, SESSION_LOCK_RENEW_MS);
289
+ renewal.unref();
290
+ try {
291
+ await this.executeLocked(req, res, user, running);
292
+ }
293
+ finally {
294
+ clearInterval(renewal);
295
+ await renewing;
296
+ if (running.requestId) {
297
+ await this.sessionManager.clearInProgress(sessionId, running.requestId).catch(() => undefined);
298
+ }
299
+ await this.sessionManager.releaseLock(sessionId, lockToken);
300
+ }
301
+ }
302
+ /** Runs an `Execute` under its session's lock. `running.requestId` is set while this request's in-progress marker is
303
+ * stored, and cleared once its response is stored instead. */
304
+ async executeLocked(req, res, user, running) {
305
+ // Loaded again under the lock: the copy read before locking may already be out of date.
306
+ const session = await this.loadOwnSession(req, user);
307
+ if (!session) {
308
+ this.sendExecuteFailure(res, RESPONSE_CODE_CONTEXT_NOT_FOUND, ERROR_SESSION_NOT_FOUND);
309
+ return;
310
+ }
311
+ const requestId = firstHeader(req, "x-requestid") ?? "";
312
+ const replay = requestId ? await this.sessionManager.storedResponse(session.uid, requestId) : undefined;
313
+ if (replay === "inProgress") {
314
+ // The same request is still running elsewhere (its lock lapsed, or its pod died moments ago).
315
+ this.sendExecuteFailure(res, RESPONSE_CODE_INVALID_SEQUENCE, RESPONSE_CODE_INVALID_SEQUENCE);
316
+ return;
317
+ }
318
+ if (replay) {
319
+ res.status(200).send(replay);
320
+ return;
321
+ }
322
+ if (requestId) {
323
+ await this.sessionManager.markInProgress(session.uid, requestId);
324
+ running.requestId = requestId;
325
+ }
326
+ await this.sessionManager.touch(session).catch(() => undefined);
327
+ const { ropsList, handleTable, maxRopOut } = decodeExecuteRequest(req.rawBody ?? Buffer.alloc(0));
328
+ const context = {
329
+ mailboxUid: session.mailboxUid,
330
+ userUid: session.userUid,
331
+ session,
332
+ mailboxRepo: this.mailboxRepo,
333
+ folderRepo: this.folderRepo,
334
+ messageRepo: this.messageRepo,
335
+ calendarEventRepo: this.calendarEventRepo,
336
+ contactRepo: this.contactRepo,
337
+ taskRepo: this.taskRepo,
338
+ labelRepo: this.labelRepo,
339
+ folderClass: this.folderClass,
340
+ messageClass: this.messageClass,
341
+ calendarEventClass: this.calendarEventClass,
342
+ contactClass: this.contactClass,
343
+ taskClass: this.taskClass,
344
+ labelClass: this.labelClass,
345
+ blobStore: this.blobStore,
346
+ scanPipeline: this.scanPipeline,
347
+ mailTransport: this.mailTransport,
348
+ handleData: this.sessionManager.handleDataStore,
349
+ audit: this.auditLogClass
350
+ ? (params) => recordAuditLog(this._objectFactory, this.auditLogClass, { config: this.config, req, user, logger: this.logger }, params)
351
+ : undefined,
352
+ notifyFolderCounts: (folderUids, options) => refreshFolderCounts(this.folderCountsContext(), folderUids, options),
353
+ };
354
+ // RopSize (2 bytes) and the echoed handle table share MaxRopOut with the ROP responses. dispatchRops only throws
355
+ // for a request it can't decode (a failing ROP gets a failure response instead).
356
+ const dispatched = await dispatchRops(ropsList, this.ropHandlers, context, {
357
+ maxOutputBytes: maxRopOut - 2 - handleTable.length * 4,
358
+ }).catch((error) => ({ error }));
359
+ const saved = await this.sessionManager.save(session);
360
+ const released = takeReleasedHandleData(session);
361
+ if (saved === "saved") {
362
+ // Streams and write chunks of handles this request released; best effort, they expire anyway.
363
+ const store = handleDataStoreOf(context);
364
+ const owners = handleDataOwners(session.uid, session.userUid);
365
+ await Promise.all(released.map((key) => store.delete(key, owners).catch(() => undefined)));
366
+ }
367
+ if (!Buffer.isBuffer(dispatched) && !(dispatched.error instanceof ExecuteBufferTooSmallError)) {
368
+ throw dispatched.error instanceof DecodeError ? new ApiError(ApiErrors.INVALID_REQUEST, 400, ApiErrorMessages.INVALID_REQUEST) : dispatched.error;
369
+ }
370
+ if (saved !== "saved") {
371
+ // conflict: another request saved first (possible once a lock has expired), so this request's handle
372
+ // changes can't be kept and the client retries. missing: the session ended meanwhile. tooBig: see
373
+ // MAX_SESSION_BYTES.
374
+ const [responseCode, errorCode] = saved === "conflict"
375
+ ? [RESPONSE_CODE_INVALID_SEQUENCE, RESPONSE_CODE_INVALID_SEQUENCE]
376
+ : saved === "missing"
377
+ ? [RESPONSE_CODE_CONTEXT_NOT_FOUND, ERROR_SESSION_NOT_FOUND]
378
+ : [0, ERROR_SESSION_TOO_BIG];
379
+ this.sendExecuteFailure(res, responseCode, errorCode);
380
+ return;
381
+ }
382
+ if (!Buffer.isBuffer(dispatched)) {
383
+ // [MS-OXCRPC] ecBufferTooSmall: not even a RopBufferTooSmall of the whole request fits, so no ROP ran.
384
+ this.sendExecuteFailure(res, 0, ERROR_BUFFER_TOO_SMALL);
385
+ return;
386
+ }
387
+ const responseRopsList = dispatched;
388
+ const responseRopBuffer = encodeRopBuffer({ ropsList: responseRopsList, handleTable });
389
+ const body = new BufferWriter();
390
+ body.writeUInt32LE(0); // StatusCode
391
+ body.writeUInt32LE(0); // ErrorCode
392
+ body.writeUInt32LE(0); // Flags
393
+ body.writeUInt32LE(responseRopBuffer.length);
394
+ body.writeBytes(responseRopBuffer);
395
+ body.writeUInt32LE(0); // AuxiliaryBufferSize
396
+ const responseBody = body.toBuffer();
397
+ if (requestId) {
398
+ await this.sessionManager.storeResponse(session.uid, requestId, responseBody);
399
+ delete running.requestId;
400
+ }
401
+ res.status(200).send(responseBody);
402
+ }
403
+ /** What `refreshFolderCounts()` (`@rapidmx/restapi`) needs to recompute, re-cache and publish a folder's counts -
404
+ * see `RopContext.notifyFolderCounts`'s own doc comment for why a ROP handler must call it after adding, deleting
405
+ * or moving a `Message` row through `context.messageRepo` itself. */
406
+ folderCountsContext() {
407
+ return {
408
+ messageRepo: this.messageRepo,
409
+ folderRepo: this.folderRepo,
410
+ folderClass: this.folderClass,
411
+ notificationUtils: this.notificationUtils,
412
+ logger: this.logger,
413
+ };
414
+ }
415
+ /** Writes an `Execute` failure body (no ROP buffer) with the given `X-ResponseCode` and `ErrorCode`. */
416
+ sendExecuteFailure(res, responseCode, errorCode) {
417
+ res.setHeader("X-ResponseCode", String(responseCode));
418
+ const body = new BufferWriter();
419
+ body.writeUInt32LE(0); // StatusCode
420
+ body.writeUInt32LE(errorCode); // ErrorCode
421
+ body.writeUInt32LE(0); // Flags
422
+ body.writeUInt32LE(0); // RopBufferSize
423
+ body.writeUInt32LE(0); // AuxiliaryBufferSize
424
+ res.status(200).send(body.toBuffer());
425
+ }
426
+ /**
427
+ * Loads the session named by the `MapiContext` cookie, but only if the authenticated caller is the user who
428
+ * `Connect`ed it. The cookie value alone is a bearer token for someone else's mailbox otherwise, so a session
429
+ * belonging to another user is treated exactly like one that doesn't exist.
430
+ */
431
+ async loadOwnSession(req, user) {
432
+ const sessionId = req.cookies["MapiContext"];
433
+ const session = sessionId ? await this.sessionManager.load(sessionId) : undefined;
434
+ return session && session.userUid === user.uid ? session : undefined;
435
+ }
436
+ async handleDisconnect(req, res, user) {
437
+ // Another user's session is left alone, with the same success response as for an unknown session.
438
+ const session = await this.loadOwnSession(req, user);
439
+ if (session) {
440
+ await this.sessionManager.destroy(session.uid);
441
+ }
442
+ const body = new BufferWriter();
443
+ body.writeUInt32LE(0); // StatusCode
444
+ body.writeUInt32LE(0); // ErrorCode
445
+ body.writeUInt32LE(0); // AuxiliaryBufferSize
446
+ res.status(200).send(body.toBuffer());
447
+ }
448
+ }
449
+ __decorate([
450
+ Inject("BlobStore"),
451
+ __metadata("design:type", Object)
452
+ ], BaseMapiEmsmdbRoute.prototype, "blobStore", void 0);
453
+ __decorate([
454
+ Inject(ScanPipeline),
455
+ __metadata("design:type", ScanPipeline)
456
+ ], BaseMapiEmsmdbRoute.prototype, "scanPipeline", void 0);
457
+ __decorate([
458
+ Inject("MailTransport"),
459
+ __metadata("design:type", Object)
460
+ ], BaseMapiEmsmdbRoute.prototype, "mailTransport", void 0);
461
+ __decorate([
462
+ Inject(NotificationUtils),
463
+ __metadata("design:type", NotificationUtils)
464
+ ], BaseMapiEmsmdbRoute.prototype, "notificationUtils", void 0);
465
+ __decorate([
466
+ Logger,
467
+ __metadata("design:type", Object)
468
+ ], BaseMapiEmsmdbRoute.prototype, "logger", void 0);
469
+ __decorate([
470
+ Config(),
471
+ __metadata("design:type", Object)
472
+ ], BaseMapiEmsmdbRoute.prototype, "config", void 0);
473
+ __decorate([
474
+ Init,
475
+ __metadata("design:type", Function),
476
+ __metadata("design:paramtypes", []),
477
+ __metadata("design:returntype", Promise)
478
+ ], BaseMapiEmsmdbRoute.prototype, "init", null);
479
+ __decorate([
480
+ Auth(["jwt"]),
481
+ Post(),
482
+ __param(0, Request),
483
+ __param(1, Response),
484
+ __param(2, AuthUser),
485
+ __metadata("design:type", Function),
486
+ __metadata("design:paramtypes", [Object, Object, Object]),
487
+ __metadata("design:returntype", Promise)
488
+ ], BaseMapiEmsmdbRoute.prototype, "dispatch", null);
489
+ //# sourceMappingURL=BaseMapiEmsmdbRoute.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BaseMapiEmsmdbRoute.js","sourceRoot":"","sources":["../../src/BaseMapiEmsmdbRoute.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,114 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
11
+ return function (target, key) { decorator(target, key, paramIndex); }
12
+ };
13
+ ///////////////////////////////////////////////////////////////////////////////
14
+ // Copyright (C) 2026 Jean-Philippe Steinmetz. All rights reserved.
15
+ // SPDX-License-Identifier: MPL-2.0
16
+ ///////////////////////////////////////////////////////////////////////////////
17
+ import { ApiError, ObjectDecorators } from "@rapidrest/core";
18
+ import { ApiErrorMessages, ApiErrors, RepoUtils, RouteDecorators } from "@rapidrest/service-core";
19
+ import { handleNspiBind, handleNspiUnbind } from "./nspi/NspiBindHandler.js";
20
+ import { handleNspiGetMatches } from "./nspi/NspiGetMatchesHandler.js";
21
+ import { resolveCallerMailboxUid } from "@rapidmx/restapi";
22
+ const { Init } = ObjectDecorators;
23
+ const { Auth, Post, Request, Response, User: AuthUser } = RouteDecorators;
24
+ function firstHeader(req, name) {
25
+ const value = req.headers[name];
26
+ return Array.isArray(value) ? value[0] : value;
27
+ }
28
+ /**
29
+ * Abstract base for the single fixed NSPI endpoint (`POST /mapi/nspi` by `[MS-OXCMAPIHTTP]` convention, though
30
+ * the concrete path is left to the consuming application to mount via `@Route(...)`, the identical
31
+ * undecorated-base-class pattern `BaseMapiEmsmdbRoute`/`BaseEasRoute` already establish). Dispatches on the
32
+ * `X-RequestType` header, the same multiplexing mechanism EMSMDB uses - but unlike EMSMDB, there is no ROP
33
+ * buffer framing here: each `X-RequestType` value (`Bind`/`Unbind`/`GetMatches`) is its own flat, self-
34
+ * contained request/response body (`[MS-OXCMAPIHTTP]` §2.2.5), so this route dispatches directly to a small
35
+ * handler function per operation instead of decoding a multiplexed stream.
36
+ *
37
+ * **Pragmatic subset scope**: only `Bind`/`Unbind`/`GetMatches` are implemented - real NSPI's other dozen-plus
38
+ * request types (`QueryRows`, `ResolveNames`, `GetProps`, `ModProps`, ...) are genuine full address-book/
39
+ * directory-replication operations this deployment's own minimal GAL-lookup use case doesn't need; an
40
+ * unrecognized `X-RequestType` gets a `501`, the same "recognized-but-deferred vs malformed" distinction
41
+ * `BaseMapiEmsmdbRoute`'s own `NotificationWait` case already draws. See `NspiBindHandler.ts`'s own doc comment
42
+ * for why no real NSPI session state is created or tracked between calls.
43
+ *
44
+ * **Auth**: `@Auth(["jwt"])`, unchanged from every other route in this app - the same reasoning
45
+ * `BaseMapiEmsmdbRoute`'s own doc comment already gives for why Bearer-token auth on an address-book endpoint
46
+ * is real, current Exchange behavior, not a deviation this library invents.
47
+ *
48
+ * `mailboxClass`/`contactClass` are supplied by the Mongo/SQL concrete subclasses. GAL search itself (see
49
+ * `NspiGetMatchesHandler.ts`) uses `RepoUtils`'s `regex(...)` operator, which compiles identically on both
50
+ * backends - no per-backend hook needed here.
51
+ *
52
+ * @author Jean-Philippe Steinmetz
53
+ */
54
+ export class BaseMapiNspiRoute {
55
+ async init() {
56
+ this.mailboxRepo = await this._objectFactory.newInstance(RepoUtils, {
57
+ name: this.mailboxClass.name,
58
+ args: [this.mailboxClass],
59
+ });
60
+ this.contactRepo = await this._objectFactory.newInstance(RepoUtils, {
61
+ name: this.contactClass.name,
62
+ args: [this.contactClass],
63
+ });
64
+ }
65
+ async dispatch(req, res, user) {
66
+ if (!this.mailboxRepo || !this.contactRepo) {
67
+ throw new ApiError(ApiErrors.INTERNAL_ERROR, 500, ApiErrorMessages.INTERNAL_ERROR);
68
+ }
69
+ if (!user) {
70
+ throw new ApiError(ApiErrors.AUTH_PERMISSION_FAILURE, 403, ApiErrorMessages.AUTH_PERMISSION_FAILURE);
71
+ }
72
+ const requestType = firstHeader(req, "x-requesttype");
73
+ res.setHeader("Content-Type", "application/mapi-http")
74
+ .setHeader("X-RequestType", requestType ?? "")
75
+ .setHeader("X-ResponseCode", "0")
76
+ .setHeader("X-ServerApplication", "RapidREST-Mail");
77
+ switch (requestType) {
78
+ case "Bind":
79
+ await handleNspiBind(res, user, this.mailboxRepo);
80
+ return;
81
+ case "Unbind":
82
+ handleNspiUnbind(res);
83
+ return;
84
+ case "GetMatches": {
85
+ const mailboxUid = await resolveCallerMailboxUid(this.mailboxRepo, user);
86
+ if (!mailboxUid) {
87
+ throw new ApiError(ApiErrors.NOT_FOUND, 404, ApiErrorMessages.NOT_FOUND);
88
+ }
89
+ await handleNspiGetMatches(req, res, mailboxUid, this.contactRepo);
90
+ return;
91
+ }
92
+ default:
93
+ res.status(501).send();
94
+ return;
95
+ }
96
+ }
97
+ }
98
+ __decorate([
99
+ Init,
100
+ __metadata("design:type", Function),
101
+ __metadata("design:paramtypes", []),
102
+ __metadata("design:returntype", Promise)
103
+ ], BaseMapiNspiRoute.prototype, "init", null);
104
+ __decorate([
105
+ Auth(["jwt"]),
106
+ Post(),
107
+ __param(0, Request),
108
+ __param(1, Response),
109
+ __param(2, AuthUser),
110
+ __metadata("design:type", Function),
111
+ __metadata("design:paramtypes", [Object, Object, Object]),
112
+ __metadata("design:returntype", Promise)
113
+ ], BaseMapiNspiRoute.prototype, "dispatch", null);
114
+ //# sourceMappingURL=BaseMapiNspiRoute.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BaseMapiNspiRoute.js","sourceRoot":"","sources":["../../src/BaseMapiNspiRoute.ts"],"names":[],"mappings":"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"}