@rapidmx/activesync-plugin 1.0.0-beta.2 → 1.0.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (201) hide show
  1. package/README.md +4 -4
  2. package/dist/lib/BaseDeviceSyncStateRoute.js +32 -0
  3. package/dist/lib/BaseDeviceSyncStateRoute.js.map +1 -1
  4. package/dist/lib/BaseEasRoute.js +91 -15
  5. package/dist/lib/BaseEasRoute.js.map +1 -1
  6. package/dist/lib/EasAuditLog.js +56 -0
  7. package/dist/lib/EasAuditLog.js.map +1 -0
  8. package/dist/lib/EasCollectionLease.js +199 -0
  9. package/dist/lib/EasCollectionLease.js.map +1 -0
  10. package/dist/lib/EasCollectionStore.js +102 -0
  11. package/dist/lib/EasCollectionStore.js.map +1 -0
  12. package/dist/lib/EasCollectionSync.js +294 -0
  13. package/dist/lib/EasCollectionSync.js.map +1 -0
  14. package/dist/lib/EasSyncKeyUtils.js +125 -62
  15. package/dist/lib/EasSyncKeyUtils.js.map +1 -1
  16. package/dist/lib/MessageMoveRules.js +100 -0
  17. package/dist/lib/MessageMoveRules.js.map +1 -0
  18. package/dist/lib/MimeHeaderUtils.js +395 -0
  19. package/dist/lib/MimeHeaderUtils.js.map +1 -0
  20. package/dist/lib/RegexPatternUtils.js +30 -0
  21. package/dist/lib/RegexPatternUtils.js.map +1 -0
  22. package/dist/lib/RestapiCompat.js +41 -0
  23. package/dist/lib/RestapiCompat.js.map +1 -0
  24. package/dist/lib/adapters/CalendarSyncAdapter.js +142 -33
  25. package/dist/lib/adapters/CalendarSyncAdapter.js.map +1 -1
  26. package/dist/lib/adapters/EmailSyncAdapter.js +114 -27
  27. package/dist/lib/adapters/EmailSyncAdapter.js.map +1 -1
  28. package/dist/lib/adapters/mongo/EmailSyncAdapterMongo.js +2 -1
  29. package/dist/lib/adapters/mongo/EmailSyncAdapterMongo.js.map +1 -1
  30. package/dist/lib/adapters/sql/EmailSyncAdapterSQL.js +2 -1
  31. package/dist/lib/adapters/sql/EmailSyncAdapterSQL.js.map +1 -1
  32. package/dist/lib/codec/WbxmlDecoder.js +86 -34
  33. package/dist/lib/codec/WbxmlDecoder.js.map +1 -1
  34. package/dist/lib/codec/WbxmlEncoder.js +103 -22
  35. package/dist/lib/codec/WbxmlEncoder.js.map +1 -1
  36. package/dist/lib/commands/ComposeMailCommand.js +105 -31
  37. package/dist/lib/commands/ComposeMailCommand.js.map +1 -1
  38. package/dist/lib/commands/FolderSyncCommand.js +67 -27
  39. package/dist/lib/commands/FolderSyncCommand.js.map +1 -1
  40. package/dist/lib/commands/GetItemEstimateCommand.js +80 -59
  41. package/dist/lib/commands/GetItemEstimateCommand.js.map +1 -1
  42. package/dist/lib/commands/ItemOperationsCommand.js +219 -56
  43. package/dist/lib/commands/ItemOperationsCommand.js.map +1 -1
  44. package/dist/lib/commands/MeetingResponseCommand.js +212 -53
  45. package/dist/lib/commands/MeetingResponseCommand.js.map +1 -1
  46. package/dist/lib/commands/MoveItemsCommand.js +54 -21
  47. package/dist/lib/commands/MoveItemsCommand.js.map +1 -1
  48. package/dist/lib/commands/PingCommand.js +288 -56
  49. package/dist/lib/commands/PingCommand.js.map +1 -1
  50. package/dist/lib/commands/ProvisionCommand.js +34 -17
  51. package/dist/lib/commands/ProvisionCommand.js.map +1 -1
  52. package/dist/lib/commands/ResolveRecipientsCommand.js +43 -5
  53. package/dist/lib/commands/ResolveRecipientsCommand.js.map +1 -1
  54. package/dist/lib/commands/SearchCommand.js +92 -17
  55. package/dist/lib/commands/SearchCommand.js.map +1 -1
  56. package/dist/lib/commands/SettingsCommand.js +2 -1
  57. package/dist/lib/commands/SettingsCommand.js.map +1 -1
  58. package/dist/lib/commands/SmartForwardCommand.js +2 -1
  59. package/dist/lib/commands/SmartForwardCommand.js.map +1 -1
  60. package/dist/lib/commands/SmartReplyCommand.js +2 -1
  61. package/dist/lib/commands/SmartReplyCommand.js.map +1 -1
  62. package/dist/lib/commands/SyncCommand.js +481 -244
  63. package/dist/lib/commands/SyncCommand.js.map +1 -1
  64. package/dist/lib/commands/mongo/GetItemEstimateCommandMongo.js +6 -1
  65. package/dist/lib/commands/mongo/GetItemEstimateCommandMongo.js.map +1 -1
  66. package/dist/lib/commands/mongo/ItemOperationsCommandMongo.js +3 -1
  67. package/dist/lib/commands/mongo/ItemOperationsCommandMongo.js.map +1 -1
  68. package/dist/lib/commands/mongo/MeetingResponseCommandMongo.js +2 -1
  69. package/dist/lib/commands/mongo/MeetingResponseCommandMongo.js.map +1 -1
  70. package/dist/lib/commands/mongo/PingCommandMongo.js +23 -0
  71. package/dist/lib/commands/mongo/PingCommandMongo.js.map +1 -0
  72. package/dist/lib/commands/mongo/SearchCommandMongo.js +3 -1
  73. package/dist/lib/commands/mongo/SearchCommandMongo.js.map +1 -1
  74. package/dist/lib/commands/mongo/SendMailCommandMongo.js +2 -1
  75. package/dist/lib/commands/mongo/SendMailCommandMongo.js.map +1 -1
  76. package/dist/lib/commands/mongo/SmartForwardCommandMongo.js +2 -1
  77. package/dist/lib/commands/mongo/SmartForwardCommandMongo.js.map +1 -1
  78. package/dist/lib/commands/mongo/SmartReplyCommandMongo.js +2 -1
  79. package/dist/lib/commands/mongo/SmartReplyCommandMongo.js.map +1 -1
  80. package/dist/lib/commands/mongo/SyncCommandMongo.js +7 -1
  81. package/dist/lib/commands/mongo/SyncCommandMongo.js.map +1 -1
  82. package/dist/lib/commands/mongo/index.js +1 -0
  83. package/dist/lib/commands/mongo/index.js.map +1 -1
  84. package/dist/lib/commands/sql/GetItemEstimateCommandSQL.js +6 -1
  85. package/dist/lib/commands/sql/GetItemEstimateCommandSQL.js.map +1 -1
  86. package/dist/lib/commands/sql/ItemOperationsCommandSQL.js +3 -1
  87. package/dist/lib/commands/sql/ItemOperationsCommandSQL.js.map +1 -1
  88. package/dist/lib/commands/sql/MeetingResponseCommandSQL.js +2 -1
  89. package/dist/lib/commands/sql/MeetingResponseCommandSQL.js.map +1 -1
  90. package/dist/lib/commands/sql/PingCommandSQL.js +23 -0
  91. package/dist/lib/commands/sql/PingCommandSQL.js.map +1 -0
  92. package/dist/lib/commands/sql/SearchCommandSQL.js +3 -1
  93. package/dist/lib/commands/sql/SearchCommandSQL.js.map +1 -1
  94. package/dist/lib/commands/sql/SendMailCommandSQL.js +2 -1
  95. package/dist/lib/commands/sql/SendMailCommandSQL.js.map +1 -1
  96. package/dist/lib/commands/sql/SmartForwardCommandSQL.js +2 -1
  97. package/dist/lib/commands/sql/SmartForwardCommandSQL.js.map +1 -1
  98. package/dist/lib/commands/sql/SmartReplyCommandSQL.js +2 -1
  99. package/dist/lib/commands/sql/SmartReplyCommandSQL.js.map +1 -1
  100. package/dist/lib/commands/sql/SyncCommandSQL.js +7 -1
  101. package/dist/lib/commands/sql/SyncCommandSQL.js.map +1 -1
  102. package/dist/lib/commands/sql/index.js +1 -0
  103. package/dist/lib/commands/sql/index.js.map +1 -1
  104. package/dist/lib/index.js +5 -0
  105. package/dist/lib/index.js.map +1 -1
  106. package/dist/lib/jobs/EasDeviceStateCleanupJob.js +44 -2
  107. package/dist/lib/jobs/EasDeviceStateCleanupJob.js.map +1 -1
  108. package/dist/lib/jobs/mongo/EasDeviceStateCleanupJobMongo.js +4 -0
  109. package/dist/lib/jobs/mongo/EasDeviceStateCleanupJobMongo.js.map +1 -1
  110. package/dist/lib/jobs/sql/EasDeviceStateCleanupJobSQL.js +9 -0
  111. package/dist/lib/jobs/sql/EasDeviceStateCleanupJobSQL.js.map +1 -1
  112. package/dist/lib/models/EasCollectionChunk.js +2 -0
  113. package/dist/lib/models/EasCollectionChunk.js.map +1 -0
  114. package/dist/lib/models/EasCollectionState.js +2 -0
  115. package/dist/lib/models/EasCollectionState.js.map +1 -0
  116. package/dist/lib/models/mongo/DeviceSyncStateMongo.js +7 -0
  117. package/dist/lib/models/mongo/DeviceSyncStateMongo.js.map +1 -1
  118. package/dist/lib/models/mongo/EasCollectionChunkMongo.js +83 -0
  119. package/dist/lib/models/mongo/EasCollectionChunkMongo.js.map +1 -0
  120. package/dist/lib/models/mongo/EasCollectionStateMongo.js +162 -0
  121. package/dist/lib/models/mongo/EasCollectionStateMongo.js.map +1 -0
  122. package/dist/lib/models/sql/DeviceSyncStateSQL.js +7 -0
  123. package/dist/lib/models/sql/DeviceSyncStateSQL.js.map +1 -1
  124. package/dist/lib/models/sql/EasCollectionChunkSQL.js +83 -0
  125. package/dist/lib/models/sql/EasCollectionChunkSQL.js.map +1 -0
  126. package/dist/lib/models/sql/EasCollectionStateSQL.js +162 -0
  127. package/dist/lib/models/sql/EasCollectionStateSQL.js.map +1 -0
  128. package/dist/lib/mongo/EasRouteMongo.js +2 -2
  129. package/dist/lib/mongo/EasRouteMongo.js.map +1 -1
  130. package/dist/lib/mongo.js +2 -0
  131. package/dist/lib/mongo.js.map +1 -1
  132. package/dist/lib/sql/EasRouteSQL.js +2 -2
  133. package/dist/lib/sql/EasRouteSQL.js.map +1 -1
  134. package/dist/lib/sql.js +2 -0
  135. package/dist/lib/sql.js.map +1 -1
  136. package/dist/types/BaseDeviceSyncStateRoute.d.ts +10 -0
  137. package/dist/types/BaseEasRoute.d.ts +15 -4
  138. package/dist/types/EasAuditLog.d.ts +47 -0
  139. package/dist/types/EasCollectionLease.d.ts +60 -0
  140. package/dist/types/EasCollectionStore.d.ts +51 -0
  141. package/dist/types/EasCollectionSync.d.ts +96 -0
  142. package/dist/types/EasCommandHandler.d.ts +7 -4
  143. package/dist/types/EasSyncKeyUtils.d.ts +73 -42
  144. package/dist/types/MessageMoveRules.d.ts +45 -0
  145. package/dist/types/MimeHeaderUtils.d.ts +73 -0
  146. package/dist/types/RegexPatternUtils.d.ts +14 -0
  147. package/dist/types/RestapiCompat.d.ts +24 -0
  148. package/dist/types/adapters/CalendarSyncAdapter.d.ts +47 -18
  149. package/dist/types/adapters/EasCollectionSyncAdapter.d.ts +18 -2
  150. package/dist/types/adapters/EmailSyncAdapter.d.ts +24 -7
  151. package/dist/types/adapters/mongo/EmailSyncAdapterMongo.d.ts +1 -0
  152. package/dist/types/adapters/sql/EmailSyncAdapterSQL.d.ts +1 -0
  153. package/dist/types/codec/WbxmlDecoder.d.ts +39 -0
  154. package/dist/types/codec/WbxmlEncoder.d.ts +36 -1
  155. package/dist/types/commands/ComposeMailCommand.d.ts +33 -15
  156. package/dist/types/commands/FolderSyncCommand.d.ts +4 -1
  157. package/dist/types/commands/GetItemEstimateCommand.d.ts +20 -15
  158. package/dist/types/commands/ItemOperationsCommand.d.ts +29 -4
  159. package/dist/types/commands/MeetingResponseCommand.d.ts +52 -13
  160. package/dist/types/commands/MoveItemsCommand.d.ts +13 -3
  161. package/dist/types/commands/PingCommand.d.ts +72 -14
  162. package/dist/types/commands/ProvisionCommand.d.ts +16 -11
  163. package/dist/types/commands/ResolveRecipientsCommand.d.ts +4 -0
  164. package/dist/types/commands/SearchCommand.d.ts +13 -1
  165. package/dist/types/commands/SyncCommand.d.ts +102 -62
  166. package/dist/types/commands/mongo/GetItemEstimateCommandMongo.d.ts +3 -0
  167. package/dist/types/commands/mongo/ItemOperationsCommandMongo.d.ts +2 -0
  168. package/dist/types/commands/mongo/MeetingResponseCommandMongo.d.ts +1 -0
  169. package/dist/types/commands/mongo/PingCommandMongo.d.ts +8 -0
  170. package/dist/types/commands/mongo/SearchCommandMongo.d.ts +2 -0
  171. package/dist/types/commands/mongo/SendMailCommandMongo.d.ts +1 -0
  172. package/dist/types/commands/mongo/SmartForwardCommandMongo.d.ts +1 -0
  173. package/dist/types/commands/mongo/SmartReplyCommandMongo.d.ts +1 -0
  174. package/dist/types/commands/mongo/SyncCommandMongo.d.ts +4 -0
  175. package/dist/types/commands/mongo/index.d.ts +1 -0
  176. package/dist/types/commands/sql/GetItemEstimateCommandSQL.d.ts +3 -0
  177. package/dist/types/commands/sql/ItemOperationsCommandSQL.d.ts +2 -0
  178. package/dist/types/commands/sql/MeetingResponseCommandSQL.d.ts +1 -0
  179. package/dist/types/commands/sql/PingCommandSQL.d.ts +8 -0
  180. package/dist/types/commands/sql/SearchCommandSQL.d.ts +2 -0
  181. package/dist/types/commands/sql/SendMailCommandSQL.d.ts +1 -0
  182. package/dist/types/commands/sql/SmartForwardCommandSQL.d.ts +1 -0
  183. package/dist/types/commands/sql/SmartReplyCommandSQL.d.ts +1 -0
  184. package/dist/types/commands/sql/SyncCommandSQL.d.ts +4 -0
  185. package/dist/types/commands/sql/index.d.ts +1 -0
  186. package/dist/types/index.d.ts +5 -0
  187. package/dist/types/jobs/EasDeviceStateCleanupJob.d.ts +15 -0
  188. package/dist/types/jobs/mongo/EasDeviceStateCleanupJobMongo.d.ts +2 -0
  189. package/dist/types/jobs/sql/EasDeviceStateCleanupJobSQL.d.ts +4 -0
  190. package/dist/types/models/DeviceSyncState.d.ts +13 -2
  191. package/dist/types/models/EasCollectionChunk.d.ts +20 -0
  192. package/dist/types/models/EasCollectionState.d.ts +79 -0
  193. package/dist/types/models/mongo/DeviceSyncStateMongo.d.ts +1 -0
  194. package/dist/types/models/mongo/EasCollectionChunkMongo.d.ts +16 -0
  195. package/dist/types/models/mongo/EasCollectionStateMongo.d.ts +27 -0
  196. package/dist/types/models/sql/DeviceSyncStateSQL.d.ts +1 -0
  197. package/dist/types/models/sql/EasCollectionChunkSQL.d.ts +16 -0
  198. package/dist/types/models/sql/EasCollectionStateSQL.d.ts +27 -0
  199. package/dist/types/mongo.d.ts +2 -0
  200. package/dist/types/sql.d.ts +2 -0
  201. package/package.json +267 -238
