@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
@@ -14,11 +14,18 @@ var __metadata = (this && this.__metadata) || function (k, v) {
14
14
  import * as crypto from "crypto";
15
15
  import { simpleParser } from "mailparser";
16
16
  import { ApiError, ObjectDecorators } from "@rapidrest/core";
17
- import { ACLAction, ACLUtils, ApiErrorMessages, ApiErrors } from "@rapidrest/service-core";
17
+ import { ACLAction, ACLUtils, ApiErrorMessages, ApiErrors, RepoUtils } from "@rapidrest/service-core";
18
18
  import { ScanPipeline } from "@rapidmx/restapi/scan";
19
- import { findOrCreateWellKnownFolder, FolderType, MessageImportance, RecipientType, RecoverableRepoUtils, scanAndRelay, } from "@rapidmx/restapi";
20
- import { childText, findChild } from "../codec/WbxmlElement.js";
21
- const { Init, Inject } = ObjectDecorators;
19
+ import { findOrCreateWellKnownFolder, FolderType, MessageImportance, prependHeaders, RecipientType, RecoverableRepoUtils, scanAndRelay, } from "@rapidmx/restapi";
20
+ import { WbxmlCodePage } from "../codec/WbxmlCodePages.js";
21
+ import { childText, element, findChild, textElement } from "../codec/WbxmlElement.js";
22
+ import { checkComposedOriginators, extractOriginatorHeaders, stripHeader } from "../MimeHeaderUtils.js";
23
+ import { boundIndexedValue } from "../RestapiCompat.js";
24
+ const { Init, Inject, Logger } = ObjectDecorators;
25
+ /** Most envelope recipients (To + Cc + Bcc) one composed message may carry. */
26
+ export const MAX_COMPOSE_RECIPIENTS = 500;
27
+ /** [MS-ASCMD] common Status 119, MessageHasNoRecipient. */
28
+ const STATUS_NO_RECIPIENT = "119";
22
29
  /** Flattens mailparser's `AddressObject | AddressObject[] | undefined` union (grouped addresses can nest an
23
30
  * `AddressObject` per group) into a plain list of SMTP addresses, dropping any entry with no address (a
24
31
  * pure-group header with no direct member). */
@@ -40,6 +47,7 @@ function collectAddresses(entry, out) {
40
47
  collectAddresses(grouped, out);
41
48
  }
42
49
  }
