@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.
- package/LICENSE +382 -0
- package/README.md +60 -0
- package/dist/lib/BaseMapiEmsmdbRoute.js +489 -0
- package/dist/lib/BaseMapiEmsmdbRoute.js.map +1 -0
- package/dist/lib/BaseMapiNspiRoute.js +114 -0
- package/dist/lib/BaseMapiNspiRoute.js.map +1 -0
- package/dist/lib/MapiSessionManager.js +475 -0
- package/dist/lib/MapiSessionManager.js.map +1 -0
- package/dist/lib/RegexPatternUtils.js +30 -0
- package/dist/lib/RegexPatternUtils.js.map +1 -0
- package/dist/lib/RopDispatcher.js +145 -0
- package/dist/lib/RopDispatcher.js.map +1 -0
- package/dist/lib/codec/AppointmentRecurrence.js +315 -0
- package/dist/lib/codec/AppointmentRecurrence.js.map +1 -0
- package/dist/lib/codec/BufferCursor.js +242 -0
- package/dist/lib/codec/BufferCursor.js.map +1 -0
- package/dist/lib/codec/GlobalObjectId.js +98 -0
- package/dist/lib/codec/GlobalObjectId.js.map +1 -0
- package/dist/lib/codec/MapiGuid.js +41 -0
- package/dist/lib/codec/MapiGuid.js.map +1 -0
- package/dist/lib/codec/MapiTimeZone.js +126 -0
- package/dist/lib/codec/MapiTimeZone.js.map +1 -0
- package/dist/lib/codec/PropertyValue.js +240 -0
- package/dist/lib/codec/PropertyValue.js.map +1 -0
- package/dist/lib/codec/RopBuffer.js +38 -0
- package/dist/lib/codec/RopBuffer.js.map +1 -0
- package/dist/lib/codec/TypedString.js +54 -0
- package/dist/lib/codec/TypedString.js.map +1 -0
- package/dist/lib/index.js +99 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/mongo/MapiEmsmdbRouteMongo.js +85 -0
- package/dist/lib/mongo/MapiEmsmdbRouteMongo.js.map +1 -0
- package/dist/lib/mongo/MapiNspiRouteMongo.js +33 -0
- package/dist/lib/mongo/MapiNspiRouteMongo.js.map +1 -0
- package/dist/lib/mongo/index.js +7 -0
- package/dist/lib/mongo/index.js.map +1 -0
- package/dist/lib/mongo.js +6 -0
- package/dist/lib/mongo.js.map +1 -0
- package/dist/lib/nspi/NspiBindHandler.js +54 -0
- package/dist/lib/nspi/NspiBindHandler.js.map +1 -0
- package/dist/lib/nspi/NspiCodec.js +106 -0
- package/dist/lib/nspi/NspiCodec.js.map +1 -0
- package/dist/lib/nspi/NspiGetMatchesHandler.js +142 -0
- package/dist/lib/nspi/NspiGetMatchesHandler.js.map +1 -0
- package/dist/lib/rop/AddressList.js +114 -0
- package/dist/lib/rop/AddressList.js.map +1 -0
- package/dist/lib/rop/CalendarEventTarget.js +30 -0
- package/dist/lib/rop/CalendarEventTarget.js.map +1 -0
- package/dist/lib/rop/CalendarNamedProperties.js +57 -0
- package/dist/lib/rop/CalendarNamedProperties.js.map +1 -0
- package/dist/lib/rop/ContactTarget.js +19 -0
- package/dist/lib/rop/ContactTarget.js.map +1 -0
- package/dist/lib/rop/ContentsTable.js +60 -0
- package/dist/lib/rop/ContentsTable.js.map +1 -0
- package/dist/lib/rop/ExecuteBudget.js +82 -0
- package/dist/lib/rop/ExecuteBudget.js.map +1 -0
- package/dist/lib/rop/FastTransferStream.js +195 -0
- package/dist/lib/rop/FastTransferStream.js.map +1 -0
- package/dist/lib/rop/FolderTarget.js +118 -0
- package/dist/lib/rop/FolderTarget.js.map +1 -0
- package/dist/lib/rop/HandleDataCache.js +412 -0
- package/dist/lib/rop/HandleDataCache.js.map +1 -0
- package/dist/lib/rop/MeetingMessageClassHandler.js +241 -0
- package/dist/lib/rop/MeetingMessageClassHandler.js.map +1 -0
- package/dist/lib/rop/MessageBodyStream.js +68 -0
- package/dist/lib/rop/MessageBodyStream.js.map +1 -0
- package/dist/lib/rop/MessageTarget.js +57 -0
- package/dist/lib/rop/MessageTarget.js.map +1 -0
- package/dist/lib/rop/NamedPropertyRegistry.js +69 -0
- package/dist/lib/rop/NamedPropertyRegistry.js.map +1 -0
- package/dist/lib/rop/PropertyResolvers.js +353 -0
- package/dist/lib/rop/PropertyResolvers.js.map +1 -0
- package/dist/lib/rop/RepoPaging.js +54 -0
- package/dist/lib/rop/RepoPaging.js.map +1 -0
- package/dist/lib/rop/RestapiRules.js +52 -0
- package/dist/lib/rop/RestapiRules.js.map +1 -0
- package/dist/lib/rop/RopCreateMessageHandler.js +55 -0
- package/dist/lib/rop/RopCreateMessageHandler.js.map +1 -0
- package/dist/lib/rop/RopDeleteFolderHandler.js +174 -0
- package/dist/lib/rop/RopDeleteFolderHandler.js.map +1 -0
- package/dist/lib/rop/RopDeleteMessagesHandler.js +110 -0
- package/dist/lib/rop/RopDeleteMessagesHandler.js.map +1 -0
- package/dist/lib/rop/RopFastTransferSourceCopyPropertiesHandler.js +61 -0
- package/dist/lib/rop/RopFastTransferSourceCopyPropertiesHandler.js.map +1 -0
- package/dist/lib/rop/RopFastTransferSourceCopyToHandler.js +60 -0
- package/dist/lib/rop/RopFastTransferSourceCopyToHandler.js.map +1 -0
- package/dist/lib/rop/RopFastTransferSourceGetBufferHandler.js +76 -0
- package/dist/lib/rop/RopFastTransferSourceGetBufferHandler.js.map +1 -0
- package/dist/lib/rop/RopGetContentsTableHandler.js +55 -0
- package/dist/lib/rop/RopGetContentsTableHandler.js.map +1 -0
- package/dist/lib/rop/RopGetHierarchyTableHandler.js +42 -0
- package/dist/lib/rop/RopGetHierarchyTableHandler.js.map +1 -0
- package/dist/lib/rop/RopGetPropertiesSpecificHandler.js +56 -0
- package/dist/lib/rop/RopGetPropertiesSpecificHandler.js.map +1 -0
- package/dist/lib/rop/RopGetPropertyIdsFromNamesHandler.js +92 -0
- package/dist/lib/rop/RopGetPropertyIdsFromNamesHandler.js.map +1 -0
- package/dist/lib/rop/RopHandler.js +6 -0
- package/dist/lib/rop/RopHandler.js.map +1 -0
- package/dist/lib/rop/RopLogonHandler.js +135 -0
- package/dist/lib/rop/RopLogonHandler.js.map +1 -0
- package/dist/lib/rop/RopOpenFolderHandler.js +48 -0
- package/dist/lib/rop/RopOpenFolderHandler.js.map +1 -0
- package/dist/lib/rop/RopOpenMessageHandler.js +83 -0
- package/dist/lib/rop/RopOpenMessageHandler.js.map +1 -0
- package/dist/lib/rop/RopOpenStreamHandler.js +84 -0
- package/dist/lib/rop/RopOpenStreamHandler.js.map +1 -0
- package/dist/lib/rop/RopQueryRowsHandler.js +120 -0
- package/dist/lib/rop/RopQueryRowsHandler.js.map +1 -0
- package/dist/lib/rop/RopReadStreamHandler.js +73 -0
- package/dist/lib/rop/RopReadStreamHandler.js.map +1 -0
- package/dist/lib/rop/RopReleaseHandler.js +23 -0
- package/dist/lib/rop/RopReleaseHandler.js.map +1 -0
- package/dist/lib/rop/RopSaveChangesMessageHandler.js +269 -0
- package/dist/lib/rop/RopSaveChangesMessageHandler.js.map +1 -0
- package/dist/lib/rop/RopSetColumnsHandler.js +50 -0
- package/dist/lib/rop/RopSetColumnsHandler.js.map +1 -0
- package/dist/lib/rop/RopSetPropertiesHandler.js +173 -0
- package/dist/lib/rop/RopSetPropertiesHandler.js.map +1 -0
- package/dist/lib/rop/RopSubmitMessageHandler.js +342 -0
- package/dist/lib/rop/RopSubmitMessageHandler.js.map +1 -0
- package/dist/lib/rop/RopWriteStreamHandler.js +72 -0
- package/dist/lib/rop/RopWriteStreamHandler.js.map +1 -0
- package/dist/lib/rop/TaskNamedProperties.js +29 -0
- package/dist/lib/rop/TaskNamedProperties.js.map +1 -0
- package/dist/lib/rop/TaskTarget.js +15 -0
- package/dist/lib/rop/TaskTarget.js.map +1 -0
- package/dist/lib/rop/TransportSend.js +16 -0
- package/dist/lib/rop/TransportSend.js.map +1 -0
- package/dist/lib/sql/MapiEmsmdbRouteSQL.js +85 -0
- package/dist/lib/sql/MapiEmsmdbRouteSQL.js.map +1 -0
- package/dist/lib/sql/MapiNspiRouteSQL.js +33 -0
- package/dist/lib/sql/MapiNspiRouteSQL.js.map +1 -0
- package/dist/lib/sql/index.js +7 -0
- package/dist/lib/sql/index.js.map +1 -0
- package/dist/lib/sql.js +6 -0
- package/dist/lib/sql.js.map +1 -0
- package/dist/types/BaseMapiEmsmdbRoute.d.ts +139 -0
- package/dist/types/BaseMapiNspiRoute.d.ts +38 -0
- package/dist/types/MapiSessionManager.d.ts +285 -0
- package/dist/types/RegexPatternUtils.d.ts +14 -0
- package/dist/types/RopDispatcher.d.ts +53 -0
- package/dist/types/codec/AppointmentRecurrence.d.ts +14 -0
- package/dist/types/codec/BufferCursor.d.ts +92 -0
- package/dist/types/codec/GlobalObjectId.d.ts +15 -0
- package/dist/types/codec/MapiGuid.d.ts +6 -0
- package/dist/types/codec/MapiTimeZone.d.ts +7 -0
- package/dist/types/codec/PropertyValue.d.ts +87 -0
- package/dist/types/codec/RopBuffer.d.ts +32 -0
- package/dist/types/codec/TypedString.d.ts +17 -0
- package/dist/types/index.d.ts +94 -0
- package/dist/types/mongo/MapiEmsmdbRouteMongo.d.ts +20 -0
- package/dist/types/mongo/MapiNspiRouteMongo.d.ts +13 -0
- package/dist/types/mongo/index.d.ts +2 -0
- package/dist/types/mongo.d.ts +1 -0
- package/dist/types/nspi/NspiBindHandler.d.ts +22 -0
- package/dist/types/nspi/NspiCodec.d.ts +59 -0
- package/dist/types/nspi/NspiGetMatchesHandler.d.ts +26 -0
- package/dist/types/rop/AddressList.d.ts +48 -0
- package/dist/types/rop/CalendarEventTarget.d.ts +33 -0
- package/dist/types/rop/CalendarNamedProperties.d.ts +37 -0
- package/dist/types/rop/ContactTarget.d.ts +29 -0
- package/dist/types/rop/ContentsTable.d.ts +16 -0
- package/dist/types/rop/ExecuteBudget.d.ts +48 -0
- package/dist/types/rop/FastTransferStream.d.ts +57 -0
- package/dist/types/rop/FolderTarget.d.ts +64 -0
- package/dist/types/rop/HandleDataCache.d.ts +145 -0
- package/dist/types/rop/MeetingMessageClassHandler.d.ts +30 -0
- package/dist/types/rop/MessageBodyStream.d.ts +35 -0
- package/dist/types/rop/MessageTarget.d.ts +45 -0
- package/dist/types/rop/NamedPropertyRegistry.d.ts +50 -0
- package/dist/types/rop/PropertyResolvers.d.ts +86 -0
- package/dist/types/rop/RepoPaging.d.ts +30 -0
- package/dist/types/rop/RestapiRules.d.ts +32 -0
- package/dist/types/rop/RopCreateMessageHandler.d.ts +24 -0
- package/dist/types/rop/RopDeleteFolderHandler.d.ts +53 -0
- package/dist/types/rop/RopDeleteMessagesHandler.d.ts +43 -0
- package/dist/types/rop/RopFastTransferSourceCopyPropertiesHandler.d.ts +25 -0
- package/dist/types/rop/RopFastTransferSourceCopyToHandler.d.ts +25 -0
- package/dist/types/rop/RopFastTransferSourceGetBufferHandler.d.ts +22 -0
- package/dist/types/rop/RopGetContentsTableHandler.d.ts +25 -0
- package/dist/types/rop/RopGetHierarchyTableHandler.d.ts +18 -0
- package/dist/types/rop/RopGetPropertiesSpecificHandler.d.ts +26 -0
- package/dist/types/rop/RopGetPropertyIdsFromNamesHandler.d.ts +23 -0
- package/dist/types/rop/RopHandler.d.ts +106 -0
- package/dist/types/rop/RopLogonHandler.d.ts +31 -0
- package/dist/types/rop/RopOpenFolderHandler.d.ts +20 -0
- package/dist/types/rop/RopOpenMessageHandler.d.ts +28 -0
- package/dist/types/rop/RopOpenStreamHandler.d.ts +28 -0
- package/dist/types/rop/RopQueryRowsHandler.d.ts +43 -0
- package/dist/types/rop/RopReadStreamHandler.d.ts +20 -0
- package/dist/types/rop/RopReleaseHandler.d.ts +15 -0
- package/dist/types/rop/RopSaveChangesMessageHandler.d.ts +59 -0
- package/dist/types/rop/RopSetColumnsHandler.d.ts +21 -0
- package/dist/types/rop/RopSetPropertiesHandler.d.ts +38 -0
- package/dist/types/rop/RopSubmitMessageHandler.d.ts +80 -0
- package/dist/types/rop/RopWriteStreamHandler.d.ts +31 -0
- package/dist/types/rop/TaskNamedProperties.d.ts +24 -0
- package/dist/types/rop/TaskTarget.d.ts +24 -0
- package/dist/types/rop/TransportSend.d.ts +10 -0
- package/dist/types/sql/MapiEmsmdbRouteSQL.d.ts +20 -0
- package/dist/types/sql/MapiNspiRouteSQL.d.ts +13 -0
- package/dist/types/sql/index.d.ts +2 -0
- package/dist/types/sql.d.ts +1 -0
- 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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"}
|