@rapidmx/mapi-plugin 1.0.0-beta.3 → 1.0.0-beta.5

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 (154) hide show
  1. package/README.md +4 -4
  2. package/dist/lib/BaseMapiEmsmdbRoute.js +229 -37
  3. package/dist/lib/BaseMapiEmsmdbRoute.js.map +1 -1
  4. package/dist/lib/MapiSessionManager.js +404 -48
  5. package/dist/lib/MapiSessionManager.js.map +1 -1
  6. package/dist/lib/RegexPatternUtils.js +30 -0
  7. package/dist/lib/RegexPatternUtils.js.map +1 -0
  8. package/dist/lib/RopDispatcher.js +130 -19
  9. package/dist/lib/RopDispatcher.js.map +1 -1
  10. package/dist/lib/codec/AppointmentRecurrence.js +22 -21
  11. package/dist/lib/codec/AppointmentRecurrence.js.map +1 -1
  12. package/dist/lib/codec/BufferCursor.js +41 -12
  13. package/dist/lib/codec/BufferCursor.js.map +1 -1
  14. package/dist/lib/codec/GlobalObjectId.js +26 -20
  15. package/dist/lib/codec/GlobalObjectId.js.map +1 -1
  16. package/dist/lib/codec/MapiTimeZone.js +43 -10
  17. package/dist/lib/codec/MapiTimeZone.js.map +1 -1
  18. package/dist/lib/codec/PropertyValue.js +28 -2
  19. package/dist/lib/codec/PropertyValue.js.map +1 -1
  20. package/dist/lib/codec/RopBuffer.js +15 -3
  21. package/dist/lib/codec/RopBuffer.js.map +1 -1
  22. package/dist/lib/mongo/MapiEmsmdbRouteMongo.js +2 -1
  23. package/dist/lib/mongo/MapiEmsmdbRouteMongo.js.map +1 -1
  24. package/dist/lib/nspi/NspiCodec.js +5 -5
  25. package/dist/lib/nspi/NspiCodec.js.map +1 -1
  26. package/dist/lib/nspi/NspiGetMatchesHandler.js +14 -14
  27. package/dist/lib/nspi/NspiGetMatchesHandler.js.map +1 -1
  28. package/dist/lib/rop/AddressList.js +109 -0
  29. package/dist/lib/rop/AddressList.js.map +1 -0
  30. package/dist/lib/rop/CalendarEventTarget.js +7 -4
  31. package/dist/lib/rop/CalendarEventTarget.js.map +1 -1
  32. package/dist/lib/rop/ContactTarget.js +2 -6
  33. package/dist/lib/rop/ContactTarget.js.map +1 -1
  34. package/dist/lib/rop/ContentsTable.js +60 -0
  35. package/dist/lib/rop/ContentsTable.js.map +1 -0
  36. package/dist/lib/rop/ExecuteBudget.js +82 -0
  37. package/dist/lib/rop/ExecuteBudget.js.map +1 -0
  38. package/dist/lib/rop/FastTransferStream.js +99 -2
  39. package/dist/lib/rop/FastTransferStream.js.map +1 -1
  40. package/dist/lib/rop/FolderTarget.js +13 -6
  41. package/dist/lib/rop/FolderTarget.js.map +1 -1
  42. package/dist/lib/rop/HandleDataCache.js +412 -0
  43. package/dist/lib/rop/HandleDataCache.js.map +1 -0
  44. package/dist/lib/rop/MeetingMessageClassHandler.js +194 -48
  45. package/dist/lib/rop/MeetingMessageClassHandler.js.map +1 -1
  46. package/dist/lib/rop/MessageBodyStream.js +33 -7
  47. package/dist/lib/rop/MessageBodyStream.js.map +1 -1
  48. package/dist/lib/rop/MessageTarget.js +17 -4
  49. package/dist/lib/rop/MessageTarget.js.map +1 -1
  50. package/dist/lib/rop/NamedPropertyRegistry.js +8 -5
  51. package/dist/lib/rop/NamedPropertyRegistry.js.map +1 -1
  52. package/dist/lib/rop/PropertyResolvers.js +3 -1
  53. package/dist/lib/rop/PropertyResolvers.js.map +1 -1
  54. package/dist/lib/rop/RepoPaging.js +54 -0
  55. package/dist/lib/rop/RepoPaging.js.map +1 -0
  56. package/dist/lib/rop/RestapiRules.js +52 -0
  57. package/dist/lib/rop/RestapiRules.js.map +1 -0
  58. package/dist/lib/rop/RopCreateMessageHandler.js +4 -2
  59. package/dist/lib/rop/RopCreateMessageHandler.js.map +1 -1
  60. package/dist/lib/rop/RopDeleteFolderHandler.js +118 -77
  61. package/dist/lib/rop/RopDeleteFolderHandler.js.map +1 -1
  62. package/dist/lib/rop/RopDeleteMessagesHandler.js +26 -1
  63. package/dist/lib/rop/RopDeleteMessagesHandler.js.map +1 -1
  64. package/dist/lib/rop/RopFastTransferSourceCopyPropertiesHandler.js +14 -14
  65. package/dist/lib/rop/RopFastTransferSourceCopyPropertiesHandler.js.map +1 -1
  66. package/dist/lib/rop/RopFastTransferSourceCopyToHandler.js +13 -14
  67. package/dist/lib/rop/RopFastTransferSourceCopyToHandler.js.map +1 -1
  68. package/dist/lib/rop/RopFastTransferSourceGetBufferHandler.js +33 -32
  69. package/dist/lib/rop/RopFastTransferSourceGetBufferHandler.js.map +1 -1
  70. package/dist/lib/rop/RopGetContentsTableHandler.js +17 -38
  71. package/dist/lib/rop/RopGetContentsTableHandler.js.map +1 -1
  72. package/dist/lib/rop/RopGetHierarchyTableHandler.js +4 -2
  73. package/dist/lib/rop/RopGetHierarchyTableHandler.js.map +1 -1
  74. package/dist/lib/rop/RopGetPropertiesSpecificHandler.js +6 -8
  75. package/dist/lib/rop/RopGetPropertiesSpecificHandler.js.map +1 -1
  76. package/dist/lib/rop/RopHandler.js +5 -1
  77. package/dist/lib/rop/RopHandler.js.map +1 -1
  78. package/dist/lib/rop/RopLogonHandler.js +5 -2
  79. package/dist/lib/rop/RopLogonHandler.js.map +1 -1
  80. package/dist/lib/rop/RopOpenFolderHandler.js +3 -1
  81. package/dist/lib/rop/RopOpenFolderHandler.js.map +1 -1
  82. package/dist/lib/rop/RopOpenMessageHandler.js +3 -1
  83. package/dist/lib/rop/RopOpenMessageHandler.js.map +1 -1
  84. package/dist/lib/rop/RopOpenStreamHandler.js +11 -8
  85. package/dist/lib/rop/RopOpenStreamHandler.js.map +1 -1
  86. package/dist/lib/rop/RopQueryRowsHandler.js +31 -5
  87. package/dist/lib/rop/RopQueryRowsHandler.js.map +1 -1
  88. package/dist/lib/rop/RopReadStreamHandler.js +22 -4
  89. package/dist/lib/rop/RopReadStreamHandler.js.map +1 -1
  90. package/dist/lib/rop/RopReleaseHandler.js +4 -1
  91. package/dist/lib/rop/RopReleaseHandler.js.map +1 -1
  92. package/dist/lib/rop/RopSaveChangesMessageHandler.js +66 -7
  93. package/dist/lib/rop/RopSaveChangesMessageHandler.js.map +1 -1
  94. package/dist/lib/rop/RopSetColumnsHandler.js +6 -8
  95. package/dist/lib/rop/RopSetColumnsHandler.js.map +1 -1
  96. package/dist/lib/rop/RopSetPropertiesHandler.js +23 -12
  97. package/dist/lib/rop/RopSetPropertiesHandler.js.map +1 -1
  98. package/dist/lib/rop/RopSubmitMessageHandler.js +148 -120
  99. package/dist/lib/rop/RopSubmitMessageHandler.js.map +1 -1
  100. package/dist/lib/rop/RopWriteStreamHandler.js +37 -10
  101. package/dist/lib/rop/RopWriteStreamHandler.js.map +1 -1
  102. package/dist/lib/rop/TaskTarget.js +2 -6
  103. package/dist/lib/rop/TaskTarget.js.map +1 -1
  104. package/dist/lib/rop/TransportSend.js +16 -0
  105. package/dist/lib/rop/TransportSend.js.map +1 -0
  106. package/dist/lib/sql/MapiEmsmdbRouteSQL.js +2 -1
  107. package/dist/lib/sql/MapiEmsmdbRouteSQL.js.map +1 -1
  108. package/dist/types/BaseMapiEmsmdbRoute.d.ts +59 -2
  109. package/dist/types/MapiSessionManager.d.ts +218 -63
  110. package/dist/types/RegexPatternUtils.d.ts +14 -0
  111. package/dist/types/RopDispatcher.d.ts +47 -11
  112. package/dist/types/codec/BufferCursor.d.ts +14 -0
  113. package/dist/types/codec/GlobalObjectId.d.ts +9 -5
  114. package/dist/types/codec/PropertyValue.d.ts +10 -0
  115. package/dist/types/codec/RopBuffer.d.ts +7 -0
  116. package/dist/types/mongo/MapiEmsmdbRouteMongo.d.ts +1 -0
  117. package/dist/types/nspi/NspiGetMatchesHandler.d.ts +2 -0
  118. package/dist/types/rop/AddressList.d.ts +48 -0
  119. package/dist/types/rop/CalendarEventTarget.d.ts +7 -3
  120. package/dist/types/rop/ContactTarget.d.ts +3 -3
  121. package/dist/types/rop/ContentsTable.d.ts +16 -0
  122. package/dist/types/rop/ExecuteBudget.d.ts +48 -0
  123. package/dist/types/rop/FastTransferStream.d.ts +41 -2
  124. package/dist/types/rop/FolderTarget.d.ts +3 -2
  125. package/dist/types/rop/HandleDataCache.d.ts +145 -0
  126. package/dist/types/rop/MeetingMessageClassHandler.d.ts +23 -29
  127. package/dist/types/rop/MessageBodyStream.d.ts +16 -7
  128. package/dist/types/rop/MessageTarget.d.ts +10 -3
  129. package/dist/types/rop/NamedPropertyRegistry.d.ts +6 -2
  130. package/dist/types/rop/PropertyResolvers.d.ts +1 -1
  131. package/dist/types/rop/RepoPaging.d.ts +30 -0
  132. package/dist/types/rop/RestapiRules.d.ts +32 -0
  133. package/dist/types/rop/RopCreateMessageHandler.d.ts +1 -0
  134. package/dist/types/rop/RopDeleteFolderHandler.d.ts +36 -21
  135. package/dist/types/rop/RopDeleteMessagesHandler.d.ts +7 -0
  136. package/dist/types/rop/RopFastTransferSourceCopyPropertiesHandler.d.ts +1 -0
  137. package/dist/types/rop/RopFastTransferSourceCopyToHandler.d.ts +1 -0
  138. package/dist/types/rop/RopFastTransferSourceGetBufferHandler.d.ts +10 -11
  139. package/dist/types/rop/RopGetContentsTableHandler.d.ts +7 -13
  140. package/dist/types/rop/RopGetHierarchyTableHandler.d.ts +1 -0
  141. package/dist/types/rop/RopHandler.d.ts +40 -1
  142. package/dist/types/rop/RopOpenFolderHandler.d.ts +1 -0
  143. package/dist/types/rop/RopOpenMessageHandler.d.ts +1 -0
  144. package/dist/types/rop/RopOpenStreamHandler.d.ts +1 -0
  145. package/dist/types/rop/RopQueryRowsHandler.d.ts +3 -0
  146. package/dist/types/rop/RopReadStreamHandler.d.ts +3 -1
  147. package/dist/types/rop/RopReleaseHandler.d.ts +1 -0
  148. package/dist/types/rop/RopSaveChangesMessageHandler.d.ts +16 -1
  149. package/dist/types/rop/RopSubmitMessageHandler.d.ts +33 -23
  150. package/dist/types/rop/RopWriteStreamHandler.d.ts +18 -8
  151. package/dist/types/rop/TaskTarget.d.ts +3 -3
  152. package/dist/types/rop/TransportSend.d.ts +10 -0
  153. package/dist/types/sql/MapiEmsmdbRouteSQL.d.ts +1 -0
  154. package/package.json +3 -3
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RepoPaging.js","sourceRoot":"","sources":["../../../src/rop/RepoPaging.ts"],"names":[],"mappings":"AAOA,6CAA6C;AAC7C,MAAM,CAAC,MAAM,cAAc,GAAG,IAAI,CAAC;AAEnC,iDAAiD;AACjD,MAAM,CAAC,MAAM,mBAAmB,GAAG,KAAK,CAAC;AAMzC;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC1B,IAAoB,EACpB,KAA0B,EAC1B,IAAc,EACd,IAAY,EACZ,WAAmB,cAAc,EACjC,MAAsB;IAEtB,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB,MAAM,IAAI,GAAQ,MAAM,IAAI,CAAC,IAAI,CAAC,EAAE,GAAG,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;IAChI,MAAM,EAAE,iBAAiB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACvC,OAAO,IAAI,CAAC;AAChB,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAC/B,IAAoB,EACpB,KAA0B,EAC1B,IAAc,EACd,MAAc,mBAAmB,EACjC,MAAsB;IAEtB,MAAM,KAAK,GAAQ,EAAE,CAAC;IACtB,KAAK,IAAI,IAAI,GAAG,CAAC,GAAI,IAAI,EAAE,EAAE,CAAC;QAC1B,MAAM,IAAI,GAAQ,MAAM,QAAQ,CAAI,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,CAAC,CAAC;QACrF,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC;QACpB,IAAI,IAAI,CAAC,MAAM,GAAG,cAAc,EAAE,CAAC;YAC/B,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,SAAS,EAAE,KAAK,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;QACzE,CAAC;QACD,IAAI,KAAK,CAAC,MAAM,IAAI,GAAG,EAAE,CAAC;YACtB,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;QAC3D,CAAC;IACL,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAC5B,IAAoB,EACpB,KAA0B,EAC1B,IAAc,EACd,KAAa,EACb,KAAa,EACb,MAAsB;IAEtB,MAAM,MAAM,GAAQ,EAAE,CAAC;IACvB,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,OAAO,MAAM,CAAC,MAAM,GAAG,KAAK,EAAE,CAAC;QAC3B,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,cAAc,CAAC,CAAC;QACnD,MAAM,IAAI,GAAQ,MAAM,QAAQ,CAAI,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,CAAC,CAAC;QACrF,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,IAAI,GAAG,cAAc,EAAE,QAAQ,GAAG,IAAI,GAAG,cAAc,GAAG,CAAC,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;QACvH,MAAM,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,CAAC;QACtB,QAAQ,IAAI,KAAK,CAAC,MAAM,CAAC;QACzB,IAAI,IAAI,CAAC,MAAM,GAAG,cAAc,EAAE,CAAC;YAC/B,MAAM;QACV,CAAC;IACL,CAAC;IACD,OAAO,MAAM,CAAC;AAClB,CAAC"}
@@ -0,0 +1,52 @@
1
+ ///////////////////////////////////////////////////////////////////////////////
2
+ // Copyright (C) 2026 Jean-Philippe Steinmetz. All rights reserved.
3
+ // SPDX-License-Identifier: MPL-2.0
4
+ ///////////////////////////////////////////////////////////////////////////////
5
+ import { createHash } from "crypto";
6
+ import { BaseEntity, ModelUtils } from "@rapidrest/service-core";
7
+ // Copies of restapi rules that @rapidmx/restapi 0.9.0 doesn't export from its package root. Keep them in sync with
8
+ // restapi's `util/ConversationUtils.ts` (`boundIndexedValue`) and `util/EntityUtils.ts` (`asEntity`).
9
+ /** The longest value `boundIndexedValue()` stores verbatim - restapi's `MAX_INDEXED_VALUE_LENGTH`. */
10
+ export const MAX_INDEXED_VALUE_LENGTH = 255;
11
+ /**
12
+ * restapi's `boundIndexedValue`: `value` unchanged when it's at most `MAX_INDEXED_VALUE_LENGTH` characters, otherwise
13
+ * `sha256:<64 hex digits>` of its UTF-8 bytes. restapi stores `CalendarEvent.icalUid` (and `Message.messageId`/
14
+ * `conversationId`) this way, so a lookup by one of those identifiers has to bound its value the same way to match.
15
+ */
16
+ export function boundIndexedValue(value) {
17
+ if (typeof value !== "string" || value.length <= MAX_INDEXED_VALUE_LENGTH) {
18
+ return value;
19
+ }
20
+ return `sha256:${createHash("sha256").update(value, "utf8").digest("hex")}`;
21
+ }
22
+ /**
23
+ * restapi's `asEntity`: `row` as an instance of `repo`'s model class. Before service-core 2.1.0, `RepoUtils.update()`
24
+ * only enforced its optimistic lock when `existing instanceof BaseEntity`, and the Mongo backend's `find()`/`findOne()`
25
+ * return plain documents, so passing one straight through turned a versioned update into an unconditional overwrite.
26
+ * 2.1.0 also locks plain documents carrying a numeric `version`, so this is now defence in depth, kept while restapi
27
+ * keeps its own copy. SQL rows are already instances and pass through as they are.
28
+ */
29
+ export function asEntity(repo, row) {
30
+ if (row instanceof BaseEntity) {
31
+ return row;
32
+ }
33
+ const modelClass = repo.modelClass;
34
+ return modelClass ? new modelClass(row) : row;
35
+ }
36
+ /**
37
+ * A `RepoUtils` query value that always matches `value` literally: service-core's `ModelUtils.literal()`, so a
38
+ * client- or sender-supplied value like `ne(x)` is never read as an operator and `me`/`null`/numeric strings are not
39
+ * substituted or coerced. Callers still compare the returned rows' field against `value` themselves.
40
+ */
41
+ export function literalQueryValue(value) {
42
+ return ModelUtils.literal(value);
43
+ }
44
+ /**
45
+ * restapi `MeetingSchedulingJob`'s eligibility rule (`jobs/MeetingSchedulingJob.ts`, `processInviteCandidates`): `true`
46
+ * while the job will still send invites for `event`'s current revision, i.e. its `inviteSequenceSent` is unset or
47
+ * differs from its `sequence`. Keep in sync with that job.
48
+ */
49
+ export function isInvitePending(event) {
50
+ return event.inviteSequenceSent == null || event.inviteSequenceSent !== event.sequence;
51
+ }
52
+ //# sourceMappingURL=RestapiRules.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RestapiRules.js","sourceRoot":"","sources":["../../../src/rop/RestapiRules.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,mEAAmE;AACnE,mCAAmC;AACnC,+EAA+E;AAC/E,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,UAAU,EAAqC,MAAM,yBAAyB,CAAC;AAEpG,mHAAmH;AACnH,sGAAsG;AAEtG,sGAAsG;AACtG,MAAM,CAAC,MAAM,wBAAwB,GAAG,GAAG,CAAC;AAE5C;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAsC,KAAQ;IAC3E,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,IAAI,wBAAwB,EAAE,CAAC;QACxE,OAAO,KAAK,CAAC;IACjB,CAAC;IACD,OAAO,UAAU,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAO,CAAC;AACrF,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,QAAQ,CAAI,IAAoB,EAAE,GAAM;IACpD,IAAI,GAAG,YAAY,UAAU,EAAE,CAAC;QAC5B,OAAO,GAAG,CAAC;IACf,CAAC;IACD,MAAM,UAAU,GAAS,IAAY,CAAC,UAAU,CAAC;IACjD,OAAO,UAAU,CAAC,CAAC,CAAC,IAAI,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;AAClD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAAa;IAC3C,OAAO,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AACrC,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,KAAgE;IAC5F,OAAO,KAAK,CAAC,kBAAkB,IAAI,IAAI,IAAI,KAAK,CAAC,kBAAkB,KAAK,KAAK,CAAC,QAAQ,CAAC;AAC3F,CAAC"}
@@ -1,3 +1,4 @@
1
+ import { assignHandle } from "../MapiSessionManager.js";
1
2
  const ROP_ID_CREATE_MESSAGE = 0x06;