@@ -11,35 +11,67 @@ var __metadata = (this && this.__metadata) || function (k, v) {
11
11
  // Copyright (C) 2026 Jean-Philippe Steinmetz. All rights reserved.
12
12
  // SPDX-License-Identifier: MPL-2.0
13
13
  ///////////////////////////////////////////////////////////////////////////////
14
+ import * as crypto from "crypto";
15
+ import { simpleParser } from "mailparser";
14
16
  import { ApiError, ObjectDecorators } from "@rapidrest/core";
15
- import { ACLAction, ACLUtils, ApiErrorMessages, ApiErrors, RepoUtils } from "@rapidrest/service-core";
17
+ import { ACLAction, ACLUtils, ApiErrorMessages, ApiErrors, ModelUtils, RepoUtils } from "@rapidrest/service-core";
16
18
  import { WbxmlCodePage } from "../codec/WbxmlCodePages.js";
17
- import { childText, element, findChild, textElement } from "../codec/WbxmlElement.js";
18
- import { AttendeeResponseStatus, RecoverableRepoUtils } from "@rapidmx/restapi";
19
- const { Init, Inject } = ObjectDecorators;
19
+ import { childText, element, findChild, findChildren, textElement } from "../codec/WbxmlElement.js";
20
+ import { AttendeeResponseStatus, buildEventIcs, parseIcsEvent, RecoverableRepoUtils, } from "@rapidmx/restapi";
21
+ import { isOrganizedBy } from "../adapters/CalendarSyncAdapter.js";
22
+ import { asEntity, boundIndexedValue } from "../RestapiCompat.js";
23
+ const { Init, Inject, Logger } = ObjectDecorators;
20
24
  /** MS-ASCMD `UserResponse`: 1=Accepted, 2=Tentatively accepted, 3=Declined. */
21
25
  const USER_RESPONSE_STATUS = {
22
26
  "1": AttendeeResponseStatus.ACCEPTED,
23
27
  "2": AttendeeResponseStatus.TENTATIVE,
24
28
  "3": AttendeeResponseStatus.DECLINED,
25
29
  };
30
+ const USER_RESPONSE_LABEL = { "1": "Accepted", "2": "Tentative", "3": "Declined" };
26
31
  const USER_RESPONSE_DECLINED = "3";
32
+ /** MS-ASCMD `MeetingResponse` `Result/Status` values this command reports. */
33
+ const STATUS_SUCCESS = "1";
34
+ const STATUS_INVALID_REQUEST = "2";
35
+ const STATUS_MAILBOX_ERROR = "3";
36
+ const STATUS_SERVER_ERROR = "4";
37
+ /** Most `Request` elements one `MeetingResponse` may carry. */
38
+ export const MAX_MEETING_RESPONSES = 100;
27
39
  /**
28
- * Handles EAS `MeetingResponse`: records the caller's own accept/tentative/decline response to a meeting on
29
- * the referenced `CalendarEvent`. `RequestId` is the same `Message`/`CalendarEvent.uid` this library already
30
- * exposes as `ServerId` elsewhere (Sync/FolderSync) - no separate lookup table is needed.
40
+ * Handles EAS `MeetingResponse`: records the caller's own accept/tentative/decline response to each meeting the
41
+ * request's `<Request>` elements name, each answered with its own `<Result>`.
31
42
  *
32
- * **Pragmatic subset**: only the first `<Request>` in the command is processed (the real spec allows several
33
- * per request, matching `Sync`'s own single-`Collection`-per-request scope in this library). A **decline**
34
- * soft-deletes the `CalendarEvent` (matching real Exchange behavior) rather than merely flipping the caller's
35
- * own `Attendee.responseStatus` - each attendee has their own row already (`CalendarEvent.mailboxUid` scopes
36
- * every event to a single mailbox, per this library's architecture), so deleting *this* row only removes the
37
- * meeting from the declining attendee's own calendar, leaving the organizer's and every other attendee's own
38
- * copy untouched. Accept/Tentative still update `Attendee.responseStatus` in place. The response omits
39
- * `CalendarId` for a decline either way, matching the spec's own convention, so a client relying on that
40
- * signal isn't misled into thinking a new item was created.
43
+ * **`RequestId`** is either the `CalendarEvent.uid` (a response from the calendar) or the `Message.uid` of the
44
+ * meeting request in the Inbox (a response from the mail view - what most clients send). For a message, the
45
+ * caller needs `READ` on its folder; its `text/calendar` part's `UID` is then resolved to the caller's own copy of
46
+ * the event (`icalUid` within the caller's mailbox) - looked up bounded (`boundIndexedValue`, as restapi stores it) and
47
+ * exact-matched in memory, since the UID is sender-controlled text a query parser could read as an operator.
41
48
  *
42
- * `calendarEventClass`/`mailboxClass` are supplied by the Mongo/SQL concrete subclasses.
49
+ * **Someone else's calendar**: whether a copy is the organizer's is judged against the event's own mailbox, not the
50
+ * caller's. A response to an event in another mailbox (a delegate with `UPDATE` on it) is refused with Status 2 when
51
+ * that event is its owner's organizer copy - it can only ever be an attendee copy. Updates are version-checked on both
52
+ * backends (`asEntity`).
53
+ *
54
+ * **Effect**: the caller must be an attendee and have `UPDATE` on the event's folder. Accept/Tentative update
55
+ * the caller's `Attendee.responseStatus`. A decline removes the caller's own copy of the event (matching Exchange)
56
+ * when the caller also has `DELETE` on the folder, and otherwise just records the declined status - a delegate
57
+ * with edit-only rights can't delete. Each mailbox has its own event row, so neither touches the organizer's or
58
+ * any other attendee's copy. `CalendarId` is omitted for a removed event. Since the caller's copy is an attendee's copy
59
+ * of someone else's meeting, neither write may look like the organizer acting to restapi's `MeetingSchedulingJob`:
60
+ * a removed copy is stamped `cancelNoticeSentAt` first (no CANCEL mailed as the organizer), and an updated copy keeps
61
+ * `inviteSequenceSent` equal to its `sequence` (no REQUEST).
62
+ *
63
+ * **Reply to the organizer**: in protocol 16.x the client asks the server to notify the organizer with
64
+ * `SendResponse`; when it's present an iTIP `REPLY` (built with restapi's `buildEventIcs`, the same payload
65
+ * `BaseCalendarEventRoute.respond` sends) is mailed from the caller's attendee address to the organizer,
66
+ * and a transport that accepts none of it (or rejects the organizer) is reported as Status 4 - the response itself is
67
+ * already recorded, so a retry only re-sends the reply. Without it (14.x clients send their own reply via `SendMail`) nothing is mailed, so the organizer
68
+ * never receives two replies.
69
+ *
70
+ * Failures are per request: an unknown/unresolvable meeting, a meeting the caller may not respond to, or a
71
+ * malformed `Request` gets Status 2 (indistinguishable, so nothing about other mailboxes leaks); a failed write
72
+ * Status 3.
73
+ *
74
+ * `calendarEventClass`/`mailboxClass`/`messageClass` are supplied by the Mongo/SQL concrete subclasses.
43
75
  *
44
76
  * @author Jean-Philippe Steinmetz
45
77
  */
@@ -56,61 +88,188 @@ export class MeetingResponseCommand {
56
88
  name: this.mailboxClass.name,
57
89
  args: [this.mailboxClass],
58
90
  });