50
+ export { stripHeader };
43
51
  /**
44
52
  * Shared implementation for EAS `SendMail`, `SmartForward`, and `SmartReply` (MS-ASCMD `ComposeMail` namespace)
45
53
  * — all three submit a client-composed raw MIME body directly (`<Mime>`, opaque WBXML content) rather than
@@ -47,23 +55,33 @@ function collectAddresses(entry, out) {
47
55
  * `BaseMessageRoute.send()`, which this class's `scanAndRelay()` call shares its scan-then-relay core with via
48
56
  * `MailSendUtils.ts`).
49
57
  *
58
+ * **Sender and envelope checks** (the MIME is entirely device-controlled): the raw bytes pass restapi's own originator
59
+ * rules before anything parses them (`MimeHeaderUtils.checkComposedOriginators`: an inline copy of restapi's
60
+ * `checkOriginatorHeaders` with `rejectAddressLikeDisplayNames`): exactly one `From`, at most one `Sender` (found by a tolerant lexer - `From :`, folded lines and bare-CR
61
+ * line breaks included), every address in them - group members too - the caller's own mailbox's primary or alias
62
+ * address, no address-like text outside an address (`<me@x> <victim@y>`), and no address in a display name or comment
63
+ * (`"ceo@y" <me@x>`, `me@x (victim@y)`), and - stricter than restapi - no empty group (`victims:;, me@x`). Anything
64
+ * else is HTTP 403 (a MIME with no `From` at all is HTTP 400), so a device can't send as, or appear to be, anyone else;
65
+ * the envelope sender is the validated `From`. A message with no To/Cc/Bcc address is answered with the command's own
66
+ * `Status` 119 (MessageHasNoRecipient), nothing relayed. The envelope is capped at `MAX_COMPOSE_RECIPIENTS`
67
+ * recipients (HTTP 400). `Bcc` recipients are delivered via the envelope, but every `Bcc` header field (found by the
68
+ * same lexer, so `Bcc :` too) is stripped from the relayed copy so other recipients never see it (the Sent Items copy
69
+ * keeps it). The Sent Items copy records the relay's own `Message-ID`/`conversationId` (bounded like restapi's), so
70
+ * recall and threading match what recipients received.
71
+ *
50
72
  * **Pragmatic subset, deliberately not the full MS-ASCMD semantics**:
51
73
  * - `SmartForward`/`SmartReply`'s `<Source>` (the message being forwarded/replied to) is used only to thread
52
74
  * the outgoing message (`inReplyTo`/`references`) and to flip the original's `Answered`/`Forwarded` flag - the
53
- * real spec has the *server* splice the original message's full content into the outgoing MIME so the client
54
- * never has to download-then-reupload it; this pragmatic subset instead expects the client's own `<Mime>` to
55
- * already be the complete outgoing message (which is what every mainstream client's own compose UI naturally
56
- * produces once it has fetched the original for display), matching this library's "pragmatic subset, not full
57
- * fidelity" precedent elsewhere (e.g. `FolderSyncCommand`'s SyncKey replay-protection gap).
75
+ * real spec has the *server* splice the original message's full content into the outgoing MIME; this subset
76
+ * expects the client's own `<Mime>` to already be the complete outgoing message. The flag flip needs `UPDATE` on
77
+ * the original's folder and is best-effort: the message has already been sent, so a denied or conflicting flag
78
+ * update is logged, never turned into a failed request.
58
79
  * - `ReplaceMime`/`AccountId`/`InstanceId` are not read - single-account, non-recurring-meeting compose only.
59
- * - Attachments present in the composed MIME are relayed correctly (`scanAndRelay()`'s `ScanPipeline` handles
60
- * the full raw message) but are not additionally persisted as `Attachment` records on the saved Sent Items
61
- * copy - `Message.hasAttachments` is still set correctly from the parsed MIME, just not each attachment's own
62
- * row (deferred, matching `ItemOperationsCommand`'s own future `Fetch`-of-Sent-Items scope).
80
+ * - Attachments present in the composed MIME are relayed correctly but are not additionally persisted as
81
+ * `Attachment` records on the saved Sent Items copy (`Message.hasAttachments` is still set).
63
82
  *
64
- * `folderClass`/`messageClass` are supplied by the Mongo/SQL concrete subclasses, and `markOriginal()` by the
65
- * `SmartForwardCommand`/`SmartReplyCommand` subclasses (a no-op here, since plain `SendMailCommand` never has a
66
- * `<Source>` to act on).
83
+ * `folderClass`/`messageClass`/`mailboxClass` are supplied by the Mongo/SQL concrete subclasses, and
84
+ * `markOriginal()` by the `SmartForwardCommand`/`SmartReplyCommand` subclasses.
67
85
  *
68
86
  * @author Jean-Philippe Steinmetz
69
87
  */
@@ -77,15 +95,19 @@ export class ComposeMailCommand {
77
95
  name: this.messageClass.name,
78
96
  args: [this.messageClass],
79
97
  });
98
+ this.mailboxRepo = await this._objectFactory.newInstance(RepoUtils, {
99
+ name: this.mailboxClass.name,
100
+ args: [this.mailboxClass],
101
+ });
80
102
  }
81
- /** Called once the outgoing message has been sent, only when the request carried a `<Source>` (i.e. this
82
- * is a `SmartForward`/`SmartReply`, never a plain `SendMail`) - flips the referenced original message's own
83
- * `Answered`/`Forwarded` flag. A no-op here; overridden by the two subclasses that need it. */
103
+ /** Called once the outgoing message has been sent, only when the request carried a `<Source>` the caller may
104
+ * update - flips the referenced original message's own `Answered`/`Forwarded` flag. A no-op here; overridden by
105
+ * the two subclasses that need it. */
84
106
  async markOriginal(_ctx, _original) {
85
- // No-op by default (SendMailCommand never calls this - it never resolves a Source).
107
+ // No-op by default (plain SendMail has nothing to flag).
86
108
  }