2
3
  /** The well-known MAPI HRESULT `MAPI_E_NOT_FOUND`, reused as this handler's only failure `ReturnValue` - an
3
4
  * unrecognized `FolderId`, the same constant `RopOpenFolderHandler` uses for its own analogous lookup miss. */
@@ -22,6 +23,7 @@ const ERROR_NOT_FOUND = 0x8004010f;
22
23
  export class RopCreateMessageHandler {
23
24
  constructor() {
24
25
  this.ropId = ROP_ID_CREATE_MESSAGE;
26
+ this.responseHandleIndexOffset = 3; // OutputHandleIndex
25
27
  }
26
28
  handle(reader, writer, context) {
27
29
  reader.readUInt8(); // LogonId - this pragmatic subset doesn't track multiple concurrent logons per session
@@ -38,12 +40,12 @@ export class RopCreateMessageHandler {
38
40
  writer.writeUInt32LE(ERROR_NOT_FOUND);
39
41
  return;
40
42
  }
41
- context.session.handles[outputHandleIndex] = {
43
+ assignHandle(context.session, outputHandleIndex, {
42
44
  type: "message",
43
45
  entityUid: "",
44
46
  draftFolderUid: target,
45
47
  draftProperties: {},
46
- };
48
+ });
47
49
  writer.writeUInt8(ROP_ID_CREATE_MESSAGE);
48
50
  writer.writeUInt8(outputHandleIndex);
49
51
  writer.writeUInt32LE(0); // ReturnValue - success
@@ -1 +1 @@
1
- {"version":3,"file":"RopCreateMessageHandler.js","sourceRoot":"","sources":["../../../src/rop/RopCreateMessageHandler.ts"],"names":[],"mappings":"AAOA,MAAM,qBAAqB,GAAG,IAAI,CAAC;AAEnC;+GAC+G;AAC/G,MAAM,eAAe,GAAG,UAAU,CAAC;AAEnC;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,OAAO,uBAAuB;IAApC;QACoB,UAAK,GAAG,qBAAqB,CAAC;IA+BlD,CAAC;IA7BU,MAAM,CAAC,MAAoB,EAAE,MAAoB,EAAE,OAAmB;QACzE,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,uFAAuF;QAC3G,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,4EAA4E;QAChG,2FAA2F;QAC3F,MAAM,iBAAiB,GAAW,MAAM,CAAC,SAAS,EAAE,CAAC;QACrD,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC,qCAAqC;QAC5D,MAAM,QAAQ,GAAW,MAAM,CAAC,eAAe,EAAE,CAAC;QAClD,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,yCAAyC;QAE7D,MAAM,MAAM,GAAuB,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC;QAClF,IAAI,CAAC,MAAM,EAAE,CAAC;YACV,MAAM,CAAC,UAAU,CAAC,qBAAqB,CAAC,CAAC;YACzC,MAAM,CAAC,UAAU,CAAC,iBAAiB,CAAC,CAAC;YACrC,MAAM,CAAC,aAAa,CAAC,eAAe,CAAC,CAAC;YACtC,OAAO;QACX,CAAC;QAED,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,iBAAiB,CAAC,GAAG;YACzC,IAAI,EAAE,SAAS;YACf,SAAS,EAAE,EAAE;YACb,cAAc,EAAE,MAAM;YACtB,eAAe,EAAE,EAAE;SACtB,CAAC;QAEF,MAAM,CAAC,UAAU,CAAC,qBAAqB,CAAC,CAAC;QACzC,MAAM,CAAC,UAAU,CAAC,iBAAiB,CAAC,CAAC;QACrC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,wBAAwB;QACjD,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,gEAAgE;IAC1F,CAAC;CACJ"}
1
+ {"version":3,"file":"RopCreateMessageHandler.js","sourceRoot":"","sources":["../../../src/rop/RopCreateMessageHandler.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAGxD,MAAM,qBAAqB,GAAG,IAAI,CAAC;AAEnC;+GAC+G;AAC/G,MAAM,eAAe,GAAG,UAAU,CAAC;AAEnC;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,OAAO,uBAAuB;IAApC;QACoB,UAAK,GAAG,qBAAqB,CAAC;QAC9B,8BAAyB,GAAG,CAAC,CAAC,CAAC,oBAAoB;IA+BvE,CAAC;IA7BU,MAAM,CAAC,MAAoB,EAAE,MAAoB,EAAE,OAAmB;QACzE,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,uFAAuF;QAC3G,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,4EAA4E;QAChG,2FAA2F;QAC3F,MAAM,iBAAiB,GAAW,MAAM,CAAC,SAAS,EAAE,CAAC;QACrD,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC,qCAAqC;QAC5D,MAAM,QAAQ,GAAW,MAAM,CAAC,eAAe,EAAE,CAAC;QAClD,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,yCAAyC;QAE7D,MAAM,MAAM,GAAuB,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC;QAClF,IAAI,CAAC,MAAM,EAAE,CAAC;YACV,MAAM,CAAC,UAAU,CAAC,qBAAqB,CAAC,CAAC;YACzC,MAAM,CAAC,UAAU,CAAC,iBAAiB,CAAC,CAAC;YACrC,MAAM,CAAC,aAAa,CAAC,eAAe,CAAC,CAAC;YACtC,OAAO;QACX,CAAC;QAED,YAAY,CAAC,OAAO,CAAC,OAAO,EAAE,iBAAiB,EAAE;YAC7C,IAAI,EAAE,SAAS;YACf,SAAS,EAAE,EAAE;YACb,cAAc,EAAE,MAAM;YACtB,eAAe,EAAE,EAAE;SACtB,CAAC,CAAC;QAEH,MAAM,CAAC,UAAU,CAAC,qBAAqB,CAAC,CAAC;QACzC,MAAM,CAAC,UAAU,CAAC,iBAAiB,CAAC,CAAC;QACrC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,wBAAwB;QACjD,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,gEAAgE;IAC1F,CAAC;CACJ"}
@@ -1,3 +1,5 @@
1
+ import { auditMessageDelete } from "./RopDeleteMessagesHandler.js";
2
+ import { findAllCapped, findPage, MAX_COLLECTION_ROWS } from "./RepoPaging.js";
1
3
  const ROP_ID_DELETE_FOLDER = 0x1d;
2
4
  /** The well-known MAPI HRESULT `MAPI_E_NOT_FOUND`, reused for an unrecognized `FolderId` - the same constant
3
5
  * `RopOpenFolderHandler` uses for its own analogous lookup miss. */
@@ -7,29 +9,41 @@ const ERROR_NOT_FOUND = 0x8004010f;
7
9
  * throughout this pragmatic subset as a generic "can't do this" signal, not a claim of exact per-condition
8
10
  * `[MS-OXCFOLD]` return-value-table parity (the real spec's own `ecFolderNotEmpty` isn't separately modeled). */
9
11
  const ERROR_INVALID_OBJECT = 0x80070005;
10
- /** `DeleteFolderFlags` bits (`[MS-OXCFOLD]` §2.2.1.3.1, confirmed this session). */
12
+ /** `MAPI_E_TOO_COMPLEX`, for a subfolder tree deeper than `MAX_FOLDER_DEPTH` or larger than `MAX_FOLDERS_PER_DELETE`. */
13
+ const ERROR_TOO_COMPLEX = 0x80040117;
14
+ /** `DeleteFolderFlags` bits (`[MS-OXCFOLD]` §2.2.1.3.1). `DELETE_HARD_DELETE` (`0x10`) is deliberately not
15
+ * honored - see the class doc comment. */
11
16
  const DEL_MESSAGES = 0x01;
12
17
  const DEL_FOLDERS = 0x04;
13
- const DELETE_HARD_DELETE = 0x10;
18
+ /** The deepest subfolder nesting a recursive delete walks. */
19
+ export const MAX_FOLDER_DEPTH = 32;
20
+ /** The most folders one recursive delete removes. */
21
+ export const MAX_FOLDERS_PER_DELETE = 1000;
22
+ /** The most items (messages, events, contacts, tasks) one `RopDeleteFolder` deletes. Past this the ROP stops,
23
+ * reports `PartialCompletion`, and leaves the folders that still hold items in place; the client can retry. */
24
+ export const MAX_ITEMS_PER_DELETE = MAX_COLLECTION_ROWS;
25
+ const UID_SORT = { uid: "ASC" };
14
26
  /**
15
- * `RopDeleteFolder` (`[MS-OXCFOLD]`/`[MS-OXCROPS]`, RopId `0x1D`): deletes a folder by `FolderId`. Reuses
16
- * `RecoverableRepoUtils.delete()` exactly as-built in Phase 2 for `Folder`/`Message`/`CalendarEvent` - see
17
- * `RopDeleteMessagesHandler.ts`'s own doc comment for why that matters (EAS's watermark-based incremental
18
- * delete detection depends on it).
27
+ * `RopDeleteFolder` (`[MS-OXCFOLD]`/`[MS-OXCROPS]`, RopId `0x1D`): deletes a folder by `FolderId`, via
28
+ * `RecoverableRepoUtils.delete()` - see `RopDeleteMessagesHandler.ts`'s own doc comment for why that matters (EAS's
29
+ * watermark-based incremental delete detection depends on it).
19
30
  *
20
- * **Real spec semantics honored, not simplified away**: per `[MS-OXCFOLD]`, `RopDeleteFolder` only operates on
21
- * an empty folder by default - `DEL_MESSAGES` must be set to also delete the folder's own messages/calendar
22
- * events/contacts/tasks, and `DEL_FOLDERS` to also delete (recursively) its subfolders; a non-empty folder
23
- * deleted without the matching flag fails rather than silently cascading. `DELETE_HARD_DELETE` maps directly
24
- * onto `RepoDeleteOptions.purge` - real, exact semantic overlap with this library's own soft-delete model, not
25
- * a coincidence this codec papers over.
31
+ * **Real spec semantics honored**: `RopDeleteFolder` only operates on an empty folder by default - `DEL_MESSAGES`
32
+ * must be set to also delete the folder's own messages/calendar events/contacts/tasks, and `DEL_FOLDERS` to also
33
+ * delete (recursively) its subfolders; a non-empty folder deleted without the matching flag fails rather than
34
+ * silently cascading.
26
35
  *
27
- * **Contacts/Tasks folders**: emptiness is checked against `contactRepo`/`taskRepo` alongside `messageRepo`/
28
- * `calendarEventRepo`, and both are included in the delete cascade - a `CONTACTS`/`TASKS` folder's real content
29
- * lives in those repos (see `ContactTarget.ts`/`TaskTarget.ts`), not `messageRepo`, so checking only the latter
30
- * would let a non-empty Contacts/Tasks folder be deleted without `DEL_MESSAGES` and orphan its rows. Guarded
31
- * with `context.contactRepo`/`taskRepo` presence checks the same way `RopGetContentsTableHandler` does, since
32
- * `RopContext`'s doc comment documents both as optional.
36
+ * **Always a soft delete.** `DELETE_HARD_DELETE` is ignored. A permanent delete has to go through restapi's legal
37
+ * hold check first (the REST routes call `LegalHoldUtils.assertNotOnLegalHold()` for `purge=true`), and that helper
38
+ * isn't part of `@rapidmx/restapi`'s public exports. A soft-deleted item stays recoverable and discoverable, which is
39
+ * exactly what a hold requires, so MAPI never purges. Each deleted message is audited as `MESSAGE_DELETE`, like the
40
+ * REST message route.
41
+ *
42
+ * **Bounded.** The subfolder tree is walked iteratively with a visited set (a corrupt `parentFolderUid` cycle
43
+ * can't loop), at most `MAX_FOLDER_DEPTH` deep and `MAX_FOLDERS_PER_DELETE` wide, and only through folders in the
44
+ * caller's own mailbox. Items are fetched in explicit, paged queries (a bare `find()` stops at 100 rows, which used
45
+ * to leave items behind in a folder that was then deleted), up to `MAX_ITEMS_PER_DELETE`. Every query and delete is
46
+ * charged to the request's `ExecuteBudget` before it runs, so repeating this ROP can't multiply the walk unbounded.
33
47
  *
34
48
  * The request's own `InputHandleIndex` (nominally the *parent* folder of the one being deleted) is validated to
35
49
  * be a real, open folder handle but not cross-checked against the target folder's actual `parentFolderUid` -
@@ -49,85 +63,112 @@ export class RopDeleteFolderHandler {
49
63
  const folderId = reader.readBigUInt64LE();
50
64
  const handle = context.session.handles[inputHandleIndex];
51
65
  if (!handle || handle.type !== "folder") {
52
- writer.writeUInt8(ROP_ID_DELETE_FOLDER);
53
- writer.writeUInt8(inputHandleIndex);
54
- writer.writeUInt32LE(ERROR_INVALID_OBJECT);
66
+ this.writeFailure(writer, inputHandleIndex, ERROR_INVALID_OBJECT);
55
67
  return;
56
68
  }
57
69
  const target = context.session.folderIds[folderId.toString()];
58
70
  if (!target?.startsWith("folder:")) {
59
- writer.writeUInt8(ROP_ID_DELETE_FOLDER);
60
- writer.writeUInt8(inputHandleIndex);
61
- writer.writeUInt32LE(ERROR_NOT_FOUND);
71
+ this.writeFailure(writer, inputHandleIndex, ERROR_NOT_FOUND);
62
72
  return;
63
73
  }
64
74
  const uid = target.slice("folder:".length);
65
- const purge = (deleteFolderFlags & DELETE_HARD_DELETE) !== 0;
66
- const messages = await context.messageRepo.find({ folderUid: uid }, { ignoreACL: true });
67
- const events = await context.calendarEventRepo.find({ folderUid: uid }, { ignoreACL: true });
68
- const contacts = context.contactRepo ? await context.contactRepo.find({ folderUid: uid }, { ignoreACL: true }) : [];
69
- const tasks = context.taskRepo ? await context.taskRepo.find({ folderUid: uid }, { ignoreACL: true }) : [];
70
- const childFolders = await context.folderRepo.find({ parentFolderUid: uid }, { ignoreACL: true });
71
- if ((messages.length > 0 || events.length > 0 || contacts.length > 0 || tasks.length > 0) &&
72
- (deleteFolderFlags & DEL_MESSAGES) === 0) {
73
- writer.writeUInt8(ROP_ID_DELETE_FOLDER);
74
- writer.writeUInt8(inputHandleIndex);
75
- writer.writeUInt32LE(ERROR_INVALID_OBJECT);
75
+ if ((deleteFolderFlags & DEL_MESSAGES) === 0 && (await this.hasItems(uid, context))) {
76
+ this.writeFailure(writer, inputHandleIndex, ERROR_INVALID_OBJECT);
76
77
  return;
77
78
  }
78
- if (childFolders.length > 0 && (deleteFolderFlags & DEL_FOLDERS) === 0) {
79
- writer.writeUInt8(ROP_ID_DELETE_FOLDER);
80
- writer.writeUInt8(inputHandleIndex);
81
- writer.writeUInt32LE(ERROR_INVALID_OBJECT);
79
+ const childQuery = { parentFolderUid: uid, mailboxUid: context.mailboxUid };
80
+ if ((deleteFolderFlags & DEL_FOLDERS) === 0 && (await findPage(context.folderRepo, childQuery, UID_SORT, 0, 1, context.budget)).length > 0) {
81
+ this.writeFailure(writer, inputHandleIndex, ERROR_INVALID_OBJECT);
82
82
  return;
83
83
  }
84
- for (const message of messages) {
85
- await context.messageRepo.delete(message.uid, { ignoreACL: true, purge });
86
- }
87
- for (const event of events) {
88
- await context.calendarEventRepo.delete(event.uid, { ignoreACL: true, purge });
89
- }
90
- for (const contact of contacts) {
91
- await context.contactRepo.delete(contact.uid, { ignoreACL: true, purge });
92
- }
93
- for (const task of tasks) {
94
- await context.taskRepo.delete(task.uid, { ignoreACL: true, purge });
84
+ const folders = await this.collectSubtree(uid, context);
85
+ if (!folders) {
86
+ this.writeFailure(writer, inputHandleIndex, ERROR_TOO_COMPLEX);
87
+ return;
95
88
  }
96
- for (const childFolder of childFolders) {
97
- await this.deleteFolderRecursive(childFolder.uid, context, purge);
89
+ const budget = { remaining: MAX_ITEMS_PER_DELETE };
90
+ let partialCompletion = false;
91
+ for (const folderUid of folders) {
92
+ if (!(await this.deleteItems(folderUid, context, budget))) {
93
+ partialCompletion = true;
94
+ // Every folder deleted below (this loop's earlier iterations) is gone outright - nothing to refresh.
95
+ // This one is different: it survives (the budget ran out before `deleteItems` could empty it, so it's
96
+ // never reached below), but `itemRepos()` always drains `context.messageRepo` for a folder before any
97
+ // other repo, so some or all of its messages may already be gone - its stored counts must be
98
+ // refreshed rather than left counting messages that no longer exist.
99
+ await context.notifyFolderCounts?.([folderUid]);
100
+ break;
101
+ }
102
+ context.budget?.chargeQueries();
103
+ await context.folderRepo.delete(folderUid, { ignoreACL: true });
98
104
  }
99
- await context.folderRepo.delete(uid, { ignoreACL: true, purge });
100
105
  writer.writeUInt8(ROP_ID_DELETE_FOLDER);
101
106
  writer.writeUInt8(inputHandleIndex);
102
107
  writer.writeUInt32LE(0); // ReturnValue - success
103
- writer.writeUInt8(0); // PartialCompletion - always false; a partial failure above already returned early
108
+ writer.writeUInt8(partialCompletion ? 1 : 0); // PartialCompletion
104
109
  }
105
- /** Cascades a `DEL_FOLDERS` delete into `folderUid`'s own messages/calendar events/subfolders before
106
- * deleting it - this data model's folder hierarchy is a plain parent-pointer tree (never a graph), so
107
- * unbounded recursion here needs no cycle protection, the same assumption `FolderTarget.resolveFolderChildren`
108
- * already makes. */
109
- async deleteFolderRecursive(folderUid, context, purge) {
110
- const messages = await context.messageRepo.find({ folderUid }, { ignoreACL: true });
111
- const events = await context.calendarEventRepo.find({ folderUid }, { ignoreACL: true });
112
- const contacts = context.contactRepo ? await context.contactRepo.find({ folderUid }, { ignoreACL: true }) : [];
113
- const tasks = context.taskRepo ? await context.taskRepo.find({ folderUid }, { ignoreACL: true }) : [];
114
- const childFolders = await context.folderRepo.find({ parentFolderUid: folderUid }, { ignoreACL: true });
115
- for (const message of messages) {
116
- await context.messageRepo.delete(message.uid, { ignoreACL: true, purge });
117
- }
118
- for (const event of events) {
119
- await context.calendarEventRepo.delete(event.uid, { ignoreACL: true, purge });
120
- }
121
- for (const contact of contacts) {
122
- await context.contactRepo.delete(contact.uid, { ignoreACL: true, purge });
110
+ writeFailure(writer, inputHandleIndex, returnValue) {
111
+ writer.writeUInt8(ROP_ID_DELETE_FOLDER);
112
+ writer.writeUInt8(inputHandleIndex);
113
+ writer.writeUInt32LE(returnValue);
114
+ }
115
+ /** The repos a folder's items can live in, skipping the optional ones the context doesn't have. */
116
+ itemRepos(context) {
117
+ return [context.messageRepo, context.calendarEventRepo, context.contactRepo, context.taskRepo].filter((repo) => repo !== undefined);
118
+ }
119
+ async hasItems(folderUid, context) {
120
+ for (const repo of this.itemRepos(context)) {
121
+ if ((await findPage(repo, { folderUid, mailboxUid: context.mailboxUid }, UID_SORT, 0, 1, context.budget)).length > 0) {
122
+ return true;
123
+ }
123
124
  }
124
- for (const task of tasks) {
125
- await context.taskRepo.delete(task.uid, { ignoreACL: true, purge });
125
+ return false;
126
+ }
127
+ /**
128
+ * `rootUid` and every folder below it in the caller's mailbox, children before their parents, or `undefined` when
129
+ * the tree is deeper than `MAX_FOLDER_DEPTH` or has more than `MAX_FOLDERS_PER_DELETE` folders.
130
+ */
131
+ async collectSubtree(rootUid, context) {
132
+ const visited = new Set([rootUid]);
133
+ const preOrder = [];
134
+ const stack = [{ uid: rootUid, depth: 0 }];
135
+ while (stack.length > 0) {
136
+ const { uid, depth } = stack.pop();
137
+ preOrder.push(uid);
138
+ const { items: children, truncated } = await findAllCapped(context.folderRepo, { parentFolderUid: uid, mailboxUid: context.mailboxUid }, UID_SORT, MAX_FOLDERS_PER_DELETE, context.budget);
139
+ if (truncated) {
140
+ return undefined;
141
+ }
142
+ for (const child of children) {
143
+ if (visited.has(child.uid)) {
144
+ continue;
145
+ }
146
+ if (depth + 1 > MAX_FOLDER_DEPTH || visited.size >= MAX_FOLDERS_PER_DELETE) {
147
+ return undefined;
148
+ }
149
+ visited.add(child.uid);
150
+ stack.push({ uid: child.uid, depth: depth + 1 });
151
+ }
126
152
  }
127
- for (const childFolder of childFolders) {
128
- await this.deleteFolderRecursive(childFolder.uid, context, purge);
153
+ return preOrder.reverse();
154
+ }
155
+ /** Soft-deletes `folderUid`'s items within `budget`. Returns `false` if items remain once the budget is spent. */
156
+ async deleteItems(folderUid, context, budget) {
157
+ for (const repo of this.itemRepos(context)) {
158
+ const { items, truncated } = await findAllCapped(repo, { folderUid, mailboxUid: context.mailboxUid }, UID_SORT, budget.remaining, context.budget);
159
+ for (const item of items) {
160
+ context.budget?.chargeQueries();
161
+ await repo.delete(item.uid, { ignoreACL: true });
162
+ if (repo === context.messageRepo) {
163
+ await auditMessageDelete(context, item);
164
+ }
165
+ }
166
+ budget.remaining -= items.length;
167
+ if (truncated) {
168
+ return false;
169
+ }
129
170
  }
130
- await context.folderRepo.delete(folderUid, { ignoreACL: true, purge });
171
+ return true;
131
172
  }
132
173
  }
133
174
  //# sourceMappingURL=RopDeleteFolderHandler.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"RopDeleteFolderHandler.js","sourceRoot":"","sources":["../../../src/rop/RopDeleteFolderHandler.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"RopDeleteFolderHandler.js","sourceRoot":"","sources":["../../../src/rop/RopDeleteFolderHandler.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,15 @@
1
+ import { AuditAction } from "@rapidmx/restapi";
1
2
  const ROP_ID_DELETE_MESSAGES = 0x1e;
3
+ /** Audits a message deleted through MAPI with the same `MESSAGE_DELETE` entry the REST message route records. */
4
+ export async function auditMessageDelete(context, message) {
5
+ await context.audit?.({
6
+ action: AuditAction.MESSAGE_DELETE,
7
+ targetType: "Message",
8
+ targetUid: message.uid,
9
+ mailboxUid: message.mailboxUid,
10
+ details: { subject: message.subject, folderUid: message.folderUid },
11
+ });
12
+ }
2
13
  /** The well-known MAPI HRESULT `MAPI_E_INVALID_OBJECT`, reused for "the referenced handle isn't a folder (or
3
14
  * doesn't exist)" - the same constant `RopGetContentsTableHandler` uses for its own analogous check. */
4
15
  const ERROR_INVALID_OBJECT = 0x80070005;
@@ -49,10 +60,21 @@ export class RopDeleteMessagesHandler {
49
60
  return;
50
61
  }
51
62
  let partialCompletion = false;
63
+ // The folders that lost a message - refreshed once below, after the loop, rather than once per message (a
64
+ // client can name several `MessageId`s in one call, several of which are often the same folder).
65
+ const affectedFolderUids = new Set();
52
66
  for (const messageId of messageIds) {
53
67
  const target = context.session.messageIds[messageId.toString()];
54
68
  if (target?.startsWith("message:")) {
55
- await context.messageRepo.delete(target.slice("message:".length), { ignoreACL: true });
69
+ const uid = target.slice("message:".length);
70
+ // Always fetched now (not just when `context.audit` is set): `notifyFolderCounts` below needs to know
71
+ // which folder this message was in, the same as `auditMessageDelete` already needed its subject.
72
+ const message = await context.messageRepo.findOne(uid, { ignoreACL: true });
73
+ await context.messageRepo.delete(uid, { ignoreACL: true });
74
+ if (message) {
75
+ affectedFolderUids.add(message.folderUid);
76
+ await auditMessageDelete(context, message);
77
+ }
56
78
  }
57
79
  else if (target?.startsWith("calendarEvent:")) {
58
80
  await context.calendarEventRepo.delete(target.slice("calendarEvent:".length), { ignoreACL: true });
@@ -61,6 +83,9 @@ export class RopDeleteMessagesHandler {
61
83
  partialCompletion = true;
62
84
  }
63
85
  }
86
+ // Not `bumpSyncKey` - only adding a message to a folder does that (see `refreshFolderCounts()`'s own doc
87
+ // comment); a deletion doesn't, matching restapi's own `ScanQueueJob.processRecall()` deletion call site.
88
+ await context.notifyFolderCounts?.(affectedFolderUids);
64
89
  writer.writeUInt8(ROP_ID_DELETE_MESSAGES);
65
90
  writer.writeUInt8(inputHandleIndex);
66
91
  writer.writeUInt32LE(0); // ReturnValue - success
@@ -1 +1 @@
1
- {"version":3,"file":"RopDeleteMessagesHandler.js","sourceRoot":"","sources":["../../../src/rop/RopDeleteMessagesHandler.ts"],"names":[],"mappings":"AAOA,MAAM,sBAAsB,GAAG,IAAI,CAAC;AAEpC;wGACwG;AACxG,MAAM,oBAAoB,GAAG,UAAU,CAAC;AAExC;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,OAAO,wBAAwB;IAArC;QACoB,UAAK,GAAG,sBAAsB,CAAC;IAsCnD,CAAC;IApCU,KAAK,CAAC,MAAM,CAAC,MAAoB,EAAE,MAAoB,EAAE,OAAmB;QAC/E,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,uFAAuF;QAC3G,MAAM,gBAAgB,GAAW,MAAM,CAAC,SAAS,EAAE,CAAC;QACpD,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,+EAA+E;QACnG,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,yDAAyD;QAC7E,MAAM,cAAc,GAAW,MAAM,CAAC,YAAY,EAAE,CAAC;QACrD,MAAM,UAAU,GAAa,EAAE,CAAC;QAChC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,cAAc,EAAE,CAAC,EAAE,EAAE,CAAC;YACtC,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,eAAe,EAAE,CAAC,CAAC;QAC9C,CAAC;QAED,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;QACzD,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACtC,MAAM,CAAC,UAAU,CAAC,sBAAsB,CAAC,CAAC;YAC1C,MAAM,CAAC,UAAU,CAAC,gBAAgB,CAAC,CAAC;YACpC,MAAM,CAAC,aAAa,CAAC,oBAAoB,CAAC,CAAC;YAC3C,OAAO;QACX,CAAC;QAED,IAAI,iBAAiB,GAAG,KAAK,CAAC;QAC9B,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;YACjC,MAAM,MAAM,GAAuB,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,SAAS,CAAC,QAAQ,EAAE,CAAC,CAAC;YACpF,IAAI,MAAM,EAAE,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;gBACjC,MAAM,OAAO,CAAC,WAAW,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAC3F,CAAC;iBAAM,IAAI,MAAM,EAAE,UAAU,CAAC,gBAAgB,CAAC,EAAE,CAAC;gBAC9C,MAAM,OAAO,CAAC,iBAAiB,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,gBAAgB,CAAC,MAAM,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YACvG,CAAC;iBAAM,CAAC;gBACJ,iBAAiB,GAAG,IAAI,CAAC;YAC7B,CAAC;QACL,CAAC;QAED,MAAM,CAAC,UAAU,CAAC,sBAAsB,CAAC,CAAC;QAC1C,MAAM,CAAC,UAAU,CAAC,gBAAgB,CAAC,CAAC;QACpC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,wBAAwB;QACjD,MAAM,CAAC,UAAU,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,oBAAoB;IACtE,CAAC;CACJ"}
1
+ {"version":3,"file":"RopDeleteMessagesHandler.js","sourceRoot":"","sources":["../../../src/rop/RopDeleteMessagesHandler.ts"],"names":[],"mappings":"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"}
@@ -1,10 +1,12 @@
1
- import { readPropertyTag } from "../codec/PropertyValue.js";
2
- import { buildFastTransferStream } from "./FastTransferStream.js";
1
+ import { readPropertyTagArray } from "../codec/PropertyValue.js";
2
+ import { openFastTransferHandle } from "./FastTransferStream.js";
3
3
  const ROP_ID_COPY_PROPERTIES = 0x69;
4
4
  /** The well-known MAPI HRESULT `MAPI_E_INVALID_OBJECT`, reused for "the referenced handle isn't a folder or
5
5
  * message (or doesn't exist)" - the same constant `RopGetPropertiesSpecificHandler` uses for its own analogous
6
6
  * check. */
7
7
  const ERROR_INVALID_OBJECT = 0x80070005;
8
+ /** `MAPI_E_TOO_BIG`: more than `MAX_PROPERTY_TAG_COUNT` tags, or a stream past `MAX_FAST_TRANSFER_BYTES`. */
9
+ const ERROR_TOO_BIG = 0x80040305;
8
10
  /**
9
11
  * `RopFastTransferSourceCopyProperties` (`[MS-OXCFXICS]`/`[MS-OXCROPS]`, RopId `0x69`): the explicit-column
10
12
  * sibling of `RopFastTransferSourceCopyTo` - `PropertyTags` here is the properties to **copy** (an include
@@ -26,6 +28,7 @@ const ERROR_INVALID_OBJECT = 0x80070005;
26
28
  export class RopFastTransferSourceCopyPropertiesHandler {
27
29
  constructor() {
28
30
  this.ropId = ROP_ID_COPY_PROPERTIES;
31
+ this.responseHandleIndexOffset = 3; // OutputHandleIndex
29
32
  }
30
33
  async handle(reader, writer, context) {
31
34
  reader.readUInt8(); // LogonId - this pragmatic subset doesn't track multiple concurrent logons per session
@@ -34,11 +37,9 @@ export class RopFastTransferSourceCopyPropertiesHandler {
34
37
  reader.readUInt8(); // Level - subfolder recursion not honored, see class doc comment
35
38
  reader.readUInt8(); // CopyFlags (1 byte here, unlike CopyTo's 4) - not honored, see class doc comment
36
39
  reader.readUInt8(); // SendOptions - not honored, see class doc comment
37
- const propertyTagCount = reader.readUInt16LE();
38
- const columns = [];
39
- for (let i = 0; i < propertyTagCount; i++) {
40
- columns.push(readPropertyTag(reader));
41
- }
40
+ const tags = readPropertyTagArray(reader, reader.readUInt16LE());
41
+ // One column per property: a repeated tag would only repeat the same value in every row of the stream.
42
+ const columns = tags?.filter((tag, index) => tags.findIndex((other) => other.propertyId === tag.propertyId) === index);
42
43
  const handle = context.session.handles[inputHandleIndex];
43
44
  if (!handle || (handle.type !== "folder" && handle.type !== "message")) {
44
45
  writer.writeUInt8(ROP_ID_COPY_PROPERTIES);
@@ -46,13 +47,12 @@ export class RopFastTransferSourceCopyPropertiesHandler {
46
47
  writer.writeUInt32LE(ERROR_INVALID_OBJECT);
47
48
  return;
48
49
  }
49
- const transferBuffer = await buildFastTransferStream(handle, context, { columns });
50
- context.session.handles[outputHandleIndex] = {
51
- type: "fastTransfer",
52
- entityUid: handle.entityUid,
53
- transferBufferBase64: transferBuffer.toString("base64"),
54
- transferPosition: 0,
55
- };
50
+ if (!columns || !(await openFastTransferHandle(context, outputHandleIndex, handle, { columns }))) {
51
+ writer.writeUInt8(ROP_ID_COPY_PROPERTIES);
52
+ writer.writeUInt8(outputHandleIndex);
53
+ writer.writeUInt32LE(ERROR_TOO_BIG);
54
+ return;
55
+ }
56
56
  writer.writeUInt8(ROP_ID_COPY_PROPERTIES);
57
57
  writer.writeUInt8(outputHandleIndex);
58
58
  writer.writeUInt32LE(0); // ReturnValue - success
@@ -1 +1 @@
1
- {"version":3,"file":"RopFastTransferSourceCopyPropertiesHandler.js","sourceRoot":"","sources":["../../../src/rop/RopFastTransferSourceCopyPropertiesHandler.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,eAAe,EAAoB,MAAM,2BAA2B,CAAC;AAC9E,OAAO,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAGlE,MAAM,sBAAsB,GAAG,IAAI,CAAC;AAEpC;;YAEY;AACZ,MAAM,oBAAoB,GAAG,UAAU,CAAC;AAExC;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,OAAO,0CAA0C;IAAvD;QACoB,UAAK,GAAG,sBAAsB,CAAC;IAmCnD,CAAC;IAjCU,KAAK,CAAC,MAAM,CAAC,MAAoB,EAAE,MAAoB,EAAE,OAAmB;QAC/E,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,uFAAuF;QAC3G,MAAM,gBAAgB,GAAW,MAAM,CAAC,SAAS,EAAE,CAAC;QACpD,MAAM,iBAAiB,GAAW,MAAM,CAAC,SAAS,EAAE,CAAC;QACrD,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,iEAAiE;QACrF,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,kFAAkF;QACtG,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,mDAAmD;QACvE,MAAM,gBAAgB,GAAW,MAAM,CAAC,YAAY,EAAE,CAAC;QACvD,MAAM,OAAO,GAAkB,EAAE,CAAC;QAClC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,gBAAgB,EAAE,CAAC,EAAE,EAAE,CAAC;YACxC,OAAO,CAAC,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC;QAC1C,CAAC;QAED,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;QACzD,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,QAAQ,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS,CAAC,EAAE,CAAC;YACrE,MAAM,CAAC,UAAU,CAAC,sBAAsB,CAAC,CAAC;YAC1C,MAAM,CAAC,UAAU,CAAC,iBAAiB,CAAC,CAAC;YACrC,MAAM,CAAC,aAAa,CAAC,oBAAoB,CAAC,CAAC;YAC3C,OAAO;QACX,CAAC;QAED,MAAM,cAAc,GAAG,MAAM,uBAAuB,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;QACnF,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,iBAAiB,CAAC,GAAG;YACzC,IAAI,EAAE,cAAc;YACpB,SAAS,EAAE,MAAM,CAAC,SAAS;YAC3B,oBAAoB,EAAE,cAAc,CAAC,QAAQ,CAAC,QAAQ,CAAC;YACvD,gBAAgB,EAAE,CAAC;SACtB,CAAC;QAEF,MAAM,CAAC,UAAU,CAAC,sBAAsB,CAAC,CAAC;QAC1C,MAAM,CAAC,UAAU,CAAC,iBAAiB,CAAC,CAAC;QACrC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,wBAAwB;IACrD,CAAC;CACJ"}
1
+ {"version":3,"file":"RopFastTransferSourceCopyPropertiesHandler.js","sourceRoot":"","sources":["../../../src/rop/RopFastTransferSourceCopyPropertiesHandler.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,oBAAoB,EAAoB,MAAM,2BAA2B,CAAC;AACnF,OAAO,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AAGjE,MAAM,sBAAsB,GAAG,IAAI,CAAC;AAEpC;;YAEY;AACZ,MAAM,oBAAoB,GAAG,UAAU,CAAC;AAExC,6GAA6G;AAC7G,MAAM,aAAa,GAAG,UAAU,CAAC;AAEjC;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,OAAO,0CAA0C;IAAvD;QACoB,UAAK,GAAG,sBAAsB,CAAC;QAC/B,8BAAyB,GAAG,CAAC,CAAC,CAAC,oBAAoB;IA+BvE,CAAC;IA7BU,KAAK,CAAC,MAAM,CAAC,MAAoB,EAAE,MAAoB,EAAE,OAAmB;QAC/E,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,uFAAuF;QAC3G,MAAM,gBAAgB,GAAW,MAAM,CAAC,SAAS,EAAE,CAAC;QACpD,MAAM,iBAAiB,GAAW,MAAM,CAAC,SAAS,EAAE,CAAC;QACrD,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,iEAAiE;QACrF,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,kFAAkF;QACtG,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,mDAAmD;QACvE,MAAM,IAAI,GAA8B,oBAAoB,CAAC,MAAM,EAAE,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC;QAC5F,uGAAuG;QACvG,MAAM,OAAO,GAAG,IAAI,EAAE,MAAM,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,UAAU,KAAK,GAAG,CAAC,UAAU,CAAC,KAAK,KAAK,CAAC,CAAC;QAEvH,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;QACzD,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,QAAQ,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS,CAAC,EAAE,CAAC;YACrE,MAAM,CAAC,UAAU,CAAC,sBAAsB,CAAC,CAAC;YAC1C,MAAM,CAAC,UAAU,CAAC,iBAAiB,CAAC,CAAC;YACrC,MAAM,CAAC,aAAa,CAAC,oBAAoB,CAAC,CAAC;YAC3C,OAAO;QACX,CAAC;QACD,IAAI,CAAC,OAAO,IAAI,CAAC,CAAC,MAAM,sBAAsB,CAAC,OAAO,EAAE,iBAAiB,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC,EAAE,CAAC;YAC/F,MAAM,CAAC,UAAU,CAAC,sBAAsB,CAAC,CAAC;YAC1C,MAAM,CAAC,UAAU,CAAC,iBAAiB,CAAC,CAAC;YACrC,MAAM,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC;YACpC,OAAO;QACX,CAAC;QAED,MAAM,CAAC,UAAU,CAAC,sBAAsB,CAAC,CAAC;QAC1C,MAAM,CAAC,UAAU,CAAC,iBAAiB,CAAC,CAAC;QACrC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,wBAAwB;IACrD,CAAC;CACJ"}
@@ -1,10 +1,12 @@
1
- import { readPropertyTag } from "../codec/PropertyValue.js";
2
- import { buildFastTransferStream } from "./FastTransferStream.js";
1
+ import { readPropertyTagArray } from "../codec/PropertyValue.js";
2
+ import { openFastTransferHandle } from "./FastTransferStream.js";
3
3
  const ROP_ID_COPY_TO = 0x4d;
4
4
  /** The well-known MAPI HRESULT `MAPI_E_INVALID_OBJECT`, reused for "the referenced handle isn't a folder or
5
5
  * message (or doesn't exist)" - the same constant `RopGetPropertiesSpecificHandler` uses for its own analogous
6
6
  * check. */
7
7
  const ERROR_INVALID_OBJECT = 0x80070005;
8
+ /** `MAPI_E_TOO_BIG`: more than `MAX_PROPERTY_TAG_COUNT` tags, or a stream past `MAX_FAST_TRANSFER_BYTES`. */
9
+ const ERROR_TOO_BIG = 0x80040305;
8
10
  /**
9
11
  * `RopFastTransferSourceCopyTo` (`[MS-OXCFXICS]`/`[MS-OXCROPS]`, RopId `0x4D`): begins a FastTransfer download
10
12
  * of an already-open Folder or Message (or Calendar item, itself a Message object on the wire) object,
@@ -26,6 +28,7 @@ const ERROR_INVALID_OBJECT = 0x80070005;
26
28
  export class RopFastTransferSourceCopyToHandler {
27
29
  constructor() {
28
30
  this.ropId = ROP_ID_COPY_TO;
31
+ this.responseHandleIndexOffset = 3; // OutputHandleIndex
29
32
  }
30
33
  async handle(reader, writer, context) {
31
34
  reader.readUInt8(); // LogonId - this pragmatic subset doesn't track multiple concurrent logons per session
@@ -34,11 +37,7 @@ export class RopFastTransferSourceCopyToHandler {
34
37
  reader.readUInt8(); // Level - subfolder recursion not honored, see class doc comment
35
38
  reader.readUInt32LE(); // CopyFlags - not honored, see class doc comment
36
39
  reader.readUInt8(); // SendOptions - not honored, see class doc comment
37
- const propertyTagCount = reader.readUInt16LE();
38
- const excludePropertyIds = new Set();
39
- for (let i = 0; i < propertyTagCount; i++) {
40
- excludePropertyIds.add(readPropertyTag(reader).propertyId);
41
- }
40
+ const excludeTags = readPropertyTagArray(reader, reader.readUInt16LE());
42
41
  const handle = context.session.handles[inputHandleIndex];
43
42
  if (!handle || (handle.type !== "folder" && handle.type !== "message")) {
44
43
  writer.writeUInt8(ROP_ID_COPY_TO);
@@ -46,13 +45,13 @@ export class RopFastTransferSourceCopyToHandler {
46
45
  writer.writeUInt32LE(ERROR_INVALID_OBJECT);
47
46
  return;
48
47
  }
49
- const transferBuffer = await buildFastTransferStream(handle, context, { excludePropertyIds });
50
- context.session.handles[outputHandleIndex] = {
51
- type: "fastTransfer",
52
- entityUid: handle.entityUid,
53
- transferBufferBase64: transferBuffer.toString("base64"),
54
- transferPosition: 0,
55
- };
48
+ const excludePropertyIds = excludeTags ? [...new Set(excludeTags.map((tag) => tag.propertyId))] : undefined;
49
+ if (!excludePropertyIds || !(await openFastTransferHandle(context, outputHandleIndex, handle, { excludePropertyIds }))) {
50
+ writer.writeUInt8(ROP_ID_COPY_TO);
51
+ writer.writeUInt8(outputHandleIndex);
52
+ writer.writeUInt32LE(ERROR_TOO_BIG);
53
+ return;
54
+ }
56
55
  writer.writeUInt8(ROP_ID_COPY_TO);
57
56
  writer.writeUInt8(outputHandleIndex);
58
57
  writer.writeUInt32LE(0); // ReturnValue - success
@@ -1 +1 @@
1
- {"version":3,"file":"RopFastTransferSourceCopyToHandler.js","sourceRoot":"","sources":["../../../src/rop/RopFastTransferSourceCopyToHandler.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAGlE,MAAM,cAAc,GAAG,IAAI,CAAC;AAE5B;;YAEY;AACZ,MAAM,oBAAoB,GAAG,UAAU,CAAC;AAExC;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,OAAO,kCAAkC;IAA/C;QACoB,UAAK,GAAG,cAAc,CAAC;IAmC3C,CAAC;IAjCU,KAAK,CAAC,MAAM,CAAC,MAAoB,EAAE,MAAoB,EAAE,OAAmB;QAC/E,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,uFAAuF;QAC3G,MAAM,gBAAgB,GAAW,MAAM,CAAC,SAAS,EAAE,CAAC;QACpD,MAAM,iBAAiB,GAAW,MAAM,CAAC,SAAS,EAAE,CAAC;QACrD,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,iEAAiE;QACrF,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC,iDAAiD;QACxE,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,mDAAmD;QACvE,MAAM,gBAAgB,GAAW,MAAM,CAAC,YAAY,EAAE,CAAC;QACvD,MAAM,kBAAkB,GAAG,IAAI,GAAG,EAAU,CAAC;QAC7C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,gBAAgB,EAAE,CAAC,EAAE,EAAE,CAAC;YACxC,kBAAkB,CAAC,GAAG,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,UAAU,CAAC,CAAC;QAC/D,CAAC;QAED,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;QACzD,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,QAAQ,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS,CAAC,EAAE,CAAC;YACrE,MAAM,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;YAClC,MAAM,CAAC,UAAU,CAAC,iBAAiB,CAAC,CAAC;YACrC,MAAM,CAAC,aAAa,CAAC,oBAAoB,CAAC,CAAC;YAC3C,OAAO;QACX,CAAC;QAED,MAAM,cAAc,GAAG,MAAM,uBAAuB,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE,kBAAkB,EAAE,CAAC,CAAC;QAC9F,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,iBAAiB,CAAC,GAAG;YACzC,IAAI,EAAE,cAAc;YACpB,SAAS,EAAE,MAAM,CAAC,SAAS;YAC3B,oBAAoB,EAAE,cAAc,CAAC,QAAQ,CAAC,QAAQ,CAAC;YACvD,gBAAgB,EAAE,CAAC;SACtB,CAAC;QAEF,MAAM,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;QAClC,MAAM,CAAC,UAAU,CAAC,iBAAiB,CAAC,CAAC;QACrC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,wBAAwB;IACrD,CAAC;CACJ"}
1
+ {"version":3,"file":"RopFastTransferSourceCopyToHandler.js","sourceRoot":"","sources":["../../../src/rop/RopFastTransferSourceCopyToHandler.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACjE,OAAO,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AAGjE,MAAM,cAAc,GAAG,IAAI,CAAC;AAE5B;;YAEY;AACZ,MAAM,oBAAoB,GAAG,UAAU,CAAC;AAExC,6GAA6G;AAC7G,MAAM,aAAa,GAAG,UAAU,CAAC;AAEjC;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,OAAO,kCAAkC;IAA/C;QACoB,UAAK,GAAG,cAAc,CAAC;QACvB,8BAAyB,GAAG,CAAC,CAAC,CAAC,oBAAoB;IA8BvE,CAAC;IA5BU,KAAK,CAAC,MAAM,CAAC,MAAoB,EAAE,MAAoB,EAAE,OAAmB;QAC/E,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,uFAAuF;QAC3G,MAAM,gBAAgB,GAAW,MAAM,CAAC,SAAS,EAAE,CAAC;QACpD,MAAM,iBAAiB,GAAW,MAAM,CAAC,SAAS,EAAE,CAAC;QACrD,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,iEAAiE;QACrF,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC,iDAAiD;QACxE,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,mDAAmD;QACvE,MAAM,WAAW,GAAG,oBAAoB,CAAC,MAAM,EAAE,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC;QAExE,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;QACzD,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,QAAQ,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS,CAAC,EAAE,CAAC;YACrE,MAAM,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;YAClC,MAAM,CAAC,UAAU,CAAC,iBAAiB,CAAC,CAAC;YACrC,MAAM,CAAC,aAAa,CAAC,oBAAoB,CAAC,CAAC;YAC3C,OAAO;QACX,CAAC;QACD,MAAM,kBAAkB,GAAG,WAAW,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAC5G,IAAI,CAAC,kBAAkB,IAAI,CAAC,CAAC,MAAM,sBAAsB,CAAC,OAAO,EAAE,iBAAiB,EAAE,MAAM,EAAE,EAAE,kBAAkB,EAAE,CAAC,CAAC,EAAE,CAAC;YACrH,MAAM,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;YAClC,MAAM,CAAC,UAAU,CAAC,iBAAiB,CAAC,CAAC;YACrC,MAAM,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC;YACpC,OAAO;QACX,CAAC;QAED,MAAM,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;QAClC,MAAM,CAAC,UAAU,CAAC,iBAAiB,CAAC,CAAC;QACrC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,wBAAwB;IACrD,CAAC;CACJ"}