91
+ this.messageRepo = await this._objectFactory.newInstance(RepoUtils, {
92
+ name: this.messageClass.name,
93
+ args: [this.messageClass],
94
+ });
59
95
  }
60
96
  async handle(ctx) {
61
- if (!this.calendarEventRepo || !this.mailboxRepo) {
97
+ if (!this.calendarEventRepo || !this.mailboxRepo || !this.messageRepo) {
62
98
  throw new ApiError(ApiErrors.INTERNAL_ERROR, 500, ApiErrorMessages.INTERNAL_ERROR);
63
99
  }
64
- const requestEl = ctx.request ? findChild(ctx.request, "Request") : undefined;
65
- if (!requestEl) {
100
+ const requestEls = ctx.request ? findChildren(ctx.request, "Request") : [];
101
+ if (requestEls.length === 0 || requestEls.length > MAX_MEETING_RESPONSES) {
66
102
  throw new ApiError(ApiErrors.INVALID_REQUEST, 400, ApiErrorMessages.INVALID_REQUEST);
67
103
  }
104
+ const mailbox = await this.mailboxRepo.findOne(ctx.mailboxUid, { ignoreACL: true });
105
+ if (!mailbox) {
106
+ throw new ApiError(ApiErrors.NOT_FOUND, 404, ApiErrorMessages.NOT_FOUND);
107
+ }
108
+ const results = [];
109
+ for (const requestEl of requestEls) {
110
+ results.push(await this.respond(ctx, mailbox, requestEl));
111
+ }
112
+ return element(WbxmlCodePage.MeetingResponse, "MeetingResponse", results);
113
+ }
114
+ result(requestId, status, calendarId) {
115
+ return element(WbxmlCodePage.MeetingResponse, "Result", [
116
+ ...(requestId ? [textElement(WbxmlCodePage.MeetingResponse, "RequestId", requestId)] : []),
117
+ textElement(WbxmlCodePage.MeetingResponse, "Status", status),
118
+ ...(calendarId ? [textElement(WbxmlCodePage.MeetingResponse, "CalendarId", calendarId)] : []),
119
+ ]);
120
+ }
121
+ async respond(ctx, mailbox, requestEl) {
68
122
  const userResponse = childText(requestEl, "UserResponse");
69
123
  const requestId = childText(requestEl, "RequestId");
70
124
  if (!userResponse || !requestId || !USER_RESPONSE_STATUS[userResponse]) {
71
- throw new ApiError(ApiErrors.INVALID_REQUEST, 400, ApiErrorMessages.INVALID_REQUEST);
72
- }
73
- const event = await this.calendarEventRepo.findOne(requestId, { ignoreACL: true });
74
- if (!event) {
75
- throw new ApiError(ApiErrors.NOT_FOUND, 404, ApiErrorMessages.NOT_FOUND);
76
- }
77
- if (!(await this.aclUtils.hasPermission(ctx.user, event.folderUid, ACLAction.UPDATE))) {
78
- throw new ApiError(ApiErrors.AUTH_PERMISSION_FAILURE, 403, ApiErrorMessages.AUTH_PERMISSION_FAILURE);
125
+ return this.result(requestId, STATUS_INVALID_REQUEST);
79
126
  }
80
- const mailbox = await this.mailboxRepo.findOne(ctx.mailboxUid, { ignoreACL: true });
81
- if (!mailbox) {
82
- throw new ApiError(ApiErrors.NOT_FOUND, 404, ApiErrorMessages.NOT_FOUND);
127
+ const event = await this.resolveEvent(ctx, requestId);
128
+ if (!event || !(await this.aclUtils.hasPermission(ctx.user, event.folderUid, ACLAction.UPDATE))) {
129
+ return this.result(requestId, STATUS_INVALID_REQUEST);
83
130
  }
84
- const callerAddresses = new Set([mailbox.primarySmtpAddress.toLowerCase(), ...mailbox.aliasAddresses.map((a) => a.toLowerCase())]);
131
+ const callerAddresses = new Set([mailbox.primarySmtpAddress, ...(mailbox.aliasAddresses ?? [])].map((a) => a.toLowerCase()));
85
132
  const attendeeIndex = event.attendees.findIndex((attendee) => callerAddresses.has(attendee.address.toLowerCase()));
86
133
  if (attendeeIndex === -1) {
87
- throw new ApiError(ApiErrors.NOT_FOUND, 404, "The caller is not an attendee of this calendar event.");
88
- }
89
- if (userResponse === USER_RESPONSE_DECLINED) {
90
- // Soft-delete this attendee's own copy of the event - see this class's own doc comment for why
91
- // that's correct here rather than merely flipping responseStatus (the real spec's own behavior:
92
- // declining removes the meeting from the declining attendee's calendar).
93
- await this.calendarEventRepo.delete(event.uid, { ignoreACL: true, user: ctx.user });
94
- }
95
- else {
96
- const attendees = event.attendees.map((attendee, i) => i === attendeeIndex ? { ...attendee, responseStatus: USER_RESPONSE_STATUS[userResponse] } : attendee);
97
- await this.calendarEventRepo.update({ uid: event.uid, version: event.version, attendees }, event, { ignoreACL: true, user: ctx.user });
98
- }
99
- return element(WbxmlCodePage.MeetingResponse, "MeetingResponse", [
100
- element(WbxmlCodePage.MeetingResponse, "Result", [
101
- textElement(WbxmlCodePage.MeetingResponse, "RequestId", requestId),
102
- textElement(WbxmlCodePage.MeetingResponse, "Status", "1"),
103
- ...(userResponse !== USER_RESPONSE_DECLINED
104
- ? [textElement(WbxmlCodePage.MeetingResponse, "CalendarId", event.uid)]
105
- : []),
106
- ]),
107
- ]);
134
+ return this.result(requestId, STATUS_INVALID_REQUEST);
135
+ }
136
+ const updatedAttendee = { ...event.attendees[attendeeIndex], responseStatus: USER_RESPONSE_STATUS[userResponse] };
137
+ // Whose copy this is decides whether it's the organizer's: the event's own mailbox, which for a delegate
138
+ // responding in someone else's calendar is not the caller's. A delegate never responds on (and so never
139
+ // deletes, or re-invites from) the owner's organizer copy of a meeting.
140
+ const owner = event.mailboxUid === ctx.mailboxUid ? mailbox : await this.mailboxRepo.findOne(event.mailboxUid, { ignoreACL: true });
141
+ if (!owner) {
142
+ return this.result(requestId, STATUS_INVALID_REQUEST);
143
+ }
144
+ const attendeeCopy = !isOrganizedBy(event, owner);
145
+ if (!attendeeCopy && owner !== mailbox) {
146
+ return this.result(requestId, STATUS_INVALID_REQUEST);
147
+ }
148
+ let removed = false;
149
+ try {
150
+ if (userResponse === USER_RESPONSE_DECLINED && (await this.aclUtils.hasPermission(ctx.user, event.folderUid, ACLAction.DELETE))) {
151
+ if (attendeeCopy && event.cancelNoticeSentAt == null) {
152
+ await this.calendarEventRepo.update({ uid: event.uid, version: event.version, cancelNoticeSentAt: new Date() }, asEntity(this.calendarEventRepo, event), { ignoreACL: true, user: ctx.user });
153
+ }
154
+ await this.calendarEventRepo.delete(event.uid, { ignoreACL: true, user: ctx.user });
155
+ removed = true;
156
+ }
157
+ else {
158
+ const attendees = event.attendees.map((attendee, i) => (i === attendeeIndex ? updatedAttendee : attendee));
159
+ const inviteSequence = attendeeCopy && event.inviteSequenceSent !== event.sequence ? { inviteSequenceSent: event.sequence ?? 0 } : {};
160
+ await this.calendarEventRepo.update({ uid: event.uid, version: event.version, attendees, ...inviteSequence }, asEntity(this.calendarEventRepo, event), { ignoreACL: true, user: ctx.user });
161
+ }
162
+ }
163
+ catch (err) {
164
+ this.logger?.warn(`MeetingResponseCommand: failed to record response to event ${event.uid}: ${err?.message}`);
165
+ return this.result(requestId, STATUS_MAILBOX_ERROR);
166
+ }
167
+ if (findChild(requestEl, "SendResponse") && !(await this.sendReply(mailbox, event, updatedAttendee, userResponse))) {
168
+ return this.result(requestId, STATUS_SERVER_ERROR, removed ? undefined : event.uid);
169
+ }
170
+ return this.result(requestId, STATUS_SUCCESS, removed ? undefined : event.uid);
171
+ }
172
+ /** Resolves `requestId` to the caller's event: an event uid directly, or a meeting request message's
173
+ * `text/calendar` `UID` looked up among the caller's own mailbox's events. */
174
+ async resolveEvent(ctx, requestId) {
175
+ const direct = await this.calendarEventRepo.findOne(requestId, { ignoreACL: true });
176
+ if (direct) {
177
+ return direct;
178
+ }
179
+ const message = await this.messageRepo.findOne(requestId, { ignoreACL: true });
180
+ if (!message || !(await this.aclUtils.hasPermission(ctx.user, message.folderUid, ACLAction.READ))) {
181
+ return undefined;
182
+ }
183
+ const icalUid = await this.meetingUidOf(message);
184
+ if (!icalUid) {
185
+ return undefined;
186
+ }
187
+ // The UID is sender-controlled: looked up bounded (restapi stores `icalUid` through `boundIndexedValue()`) as a
188
+ // `ModelUtils.literal()`, and exact-matched in memory as restapi's `ScanQueueJob.findCalendarEventRows()` does -
189
+ // a UID like `ne(x)` must never be parsed as a query operator and select (and let a decline delete) a different
190
+ // meeting.
191
+ const key = boundIndexedValue(icalUid);
192
+ const events = (await this.calendarEventRepo.find({ mailboxUid: ctx.mailboxUid, icalUid: ModelUtils.literal(key), limit: 50 }, {
193
+ ignoreACL: true,
194
+ limit: 50,
195
+ })).filter((row) => row.icalUid === key && row.mailboxUid === ctx.mailboxUid);
196
+ // The series master (no `recurrenceId`) is what a response to the whole invitation applies to.
197
+ return events.find((candidate) => !candidate.recurrenceId) ?? events[0];
198
+ }
199
+ /** The iCalendar `UID` of a meeting request message's `text/calendar` part, if it has one. */
200
+ async meetingUidOf(message) {
201
+ try {
202
+ const parsed = await simpleParser(await this.blobStore.get(message.bodyBlobKey));
203
+ const calendarPart = parsed.attachments.find((part) => part.contentType === "text/calendar");
204
+ return calendarPart ? parseIcsEvent(calendarPart.content.toString("utf-8"))?.uid : undefined;
205
+ }
206
+ catch {
207
+ return undefined;
208
+ }
209
+ }
210
+ /** Mails an iTIP `REPLY` for `attendee`'s response to the event's organizer. Returns `false` (after logging) when
211
+ * the transport threw or didn't accept the message for the organizer - checked the way restapi's `sendOrThrow`
212
+ * does, since every bundled transport reports a relay failure through `TransportResult.rejected` rather than
213
+ * throwing. */
214
+ async sendReply(mailbox, event, attendee, userResponse) {
215
+ try {
216
+ const ics = buildEventIcs({ ...event, attendees: [attendee] }, "REPLY", { onlyAttendee: attendee });
217
+ const boundary = `eas-${crypto.randomUUID()}`;
218
+ const subject = `${USER_RESPONSE_LABEL[userResponse]}: ${event.title}`.replace(/[\r\n]+/g, " ");
219
+ const fromName = (mailbox.displayName ?? "").replace(/[\r\n"]+/g, " ");
220
+ const raw = [
221
+ `From: "${fromName}" <${attendee.address}>`,
222
+ `To: <${event.organizer.address}>`,
223
+ `Subject: ${subject}`,
224
+ `Date: ${new Date().toUTCString()}`,
225
+ `Message-ID: <${crypto.randomUUID()}@eas>`,
226
+ "MIME-Version: 1.0",
227
+ `Content-Type: multipart/alternative; boundary="${boundary}"`,
228
+ "",
229
+ `--${boundary}`,
230
+ "Content-Type: text/plain; charset=utf-8",
231
+ "",
232
+ `${attendee.displayName ?? attendee.address} has responded ${USER_RESPONSE_LABEL[userResponse].toLowerCase()} to: ${event.title}`,
233
+ `--${boundary}`,
234
+ 'Content-Type: text/calendar; charset=utf-8; method=REPLY',
235
+ "",
236
+ ics,
237
+ `--${boundary}--`,
238
+ "",
239
+ ].join("\r\n");
240
+ const result = await this.mailTransport.send({
241
+ raw: Buffer.from(raw, "utf-8"),
242
+ envelopeFrom: attendee.address,
243
+ envelopeTo: [event.organizer.address],
244
+ });
245
+ if (!result || (result.accepted ?? []).length === 0 || (result.rejected ?? []).length > 0) {
246
+ this.logger?.warn(`MeetingResponseCommand: the mail transport did not accept the iTIP REPLY for event ${event.uid}`);
247
+ return false;
248
+ }
249
+ return true;
250
+ }
251
+ catch (err) {
252
+ this.logger?.warn(`MeetingResponseCommand: failed to send iTIP REPLY for event ${event.uid}: ${err?.message}`);
253
+ return false;
254
+ }
108
255
  }
109
256
  }
110
257
  __decorate([
111
258
  Inject(ACLUtils),
112
259
  __metadata("design:type", ACLUtils)
113
260
  ], MeetingResponseCommand.prototype, "aclUtils", void 0);
261
+ __decorate([
262
+ Inject("BlobStore"),
263
+ __metadata("design:type", Object)
264
+ ], MeetingResponseCommand.prototype, "blobStore", void 0);
265
+ __decorate([
266
+ Inject("MailTransport"),
267
+ __metadata("design:type", Object)
268
+ ], MeetingResponseCommand.prototype, "mailTransport", void 0);
269
+ __decorate([
270
+ Logger,
271
+ __metadata("design:type", Object)
272
+ ], MeetingResponseCommand.prototype, "logger", void 0);
114
273
  __decorate([
115
274
  Init,
116
275
  __metadata("design:type", Function),
@@ -1 +1 @@
1
- {"version":3,"file":"MeetingResponseCommand.js","sourceRoot":"","sources":["../../../src/commands/MeetingResponseCommand.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"MeetingResponseCommand.js","sourceRoot":"","sources":["../../../src/commands/MeetingResponseCommand.ts"],"names":[],"mappings":"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"}
@@ -11,25 +11,38 @@ var __metadata = (this && this.__metadata) || function (k, v) {
11
11
  // Copyright (C) 2026 Jean-Philippe Steinmetz. All rights reserved.
12
12
  // SPDX-License-Identifier: MPL-2.0
13
13
  ///////////////////////////////////////////////////////////////////////////////
14
- import { ObjectDecorators } from "@rapidrest/core";
15
- import { ACLAction, ACLUtils, RepoUtils } from "@rapidrest/service-core";
14
+ import { ApiError, ObjectDecorators } from "@rapidrest/core";
15
+ import { ACLAction, ACLUtils, ApiErrors, RepoUtils } from "@rapidrest/service-core";
16
16
  import { WbxmlCodePage } from "../codec/WbxmlCodePages.js";
17
17
  import { childText, element, findChildren, textElement } from "../codec/WbxmlElement.js";
18
+ import { planMessageMove } from "../MessageMoveRules.js";
19
+ import { asEntity } from "../RestapiCompat.js";
18
20
  const { Init, Inject } = ObjectDecorators;
19
- /** [MS-ASCMD] `Move` `Status` codes this pragmatic subset actually distinguishes: `1` success, everything else
20
- * collapses to `3` ("failure") - an approximation, not a byte-exact enumeration of every real status code
21
- * MS-ASCMD's `Move` page defines, matching `ProvisionCommand`'s own identical precedent for the same reasoning
22
- * (this library only needs to tell a client "it worked" from "it didn't, don't retry blindly"). */
23
- const STATUS_SUCCESS = "1";
24
- const STATUS_FAILURE = "3";
21
+ /** [MS-ASCMD] `MoveItems` `Status` codes (section 2.2.3.177.10): `3` is success - not `1`, which means an invalid
22
+ * source. */
23
+ const STATUS_INVALID_SOURCE = "1";
24
+ const STATUS_INVALID_DESTINATION = "2";
25
+ const STATUS_SUCCESS = "3";
26
+ const STATUS_SAME_FOLDER = "4";
27
+ const STATUS_LOCKED = "7";
28
+ /** Most `Move` elements one `MoveItems` request may carry - more is rejected with HTTP 400. */
29
+ export const MAX_MOVES_PER_REQUEST = 500;
25
30
  /**
26
31
  * Handles the standalone EAS `MoveItems` command: moves one or more `Message`s between folders in the caller's
27
- * own mailbox by `ServerId`/`uid`.
32
+ * own mailbox by `ServerId`/`uid`. A move never crosses mailboxes (the destination folder must belong to the
33
+ * message's own mailbox) and a request may carry at most `MAX_MOVES_PER_REQUEST` moves.
34
+ *
35
+ * Each move gets its own [MS-ASCMD] status: `3` success; `1` an unknown/unreadable message, or a `SrcFldId` that
36
+ * isn't where it lives (or can't be updated); `2` an unknown destination, one in another mailbox, one the caller
37
+ * can't create in, Outbox, or Drafts for a message that isn't already a draft (`MessageMoveRules.planMessageMove`);
38
+ * `4` source and destination are the same folder; `7` the update itself failed (e.g. a concurrent edit's version
39
+ * conflict - the update is version-checked on both backends), a send in flight, or the message already left Outbox as sent - one failing
40
+ * move never aborts the others. Moving a message out of Outbox cancels its scheduled send.
28
41
  *
29
42
  * **Pragmatic subset**: `Message` only - `Contacts`/`Calendar`/`Tasks` moves are rare in practice (unlike
30
43
  * `Message`, whose Inbox-to-subfolder filing is a real, common client action) and would each need their own
31
- * ACL/ownership verification path for comparatively little value; a client attempting one gets `Status 3`
32
- * rather than being silently ignored. `DstMsgId` in the response is always the same `uid` as `SrcMsgId` - this
44
+ * ACL/ownership verification path for comparatively little value; a client attempting one gets Status 1 (its
45
+ * `SrcMsgId` isn't a message) rather than being silently ignored. `DstMsgId` in the response is always the same `uid` as `SrcMsgId` - this
33
46
  * library never mints a new identifier on move, unlike a real Exchange server, which sometimes does.
34
47
  *
35
48
  * `messageClass`/`folderClass` are supplied by the Mongo/SQL concrete subclasses.
@@ -52,6 +65,9 @@ export class MoveItemsCommand {
52
65
  }
53
66
  async handle(ctx) {
54
67
  const moveEls = ctx.request ? findChildren(ctx.request, "Move") : [];
68
+ if (moveEls.length > MAX_MOVES_PER_REQUEST) {
69
+ throw new ApiError(ApiErrors.INVALID_REQUEST, 400, `MoveItems supports at most ${MAX_MOVES_PER_REQUEST} Move elements per request.`);
70
+ }
55
71
  const responses = [];
56
72
  for (const moveEl of moveEls) {
57
73
  responses.push(await this.moveOne(ctx, moveEl));
@@ -62,24 +78,41 @@ export class MoveItemsCommand {
62
78
  const srcMsgId = childText(moveEl, "SrcMsgId");
63
79
  const srcFldId = childText(moveEl, "SrcFldId");
64
80
  const dstFldId = childText(moveEl, "DstFldId");
65
- if (!srcMsgId || !srcFldId || !dstFldId) {
66
- return this.responseElement(srcMsgId, STATUS_FAILURE, undefined);
81
+ if (!srcMsgId || !srcFldId) {
82
+ return this.responseElement(srcMsgId, STATUS_INVALID_SOURCE, undefined);
83
+ }
84
+ if (!dstFldId) {
85
+ return this.responseElement(srcMsgId, STATUS_INVALID_DESTINATION, undefined);
86
+ }
87
+ if (srcFldId === dstFldId) {
88
+ return this.responseElement(srcMsgId, STATUS_SAME_FOLDER, undefined);
67
89
  }
68
90
  const message = await this.messageRepo.findOne(srcMsgId, { ignoreACL: true });
69
91
  // The client's claimed SrcFldId must match where the message actually lives - protects against a
70
92
  // stale/mismatched client cache rather than trusting the claim outright.
71
- if (!message || message.folderUid !== srcFldId) {
72
- return this.responseElement(srcMsgId, STATUS_FAILURE, undefined);
93
+ if (!message || message.folderUid !== srcFldId || !(await this.aclUtils.hasPermission(ctx.user, srcFldId, ACLAction.UPDATE))) {
94
+ return this.responseElement(srcMsgId, STATUS_INVALID_SOURCE, undefined);
73
95
  }
74
- if (!(await this.aclUtils.hasPermission(ctx.user, srcFldId, ACLAction.UPDATE)) ||
75
- !(await this.aclUtils.hasPermission(ctx.user, dstFldId, ACLAction.CREATE))) {
76
- return this.responseElement(srcMsgId, STATUS_FAILURE, undefined);
96
+ if (!(await this.aclUtils.hasPermission(ctx.user, dstFldId, ACLAction.CREATE))) {
97
+ return this.responseElement(srcMsgId, STATUS_INVALID_DESTINATION, undefined);
77
98
  }
78
99
  const destFolder = await this.folderRepo.findOne(dstFldId, { ignoreACL: true });
79
- if (!destFolder || destFolder.mailboxUid !== ctx.mailboxUid) {
80
- return this.responseElement(srcMsgId, STATUS_FAILURE, undefined);
100
+ // A message never changes mailbox by moving: its `mailboxUid` (quota, retention, search scoping) must keep
101
+ // matching its folder's mailbox, so a destination in any other mailbox is refused.
102
+ if (!destFolder || destFolder.mailboxUid !== message.mailboxUid) {
103
+ return this.responseElement(srcMsgId, STATUS_INVALID_DESTINATION, undefined);
104
+ }
105
+ const sourceFolder = await this.folderRepo.findOne(message.folderUid, { ignoreACL: true });
106
+ const plan = planMessageMove(message, sourceFolder?.type, destFolder.type);
107
+ if (!plan.allowed) {
108
+ return this.responseElement(srcMsgId, plan.reason === "destination" ? STATUS_INVALID_DESTINATION : STATUS_LOCKED, undefined);
109
+ }
110
+ try {
111
+ await this.messageRepo.update({ uid: message.uid, version: message.version, folderUid: dstFldId, ...plan.patch }, asEntity(this.messageRepo, message), { ignoreACL: true, user: ctx.user });
112
+ }
113
+ catch {
114
+ return this.responseElement(srcMsgId, STATUS_LOCKED, undefined);
81
115
  }
82
- await this.messageRepo.update({ uid: message.uid, version: message.version, folderUid: dstFldId }, message, { ignoreACL: true, user: ctx.user });
83
116
  return this.responseElement(srcMsgId, STATUS_SUCCESS, message.uid);
84
117
  }
85
118
  responseElement(srcMsgId, status, dstMsgId) {
@@ -1 +1 @@
1
- {"version":3,"file":"MoveItemsCommand.js","sourceRoot":"","sources":["../../../src/commands/MoveItemsCommand.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"MoveItemsCommand.js","sourceRoot":"","sources":["../../../src/commands/MoveItemsCommand.ts"],"names":[],"mappings":"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"}