87
109
  async handle(ctx) {
88
- if (!this.folderRepo || !this.messageRepo || !this.blobStore || !this.mailTransport || !this.scanPipeline) {
110
+ if (!this.folderRepo || !this.messageRepo || !this.mailboxRepo || !this.blobStore || !this.mailTransport || !this.scanPipeline) {
89
111
  throw new ApiError(ApiErrors.INTERNAL_ERROR, 500, ApiErrorMessages.INTERNAL_ERROR);
90
112
  }
91
113
  if (!ctx.request) {
@@ -99,8 +121,7 @@ export class ComposeMailCommand {
99
121
  throw new ApiError(ApiErrors.INVALID_REQUEST, 400, "A SendMail/SmartForward/SmartReply request must include a MIME body.");
100
122
  }
101
123
  // A `SmartForward`/`SmartReply` request identifies the message being acted on via `<Source><ItemId>` -
102
- // the same `Message.uid` this library already exposes as `ServerId` in Sync/FolderSync responses, so
103
- // no separate lookup table is needed. `SendMail` never carries a `<Source>` at all.
124
+ // the same `Message.uid` this library already exposes as `ServerId` in Sync/FolderSync responses.
104
125
  let original;
105
126
  const sourceEl = findChild(ctx.request, "Source");
106
127
  if (sourceEl) {
@@ -117,28 +138,69 @@ export class ComposeMailCommand {
117
138
  }
118
139
  original = found;
119
140
  }
141
+ const mailbox = await this.mailboxRepo.findOne(ctx.mailboxUid, { ignoreACL: true });
142
+ if (!mailbox) {
143
+ throw new ApiError(ApiErrors.NOT_FOUND, 404, ApiErrorMessages.NOT_FOUND);
144
+ }
145
+ const ownAddresses = new Set([mailbox.primarySmtpAddress, ...(mailbox.aliasAddresses ?? [])].filter((a) => typeof a === "string").map((a) => a.toLowerCase()));
146
+ const isAllowed = (address) => ownAddresses.has(address.toLowerCase());
147
+ // Checked on the raw bytes before parsing, with restapi's own sender rules (see `MimeHeaderUtils`): mailparser
148
+ // keeps just one of several `From`/`Sender` fields and recovers malformed address lists tolerantly, so checking
149
+ // only its parse would validate something other than what a recipient may be shown.
150
+ if (extractOriginatorHeaders(raw).from.length === 0) {
151
+ throw new ApiError(ApiErrors.INVALID_REQUEST, 400, "The composed Mime has no resolvable From/To address.");
152
+ }
153
+ if (checkComposedOriginators(raw, isAllowed) !== undefined) {
154
+ throw new ApiError(ApiErrors.AUTH_PERMISSION_FAILURE, 403, "The composed message's From address is not one of this mailbox's addresses.");
155
+ }
120
156
  const parsed = await simpleParser(raw);
121
- const envelopeFrom = parsed.from?.value[0]?.address;
122
- const envelopeTo = [...addressesOf(parsed.to), ...addressesOf(parsed.cc), ...addressesOf(parsed.bcc)];
123
- if (!envelopeFrom || envelopeTo.length === 0) {
157
+ const fromAddresses = addressesOf(parsed.from);
158
+ const envelopeFrom = fromAddresses[0];
159
+ const envelopeTo = [...addressesOf(parsed.to), ...addressesOf(parsed.cc), ...addressesOf(parsed.bcc)].filter((address) => address.trim().length > 0);
160
+ /* v8 ignore start -- checkComposedOriginators() already required a From address mailparser reads too */
161
+ if (!envelopeFrom) {
124
162
  throw new ApiError(ApiErrors.INVALID_REQUEST, 400, "The composed Mime has no resolvable From/To address.");
125
163
  }
126
- const { sanitizedHtmlBlobKey } = await scanAndRelay(raw, envelopeFrom, envelopeTo, this.scanPipeline, this.mailTransport, this.blobStore);
164
+ /* v8 ignore stop */
165
+ if (envelopeTo.length === 0) {
166
+ // Like restapi's send() (400 for a message with no To/Cc/Bcc recipient), refused before anything is relayed -
167
+ // reported the ActiveSync way, [MS-ASCMD] Status 119 (MessageHasNoRecipient) in the command's own response.
168
+ return element(WbxmlCodePage.ComposeMail, this.command, [textElement(WbxmlCodePage.ComposeMail, "Status", STATUS_NO_RECIPIENT)]);
169
+ }
170
+ if (envelopeTo.length > MAX_COMPOSE_RECIPIENTS) {
171
+ throw new ApiError(ApiErrors.INVALID_REQUEST, 400, `A composed message may have at most ${MAX_COMPOSE_RECIPIENTS} recipients.`);
172
+ }
173
+ // Defense in depth: the raw checks above already cover every address mailparser can report.
174
+ /* v8 ignore start -- unreachable while checkOriginatorHeaders() passes; kept in case the two parsers ever disagree */
175
+ const senderAddresses = addressesOf(parsed.headers.get("sender"));
176
+ if ([...fromAddresses, ...senderAddresses].some((address) => !isAllowed(address))) {
177
+ throw new ApiError(ApiErrors.AUTH_PERMISSION_FAILURE, 403, "The composed message's From address is not one of this mailbox's addresses.");
178
+ }
179
+ /* v8 ignore stop */
180
+ const stripped = stripHeader(raw, "bcc");
181
+ const relayed = await scanAndRelay(stripped, envelopeFrom, envelopeTo, this.scanPipeline, this.mailTransport, this.blobStore);
127
182
  if (findChild(ctx.request, "SaveInSentItems")) {
128
183
  const bodyBlobKey = `bodies/${crypto.randomUUID()}`;
129
- await this.blobStore.put(bodyBlobKey, raw, { contentType: "message/rfc822" });
184
+ // The Sent Items copy keeps its Bcc header, but must carry the `Message-ID` the message was actually relayed
185
+ // with: `scanAndRelay()` injects one when the device's MIME had none, and recall/threading match on it.
186
+ const stored = relayed.raw !== stripped ? prependHeaders(raw, [{ name: "Message-ID", value: `<${relayed.messageId}>` }]) : raw;
187
+ await this.blobStore.put(bodyBlobKey, stored, { contentType: "message/rfc822" });
130
188
  const sentFolder = await findOrCreateWellKnownFolder(this.folderRepo, this.folderClass, ctx.mailboxUid, FolderType.SENT_ITEMS, ctx.user);
131
189
  await this.messageRepo.create(new this.messageClass({
132
190
  folderUid: sentFolder.uid,
133
191
  mailboxUid: ctx.mailboxUid,
134
- messageId: parsed.messageId ?? `${crypto.randomUUID()}@eas`,
192
+ // The relay's own values (angle brackets stripped, as every recipient's ingest stores them), bounded
193
+ // the way restapi stores indexed identifiers - not mailparser's bracketed `Message-ID`.
194
+ messageId: boundIndexedValue(relayed.messageId),
195
+ conversationId: boundIndexedValue(relayed.conversationId),
135
196
  subject: parsed.subject ?? "",
136
197
  from: { address: envelopeFrom, type: RecipientType.TO },
137
198
  recipients: buildRecipients(parsed),
138
199
  sentDate: new Date(),
139
200
  receivedDate: new Date(),
140
201
  bodyBlobKey,
141
- sanitizedHtmlBlobKey,
202
+ sanitizedHtmlBlobKey: relayed.sanitizedHtmlBlobKey,
203
+ encrypted: relayed.encrypted,
142
204
  bodyPreview: (parsed.text ?? "").slice(0, 200),
143
205
  flags: { read: true, flagged: false, answered: false, forwarded: false },
144
206
  importance: MessageImportance.NORMAL,
@@ -148,7 +210,15 @@ export class ComposeMailCommand {
148
210
  }), { ignoreACL: true, user: ctx.user });
149
211
  }
150
212
  if (original) {
151
- await this.markOriginal(ctx, original);
213
+ // The message is already on its way - flagging the original is best-effort bookkeeping.
214
+ try {
215
+ if (await this.aclUtils.hasPermission(ctx.user, original.folderUid, ACLAction.UPDATE)) {
216
+ await this.markOriginal(ctx, original);
217
+ }
218
+ }
219
+ catch (err) {
220
+ this.logger?.warn(`${this.command}: failed to flag original message ${original.uid}: ${err?.message}`);
221
+ }
152
222
  }
153
223
  // Per MS-ASCMD: a successful SendMail/SmartForward/SmartReply response is an empty HTTP 200 body, not
154
224
  // a Status-coded WBXML document like FolderSync/Sync/Provision return.
@@ -171,6 +241,10 @@ __decorate([
171
241
  Inject(ACLUtils),
172
242
  __metadata("design:type", ACLUtils)
173
243
  ], ComposeMailCommand.prototype, "aclUtils", void 0);
244
+ __decorate([
245
+ Logger,
246
+ __metadata("design:type", Object)
247
+ ], ComposeMailCommand.prototype, "logger", void 0);
174
248
  __decorate([
175
249
  Init,
176
250
  __metadata("design:type", Function),
@@ -1 +1 @@
1
- {"version":3,"file":"ComposeMailCommand.js","sourceRoot":"","sources":["../../../src/commands/ComposeMailCommand.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ComposeMailCommand.js","sourceRoot":"","sources":["../../../src/commands/ComposeMailCommand.ts"],"names":[],"mappings":"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"}
@@ -15,7 +15,7 @@ import { ApiError, ObjectDecorators } from "@rapidrest/core";
15
15
  import { ApiErrorMessages, ApiErrors, RepoUtils } from "@rapidrest/service-core";
16
16
  import { WbxmlCodePage } from "../codec/WbxmlCodePages.js";
17
17
  import { childText, element, textElement } from "../codec/WbxmlElement.js";
18
- import { computeChanges, formatSyncKey, persistDeviceSyncState, resolveSyncKey } from "../EasSyncKeyUtils.js";
18
+ import { computeChanges, epochCursor, formatSyncKey, persistDeviceSyncState, resolveSyncKey, } from "../EasSyncKeyUtils.js";
19
19
  import { FolderType } from "@rapidmx/restapi";
20
20
  const { Config, Init } = ObjectDecorators;
21
21
  /** Maps this library's `FolderType` to the closest MS-ASCMD folder type code. Several of this app's types
@@ -47,17 +47,28 @@ const ROOT_PARENT_ID = "0";
47
47
  * particular folder) key `DeviceSyncState.folderSyncKeys` is stored under for this cursor - distinct from any
48
48
  * real `Folder.uid`, which is exactly why using the mailbox's own uid here would be ambiguous. */
49
49
  const FOLDER_HIERARCHY_CURSOR_KEY = "$foldersync";
50
+ /** Where the key issued before the current one is kept, so a client that never received the last response can retry
51
+ * with it. */
52
+ const FOLDER_HIERARCHY_PREVIOUS_KEY = "$foldersyncPrevious";
53
+ /** [MS-ASCMD] FolderSync Status 9: the synchronization key is invalid or doesn't match - restart from `SyncKey 0`. */
54
+ const STATUS_INVALID_SYNC_KEY = "9";
50
55
  /** Caps how many folder changes are enumerated per round - real mailboxes rarely have more than a few dozen
51
56
  * folders, so this is generous, not a real-world binding constraint; it exists so `computeChanges()`'s
52
57
  * `MoreAvailable` mechanism is exercised the same way it will be for `SyncCommand`'s much larger item
53
58
  * collections. */
54
59
  const DEFAULT_WINDOW_SIZE = 512;
60
+ /** Safety bound on how many `windowSize` pages a `SyncKey "0"` request reads while returning the full hierarchy. */
61
+ const MAX_INITIAL_PAGES = 100;
55
62
  /**
56
63
  * Handles EAS `FolderSync`: enumerates `Add`/`Update`/`Delete`s for the caller's mailbox's `Folder` hierarchy
57
64
  * since the device's last `FolderSync`, using the shared watermark-based cursor mechanism in
58
65
  * `EasSyncKeyUtils.ts` (scoped by `mailboxUid` over the `Folder` collection, rather than `folderUid` over a
59
66
  * per-folder item collection the way `SyncCommand` will be).
60
67
  *
68
+ * **Keys**: an unknown `SyncKey` is answered with Status 9 ([MS-ASCMD] FolderSync: "synchronization key mismatch or
69
+ * invalid synchronization key"; the client restarts from `0`). The key issued before the current one is still
70
+ * accepted: the key itself encodes its cursor, so a retry simply recomputes that round's changes.
71
+ *
61
72
  * `folderClass` is supplied by the Mongo/SQL concrete subclasses, following the exact one-line-per-backend
62
73
  * pattern used throughout this library's other routes/jobs.
63
74
  *
@@ -79,31 +90,64 @@ export class FolderSyncCommand {
79
90
  throw new ApiError(ApiErrors.INTERNAL_ERROR, 500, ApiErrorMessages.INTERNAL_ERROR);
80
91
  }
81
92
  const clientSyncKey = ctx.request ? childText(ctx.request, "SyncKey") : undefined;
82
- const storedSyncKey = ctx.deviceSyncState.folderSyncKeys[FOLDER_HIERARCHY_CURSOR_KEY];
83
- const resolution = resolveSyncKey(clientSyncKey, storedSyncKey);
93
+ const storedSyncKey = ctx.deviceSyncState.folderSyncKeys?.[FOLDER_HIERARCHY_CURSOR_KEY];
94
+ const previousSyncKey = ctx.deviceSyncState.folderSyncKeys?.[FOLDER_HIERARCHY_PREVIOUS_KEY];
95
+ let resolution = resolveSyncKey(clientSyncKey, storedSyncKey);
96
+ let retry = false;
97
+ if (resolution.kind === "invalid" && previousSyncKey !== undefined) {
98
+ resolution = resolveSyncKey(clientSyncKey, previousSyncKey);
99
+ retry = resolution.kind === "valid";
100
+ }
84
101
  if (resolution.kind === "invalid") {
85
102
  return element(WbxmlCodePage.FolderHierarchy, "FolderSync", [
86
- textElement(WbxmlCodePage.FolderHierarchy, "Status", "3"),
103
+ textElement(WbxmlCodePage.FolderHierarchy, "Status", STATUS_INVALID_SYNC_KEY),
87
104
  ]);
88
105
  }
106
+ let generation;
107
+ let adds;
108
+ let updates = [];
109
+ let deletes = [];
110
+ let cursor;
89
111
  if (resolution.kind === "initial") {
90
- // Per spec: the first response to SyncKey "0" never returns items itself, only establishes a
91
- // cursor - but that cursor's watermark must be the epoch, not "now": the client's *next* request
92
- // (with this key) is its true first full sync and must report every existing folder as an Add,
93
- // including ones that existed and were last modified long before this handshake ever started.
94
- // Watermarking at "now" here would silently skip all of those (an epoch-old folder never appears
95
- // as "modified after now"), which is exactly backwards for a brand-new device's first sync.
96
- const newKey = formatSyncKey({ generation: 1, watermark: new Date(0) });
97
- await this.persistSyncKey(ctx, newKey);
98
- return element(WbxmlCodePage.FolderHierarchy, "FolderSync", [
99
- textElement(WbxmlCodePage.FolderHierarchy, "Status", "1"),
100
- textElement(WbxmlCodePage.FolderHierarchy, "SyncKey", newKey),
101
- ]);
112
+ // [MS-ASCMD] FolderSync: a SyncKey "0" request returns the entire folder hierarchy as Adds together
113
+ // with the new key - there is no separate "empty handshake" round (unlike Sync), and FolderSync has no
114
+ // MoreAvailable, so every page is read here rather than leaving the rest for a later round (where a
115
+ // folder created before that round's cursor would wrongly be reported as an Update).
116
+ generation = 0;
117
+ adds = [];
118
+ cursor = epochCursor();
119
+ for (let page = 0; page < MAX_INITIAL_PAGES; page++) {
120
+ const changes = await computeChanges(this.folderRepo, "mailboxUid", ctx.mailboxUid, cursor, this.windowSize);
121
+ adds.push(...changes.adds, ...changes.changes);
122
+ cursor = changes.cursor;
123
+ if (!changes.moreAvailable) {
124
+ break;
125
+ }
126
+ }
127
+ }
128
+ else {
129
+ generation = resolution.key.generation;
130
+ const changes = await computeChanges(this.folderRepo, "mailboxUid", ctx.mailboxUid, { date: resolution.key.watermark, uid: resolution.key.uid ?? "" }, this.windowSize);
131
+ adds = changes.adds;
132
+ updates = changes.changes;
133
+ deletes = changes.deletes;
134
+ cursor = changes.cursor;
102
135
  }
103
- const changes = await computeChanges(this.folderRepo, "mailboxUid", ctx.mailboxUid, resolution.key.watermark, this.windowSize);
104
- const newKey = formatSyncKey({ generation: resolution.key.generation + 1, watermark: changes.newWatermark });
105
- await this.persistSyncKey(ctx, newKey);
106
- const totalChanges = changes.adds.length + changes.changes.length + changes.deletes.length;
136
+ const newKey = formatSyncKey({ generation: generation + 1, watermark: cursor.date, uid: cursor.uid });
137
+ // A retry keeps the previous key as it was; a normal round makes the key just consumed the previous one; a
138
+ // restart forgets it.
139
+ const previous = retry ? previousSyncKey : resolution.kind === "valid" ? clientSyncKey : undefined;
140
+ await persistDeviceSyncState(ctx.deviceSyncState, ctx.deviceSyncStateRepo, (current) => {
141
+ const { [FOLDER_HIERARCHY_PREVIOUS_KEY]: _dropped, ...rest } = current.folderSyncKeys ?? {};
142
+ return {
143
+ folderSyncKeys: {
144
+ ...rest,
145
+ [FOLDER_HIERARCHY_CURSOR_KEY]: newKey,
146
+ ...(previous !== undefined ? { [FOLDER_HIERARCHY_PREVIOUS_KEY]: previous } : {}),
147
+ },
148
+ };
149
+ });
150
+ const totalChanges = adds.length + updates.length + deletes.length;
107
151
  if (totalChanges === 0) {
108
152
  return element(WbxmlCodePage.FolderHierarchy, "FolderSync", [
109
153
  textElement(WbxmlCodePage.FolderHierarchy, "Status", "1"),
@@ -111,9 +155,9 @@ export class FolderSyncCommand {
111
155
  ]);
112
156
  }
113
157
  const changeElements = [
114
- ...changes.adds.map((folder) => this.folderToChangeElement("Add", folder)),
115
- ...changes.changes.map((folder) => this.folderToChangeElement("Update", folder)),
116
- ...changes.deletes.map((folder) => element(WbxmlCodePage.FolderHierarchy, "Delete", [
158
+ ...adds.map((folder) => this.folderToChangeElement("Add", folder)),
159
+ ...updates.map((folder) => this.folderToChangeElement("Update", folder)),
160
+ ...deletes.map((folder) => element(WbxmlCodePage.FolderHierarchy, "Delete", [
117
161
  textElement(WbxmlCodePage.FolderHierarchy, "ServerId", folder.uid),
118
162
  ])),
119
163
  ];
@@ -137,10 +181,6 @@ export class FolderSyncCommand {
137
181
  textElement(WbxmlCodePage.FolderHierarchy, "Type", FOLDER_TYPE_CODES[folder.type] ?? FOLDER_TYPE_CODES[FolderType.USER]),
138
182
  ]);
139
183
  }
140
- async persistSyncKey(ctx, newKey) {
141
- const folderSyncKeys = { ...ctx.deviceSyncState.folderSyncKeys, [FOLDER_HIERARCHY_CURSOR_KEY]: newKey };
142
- await persistDeviceSyncState(ctx.deviceSyncState, ctx.deviceSyncStateRepo, { folderSyncKeys });
143
- }
144
184
  }
145
185
  __decorate([
146
186
  Config("mail:eas:foldersync_window_size", DEFAULT_WINDOW_SIZE),
@@ -1 +1 @@
1
- {"version":3,"file":"FolderSyncCommand.js","sourceRoot":"","sources":["../../../src/commands/FolderSyncCommand.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"FolderSyncCommand.js","sourceRoot":"","sources":["../../../src/commands/FolderSyncCommand.ts"],"names":[],"mappings":"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"}