@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.
Files changed (225) hide show
  1. package/LICENSE +382 -0
  2. package/README.md +57 -0
  3. package/dist/lib/BaseDeviceSyncStateRoute.js +90 -0
  4. package/dist/lib/BaseDeviceSyncStateRoute.js.map +1 -0
  5. package/dist/lib/BaseEasRoute.js +213 -0
  6. package/dist/lib/BaseEasRoute.js.map +1 -0
  7. package/dist/lib/CompactDateTime.js +43 -0
  8. package/dist/lib/CompactDateTime.js.map +1 -0
  9. package/dist/lib/EasCommandHandler.js +2 -0
  10. package/dist/lib/EasCommandHandler.js.map +1 -0
  11. package/dist/lib/EasSyncKeyUtils.js +119 -0
  12. package/dist/lib/EasSyncKeyUtils.js.map +1 -0
  13. package/dist/lib/adapters/CalendarSyncAdapter.js +255 -0
  14. package/dist/lib/adapters/CalendarSyncAdapter.js.map +1 -0
  15. package/dist/lib/adapters/ContactsSyncAdapter.js +164 -0
  16. package/dist/lib/adapters/ContactsSyncAdapter.js.map +1 -0
  17. package/dist/lib/adapters/EasCollectionSyncAdapter.js +2 -0
  18. package/dist/lib/adapters/EasCollectionSyncAdapter.js.map +1 -0
  19. package/dist/lib/adapters/EmailSyncAdapter.js +269 -0
  20. package/dist/lib/adapters/EmailSyncAdapter.js.map +1 -0
  21. package/dist/lib/adapters/TasksSyncAdapter.js +100 -0
  22. package/dist/lib/adapters/TasksSyncAdapter.js.map +1 -0
  23. package/dist/lib/adapters/mongo/EmailSyncAdapterMongo.js +16 -0
  24. package/dist/lib/adapters/mongo/EmailSyncAdapterMongo.js.map +1 -0
  25. package/dist/lib/adapters/mongo/index.js +6 -0
  26. package/dist/lib/adapters/mongo/index.js.map +1 -0
  27. package/dist/lib/adapters/sql/EmailSyncAdapterSQL.js +16 -0
  28. package/dist/lib/adapters/sql/EmailSyncAdapterSQL.js.map +1 -0
  29. package/dist/lib/adapters/sql/index.js +6 -0
  30. package/dist/lib/adapters/sql/index.js.map +1 -0
  31. package/dist/lib/codec/WbxmlCodePages.js +706 -0
  32. package/dist/lib/codec/WbxmlCodePages.js.map +1 -0
  33. package/dist/lib/codec/WbxmlDecoder.js +154 -0
  34. package/dist/lib/codec/WbxmlDecoder.js.map +1 -0
  35. package/dist/lib/codec/WbxmlElement.js +31 -0
  36. package/dist/lib/codec/WbxmlElement.js.map +1 -0
  37. package/dist/lib/codec/WbxmlEncoder.js +88 -0
  38. package/dist/lib/codec/WbxmlEncoder.js.map +1 -0
  39. package/dist/lib/commands/ComposeMailCommand.js +187 -0
  40. package/dist/lib/commands/ComposeMailCommand.js.map +1 -0
  41. package/dist/lib/commands/FolderSyncCommand.js +155 -0
  42. package/dist/lib/commands/FolderSyncCommand.js.map +1 -0
  43. package/dist/lib/commands/GetItemEstimateCommand.js +149 -0
  44. package/dist/lib/commands/GetItemEstimateCommand.js.map +1 -0
  45. package/dist/lib/commands/ItemOperationsCommand.js +337 -0
  46. package/dist/lib/commands/ItemOperationsCommand.js.map +1 -0
  47. package/dist/lib/commands/MeetingResponseCommand.js +120 -0
  48. package/dist/lib/commands/MeetingResponseCommand.js.map +1 -0
  49. package/dist/lib/commands/MoveItemsCommand.js +103 -0
  50. package/dist/lib/commands/MoveItemsCommand.js.map +1 -0
  51. package/dist/lib/commands/PingCommand.js +145 -0
  52. package/dist/lib/commands/PingCommand.js.map +1 -0
  53. package/dist/lib/commands/ProvisionCommand.js +164 -0
  54. package/dist/lib/commands/ProvisionCommand.js.map +1 -0
  55. package/dist/lib/commands/ResolveRecipientsCommand.js +123 -0
  56. package/dist/lib/commands/ResolveRecipientsCommand.js.map +1 -0
  57. package/dist/lib/commands/SearchCommand.js +234 -0
  58. package/dist/lib/commands/SearchCommand.js.map +1 -0
  59. package/dist/lib/commands/SendMailCommand.js +18 -0
  60. package/dist/lib/commands/SendMailCommand.js.map +1 -0
  61. package/dist/lib/commands/SettingsCommand.js +138 -0
  62. package/dist/lib/commands/SettingsCommand.js.map +1 -0
  63. package/dist/lib/commands/SmartForwardCommand.js +22 -0
  64. package/dist/lib/commands/SmartForwardCommand.js.map +1 -0
  65. package/dist/lib/commands/SmartReplyCommand.js +22 -0
  66. package/dist/lib/commands/SmartReplyCommand.js.map +1 -0
  67. package/dist/lib/commands/SyncCommand.js +406 -0
  68. package/dist/lib/commands/SyncCommand.js.map +1 -0
  69. package/dist/lib/commands/mongo/FolderSyncCommandMongo.js +16 -0
  70. package/dist/lib/commands/mongo/FolderSyncCommandMongo.js.map +1 -0
  71. package/dist/lib/commands/mongo/GetItemEstimateCommandMongo.js +21 -0
  72. package/dist/lib/commands/mongo/GetItemEstimateCommandMongo.js.map +1 -0
  73. package/dist/lib/commands/mongo/ItemOperationsCommandMongo.js +18 -0
  74. package/dist/lib/commands/mongo/ItemOperationsCommandMongo.js.map +1 -0
  75. package/dist/lib/commands/mongo/MeetingResponseCommandMongo.js +17 -0
  76. package/dist/lib/commands/mongo/MeetingResponseCommandMongo.js.map +1 -0
  77. package/dist/lib/commands/mongo/MoveItemsCommandMongo.js +17 -0
  78. package/dist/lib/commands/mongo/MoveItemsCommandMongo.js.map +1 -0
  79. package/dist/lib/commands/mongo/ResolveRecipientsCommandMongo.js +16 -0
  80. package/dist/lib/commands/mongo/ResolveRecipientsCommandMongo.js.map +1 -0
  81. package/dist/lib/commands/mongo/SearchCommandMongo.js +19 -0
  82. package/dist/lib/commands/mongo/SearchCommandMongo.js.map +1 -0
  83. package/dist/lib/commands/mongo/SendMailCommandMongo.js +17 -0
  84. package/dist/lib/commands/mongo/SendMailCommandMongo.js.map +1 -0
  85. package/dist/lib/commands/mongo/SettingsCommandMongo.js +16 -0
  86. package/dist/lib/commands/mongo/SettingsCommandMongo.js.map +1 -0
  87. package/dist/lib/commands/mongo/SmartForwardCommandMongo.js +17 -0
  88. package/dist/lib/commands/mongo/SmartForwardCommandMongo.js.map +1 -0
  89. package/dist/lib/commands/mongo/SmartReplyCommandMongo.js +17 -0
  90. package/dist/lib/commands/mongo/SmartReplyCommandMongo.js.map +1 -0
  91. package/dist/lib/commands/mongo/SyncCommandMongo.js +26 -0
  92. package/dist/lib/commands/mongo/SyncCommandMongo.js.map +1 -0
  93. package/dist/lib/commands/mongo/index.js +14 -0
  94. package/dist/lib/commands/mongo/index.js.map +1 -0
  95. package/dist/lib/commands/sql/FolderSyncCommandSQL.js +16 -0
  96. package/dist/lib/commands/sql/FolderSyncCommandSQL.js.map +1 -0
  97. package/dist/lib/commands/sql/GetItemEstimateCommandSQL.js +21 -0
  98. package/dist/lib/commands/sql/GetItemEstimateCommandSQL.js.map +1 -0
  99. package/dist/lib/commands/sql/ItemOperationsCommandSQL.js +18 -0
  100. package/dist/lib/commands/sql/ItemOperationsCommandSQL.js.map +1 -0
  101. package/dist/lib/commands/sql/MeetingResponseCommandSQL.js +17 -0
  102. package/dist/lib/commands/sql/MeetingResponseCommandSQL.js.map +1 -0
  103. package/dist/lib/commands/sql/MoveItemsCommandSQL.js +17 -0
  104. package/dist/lib/commands/sql/MoveItemsCommandSQL.js.map +1 -0
  105. package/dist/lib/commands/sql/ResolveRecipientsCommandSQL.js +16 -0
  106. package/dist/lib/commands/sql/ResolveRecipientsCommandSQL.js.map +1 -0
  107. package/dist/lib/commands/sql/SearchCommandSQL.js +19 -0
  108. package/dist/lib/commands/sql/SearchCommandSQL.js.map +1 -0
  109. package/dist/lib/commands/sql/SendMailCommandSQL.js +17 -0
  110. package/dist/lib/commands/sql/SendMailCommandSQL.js.map +1 -0
  111. package/dist/lib/commands/sql/SettingsCommandSQL.js +16 -0
  112. package/dist/lib/commands/sql/SettingsCommandSQL.js.map +1 -0
  113. package/dist/lib/commands/sql/SmartForwardCommandSQL.js +17 -0
  114. package/dist/lib/commands/sql/SmartForwardCommandSQL.js.map +1 -0
  115. package/dist/lib/commands/sql/SmartReplyCommandSQL.js +17 -0
  116. package/dist/lib/commands/sql/SmartReplyCommandSQL.js.map +1 -0
  117. package/dist/lib/commands/sql/SyncCommandSQL.js +26 -0
  118. package/dist/lib/commands/sql/SyncCommandSQL.js.map +1 -0
  119. package/dist/lib/commands/sql/index.js +14 -0
  120. package/dist/lib/commands/sql/index.js.map +1 -0
  121. package/dist/lib/index.js +58 -0
  122. package/dist/lib/index.js.map +1 -0
  123. package/dist/lib/jobs/EasDeviceStateCleanupJob.js +101 -0
  124. package/dist/lib/jobs/EasDeviceStateCleanupJob.js.map +1 -0
  125. package/dist/lib/jobs/mongo/EasDeviceStateCleanupJobMongo.js +13 -0
  126. package/dist/lib/jobs/mongo/EasDeviceStateCleanupJobMongo.js.map +1 -0
  127. package/dist/lib/jobs/sql/EasDeviceStateCleanupJobSQL.js +13 -0
  128. package/dist/lib/jobs/sql/EasDeviceStateCleanupJobSQL.js.map +1 -0
  129. package/dist/lib/models/DeviceSyncState.js +2 -0
  130. package/dist/lib/models/DeviceSyncState.js.map +1 -0
  131. package/dist/lib/models/mongo/DeviceSyncStateMongo.js +130 -0
  132. package/dist/lib/models/mongo/DeviceSyncStateMongo.js.map +1 -0
  133. package/dist/lib/models/sql/DeviceSyncStateSQL.js +130 -0
  134. package/dist/lib/models/sql/DeviceSyncStateSQL.js.map +1 -0
  135. package/dist/lib/mongo/DeviceSyncStateRouteMongo.js +28 -0
  136. package/dist/lib/mongo/DeviceSyncStateRouteMongo.js.map +1 -0
  137. package/dist/lib/mongo/EasRouteMongo.js +63 -0
  138. package/dist/lib/mongo/EasRouteMongo.js.map +1 -0
  139. package/dist/lib/mongo/index.js +9 -0
  140. package/dist/lib/mongo/index.js.map +1 -0
  141. package/dist/lib/mongo.js +15 -0
  142. package/dist/lib/mongo.js.map +1 -0
  143. package/dist/lib/sql/DeviceSyncStateRouteSQL.js +28 -0
  144. package/dist/lib/sql/DeviceSyncStateRouteSQL.js.map +1 -0
  145. package/dist/lib/sql/EasRouteSQL.js +63 -0
  146. package/dist/lib/sql/EasRouteSQL.js.map +1 -0
  147. package/dist/lib/sql/index.js +9 -0
  148. package/dist/lib/sql/index.js.map +1 -0
  149. package/dist/lib/sql.js +15 -0
  150. package/dist/lib/sql.js.map +1 -0
  151. package/dist/types/BaseDeviceSyncStateRoute.d.ts +34 -0
  152. package/dist/types/BaseEasRoute.d.ts +65 -0
  153. package/dist/types/CompactDateTime.d.ts +22 -0
  154. package/dist/types/EasCommandHandler.d.ts +59 -0
  155. package/dist/types/EasSyncKeyUtils.d.ts +89 -0
  156. package/dist/types/adapters/CalendarSyncAdapter.d.ts +52 -0
  157. package/dist/types/adapters/ContactsSyncAdapter.d.ts +33 -0
  158. package/dist/types/adapters/EasCollectionSyncAdapter.d.ts +55 -0
  159. package/dist/types/adapters/EmailSyncAdapter.d.ts +71 -0
  160. package/dist/types/adapters/TasksSyncAdapter.d.ts +32 -0
  161. package/dist/types/adapters/mongo/EmailSyncAdapterMongo.d.ts +7 -0
  162. package/dist/types/adapters/mongo/index.d.ts +1 -0
  163. package/dist/types/adapters/sql/EmailSyncAdapterSQL.d.ts +7 -0
  164. package/dist/types/adapters/sql/index.d.ts +1 -0
  165. package/dist/types/codec/WbxmlCodePages.d.ts +45 -0
  166. package/dist/types/codec/WbxmlDecoder.d.ts +33 -0
  167. package/dist/types/codec/WbxmlElement.d.ts +43 -0
  168. package/dist/types/codec/WbxmlEncoder.d.ts +20 -0
  169. package/dist/types/commands/ComposeMailCommand.d.ts +50 -0
  170. package/dist/types/commands/FolderSyncCommand.d.ts +25 -0
  171. package/dist/types/commands/GetItemEstimateCommand.d.ts +43 -0
  172. package/dist/types/commands/ItemOperationsCommand.d.ts +72 -0
  173. package/dist/types/commands/MeetingResponseCommand.d.ts +32 -0
  174. package/dist/types/commands/MoveItemsCommand.d.ts +29 -0
  175. package/dist/types/commands/PingCommand.d.ts +33 -0
  176. package/dist/types/commands/ProvisionCommand.d.ts +54 -0
  177. package/dist/types/commands/ResolveRecipientsCommand.d.ts +36 -0
  178. package/dist/types/commands/SearchCommand.d.ts +60 -0
  179. package/dist/types/commands/SendMailCommand.d.ts +10 -0
  180. package/dist/types/commands/SettingsCommand.d.ts +37 -0
  181. package/dist/types/commands/SmartForwardCommand.d.ts +17 -0
  182. package/dist/types/commands/SmartReplyCommand.d.ts +17 -0
  183. package/dist/types/commands/SyncCommand.d.ts +100 -0
  184. package/dist/types/commands/mongo/FolderSyncCommandMongo.d.ts +8 -0
  185. package/dist/types/commands/mongo/GetItemEstimateCommandMongo.d.ts +7 -0
  186. package/dist/types/commands/mongo/ItemOperationsCommandMongo.d.ts +9 -0
  187. package/dist/types/commands/mongo/MeetingResponseCommandMongo.d.ts +8 -0
  188. package/dist/types/commands/mongo/MoveItemsCommandMongo.d.ts +8 -0
  189. package/dist/types/commands/mongo/ResolveRecipientsCommandMongo.d.ts +7 -0
  190. package/dist/types/commands/mongo/SearchCommandMongo.d.ts +9 -0
  191. package/dist/types/commands/mongo/SendMailCommandMongo.d.ts +8 -0
  192. package/dist/types/commands/mongo/SettingsCommandMongo.d.ts +7 -0
  193. package/dist/types/commands/mongo/SmartForwardCommandMongo.d.ts +8 -0
  194. package/dist/types/commands/mongo/SmartReplyCommandMongo.d.ts +8 -0
  195. package/dist/types/commands/mongo/SyncCommandMongo.d.ts +8 -0
  196. package/dist/types/commands/mongo/index.d.ts +9 -0
  197. package/dist/types/commands/sql/FolderSyncCommandSQL.d.ts +8 -0
  198. package/dist/types/commands/sql/GetItemEstimateCommandSQL.d.ts +7 -0
  199. package/dist/types/commands/sql/ItemOperationsCommandSQL.d.ts +9 -0
  200. package/dist/types/commands/sql/MeetingResponseCommandSQL.d.ts +8 -0
  201. package/dist/types/commands/sql/MoveItemsCommandSQL.d.ts +8 -0
  202. package/dist/types/commands/sql/ResolveRecipientsCommandSQL.d.ts +7 -0
  203. package/dist/types/commands/sql/SearchCommandSQL.d.ts +9 -0
  204. package/dist/types/commands/sql/SendMailCommandSQL.d.ts +8 -0
  205. package/dist/types/commands/sql/SettingsCommandSQL.d.ts +7 -0
  206. package/dist/types/commands/sql/SmartForwardCommandSQL.d.ts +8 -0
  207. package/dist/types/commands/sql/SmartReplyCommandSQL.d.ts +8 -0
  208. package/dist/types/commands/sql/SyncCommandSQL.d.ts +8 -0
  209. package/dist/types/commands/sql/index.d.ts +9 -0
  210. package/dist/types/index.d.ts +53 -0
  211. package/dist/types/jobs/EasDeviceStateCleanupJob.d.ts +26 -0
  212. package/dist/types/jobs/mongo/EasDeviceStateCleanupJobMongo.d.ts +5 -0
  213. package/dist/types/jobs/sql/EasDeviceStateCleanupJobSQL.d.ts +5 -0
  214. package/dist/types/models/DeviceSyncState.d.ts +30 -0
  215. package/dist/types/models/mongo/DeviceSyncStateMongo.d.ts +22 -0
  216. package/dist/types/models/sql/DeviceSyncStateSQL.d.ts +22 -0
  217. package/dist/types/mongo/DeviceSyncStateRouteMongo.d.ts +8 -0
  218. package/dist/types/mongo/EasRouteMongo.d.ts +14 -0
  219. package/dist/types/mongo/index.d.ts +4 -0
  220. package/dist/types/mongo.d.ts +10 -0
  221. package/dist/types/sql/DeviceSyncStateRouteSQL.d.ts +8 -0
  222. package/dist/types/sql/EasRouteSQL.d.ts +14 -0
  223. package/dist/types/sql/index.d.ts +4 -0
  224. package/dist/types/sql.d.ts +10 -0
  225. 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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"}