@rapidmx/activesync-plugin 1.0.0-beta.2
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 +57 -0
- package/dist/lib/BaseDeviceSyncStateRoute.js +90 -0
- package/dist/lib/BaseDeviceSyncStateRoute.js.map +1 -0
- package/dist/lib/BaseEasRoute.js +213 -0
- package/dist/lib/BaseEasRoute.js.map +1 -0
- package/dist/lib/CompactDateTime.js +43 -0
- package/dist/lib/CompactDateTime.js.map +1 -0
- package/dist/lib/EasCommandHandler.js +2 -0
- package/dist/lib/EasCommandHandler.js.map +1 -0
- package/dist/lib/EasSyncKeyUtils.js +119 -0
- package/dist/lib/EasSyncKeyUtils.js.map +1 -0
- package/dist/lib/adapters/CalendarSyncAdapter.js +255 -0
- package/dist/lib/adapters/CalendarSyncAdapter.js.map +1 -0
- package/dist/lib/adapters/ContactsSyncAdapter.js +164 -0
- package/dist/lib/adapters/ContactsSyncAdapter.js.map +1 -0
- package/dist/lib/adapters/EasCollectionSyncAdapter.js +2 -0
- package/dist/lib/adapters/EasCollectionSyncAdapter.js.map +1 -0
- package/dist/lib/adapters/EmailSyncAdapter.js +269 -0
- package/dist/lib/adapters/EmailSyncAdapter.js.map +1 -0
- package/dist/lib/adapters/TasksSyncAdapter.js +100 -0
- package/dist/lib/adapters/TasksSyncAdapter.js.map +1 -0
- package/dist/lib/adapters/mongo/EmailSyncAdapterMongo.js +16 -0
- package/dist/lib/adapters/mongo/EmailSyncAdapterMongo.js.map +1 -0
- package/dist/lib/adapters/mongo/index.js +6 -0
- package/dist/lib/adapters/mongo/index.js.map +1 -0
- package/dist/lib/adapters/sql/EmailSyncAdapterSQL.js +16 -0
- package/dist/lib/adapters/sql/EmailSyncAdapterSQL.js.map +1 -0
- package/dist/lib/adapters/sql/index.js +6 -0
- package/dist/lib/adapters/sql/index.js.map +1 -0
- package/dist/lib/codec/WbxmlCodePages.js +706 -0
- package/dist/lib/codec/WbxmlCodePages.js.map +1 -0
- package/dist/lib/codec/WbxmlDecoder.js +154 -0
- package/dist/lib/codec/WbxmlDecoder.js.map +1 -0
- package/dist/lib/codec/WbxmlElement.js +31 -0
- package/dist/lib/codec/WbxmlElement.js.map +1 -0
- package/dist/lib/codec/WbxmlEncoder.js +88 -0
- package/dist/lib/codec/WbxmlEncoder.js.map +1 -0
- package/dist/lib/commands/ComposeMailCommand.js +187 -0
- package/dist/lib/commands/ComposeMailCommand.js.map +1 -0
- package/dist/lib/commands/FolderSyncCommand.js +155 -0
- package/dist/lib/commands/FolderSyncCommand.js.map +1 -0
- package/dist/lib/commands/GetItemEstimateCommand.js +149 -0
- package/dist/lib/commands/GetItemEstimateCommand.js.map +1 -0
- package/dist/lib/commands/ItemOperationsCommand.js +337 -0
- package/dist/lib/commands/ItemOperationsCommand.js.map +1 -0
- package/dist/lib/commands/MeetingResponseCommand.js +120 -0
- package/dist/lib/commands/MeetingResponseCommand.js.map +1 -0
- package/dist/lib/commands/MoveItemsCommand.js +103 -0
- package/dist/lib/commands/MoveItemsCommand.js.map +1 -0
- package/dist/lib/commands/PingCommand.js +145 -0
- package/dist/lib/commands/PingCommand.js.map +1 -0
- package/dist/lib/commands/ProvisionCommand.js +164 -0
- package/dist/lib/commands/ProvisionCommand.js.map +1 -0
- package/dist/lib/commands/ResolveRecipientsCommand.js +123 -0
- package/dist/lib/commands/ResolveRecipientsCommand.js.map +1 -0
- package/dist/lib/commands/SearchCommand.js +234 -0
- package/dist/lib/commands/SearchCommand.js.map +1 -0
- package/dist/lib/commands/SendMailCommand.js +18 -0
- package/dist/lib/commands/SendMailCommand.js.map +1 -0
- package/dist/lib/commands/SettingsCommand.js +138 -0
- package/dist/lib/commands/SettingsCommand.js.map +1 -0
- package/dist/lib/commands/SmartForwardCommand.js +22 -0
- package/dist/lib/commands/SmartForwardCommand.js.map +1 -0
- package/dist/lib/commands/SmartReplyCommand.js +22 -0
- package/dist/lib/commands/SmartReplyCommand.js.map +1 -0
- package/dist/lib/commands/SyncCommand.js +406 -0
- package/dist/lib/commands/SyncCommand.js.map +1 -0
- package/dist/lib/commands/mongo/FolderSyncCommandMongo.js +16 -0
- package/dist/lib/commands/mongo/FolderSyncCommandMongo.js.map +1 -0
- package/dist/lib/commands/mongo/GetItemEstimateCommandMongo.js +21 -0
- package/dist/lib/commands/mongo/GetItemEstimateCommandMongo.js.map +1 -0
- package/dist/lib/commands/mongo/ItemOperationsCommandMongo.js +18 -0
- package/dist/lib/commands/mongo/ItemOperationsCommandMongo.js.map +1 -0
- package/dist/lib/commands/mongo/MeetingResponseCommandMongo.js +17 -0
- package/dist/lib/commands/mongo/MeetingResponseCommandMongo.js.map +1 -0
- package/dist/lib/commands/mongo/MoveItemsCommandMongo.js +17 -0
- package/dist/lib/commands/mongo/MoveItemsCommandMongo.js.map +1 -0
- package/dist/lib/commands/mongo/ResolveRecipientsCommandMongo.js +16 -0
- package/dist/lib/commands/mongo/ResolveRecipientsCommandMongo.js.map +1 -0
- package/dist/lib/commands/mongo/SearchCommandMongo.js +19 -0
- package/dist/lib/commands/mongo/SearchCommandMongo.js.map +1 -0
- package/dist/lib/commands/mongo/SendMailCommandMongo.js +17 -0
- package/dist/lib/commands/mongo/SendMailCommandMongo.js.map +1 -0
- package/dist/lib/commands/mongo/SettingsCommandMongo.js +16 -0
- package/dist/lib/commands/mongo/SettingsCommandMongo.js.map +1 -0
- package/dist/lib/commands/mongo/SmartForwardCommandMongo.js +17 -0
- package/dist/lib/commands/mongo/SmartForwardCommandMongo.js.map +1 -0
- package/dist/lib/commands/mongo/SmartReplyCommandMongo.js +17 -0
- package/dist/lib/commands/mongo/SmartReplyCommandMongo.js.map +1 -0
- package/dist/lib/commands/mongo/SyncCommandMongo.js +26 -0
- package/dist/lib/commands/mongo/SyncCommandMongo.js.map +1 -0
- package/dist/lib/commands/mongo/index.js +14 -0
- package/dist/lib/commands/mongo/index.js.map +1 -0
- package/dist/lib/commands/sql/FolderSyncCommandSQL.js +16 -0
- package/dist/lib/commands/sql/FolderSyncCommandSQL.js.map +1 -0
- package/dist/lib/commands/sql/GetItemEstimateCommandSQL.js +21 -0
- package/dist/lib/commands/sql/GetItemEstimateCommandSQL.js.map +1 -0
- package/dist/lib/commands/sql/ItemOperationsCommandSQL.js +18 -0
- package/dist/lib/commands/sql/ItemOperationsCommandSQL.js.map +1 -0
- package/dist/lib/commands/sql/MeetingResponseCommandSQL.js +17 -0
- package/dist/lib/commands/sql/MeetingResponseCommandSQL.js.map +1 -0
- package/dist/lib/commands/sql/MoveItemsCommandSQL.js +17 -0
- package/dist/lib/commands/sql/MoveItemsCommandSQL.js.map +1 -0
- package/dist/lib/commands/sql/ResolveRecipientsCommandSQL.js +16 -0
- package/dist/lib/commands/sql/ResolveRecipientsCommandSQL.js.map +1 -0
- package/dist/lib/commands/sql/SearchCommandSQL.js +19 -0
- package/dist/lib/commands/sql/SearchCommandSQL.js.map +1 -0
- package/dist/lib/commands/sql/SendMailCommandSQL.js +17 -0
- package/dist/lib/commands/sql/SendMailCommandSQL.js.map +1 -0
- package/dist/lib/commands/sql/SettingsCommandSQL.js +16 -0
- package/dist/lib/commands/sql/SettingsCommandSQL.js.map +1 -0
- package/dist/lib/commands/sql/SmartForwardCommandSQL.js +17 -0
- package/dist/lib/commands/sql/SmartForwardCommandSQL.js.map +1 -0
- package/dist/lib/commands/sql/SmartReplyCommandSQL.js +17 -0
- package/dist/lib/commands/sql/SmartReplyCommandSQL.js.map +1 -0
- package/dist/lib/commands/sql/SyncCommandSQL.js +26 -0
- package/dist/lib/commands/sql/SyncCommandSQL.js.map +1 -0
- package/dist/lib/commands/sql/index.js +14 -0
- package/dist/lib/commands/sql/index.js.map +1 -0
- package/dist/lib/index.js +58 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/jobs/EasDeviceStateCleanupJob.js +101 -0
- package/dist/lib/jobs/EasDeviceStateCleanupJob.js.map +1 -0
- package/dist/lib/jobs/mongo/EasDeviceStateCleanupJobMongo.js +13 -0
- package/dist/lib/jobs/mongo/EasDeviceStateCleanupJobMongo.js.map +1 -0
- package/dist/lib/jobs/sql/EasDeviceStateCleanupJobSQL.js +13 -0
- package/dist/lib/jobs/sql/EasDeviceStateCleanupJobSQL.js.map +1 -0
- package/dist/lib/models/DeviceSyncState.js +2 -0
- package/dist/lib/models/DeviceSyncState.js.map +1 -0
- package/dist/lib/models/mongo/DeviceSyncStateMongo.js +130 -0
- package/dist/lib/models/mongo/DeviceSyncStateMongo.js.map +1 -0
- package/dist/lib/models/sql/DeviceSyncStateSQL.js +130 -0
- package/dist/lib/models/sql/DeviceSyncStateSQL.js.map +1 -0
- package/dist/lib/mongo/DeviceSyncStateRouteMongo.js +28 -0
- package/dist/lib/mongo/DeviceSyncStateRouteMongo.js.map +1 -0
- package/dist/lib/mongo/EasRouteMongo.js +63 -0
- package/dist/lib/mongo/EasRouteMongo.js.map +1 -0
- package/dist/lib/mongo/index.js +9 -0
- package/dist/lib/mongo/index.js.map +1 -0
- package/dist/lib/mongo.js +15 -0
- package/dist/lib/mongo.js.map +1 -0
- package/dist/lib/sql/DeviceSyncStateRouteSQL.js +28 -0
- package/dist/lib/sql/DeviceSyncStateRouteSQL.js.map +1 -0
- package/dist/lib/sql/EasRouteSQL.js +63 -0
- package/dist/lib/sql/EasRouteSQL.js.map +1 -0
- package/dist/lib/sql/index.js +9 -0
- package/dist/lib/sql/index.js.map +1 -0
- package/dist/lib/sql.js +15 -0
- package/dist/lib/sql.js.map +1 -0
- package/dist/types/BaseDeviceSyncStateRoute.d.ts +34 -0
- package/dist/types/BaseEasRoute.d.ts +65 -0
- package/dist/types/CompactDateTime.d.ts +22 -0
- package/dist/types/EasCommandHandler.d.ts +59 -0
- package/dist/types/EasSyncKeyUtils.d.ts +89 -0
- package/dist/types/adapters/CalendarSyncAdapter.d.ts +52 -0
- package/dist/types/adapters/ContactsSyncAdapter.d.ts +33 -0
- package/dist/types/adapters/EasCollectionSyncAdapter.d.ts +55 -0
- package/dist/types/adapters/EmailSyncAdapter.d.ts +71 -0
- package/dist/types/adapters/TasksSyncAdapter.d.ts +32 -0
- package/dist/types/adapters/mongo/EmailSyncAdapterMongo.d.ts +7 -0
- package/dist/types/adapters/mongo/index.d.ts +1 -0
- package/dist/types/adapters/sql/EmailSyncAdapterSQL.d.ts +7 -0
- package/dist/types/adapters/sql/index.d.ts +1 -0
- package/dist/types/codec/WbxmlCodePages.d.ts +45 -0
- package/dist/types/codec/WbxmlDecoder.d.ts +33 -0
- package/dist/types/codec/WbxmlElement.d.ts +43 -0
- package/dist/types/codec/WbxmlEncoder.d.ts +20 -0
- package/dist/types/commands/ComposeMailCommand.d.ts +50 -0
- package/dist/types/commands/FolderSyncCommand.d.ts +25 -0
- package/dist/types/commands/GetItemEstimateCommand.d.ts +43 -0
- package/dist/types/commands/ItemOperationsCommand.d.ts +72 -0
- package/dist/types/commands/MeetingResponseCommand.d.ts +32 -0
- package/dist/types/commands/MoveItemsCommand.d.ts +29 -0
- package/dist/types/commands/PingCommand.d.ts +33 -0
- package/dist/types/commands/ProvisionCommand.d.ts +54 -0
- package/dist/types/commands/ResolveRecipientsCommand.d.ts +36 -0
- package/dist/types/commands/SearchCommand.d.ts +60 -0
- package/dist/types/commands/SendMailCommand.d.ts +10 -0
- package/dist/types/commands/SettingsCommand.d.ts +37 -0
- package/dist/types/commands/SmartForwardCommand.d.ts +17 -0
- package/dist/types/commands/SmartReplyCommand.d.ts +17 -0
- package/dist/types/commands/SyncCommand.d.ts +100 -0
- package/dist/types/commands/mongo/FolderSyncCommandMongo.d.ts +8 -0
- package/dist/types/commands/mongo/GetItemEstimateCommandMongo.d.ts +7 -0
- package/dist/types/commands/mongo/ItemOperationsCommandMongo.d.ts +9 -0
- package/dist/types/commands/mongo/MeetingResponseCommandMongo.d.ts +8 -0
- package/dist/types/commands/mongo/MoveItemsCommandMongo.d.ts +8 -0
- package/dist/types/commands/mongo/ResolveRecipientsCommandMongo.d.ts +7 -0
- package/dist/types/commands/mongo/SearchCommandMongo.d.ts +9 -0
- package/dist/types/commands/mongo/SendMailCommandMongo.d.ts +8 -0
- package/dist/types/commands/mongo/SettingsCommandMongo.d.ts +7 -0
- package/dist/types/commands/mongo/SmartForwardCommandMongo.d.ts +8 -0
- package/dist/types/commands/mongo/SmartReplyCommandMongo.d.ts +8 -0
- package/dist/types/commands/mongo/SyncCommandMongo.d.ts +8 -0
- package/dist/types/commands/mongo/index.d.ts +9 -0
- package/dist/types/commands/sql/FolderSyncCommandSQL.d.ts +8 -0
- package/dist/types/commands/sql/GetItemEstimateCommandSQL.d.ts +7 -0
- package/dist/types/commands/sql/ItemOperationsCommandSQL.d.ts +9 -0
- package/dist/types/commands/sql/MeetingResponseCommandSQL.d.ts +8 -0
- package/dist/types/commands/sql/MoveItemsCommandSQL.d.ts +8 -0
- package/dist/types/commands/sql/ResolveRecipientsCommandSQL.d.ts +7 -0
- package/dist/types/commands/sql/SearchCommandSQL.d.ts +9 -0
- package/dist/types/commands/sql/SendMailCommandSQL.d.ts +8 -0
- package/dist/types/commands/sql/SettingsCommandSQL.d.ts +7 -0
- package/dist/types/commands/sql/SmartForwardCommandSQL.d.ts +8 -0
- package/dist/types/commands/sql/SmartReplyCommandSQL.d.ts +8 -0
- package/dist/types/commands/sql/SyncCommandSQL.d.ts +8 -0
- package/dist/types/commands/sql/index.d.ts +9 -0
- package/dist/types/index.d.ts +53 -0
- package/dist/types/jobs/EasDeviceStateCleanupJob.d.ts +26 -0
- package/dist/types/jobs/mongo/EasDeviceStateCleanupJobMongo.d.ts +5 -0
- package/dist/types/jobs/sql/EasDeviceStateCleanupJobSQL.d.ts +5 -0
- package/dist/types/models/DeviceSyncState.d.ts +30 -0
- package/dist/types/models/mongo/DeviceSyncStateMongo.d.ts +22 -0
- package/dist/types/models/sql/DeviceSyncStateSQL.d.ts +22 -0
- package/dist/types/mongo/DeviceSyncStateRouteMongo.d.ts +8 -0
- package/dist/types/mongo/EasRouteMongo.d.ts +14 -0
- package/dist/types/mongo/index.d.ts +4 -0
- package/dist/types/mongo.d.ts +10 -0
- package/dist/types/sql/DeviceSyncStateRouteSQL.d.ts +8 -0
- package/dist/types/sql/EasRouteSQL.d.ts +14 -0
- package/dist/types/sql/index.d.ts +4 -0
- package/dist/types/sql.d.ts +10 -0
- package/package.json +238 -0
|
@@ -0,0 +1,145 @@
|
|
|
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
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
11
|
+
// Copyright (C) 2026 Jean-Philippe Steinmetz. All rights reserved.
|
|
12
|
+
// SPDX-License-Identifier: MPL-2.0
|
|
13
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
14
|
+
import { createClient } from "redis";
|
|
15
|
+
import { ObjectDecorators } from "@rapidrest/core";
|
|
16
|
+
import { ACLAction, ACLUtils } from "@rapidrest/service-core";
|
|
17
|
+
import { WbxmlCodePage } from "../codec/WbxmlCodePages.js";
|
|
18
|
+
import { childText, element, findChild, findChildren, textElement } from "../codec/WbxmlElement.js";
|
|
19
|
+
const { Config, Inject } = ObjectDecorators;
|
|
20
|
+
/** MS-ASCMD `Ping` `Status` codes this pragmatic subset distinguishes - not the full enumeration the real
|
|
21
|
+
* spec defines (e.g. it also has codes for "too many folders", "folder hierarchy changed", etc.), matching
|
|
22
|
+
* this library's "pragmatic subset, not full fidelity" precedent elsewhere. */
|
|
23
|
+
const STATUS_NO_CHANGES = "1";
|
|
24
|
+
const STATUS_CHANGES_FOUND = "2";
|
|
25
|
+
const STATUS_MISSING_PARAMETERS = "3";
|
|
26
|
+
/**
|
|
27
|
+
* Handles EAS `Ping`: a long-poll HTTP request that blocks for up to `HeartbeatInterval` seconds waiting for a
|
|
28
|
+
* change in any of the client-specified folders, then reports which (if any) actually changed. No websocket
|
|
29
|
+
* needed - subscribes a plain, request-scoped Redis client (the same construction `BasePushRoute.connect()`
|
|
30
|
+
* uses, minus its own per-socket bookkeeping this one-shot request doesn't need) directly to the requested
|
|
31
|
+
* `folderUid` channels. Every `Message`/`CalendarEvent`/etc. mutation already publishes to exactly these
|
|
32
|
+
* channels via `BaseScopedChildRoute.notify()`/`RepoUtils`'s own push - `Ping` needs no new publish call site
|
|
33
|
+
* of its own, only a subscriber.
|
|
34
|
+
*
|
|
35
|
+
* Requested folder uids are filtered down to only those the caller currently has `READ` on before subscribing -
|
|
36
|
+
* matching every other command's ownership check on a client-supplied id, since without it a device could
|
|
37
|
+
* long-poll indefinitely on any folder uid it happens to know (including one it used to have legitimate access
|
|
38
|
+
* to and no longer does) and learn purely from this channel when that other mailbox's data changes.
|
|
39
|
+
*
|
|
40
|
+
* @author Jean-Philippe Steinmetz
|
|
41
|
+
*/
|
|
42
|
+
export class PingCommand {
|
|
43
|
+
constructor() {
|
|
44
|
+
this.command = "Ping";
|
|
45
|
+
this.minHeartbeatSeconds = 60;
|
|
46
|
+
this.maxHeartbeatSeconds = 1740;
|
|
47
|
+
}
|
|
48
|
+
async handle(ctx) {
|
|
49
|
+
if (!ctx.request) {
|
|
50
|
+
return this.statusResponse(STATUS_MISSING_PARAMETERS);
|
|
51
|
+
}
|
|
52
|
+
const foldersEl = findChild(ctx.request, "Folders");
|
|
53
|
+
const requestedFolderUids = foldersEl
|
|
54
|
+
? findChildren(foldersEl, "Folder")
|
|
55
|
+
.map((folderEl) => childText(folderEl, "ServerId"))
|
|
56
|
+
.filter((uid) => !!uid)
|
|
57
|
+
: [];
|
|
58
|
+
if (requestedFolderUids.length === 0) {
|
|
59
|
+
return this.statusResponse(STATUS_MISSING_PARAMETERS);
|
|
60
|
+
}
|
|
61
|
+
// Every other command checks ownership before acting on a client-supplied folder id - `Ping` must too,
|
|
62
|
+
// or a device that once had a folder shared with it (and so learned its real uid) could keep watching
|
|
63
|
+
// that folder's live activity via this long-poll indefinitely, even after the share is later revoked.
|
|
64
|
+
// Filtered down to the permitted subset rather than failing the whole request: `Ping`'s wire response
|
|
65
|
+
// has no per-folder status to report a partial denial through, and a client legitimately re-sends the
|
|
66
|
+
// same folder list across requests regardless of access changes it has no way to know about yet.
|
|
67
|
+
const permitted = await Promise.all(requestedFolderUids.map((uid) => this.aclUtils.hasPermission(ctx.user, uid, ACLAction.READ)));
|
|
68
|
+
const folderUids = requestedFolderUids.filter((_uid, i) => permitted[i]);
|
|
69
|
+
if (folderUids.length === 0) {
|
|
70
|
+
return this.statusResponse(STATUS_MISSING_PARAMETERS);
|
|
71
|
+
}
|
|
72
|
+
const requestedSeconds = Number(childText(ctx.request, "HeartbeatInterval") ?? this.minHeartbeatSeconds);
|
|
73
|
+
const heartbeatSeconds = Math.min(Math.max(Number.isFinite(requestedSeconds) ? requestedSeconds : this.minHeartbeatSeconds, this.minHeartbeatSeconds), this.maxHeartbeatSeconds);
|
|
74
|
+
const changedFolderUids = await this.waitForChange(folderUids, heartbeatSeconds);
|
|
75
|
+
if (changedFolderUids.length === 0) {
|
|
76
|
+
return this.statusResponse(STATUS_NO_CHANGES);
|
|
77
|
+
}
|
|
78
|
+
return element(WbxmlCodePage.Ping, "Ping", [
|
|
79
|
+
textElement(WbxmlCodePage.Ping, "Status", STATUS_CHANGES_FOUND),
|
|
80
|
+
element(WbxmlCodePage.Ping, "Folders", changedFolderUids.map((uid) => textElement(WbxmlCodePage.Ping, "Folder", uid))),
|
|
81
|
+
]);
|
|
82
|
+
}
|
|
83
|
+
statusResponse(status) {
|
|
84
|
+
return element(WbxmlCodePage.Ping, "Ping", [textElement(WbxmlCodePage.Ping, "Status", status)]);
|
|
85
|
+
}
|
|
86
|
+
/** Subscribes to `folderUids` and resolves with whichever of them published a change first, or `[]` if
|
|
87
|
+
* `timeoutSeconds` elapses with no publish. A missing `datastores:events` config (Redis pub/sub not set up
|
|
88
|
+
* for this deployment) fails open to "no changes" rather than blocking forever or throwing - `Ping` is a
|
|
89
|
+
* best-effort low-latency notification path, not the only way a client ever discovers new mail (it will
|
|
90
|
+
* eventually poll a real `Sync`/`FolderSync` regardless). */
|
|
91
|
+
async waitForChange(folderUids, timeoutSeconds) {
|
|
92
|
+
if (!this.redisConfig) {
|
|
93
|
+
return [];
|
|
94
|
+
}
|
|
95
|
+
const client = createClient({ url: this.redisConfig.url });
|
|
96
|
+
await client.connect();
|
|
97
|
+
try {
|
|
98
|
+
return await new Promise((resolve) => {
|
|
99
|
+
let settled = false;
|
|
100
|
+
const timer = setTimeout(() => {
|
|
101
|
+
if (!settled) {
|
|
102
|
+
settled = true;
|
|
103
|
+
resolve([]);
|
|
104
|
+
}
|
|
105
|
+
}, timeoutSeconds * 1000);
|
|
106
|
+
client
|
|
107
|
+
.subscribe(folderUids, (_message, channel) => {
|
|
108
|
+
if (!settled) {
|
|
109
|
+
settled = true;
|
|
110
|
+
clearTimeout(timer);
|
|
111
|
+
resolve([channel]);
|
|
112
|
+
}
|
|
113
|
+
})
|
|
114
|
+
.catch(() => {
|
|
115
|
+
if (!settled) {
|
|
116
|
+
settled = true;
|
|
117
|
+
clearTimeout(timer);
|
|
118
|
+
resolve([]);
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
finally {
|
|
124
|
+
await client.unsubscribe(folderUids).catch(() => undefined);
|
|
125
|
+
await client.disconnect().catch(() => undefined);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
__decorate([
|
|
130
|
+
Config("datastores:events", null),
|
|
131
|
+
__metadata("design:type", Object)
|
|
132
|
+
], PingCommand.prototype, "redisConfig", void 0);
|
|
133
|
+
__decorate([
|
|
134
|
+
Config("mail:eas:ping_min_heartbeat_seconds", 60),
|
|
135
|
+
__metadata("design:type", Number)
|
|
136
|
+
], PingCommand.prototype, "minHeartbeatSeconds", void 0);
|
|
137
|
+
__decorate([
|
|
138
|
+
Config("mail:eas:ping_max_heartbeat_seconds", 1740),
|
|
139
|
+
__metadata("design:type", Number)
|
|
140
|
+
], PingCommand.prototype, "maxHeartbeatSeconds", void 0);
|
|
141
|
+
__decorate([
|
|
142
|
+
Inject(ACLUtils),
|
|
143
|
+
__metadata("design:type", ACLUtils)
|
|
144
|
+
], PingCommand.prototype, "aclUtils", void 0);
|
|
145
|
+
//# sourceMappingURL=PingCommand.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PingCommand.js","sourceRoot":"","sources":["../../../src/commands/PingCommand.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,164 @@
|
|
|
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
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
11
|
+
// Copyright (C) 2026 Jean-Philippe Steinmetz. All rights reserved.
|
|
12
|
+
// SPDX-License-Identifier: MPL-2.0
|
|
13
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
14
|
+
import * as crypto from "crypto";
|
|
15
|
+
import { ObjectDecorators } from "@rapidrest/core";
|
|
16
|
+
import { WbxmlCodePage } from "../codec/WbxmlCodePages.js";
|
|
17
|
+
import { childText, element, findChild, textElement } from "../codec/WbxmlElement.js";
|
|
18
|
+
import { persistDeviceSyncState } from "../EasSyncKeyUtils.js";
|
|
19
|
+
const { Config } = ObjectDecorators;
|
|
20
|
+
const DEFAULT_POLICY_TYPE = "MS-EAS-Provisioning-WBXML";
|
|
21
|
+
/**
|
|
22
|
+
* Handles the two-request EAS `Provision` handshake (MS-ASPROV) every client must complete before any other
|
|
23
|
+
* command is honored (see `BaseEasRoute`'s provisioning gate), plus the three-step `RemoteWipe` sub-flow that
|
|
24
|
+
* rides the same command.
|
|
25
|
+
*
|
|
26
|
+
* **Policy issuance/enforcement**: the password/encryption requirements sent back in request 1's policy
|
|
27
|
+
* document are sourced from `@Config("mail:eas:provision:*")` (defaults below are permissive but not
|
|
28
|
+
* `0`/`false` across the board, unlike the old hardcoded document) - a deployment can tighten them without
|
|
29
|
+
* code changes. Enforcement itself is honest but shallow: request 2 must carry back `Status: 1` on its own
|
|
30
|
+
* `Policy` (a device that reports it could *not* apply the policy, or omits `Status` entirely, is rejected
|
|
31
|
+
* without provisioning) - this library does not itself verify the device's actual password/encryption state
|
|
32
|
+
* beyond trusting that self-reported status, matching MS-ASPROV's own protocol design (the wire protocol has
|
|
33
|
+
* no way for the server to inspect device state directly either).
|
|
34
|
+
*
|
|
35
|
+
* - **Request 1** (no `PolicyKey` in the body): mint a new policy key, store it on `DeviceSyncState` (not yet
|
|
36
|
+
* provisioned), and send back the policy document under that key. If a `RemoteWipe` was requested for this
|
|
37
|
+
* device (`DeviceSyncState.remoteWipeRequested`, set by an admin - see the remote-wipe route), skip normal
|
|
38
|
+
* policy issuance entirely and send the `RemoteWipe` directive instead.
|
|
39
|
+
* - **Request 2** (client echoes the `PolicyKey` back, acknowledging the policy): if the key matches what was
|
|
40
|
+
* minted in request 1 *and* the client's own `Status` is `1`, mark the device provisioned and re-confirm the
|
|
41
|
+
* same key; anything else (a stale/replayed key, a device that never actually saw request 1's response, or a
|
|
42
|
+
* device reporting it could not comply) is rejected without provisioning.
|
|
43
|
+
* - **RemoteWipe acknowledgement**: after wiping itself, a device sends a bare `<Provision><RemoteWipe>
|
|
44
|
+
* <Status>1</Status></RemoteWipe></Provision>` (no `Policies`). Detected first, ahead of the normal
|
|
45
|
+
* issue/acknowledge branching. Clears `remoteWipeRequested` and stamps `remoteWipeAcknowledgedAt` for audit,
|
|
46
|
+
* but deliberately leaves `provisioned` untouched (`false`, from when the wipe was requested) - the device
|
|
47
|
+
* must complete a genuine fresh Provision handshake to re-add the account, it does not fall straight back into
|
|
48
|
+
* "provisioned". `remoteWipeAccountOnly` is recorded for admin audit only; the wire directive sent to the
|
|
49
|
+
* device is identical either way (a real "wipe just this account's data" vs. "wipe the whole device"
|
|
50
|
+
* distinction would require an MDM-capable client extension this library doesn't implement).
|
|
51
|
+
*
|
|
52
|
+
* @author Jean-Philippe Steinmetz
|
|
53
|
+
*/
|
|
54
|
+
export class ProvisionCommand {
|
|
55
|
+
constructor() {
|
|
56
|
+
this.command = "Provision";
|
|
57
|
+
this.passwordEnabled = true;
|
|
58
|
+
this.minPasswordLength = 4;
|
|
59
|
+
this.maxFailedAttempts = 8;
|
|
60
|
+
this.requireDeviceEncryption = true;
|
|
61
|
+
this.allowSimplePassword = false;
|
|
62
|
+
}
|
|
63
|
+
async handle(ctx) {
|
|
64
|
+
if (ctx.request && findChild(ctx.request, "RemoteWipe")) {
|
|
65
|
+
return await this.acknowledgeRemoteWipe(ctx);
|
|
66
|
+
}
|
|
67
|
+
const policiesEl = ctx.request ? findChild(ctx.request, "Policies") : undefined;
|
|
68
|
+
const policyEl = policiesEl ? findChild(policiesEl, "Policy") : undefined;
|
|
69
|
+
const policyType = (policyEl ? childText(policyEl, "PolicyType") : undefined) ?? DEFAULT_POLICY_TYPE;
|
|
70
|
+
const clientPolicyKey = policyEl ? childText(policyEl, "PolicyKey") : undefined;
|
|
71
|
+
const clientStatus = policyEl ? childText(policyEl, "Status") : undefined;
|
|
72
|
+
if (!clientPolicyKey) {
|
|
73
|
+
return await this.issuePolicy(ctx, policyType);
|
|
74
|
+
}
|
|
75
|
+
return await this.acknowledgePolicy(ctx, policyType, clientPolicyKey, clientStatus);
|
|
76
|
+
}
|
|
77
|
+
/** Request 1: mint and store a new policy key, send the policy document - or, if a remote wipe is
|
|
78
|
+
* pending for this device, the `RemoteWipe` directive instead. */
|
|
79
|
+
async issuePolicy(ctx, policyType) {
|
|
80
|
+
if (ctx.deviceSyncState.remoteWipeRequested) {
|
|
81
|
+
return element(WbxmlCodePage.Provision, "Provision", [
|
|
82
|
+
textElement(WbxmlCodePage.Provision, "Status", "1"),
|
|
83
|
+
element(WbxmlCodePage.Provision, "RemoteWipe", [textElement(WbxmlCodePage.Provision, "Status", "1")]),
|
|
84
|
+
]);
|
|
85
|
+
}
|
|
86
|
+
const policyKey = crypto.randomBytes(8).toString("hex");
|
|
87
|
+
await this.persist(ctx, { policyKey, provisioned: false });
|
|
88
|
+
return element(WbxmlCodePage.Provision, "Provision", [
|
|
89
|
+
textElement(WbxmlCodePage.Provision, "Status", "1"),
|
|
90
|
+
element(WbxmlCodePage.Provision, "Policies", [
|
|
91
|
+
element(WbxmlCodePage.Provision, "Policy", [
|
|
92
|
+
textElement(WbxmlCodePage.Provision, "PolicyType", policyType),
|
|
93
|
+
textElement(WbxmlCodePage.Provision, "Status", "1"),
|
|
94
|
+
textElement(WbxmlCodePage.Provision, "PolicyKey", policyKey),
|
|
95
|
+
element(WbxmlCodePage.Provision, "Data", [
|
|
96
|
+
element(WbxmlCodePage.Provision, "EASProvisionDoc", [
|
|
97
|
+
textElement(WbxmlCodePage.Provision, "DevicePasswordEnabled", this.passwordEnabled ? "1" : "0"),
|
|
98
|
+
textElement(WbxmlCodePage.Provision, "MinDevicePasswordLength", String(this.minPasswordLength)),
|
|
99
|
+
textElement(WbxmlCodePage.Provision, "MaxDevicePasswordFailedAttempts", String(this.maxFailedAttempts)),
|
|
100
|
+
textElement(WbxmlCodePage.Provision, "AllowSimpleDevicePassword", this.allowSimplePassword ? "1" : "0"),
|
|
101
|
+
textElement(WbxmlCodePage.Provision, "RequireDeviceEncryption", this.requireDeviceEncryption ? "1" : "0"),
|
|
102
|
+
textElement(WbxmlCodePage.Provision, "AttachmentsEnabled", "1"),
|
|
103
|
+
]),
|
|
104
|
+
]),
|
|
105
|
+
]),
|
|
106
|
+
]),
|
|
107
|
+
]);
|
|
108
|
+
}
|
|
109
|
+
/** Request 2: the client acknowledges the policy key it was handed in request 1, self-reporting whether
|
|
110
|
+
* it actually applied the policy via its own `Status`. */
|
|
111
|
+
async acknowledgePolicy(ctx, policyType, clientPolicyKey, clientStatus) {
|
|
112
|
+
if (clientPolicyKey !== ctx.deviceSyncState.policyKey || clientStatus !== "1") {
|
|
113
|
+
// Status 2 ("protocol error" per MS-ASPROV) - an approximation, not a byte-exact enumeration of
|
|
114
|
+
// every real status code MS-ASPROV defines; this pragmatic subset only distinguishes success from
|
|
115
|
+
// "something is wrong, start over" (see this class's own doc comment on scope), and deliberately
|
|
116
|
+
// does not distinguish a key mismatch from a device self-reporting non-compliance.
|
|
117
|
+
return element(WbxmlCodePage.Provision, "Provision", [textElement(WbxmlCodePage.Provision, "Status", "2")]);
|
|
118
|
+
}
|
|
119
|
+
await this.persist(ctx, { policyKey: clientPolicyKey, provisioned: true });
|
|
120
|
+
return element(WbxmlCodePage.Provision, "Provision", [
|
|
121
|
+
textElement(WbxmlCodePage.Provision, "Status", "1"),
|
|
122
|
+
element(WbxmlCodePage.Provision, "Policies", [
|
|
123
|
+
element(WbxmlCodePage.Provision, "Policy", [
|
|
124
|
+
textElement(WbxmlCodePage.Provision, "PolicyType", policyType),
|
|
125
|
+
textElement(WbxmlCodePage.Provision, "Status", "1"),
|
|
126
|
+
textElement(WbxmlCodePage.Provision, "PolicyKey", clientPolicyKey),
|
|
127
|
+
]),
|
|
128
|
+
]),
|
|
129
|
+
]);
|
|
130
|
+
}
|
|
131
|
+
/** The device has wiped itself and is acknowledging - clear the pending flag but leave `provisioned`
|
|
132
|
+
* alone (still `false`, from when the wipe was requested) so a genuine re-provision is required. */
|
|
133
|
+
async acknowledgeRemoteWipe(ctx) {
|
|
134
|
+
await persistDeviceSyncState(ctx.deviceSyncState, ctx.deviceSyncStateRepo, {
|
|
135
|
+
remoteWipeRequested: false,
|
|
136
|
+
remoteWipeAcknowledgedAt: new Date(),
|
|
137
|
+
});
|
|
138
|
+
return element(WbxmlCodePage.Provision, "Provision", [textElement(WbxmlCodePage.Provision, "Status", "1")]);
|
|
139
|
+
}
|
|
140
|
+
async persist(ctx, changes) {
|
|
141
|
+
await persistDeviceSyncState(ctx.deviceSyncState, ctx.deviceSyncStateRepo, changes);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
__decorate([
|
|
145
|
+
Config("mail:eas:provision:password_enabled", true),
|
|
146
|
+
__metadata("design:type", Boolean)
|
|
147
|
+
], ProvisionCommand.prototype, "passwordEnabled", void 0);
|
|
148
|
+
__decorate([
|
|
149
|
+
Config("mail:eas:provision:min_password_length", 4),
|
|
150
|
+
__metadata("design:type", Number)
|
|
151
|
+
], ProvisionCommand.prototype, "minPasswordLength", void 0);
|
|
152
|
+
__decorate([
|
|
153
|
+
Config("mail:eas:provision:max_failed_attempts", 8),
|
|
154
|
+
__metadata("design:type", Number)
|
|
155
|
+
], ProvisionCommand.prototype, "maxFailedAttempts", void 0);
|
|
156
|
+
__decorate([
|
|
157
|
+
Config("mail:eas:provision:require_device_encryption", true),
|
|
158
|
+
__metadata("design:type", Boolean)
|
|
159
|
+
], ProvisionCommand.prototype, "requireDeviceEncryption", void 0);
|
|
160
|
+
__decorate([
|
|
161
|
+
Config("mail:eas:provision:allow_simple_password", false),
|
|
162
|
+
__metadata("design:type", Boolean)
|
|
163
|
+
], ProvisionCommand.prototype, "allowSimplePassword", void 0);
|
|
164
|
+
//# sourceMappingURL=ProvisionCommand.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ProvisionCommand.js","sourceRoot":"","sources":["../../../src/commands/ProvisionCommand.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,123 @@
|
|
|
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
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
11
|
+
// Copyright (C) 2026 Jean-Philippe Steinmetz. All rights reserved.
|
|
12
|
+
// SPDX-License-Identifier: MPL-2.0
|
|
13
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
14
|
+
import { ObjectDecorators, StringUtils } from "@rapidrest/core";
|
|
15
|
+
import { RepoUtils } from "@rapidrest/service-core";
|
|
16
|
+
import { WbxmlCodePage } from "../codec/WbxmlCodePages.js";
|
|
17
|
+
import { element, findChildren, textElement } from "../codec/WbxmlElement.js";
|
|
18
|
+
const { Config, Init } = ObjectDecorators;
|
|
19
|
+
/** A simple, deliberately permissive email-address shape check - just enough to distinguish "the client sent
|
|
20
|
+
* an address it already knows how to reach" from "the client sent a partial name/string that needs GAL
|
|
21
|
+
* lookup", not full RFC 5322 validation. */
|
|
22
|
+
const LOOKS_LIKE_EMAIL = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
23
|
+
/** [MS-ASCMD] `ResolveRecipients` per-`To` `Status` codes this pragmatic subset actually distinguishes: `1`
|
|
24
|
+
* success (at least one match, or the input already looked like a resolvable address), `4` no match found -
|
|
25
|
+
* an approximation, not a byte-exact enumeration of every real status code MS-ASCMD's `ResolveRecipients` page
|
|
26
|
+
* defines, matching `ProvisionCommand`'s own identical precedent for the same reasoning. */
|
|
27
|
+
const STATUS_SUCCESS = "1";
|
|
28
|
+
const STATUS_NOT_FOUND = "4";
|
|
29
|
+
/**
|
|
30
|
+
* Handles EAS `ResolveRecipients`: resolves each `<To>` value (a display name, partial name, or address) the
|
|
31
|
+
* client is unsure how to reach against the mailbox's own `Contact` (GAL) store - the same substring-match
|
|
32
|
+
* approach `SearchCommand` uses for its own `Store Name="GAL"` lookups, duplicated rather than shared (small
|
|
33
|
+
* enough, and specific enough to each command's own surrounding logic, that extracting a shared utility for two
|
|
34
|
+
* call sites isn't worth a new cross-command dependency). Both use `RepoUtils`' `regex(...)` operator (not
|
|
35
|
+
* `like(...)`, which compiles a glob pattern under `@rapidrest/service-core` ^2.0 - anchored, and with no escape
|
|
36
|
+
* for a literal `*`/`?`): `StringUtils.escapeRegExp(value)` gives a genuine, unanchored literal-substring match
|
|
37
|
+
* with no residual wildcard ambiguity, matching the same fix already applied to the `mapi` plugin's own
|
|
38
|
+
* identical GAL-search gap.
|
|
39
|
+
*
|
|
40
|
+
* **Pragmatic subset**: no free-busy `Availability`, no S/MIME `Certificates`/`Options` handling at all - this
|
|
41
|
+
* command's real-world use is overwhelmingly enterprise S/MIME certificate lookup, which this library doesn't
|
|
42
|
+
* implement elsewhere either (`ComposeMailCommand` sends plain MIME, never signed/encrypted). A `<To>` value
|
|
43
|
+
* that already looks like an email address (`LOOKS_LIKE_EMAIL`) is echoed straight back as its own single
|
|
44
|
+
* exact match rather than searched for, matching a real client's own common case of resolving an address it
|
|
45
|
+
* already typed correctly.
|
|
46
|
+
*
|
|
47
|
+
* `contactClass` is supplied by the Mongo/SQL concrete subclasses.
|
|
48
|
+
*
|
|
49
|
+
* @author Jean-Philippe Steinmetz
|
|
50
|
+
*/
|
|
51
|
+
export class ResolveRecipientsCommand {
|
|
52
|
+
constructor() {
|
|
53
|
+
this.command = "ResolveRecipients";
|
|
54
|
+
this.maxMatches = 10;
|
|
55
|
+
}
|
|
56
|
+
async init() {
|
|
57
|
+
this.contactRepo = await this._objectFactory.newInstance(RepoUtils, {
|
|
58
|
+
name: this.contactClass.name,
|
|
59
|
+
args: [this.contactClass],
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
async handle(ctx) {
|
|
63
|
+
const toEls = ctx.request ? findChildren(ctx.request, "To") : [];
|
|
64
|
+
const responses = [];
|
|
65
|
+
for (const toEl of toEls) {
|
|
66
|
+
const value = toEl.text ?? "";
|
|
67
|
+
responses.push(await this.resolveOne(ctx, value));
|
|
68
|
+
}
|
|
69
|
+
return element(WbxmlCodePage.ResolveRecipients, "ResolveRecipients", [
|
|
70
|
+
textElement(WbxmlCodePage.ResolveRecipients, "Status", "1"),
|
|
71
|
+
...responses,
|
|
72
|
+
]);
|
|
73
|
+
}
|
|
74
|
+
async resolveOne(ctx, value) {
|
|
75
|
+
if (LOOKS_LIKE_EMAIL.test(value)) {
|
|
76
|
+
return this.responseElement(value, STATUS_SUCCESS, [this.recipientElement(value, undefined)]);
|
|
77
|
+
}
|
|
78
|
+
const pattern = StringUtils.escapeRegExp(value);
|
|
79
|
+
const findOptions = { ignoreACL: true, limit: this.maxMatches };
|
|
80
|
+
const perField = await Promise.all(["displayName", "givenName", "surname"].map((field) => this.contactRepo.find({ mailboxUid: ctx.mailboxUid, [field]: `regex(${pattern})` }, findOptions)));
|
|
81
|
+
const byUid = new Map();
|
|
82
|
+
for (const contact of perField.flat()) {
|
|
83
|
+
byUid.set(contact.uid, contact);
|
|
84
|
+
}
|
|
85
|
+
const matches = Array.from(byUid.values())
|
|
86
|
+
.filter((contact) => contact.emails[0])
|
|
87
|
+
.slice(0, this.maxMatches);
|
|
88
|
+
if (matches.length === 0) {
|
|
89
|
+
return this.responseElement(value, STATUS_NOT_FOUND, []);
|
|
90
|
+
}
|
|
91
|
+
return this.responseElement(value, STATUS_SUCCESS, matches.map((contact) => this.recipientElement(contact.emails[0].address, contact.displayName)));
|
|
92
|
+
}
|
|
93
|
+
recipientElement(address, displayName) {
|
|
94
|
+
return element(WbxmlCodePage.ResolveRecipients, "Recipient", [
|
|
95
|
+
textElement(WbxmlCodePage.ResolveRecipients, "Type", "1"),
|
|
96
|
+
...(displayName ? [textElement(WbxmlCodePage.ResolveRecipients, "DisplayName", displayName)] : []),
|
|
97
|
+
textElement(WbxmlCodePage.ResolveRecipients, "EmailAddress", address),
|
|
98
|
+
]);
|
|
99
|
+
}
|
|
100
|
+
responseElement(to, status, recipients) {
|
|
101
|
+
return element(WbxmlCodePage.ResolveRecipients, "Response", [
|
|
102
|
+
textElement(WbxmlCodePage.ResolveRecipients, "To", to),
|
|
103
|
+
textElement(WbxmlCodePage.ResolveRecipients, "Status", status),
|
|
104
|
+
...(recipients.length > 0
|
|
105
|
+
? [
|
|
106
|
+
textElement(WbxmlCodePage.ResolveRecipients, "RecipientCount", String(recipients.length)),
|
|
107
|
+
...recipients,
|
|
108
|
+
]
|
|
109
|
+
: []),
|
|
110
|
+
]);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
__decorate([
|
|
114
|
+
Config("mail:eas:resolve_recipients_max_matches", 10),
|
|
115
|
+
__metadata("design:type", Number)
|
|
116
|
+
], ResolveRecipientsCommand.prototype, "maxMatches", void 0);
|
|
117
|
+
__decorate([
|
|
118
|
+
Init,
|
|
119
|
+
__metadata("design:type", Function),
|
|
120
|
+
__metadata("design:paramtypes", []),
|
|
121
|
+
__metadata("design:returntype", Promise)
|
|
122
|
+
], ResolveRecipientsCommand.prototype, "init", null);
|
|
123
|
+
//# sourceMappingURL=ResolveRecipientsCommand.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ResolveRecipientsCommand.js","sourceRoot":"","sources":["../../../src/commands/ResolveRecipientsCommand.ts"],"names":[],"mappings":"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"}
|