@rapidmx/activesync 1.0.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +382 -0
- package/README.md +57 -0
- package/dist/lib/BaseDeviceSyncStateRoute.js +90 -0
- package/dist/lib/BaseDeviceSyncStateRoute.js.map +1 -0
- package/dist/lib/BaseEasRoute.js +213 -0
- package/dist/lib/BaseEasRoute.js.map +1 -0
- package/dist/lib/CompactDateTime.js +43 -0
- package/dist/lib/CompactDateTime.js.map +1 -0
- package/dist/lib/EasCommandHandler.js +2 -0
- package/dist/lib/EasCommandHandler.js.map +1 -0
- package/dist/lib/EasSyncKeyUtils.js +119 -0
- package/dist/lib/EasSyncKeyUtils.js.map +1 -0
- package/dist/lib/adapters/CalendarSyncAdapter.js +255 -0
- package/dist/lib/adapters/CalendarSyncAdapter.js.map +1 -0
- package/dist/lib/adapters/ContactsSyncAdapter.js +164 -0
- package/dist/lib/adapters/ContactsSyncAdapter.js.map +1 -0
- package/dist/lib/adapters/EasCollectionSyncAdapter.js +2 -0
- package/dist/lib/adapters/EasCollectionSyncAdapter.js.map +1 -0
- package/dist/lib/adapters/EmailSyncAdapter.js +230 -0
- package/dist/lib/adapters/EmailSyncAdapter.js.map +1 -0
- package/dist/lib/adapters/TasksSyncAdapter.js +100 -0
- package/dist/lib/adapters/TasksSyncAdapter.js.map +1 -0
- package/dist/lib/codec/WbxmlCodePages.js +706 -0
- package/dist/lib/codec/WbxmlCodePages.js.map +1 -0
- package/dist/lib/codec/WbxmlDecoder.js +154 -0
- package/dist/lib/codec/WbxmlDecoder.js.map +1 -0
- package/dist/lib/codec/WbxmlElement.js +31 -0
- package/dist/lib/codec/WbxmlElement.js.map +1 -0
- package/dist/lib/codec/WbxmlEncoder.js +88 -0
- package/dist/lib/codec/WbxmlEncoder.js.map +1 -0
- package/dist/lib/commands/ComposeMailCommand.js +187 -0
- package/dist/lib/commands/ComposeMailCommand.js.map +1 -0
- package/dist/lib/commands/FolderSyncCommand.js +150 -0
- package/dist/lib/commands/FolderSyncCommand.js.map +1 -0
- package/dist/lib/commands/GetItemEstimateCommand.js +149 -0
- package/dist/lib/commands/GetItemEstimateCommand.js.map +1 -0
- package/dist/lib/commands/ItemOperationsCommand.js +337 -0
- package/dist/lib/commands/ItemOperationsCommand.js.map +1 -0
- package/dist/lib/commands/MeetingResponseCommand.js +120 -0
- package/dist/lib/commands/MeetingResponseCommand.js.map +1 -0
- package/dist/lib/commands/MoveItemsCommand.js +103 -0
- package/dist/lib/commands/MoveItemsCommand.js.map +1 -0
- package/dist/lib/commands/PingCommand.js +145 -0
- package/dist/lib/commands/PingCommand.js.map +1 -0
- package/dist/lib/commands/ProvisionCommand.js +164 -0
- package/dist/lib/commands/ProvisionCommand.js.map +1 -0
- package/dist/lib/commands/ResolveRecipientsCommand.js +124 -0
- package/dist/lib/commands/ResolveRecipientsCommand.js.map +1 -0
- package/dist/lib/commands/SearchCommand.js +137 -0
- package/dist/lib/commands/SearchCommand.js.map +1 -0
- package/dist/lib/commands/SendMailCommand.js +18 -0
- package/dist/lib/commands/SendMailCommand.js.map +1 -0
- package/dist/lib/commands/SettingsCommand.js +138 -0
- package/dist/lib/commands/SettingsCommand.js.map +1 -0
- package/dist/lib/commands/SmartForwardCommand.js +22 -0
- package/dist/lib/commands/SmartForwardCommand.js.map +1 -0
- package/dist/lib/commands/SmartReplyCommand.js +22 -0
- package/dist/lib/commands/SmartReplyCommand.js.map +1 -0
- package/dist/lib/commands/SyncCommand.js +406 -0
- package/dist/lib/commands/SyncCommand.js.map +1 -0
- package/dist/lib/commands/mongo/FolderSyncCommandMongo.js +16 -0
- package/dist/lib/commands/mongo/FolderSyncCommandMongo.js.map +1 -0
- package/dist/lib/commands/mongo/GetItemEstimateCommandMongo.js +21 -0
- package/dist/lib/commands/mongo/GetItemEstimateCommandMongo.js.map +1 -0
- package/dist/lib/commands/mongo/ItemOperationsCommandMongo.js +18 -0
- package/dist/lib/commands/mongo/ItemOperationsCommandMongo.js.map +1 -0
- package/dist/lib/commands/mongo/MeetingResponseCommandMongo.js +17 -0
- package/dist/lib/commands/mongo/MeetingResponseCommandMongo.js.map +1 -0
- package/dist/lib/commands/mongo/MoveItemsCommandMongo.js +17 -0
- package/dist/lib/commands/mongo/MoveItemsCommandMongo.js.map +1 -0
- package/dist/lib/commands/mongo/ResolveRecipientsCommandMongo.js +20 -0
- package/dist/lib/commands/mongo/ResolveRecipientsCommandMongo.js.map +1 -0
- package/dist/lib/commands/mongo/SearchCommandMongo.js +20 -0
- package/dist/lib/commands/mongo/SearchCommandMongo.js.map +1 -0
- package/dist/lib/commands/mongo/SendMailCommandMongo.js +17 -0
- package/dist/lib/commands/mongo/SendMailCommandMongo.js.map +1 -0
- package/dist/lib/commands/mongo/SettingsCommandMongo.js +16 -0
- package/dist/lib/commands/mongo/SettingsCommandMongo.js.map +1 -0
- package/dist/lib/commands/mongo/SmartForwardCommandMongo.js +17 -0
- package/dist/lib/commands/mongo/SmartForwardCommandMongo.js.map +1 -0
- package/dist/lib/commands/mongo/SmartReplyCommandMongo.js +17 -0
- package/dist/lib/commands/mongo/SmartReplyCommandMongo.js.map +1 -0
- package/dist/lib/commands/mongo/SyncCommandMongo.js +26 -0
- package/dist/lib/commands/mongo/SyncCommandMongo.js.map +1 -0
- package/dist/lib/commands/mongo/index.js +14 -0
- package/dist/lib/commands/mongo/index.js.map +1 -0
- package/dist/lib/commands/sql/FolderSyncCommandSQL.js +16 -0
- package/dist/lib/commands/sql/FolderSyncCommandSQL.js.map +1 -0
- package/dist/lib/commands/sql/GetItemEstimateCommandSQL.js +21 -0
- package/dist/lib/commands/sql/GetItemEstimateCommandSQL.js.map +1 -0
- package/dist/lib/commands/sql/ItemOperationsCommandSQL.js +18 -0
- package/dist/lib/commands/sql/ItemOperationsCommandSQL.js.map +1 -0
- package/dist/lib/commands/sql/MeetingResponseCommandSQL.js +17 -0
- package/dist/lib/commands/sql/MeetingResponseCommandSQL.js.map +1 -0
- package/dist/lib/commands/sql/MoveItemsCommandSQL.js +17 -0
- package/dist/lib/commands/sql/MoveItemsCommandSQL.js.map +1 -0
- package/dist/lib/commands/sql/ResolveRecipientsCommandSQL.js +20 -0
- package/dist/lib/commands/sql/ResolveRecipientsCommandSQL.js.map +1 -0
- package/dist/lib/commands/sql/SearchCommandSQL.js +20 -0
- package/dist/lib/commands/sql/SearchCommandSQL.js.map +1 -0
- package/dist/lib/commands/sql/SendMailCommandSQL.js +17 -0
- package/dist/lib/commands/sql/SendMailCommandSQL.js.map +1 -0
- package/dist/lib/commands/sql/SettingsCommandSQL.js +16 -0
- package/dist/lib/commands/sql/SettingsCommandSQL.js.map +1 -0
- package/dist/lib/commands/sql/SmartForwardCommandSQL.js +17 -0
- package/dist/lib/commands/sql/SmartForwardCommandSQL.js.map +1 -0
- package/dist/lib/commands/sql/SmartReplyCommandSQL.js +17 -0
- package/dist/lib/commands/sql/SmartReplyCommandSQL.js.map +1 -0
- package/dist/lib/commands/sql/SyncCommandSQL.js +26 -0
- package/dist/lib/commands/sql/SyncCommandSQL.js.map +1 -0
- package/dist/lib/commands/sql/index.js +14 -0
- package/dist/lib/commands/sql/index.js.map +1 -0
- package/dist/lib/index.js +56 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/mongo/DeviceSyncStateRouteMongo.js +16 -0
- package/dist/lib/mongo/DeviceSyncStateRouteMongo.js.map +1 -0
- package/dist/lib/mongo/EasRouteMongo.js +51 -0
- package/dist/lib/mongo/EasRouteMongo.js.map +1 -0
- package/dist/lib/mongo/index.js +8 -0
- package/dist/lib/mongo/index.js.map +1 -0
- package/dist/lib/mongo.js +6 -0
- package/dist/lib/mongo.js.map +1 -0
- package/dist/lib/sql/DeviceSyncStateRouteSQL.js +16 -0
- package/dist/lib/sql/DeviceSyncStateRouteSQL.js.map +1 -0
- package/dist/lib/sql/EasRouteSQL.js +50 -0
- package/dist/lib/sql/EasRouteSQL.js.map +1 -0
- package/dist/lib/sql/index.js +8 -0
- package/dist/lib/sql/index.js.map +1 -0
- package/dist/lib/sql.js +6 -0
- package/dist/lib/sql.js.map +1 -0
- package/dist/types/BaseDeviceSyncStateRoute.d.ts +34 -0
- package/dist/types/BaseEasRoute.d.ts +64 -0
- package/dist/types/CompactDateTime.d.ts +22 -0
- package/dist/types/EasCommandHandler.d.ts +59 -0
- package/dist/types/EasSyncKeyUtils.d.ts +89 -0
- package/dist/types/adapters/CalendarSyncAdapter.d.ts +52 -0
- package/dist/types/adapters/ContactsSyncAdapter.d.ts +33 -0
- package/dist/types/adapters/EasCollectionSyncAdapter.d.ts +52 -0
- package/dist/types/adapters/EmailSyncAdapter.d.ts +53 -0
- package/dist/types/adapters/TasksSyncAdapter.d.ts +32 -0
- package/dist/types/codec/WbxmlCodePages.d.ts +45 -0
- package/dist/types/codec/WbxmlDecoder.d.ts +33 -0
- package/dist/types/codec/WbxmlElement.d.ts +43 -0
- package/dist/types/codec/WbxmlEncoder.d.ts +20 -0
- package/dist/types/commands/ComposeMailCommand.d.ts +50 -0
- package/dist/types/commands/FolderSyncCommand.d.ts +25 -0
- package/dist/types/commands/GetItemEstimateCommand.d.ts +43 -0
- package/dist/types/commands/ItemOperationsCommand.d.ts +72 -0
- package/dist/types/commands/MeetingResponseCommand.d.ts +32 -0
- package/dist/types/commands/MoveItemsCommand.d.ts +29 -0
- package/dist/types/commands/PingCommand.d.ts +33 -0
- package/dist/types/commands/ProvisionCommand.d.ts +54 -0
- package/dist/types/commands/ResolveRecipientsCommand.d.ts +34 -0
- package/dist/types/commands/SearchCommand.d.ts +39 -0
- package/dist/types/commands/SendMailCommand.d.ts +10 -0
- package/dist/types/commands/SettingsCommand.d.ts +37 -0
- package/dist/types/commands/SmartForwardCommand.d.ts +17 -0
- package/dist/types/commands/SmartReplyCommand.d.ts +17 -0
- package/dist/types/commands/SyncCommand.d.ts +100 -0
- package/dist/types/commands/mongo/FolderSyncCommandMongo.d.ts +8 -0
- package/dist/types/commands/mongo/GetItemEstimateCommandMongo.d.ts +7 -0
- package/dist/types/commands/mongo/ItemOperationsCommandMongo.d.ts +9 -0
- package/dist/types/commands/mongo/MeetingResponseCommandMongo.d.ts +8 -0
- package/dist/types/commands/mongo/MoveItemsCommandMongo.d.ts +8 -0
- package/dist/types/commands/mongo/ResolveRecipientsCommandMongo.d.ts +8 -0
- package/dist/types/commands/mongo/SearchCommandMongo.d.ts +8 -0
- package/dist/types/commands/mongo/SendMailCommandMongo.d.ts +8 -0
- package/dist/types/commands/mongo/SettingsCommandMongo.d.ts +7 -0
- package/dist/types/commands/mongo/SmartForwardCommandMongo.d.ts +8 -0
- package/dist/types/commands/mongo/SmartReplyCommandMongo.d.ts +8 -0
- package/dist/types/commands/mongo/SyncCommandMongo.d.ts +8 -0
- package/dist/types/commands/mongo/index.d.ts +9 -0
- package/dist/types/commands/sql/FolderSyncCommandSQL.d.ts +8 -0
- package/dist/types/commands/sql/GetItemEstimateCommandSQL.d.ts +7 -0
- package/dist/types/commands/sql/ItemOperationsCommandSQL.d.ts +9 -0
- package/dist/types/commands/sql/MeetingResponseCommandSQL.d.ts +8 -0
- package/dist/types/commands/sql/MoveItemsCommandSQL.d.ts +8 -0
- package/dist/types/commands/sql/ResolveRecipientsCommandSQL.d.ts +8 -0
- package/dist/types/commands/sql/SearchCommandSQL.d.ts +8 -0
- package/dist/types/commands/sql/SendMailCommandSQL.d.ts +8 -0
- package/dist/types/commands/sql/SettingsCommandSQL.d.ts +7 -0
- package/dist/types/commands/sql/SmartForwardCommandSQL.d.ts +8 -0
- package/dist/types/commands/sql/SmartReplyCommandSQL.d.ts +8 -0
- package/dist/types/commands/sql/SyncCommandSQL.d.ts +8 -0
- package/dist/types/commands/sql/index.d.ts +9 -0
- package/dist/types/index.d.ts +51 -0
- package/dist/types/mongo/DeviceSyncStateRouteMongo.d.ts +8 -0
- package/dist/types/mongo/EasRouteMongo.d.ts +14 -0
- package/dist/types/mongo/index.d.ts +3 -0
- package/dist/types/mongo.d.ts +1 -0
- package/dist/types/sql/DeviceSyncStateRouteSQL.d.ts +8 -0
- package/dist/types/sql/EasRouteSQL.d.ts +13 -0
- package/dist/types/sql/index.d.ts +3 -0
- package/dist/types/sql.d.ts +1 -0
- package/package.json +94 -0
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
11
|
+
// Copyright (C) 2026 Jean-Philippe Steinmetz. All rights reserved.
|
|
12
|
+
// SPDX-License-Identifier: MPL-2.0
|
|
13
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
14
|
+
import { simpleParser } from "mailparser";
|
|
15
|
+
import { ApiError, ObjectDecorators } from "@rapidrest/core";
|
|
16
|
+
import { ACLAction, ACLUtils, ApiErrorMessages, ApiErrors, RepoUtils } from "@rapidrest/service-core";
|
|
17
|
+
import { RecoverableRepoUtils } from "@rapidmx/restapi";
|
|
18
|
+
import { WbxmlCodePage } from "../codec/WbxmlCodePages.js";
|
|
19
|
+
import { childText, element, findChild, findChildren, opaqueElement, textElement } from "../codec/WbxmlElement.js";
|
|
20
|
+
import { decodeConversationId } from "../adapters/EmailSyncAdapter.js";
|
|
21
|
+
const { Config, Init, Inject } = ObjectDecorators;
|
|
22
|
+
/** Caps how many messages `emptyFolderContents`/`moveConversation` process per backing `find()`/delete-batch
|
|
23
|
+
* round - keeps memory bounded and, for `emptyFolderContents`, lets an arbitrarily large folder still be fully
|
|
24
|
+
* emptied via repeated batches rather than one unbounded query. */
|
|
25
|
+
const DEFAULT_BATCH_SIZE = 500;
|
|
26
|
+
/** Truncates UTF-8 text to at most `maxBytes` bytes without splitting a multi-byte character in half - backs
|
|
27
|
+
* off past any trailing UTF-8 continuation byte (`10xxxxxx`) before decoding back to a string. Only ever
|
|
28
|
+
* called once the caller has already confirmed the text exceeds `maxBytes` - trusts that rather than
|
|
29
|
+
* re-checking it here. */
|
|
30
|
+
function truncateUtf8(text, maxBytes) {
|
|
31
|
+
const buf = Buffer.from(text, "utf8");
|
|
32
|
+
let end = maxBytes;
|
|
33
|
+
while (end > 0 && (buf[end] & 0xc0) === 0x80) {
|
|
34
|
+
end--;
|
|
35
|
+
}
|
|
36
|
+
return buf.subarray(0, end).toString("utf8");
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Handles EAS `ItemOperations`: `Fetch` (a `Message`'s full body or an `Attachment`'s binary content, by the
|
|
40
|
+
* same `ServerId`/uid this library already exposes elsewhere) and `EmptyFolderContents`.
|
|
41
|
+
*
|
|
42
|
+
* Per the published [MS-ASCMD] `ItemOperations` request schema (confirmed directly, not assumed), the command
|
|
43
|
+
* is a strict choice of exactly three operations - `Fetch` (unbounded), `EmptyFolderContents`, and `Move` -
|
|
44
|
+
* with no fourth "write a new item" capability anywhere in it; `Store` is a required *child* of `Fetch`
|
|
45
|
+
* (`"Mailbox"` or `"DocumentLibrary"`, the same store-selector role it plays in `SearchCommand`), not a
|
|
46
|
+
* separate write/upload command as its name might suggest.
|
|
47
|
+
*
|
|
48
|
+
* **Pragmatic subset, deliberately not the full MS-ASCMD `ItemOperations` semantics**:
|
|
49
|
+
* - `Move` moves an entire *conversation* (by `ConversationId`, opaque binary - see `EmailSyncAdapter`'s
|
|
50
|
+
* `encodeConversationId`/`decodeConversationId`) to a destination folder - unrelated to the standalone
|
|
51
|
+
* `MoveItems` command's per-message `SrcFldId`/`SrcMsgId`/`DstFldId` shape. Every `Message` sharing the
|
|
52
|
+
* decoded `conversationId` across the whole mailbox (not just one folder) that the caller has `UPDATE` on is
|
|
53
|
+
* relocated to `DstFldId`; one lacking permission is silently skipped rather than failing the whole move (a
|
|
54
|
+
* conversation can legitimately span folders the caller doesn't control, e.g. a shared mailbox's Inbox). An
|
|
55
|
+
* optional `MoveAlways` (a hint to keep auto-moving future messages in this conversation) is accepted but not
|
|
56
|
+
* acted on - this library's `MailFilterRule` has no conversation-scoped condition to key an ongoing rule off
|
|
57
|
+
* of, a documented simplification, not silent data loss (the move itself still happens).
|
|
58
|
+
* - `Store: "DocumentLibrary"` is rejected per-`Fetch` - matches `SearchCommand`'s own GAL-only scope decision;
|
|
59
|
+
* this library has no document-library model.
|
|
60
|
+
* - A `Fetch` failure (not found, no permission, malformed) aborts the whole request via an HTTP-level error
|
|
61
|
+
* rather than an embedded per-`Fetch` `Status` code the way `Sync`/`MoveItems`/`ResolveRecipients` report
|
|
62
|
+
* their own per-item failures - a deliberate, documented simplification carried over unchanged from this
|
|
63
|
+
* command's original single-`Fetch` design, not a new gap introduced by adding multi-`Fetch` support.
|
|
64
|
+
* - Only the "inline" delivery method is used (content embedded directly in the WBXML response) - the real
|
|
65
|
+
* spec's "multipart" alternative (WBXML as one part, binary content as a separate part) is not implemented;
|
|
66
|
+
* every attachment this library's own `ScanPipeline` already accepts is assumed to fit comfortably in memory
|
|
67
|
+
* for one response, the same assumption `BaseAttachmentRoute.download()` already makes.
|
|
68
|
+
* - `Options/BodyPreference`'s `Type`/`TruncationSize` are honored for a `Message` body fetch (plain text,
|
|
69
|
+
* HTML, or - `Type 4` - the raw MIME source verbatim); byte-range fetching (`Range`) is not implemented.
|
|
70
|
+
* - `EmptyFolderContents`'s `DeleteSubFolders` option is rejected outright rather than silently ignored -
|
|
71
|
+
* recursive subfolder deletion is out of scope for this pragmatic subset; emptying a single folder's own
|
|
72
|
+
* `Message`s is the common case this implements.
|
|
73
|
+
*
|
|
74
|
+
* `folderClass`/`messageClass`/`attachmentClass` are supplied by the Mongo/SQL concrete subclasses.
|
|
75
|
+
*
|
|
76
|
+
* @author Jean-Philippe Steinmetz
|
|
77
|
+
*/
|
|
78
|
+
export class ItemOperationsCommand {
|
|
79
|
+
constructor() {
|
|
80
|
+
this.command = "ItemOperations";
|
|
81
|
+
this.maxFetchesPerRequest = 25;
|
|
82
|
+
this.batchSize = DEFAULT_BATCH_SIZE;
|
|
83
|
+
}
|
|
84
|
+
async init() {
|
|
85
|
+
this.folderRepo = await this._objectFactory.newInstance(RepoUtils, {
|
|
86
|
+
name: this.folderClass.name,
|
|
87
|
+
args: [this.folderClass],
|
|
88
|
+
});
|
|
89
|
+
this.messageRepo = await this._objectFactory.newInstance(RecoverableRepoUtils, {
|
|
90
|
+
name: this.messageClass.name,
|
|
91
|
+
args: [this.messageClass],
|
|
92
|
+
});
|
|
93
|
+
this.attachmentRepo = await this._objectFactory.newInstance(RepoUtils, {
|
|
94
|
+
name: this.attachmentClass.name,
|
|
95
|
+
args: [this.attachmentClass],
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
async handle(ctx) {
|
|
99
|
+
if (!this.folderRepo || !this.messageRepo || !this.attachmentRepo || !this.blobStore) {
|
|
100
|
+
throw new ApiError(ApiErrors.INTERNAL_ERROR, 500, ApiErrorMessages.INTERNAL_ERROR);
|
|
101
|
+
}
|
|
102
|
+
const fetchEls = ctx.request ? findChildren(ctx.request, "Fetch") : [];
|
|
103
|
+
const emptyEl = ctx.request ? findChild(ctx.request, "EmptyFolderContents") : undefined;
|
|
104
|
+
const moveEl = ctx.request ? findChild(ctx.request, "Move") : undefined;
|
|
105
|
+
if (fetchEls.length === 0 && !emptyEl && !moveEl) {
|
|
106
|
+
throw new ApiError(ApiErrors.INVALID_REQUEST, 400, ApiErrorMessages.INVALID_REQUEST);
|
|
107
|
+
}
|
|
108
|
+
// Each Fetch's content (a full message body or a whole attachment) is buffered in memory and assumed
|
|
109
|
+
// to individually fit comfortably (see this class's own doc comment) - with no cap on the *count* of
|
|
110
|
+
// Fetches, a single request could still force many such buffers to be built and held at once (even
|
|
111
|
+
// repeated fetches of the caller's own single large item), amplifying memory/IO cost far past what one
|
|
112
|
+
// real device round-trip needs. Rejected outright rather than silently truncated, matching this
|
|
113
|
+
// command's own precedent for `DeleteSubFolders`/`DocumentLibrary`.
|
|
114
|
+
if (fetchEls.length > this.maxFetchesPerRequest) {
|
|
115
|
+
throw new ApiError(ApiErrors.INVALID_REQUEST, 400, `ItemOperations supports at most ${this.maxFetchesPerRequest} Fetch elements per request.`);
|
|
116
|
+
}
|
|
117
|
+
const responseChildren = [];
|
|
118
|
+
for (const fetchEl of fetchEls) {
|
|
119
|
+
const store = childText(fetchEl, "Store");
|
|
120
|
+
if (store === "DocumentLibrary") {
|
|
121
|
+
throw new ApiError(ApiErrors.INVALID_REQUEST, 400, "DocumentLibrary fetches are not supported.");
|
|
122
|
+
}
|
|
123
|
+
const fileReference = childText(fetchEl, "FileReference");
|
|
124
|
+
const serverId = childText(fetchEl, "ServerId");
|
|
125
|
+
const optionsEl = findChild(fetchEl, "Options");
|
|
126
|
+
responseChildren.push(fileReference
|
|
127
|
+
? await this.fetchAttachment(ctx, fileReference)
|
|
128
|
+
: await this.fetchMessage(ctx, serverId, optionsEl));
|
|
129
|
+
}
|
|
130
|
+
if (emptyEl) {
|
|
131
|
+
responseChildren.push(await this.emptyFolderContents(ctx, emptyEl));
|
|
132
|
+
}
|
|
133
|
+
if (moveEl) {
|
|
134
|
+
responseChildren.push(await this.moveConversation(ctx, moveEl));
|
|
135
|
+
}
|
|
136
|
+
return element(WbxmlCodePage.ItemOperations, "ItemOperations", [
|
|
137
|
+
textElement(WbxmlCodePage.ItemOperations, "Status", "1"),
|
|
138
|
+
element(WbxmlCodePage.ItemOperations, "Response", responseChildren),
|
|
139
|
+
]);
|
|
140
|
+
}
|
|
141
|
+
async fetchMessage(ctx, serverId, optionsEl) {
|
|
142
|
+
if (!serverId) {
|
|
143
|
+
throw new ApiError(ApiErrors.INVALID_REQUEST, 400, "Fetch requires either a ServerId or a FileReference.");
|
|
144
|
+
}
|
|
145
|
+
const message = await this.messageRepo.findOne(serverId, { ignoreACL: true });
|
|
146
|
+
if (!message) {
|
|
147
|
+
throw new ApiError(ApiErrors.NOT_FOUND, 404, ApiErrorMessages.NOT_FOUND);
|
|
148
|
+
}
|
|
149
|
+
if (!(await this.aclUtils.hasPermission(ctx.user, message.folderUid, ACLAction.READ))) {
|
|
150
|
+
throw new ApiError(ApiErrors.AUTH_PERMISSION_FAILURE, 403, ApiErrorMessages.AUTH_PERMISSION_FAILURE);
|
|
151
|
+
}
|
|
152
|
+
const bodyPreferenceEl = optionsEl ? findChild(optionsEl, "BodyPreference") : undefined;
|
|
153
|
+
const requestedType = bodyPreferenceEl ? childText(bodyPreferenceEl, "Type") : undefined;
|
|
154
|
+
const truncationSizeText = bodyPreferenceEl ? childText(bodyPreferenceEl, "TruncationSize") : undefined;
|
|
155
|
+
const truncationSize = truncationSizeText !== undefined ? Number(truncationSizeText) : undefined;
|
|
156
|
+
// Prefer the already-sanitized HTML body (script/active-content stripped by ScanPipeline at
|
|
157
|
+
// ingestion/send time) over re-deriving anything from the raw MIME - the same preference
|
|
158
|
+
// `Message.sanitizedHtmlBlobKey`'s own doc comment describes for any renderer. `Type 4` (MIME) is an
|
|
159
|
+
// explicit client request for the verbatim raw source instead, honored regardless of that preference.
|
|
160
|
+
let bodyType = "1";
|
|
161
|
+
let bodyText;
|
|
162
|
+
if (requestedType === "4") {
|
|
163
|
+
bodyType = "4";
|
|
164
|
+
bodyText = (await this.blobStore.get(message.bodyBlobKey)).toString("utf-8");
|
|
165
|
+
}
|
|
166
|
+
else if (message.sanitizedHtmlBlobKey) {
|
|
167
|
+
bodyType = "2";
|
|
168
|
+
bodyText = (await this.blobStore.get(message.sanitizedHtmlBlobKey)).toString("utf-8");
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
const raw = await this.blobStore.get(message.bodyBlobKey);
|
|
172
|
+
const parsed = await simpleParser(raw);
|
|
173
|
+
bodyText = parsed.text ?? "";
|
|
174
|
+
}
|
|
175
|
+
// Per MS-ASAIRSYNCBASE, EstimatedDataSize reports the body's size BEFORE any truncation was applied
|
|
176
|
+
// (so the client knows how much more content exists beyond what it received) - captured here, before
|
|
177
|
+
// truncateUtf8() below reassigns bodyText to the shorter value.
|
|
178
|
+
const estimatedDataSize = Buffer.byteLength(bodyText, "utf8");
|
|
179
|
+
let truncated = false;
|
|
180
|
+
if (truncationSize !== undefined && Number.isFinite(truncationSize) && estimatedDataSize > truncationSize) {
|
|
181
|
+
bodyText = truncateUtf8(bodyText, truncationSize);
|
|
182
|
+
truncated = true;
|
|
183
|
+
}
|
|
184
|
+
return element(WbxmlCodePage.ItemOperations, "Fetch", [
|
|
185
|
+
textElement(WbxmlCodePage.ItemOperations, "Status", "1"),
|
|
186
|
+
textElement(WbxmlCodePage.AirSync, "Class", "Email"),
|
|
187
|
+
textElement(WbxmlCodePage.AirSync, "CollectionId", message.folderUid),
|
|
188
|
+
textElement(WbxmlCodePage.AirSync, "ServerId", serverId),
|
|
189
|
+
element(WbxmlCodePage.ItemOperations, "Properties", [
|
|
190
|
+
element(WbxmlCodePage.AirSyncBase, "Body", [
|
|
191
|
+
textElement(WbxmlCodePage.AirSyncBase, "Type", bodyType),
|
|
192
|
+
textElement(WbxmlCodePage.AirSyncBase, "EstimatedDataSize", String(estimatedDataSize)),
|
|
193
|
+
textElement(WbxmlCodePage.AirSyncBase, "Truncated", truncated ? "1" : "0"),
|
|
194
|
+
textElement(WbxmlCodePage.AirSyncBase, "Data", bodyText),
|
|
195
|
+
]),
|
|
196
|
+
]),
|
|
197
|
+
]);
|
|
198
|
+
}
|
|
199
|
+
async emptyFolderContents(ctx, emptyEl) {
|
|
200
|
+
// Reuses AirSync's own `CollectionId` (the same tag `Sync`/`Fetch` responses already reference a
|
|
201
|
+
// folder by) rather than a page-specific tag - `WbxmlCodePage.ItemOperations` has no `FolderId` token
|
|
202
|
+
// of its own at all, confirmed against its own tag table.
|
|
203
|
+
const folderUid = childText(emptyEl, "CollectionId");
|
|
204
|
+
if (!folderUid) {
|
|
205
|
+
throw new ApiError(ApiErrors.INVALID_REQUEST, 400, "EmptyFolderContents requires a CollectionId.");
|
|
206
|
+
}
|
|
207
|
+
const optionsEl = findChild(emptyEl, "Options");
|
|
208
|
+
if (optionsEl && findChild(optionsEl, "DeleteSubFolders")) {
|
|
209
|
+
// Documented gap, not silently ignored - see this class's own doc comment.
|
|
210
|
+
throw new ApiError(ApiErrors.INVALID_REQUEST, 400, "DeleteSubFolders is not supported.");
|
|
211
|
+
}
|
|
212
|
+
if (!(await this.aclUtils.hasPermission(ctx.user, folderUid, ACLAction.DELETE))) {
|
|
213
|
+
throw new ApiError(ApiErrors.AUTH_PERMISSION_FAILURE, 403, ApiErrorMessages.AUTH_PERMISSION_FAILURE);
|
|
214
|
+
}
|
|
215
|
+
// Processed in bounded batches rather than one unbounded `find()` - a folder with a very large number
|
|
216
|
+
// of messages would otherwise force the whole set into memory at once. Deletes within a batch still run
|
|
217
|
+
// sequentially, not via `Promise.all` - the SQL backend (`better-sqlite3`) shares one connection per
|
|
218
|
+
// request and each `delete()` opens its own transaction, so concurrent deletes fail outright with
|
|
219
|
+
// "cannot start a transaction within a transaction" (confirmed against real SQL test failures, not
|
|
220
|
+
// theoretical). Looping (rather than a single pass) still empties the folder completely, however large -
|
|
221
|
+
// a deleted row no longer matches this same `find()` (soft-deleted rows are excluded from an ordinary
|
|
222
|
+
// query by default), so the loop always terminates once truly empty.
|
|
223
|
+
for (;;) {
|
|
224
|
+
const batch = await this.messageRepo.find({ folderUid, limit: this.batchSize }, {
|
|
225
|
+
ignoreACL: true,
|
|
226
|
+
limit: this.batchSize,
|
|
227
|
+
});
|
|
228
|
+
if (batch.length === 0) {
|
|
229
|
+
break;
|
|
230
|
+
}
|
|
231
|
+
for (const message of batch) {
|
|
232
|
+
await this.messageRepo.delete(message.uid, { ignoreACL: true, user: ctx.user });
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return element(WbxmlCodePage.ItemOperations, "EmptyFolderContents", [
|
|
236
|
+
textElement(WbxmlCodePage.ItemOperations, "Status", "1"),
|
|
237
|
+
]);
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Handles `ItemOperations`' `Move`: relocates every `Message` sharing a decoded `ConversationId` to
|
|
241
|
+
* `DstFldId`. Unlike `emptyFolderContents`/`fetchMessage` (which fail the whole request via a thrown
|
|
242
|
+
* `ApiError`), this reports failure through the embedded `Status` the same way `MoveItemsCommand` does for
|
|
243
|
+
* its own per-item results - a malformed/unresolvable `Move` is a normal outcome for this operation, not an
|
|
244
|
+
* exceptional one. Reports failure (`Status 3`) if not even one message was actually moved - including when
|
|
245
|
+
* every message sharing the conversation sits in a folder the caller lacks `UPDATE` on, which would
|
|
246
|
+
* otherwise silently fall through to a false "success" with nothing having moved.
|
|
247
|
+
*/
|
|
248
|
+
async moveConversation(ctx, moveEl) {
|
|
249
|
+
const conversationIdEl = findChild(moveEl, "ConversationId");
|
|
250
|
+
const dstFldId = childText(moveEl, "DstFldId");
|
|
251
|
+
if (!conversationIdEl?.opaque || !dstFldId) {
|
|
252
|
+
return this.moveResponse("3");
|
|
253
|
+
}
|
|
254
|
+
const destFolder = await this.folderRepo.findOne(dstFldId, { ignoreACL: true });
|
|
255
|
+
if (!destFolder ||
|
|
256
|
+
destFolder.mailboxUid !== ctx.mailboxUid ||
|
|
257
|
+
!(await this.aclUtils.hasPermission(ctx.user, dstFldId, ACLAction.CREATE))) {
|
|
258
|
+
return this.moveResponse("3");
|
|
259
|
+
}
|
|
260
|
+
const conversationId = decodeConversationId(conversationIdEl.opaque);
|
|
261
|
+
// Capped rather than an unbounded `find()` - an unusually long-running thread could otherwise return an
|
|
262
|
+
// unbounded number of rows for one request.
|
|
263
|
+
const messages = await this.messageRepo.find({ mailboxUid: ctx.mailboxUid, conversationId, limit: this.batchSize }, { ignoreACL: true, limit: this.batchSize });
|
|
264
|
+
if (messages.length === 0) {
|
|
265
|
+
return this.moveResponse("3");
|
|
266
|
+
}
|
|
267
|
+
// The permission checks (reads) are independent and safe to run concurrently, but the updates
|
|
268
|
+
// themselves are not: the SQL backend (`better-sqlite3`) shares one connection per request and each
|
|
269
|
+
// `update()` opens its own transaction, so concurrent updates fail outright with "cannot start a
|
|
270
|
+
// transaction within a transaction" (confirmed against real SQL test failures, not theoretical) -
|
|
271
|
+
// updates run sequentially in a plain loop instead.
|
|
272
|
+
const permitted = await Promise.all(messages.map((message) => this.aclUtils.hasPermission(ctx.user, message.folderUid, ACLAction.UPDATE)));
|
|
273
|
+
let movedAny = false;
|
|
274
|
+
for (let i = 0; i < messages.length; i++) {
|
|
275
|
+
if (!permitted[i]) {
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
const message = messages[i];
|
|
279
|
+
await this.messageRepo.update({ uid: message.uid, version: message.version, folderUid: dstFldId }, message, { ignoreACL: true, user: ctx.user });
|
|
280
|
+
movedAny = true;
|
|
281
|
+
}
|
|
282
|
+
if (!movedAny) {
|
|
283
|
+
return this.moveResponse("3");
|
|
284
|
+
}
|
|
285
|
+
return element(WbxmlCodePage.ItemOperations, "Move", [
|
|
286
|
+
textElement(WbxmlCodePage.ItemOperations, "Status", "1"),
|
|
287
|
+
textElement(WbxmlCodePage.ItemOperations, "DstFldId", dstFldId),
|
|
288
|
+
opaqueElement(WbxmlCodePage.ItemOperations, "ConversationId", conversationIdEl.opaque),
|
|
289
|
+
]);
|
|
290
|
+
}
|
|
291
|
+
moveResponse(status) {
|
|
292
|
+
return element(WbxmlCodePage.ItemOperations, "Move", [textElement(WbxmlCodePage.ItemOperations, "Status", status)]);
|
|
293
|
+
}
|
|
294
|
+
async fetchAttachment(ctx, fileReference) {
|
|
295
|
+
const attachment = await this.attachmentRepo.findOne(fileReference, { ignoreACL: true });
|
|
296
|
+
if (!attachment) {
|
|
297
|
+
throw new ApiError(ApiErrors.NOT_FOUND, 404, ApiErrorMessages.NOT_FOUND);
|
|
298
|
+
}
|
|
299
|
+
if (!(await this.aclUtils.hasPermission(ctx.user, attachment.folderUid, ACLAction.READ))) {
|
|
300
|
+
throw new ApiError(ApiErrors.AUTH_PERMISSION_FAILURE, 403, ApiErrorMessages.AUTH_PERMISSION_FAILURE);
|
|
301
|
+
}
|
|
302
|
+
const content = await this.blobStore.get(attachment.blobKey);
|
|
303
|
+
return element(WbxmlCodePage.ItemOperations, "Fetch", [
|
|
304
|
+
textElement(WbxmlCodePage.ItemOperations, "Status", "1"),
|
|
305
|
+
textElement(WbxmlCodePage.AirSyncBase, "FileReference", fileReference),
|
|
306
|
+
element(WbxmlCodePage.ItemOperations, "Properties", [
|
|
307
|
+
textElement(WbxmlCodePage.AirSyncBase, "ContentType", attachment.mimeType),
|
|
308
|
+
// "Inline" delivery per MS-ASCMD: binary content is base64-encoded and embedded directly in the
|
|
309
|
+
// WBXML, rather than this library's own opaque/binary element type.
|
|
310
|
+
textElement(WbxmlCodePage.ItemOperations, "Data", content.toString("base64")),
|
|
311
|
+
]),
|
|
312
|
+
]);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
__decorate([
|
|
316
|
+
Inject("BlobStore"),
|
|
317
|
+
__metadata("design:type", Object)
|
|
318
|
+
], ItemOperationsCommand.prototype, "blobStore", void 0);
|
|
319
|
+
__decorate([
|
|
320
|
+
Inject(ACLUtils),
|
|
321
|
+
__metadata("design:type", ACLUtils)
|
|
322
|
+
], ItemOperationsCommand.prototype, "aclUtils", void 0);
|
|
323
|
+
__decorate([
|
|
324
|
+
Config("mail:eas:itemoperations_max_fetch", 25),
|
|
325
|
+
__metadata("design:type", Number)
|
|
326
|
+
], ItemOperationsCommand.prototype, "maxFetchesPerRequest", void 0);
|
|
327
|
+
__decorate([
|
|
328
|
+
Config("mail:eas:itemoperations_batch_size", DEFAULT_BATCH_SIZE),
|
|
329
|
+
__metadata("design:type", Number)
|
|
330
|
+
], ItemOperationsCommand.prototype, "batchSize", void 0);
|
|
331
|
+
__decorate([
|
|
332
|
+
Init,
|
|
333
|
+
__metadata("design:type", Function),
|
|
334
|
+
__metadata("design:paramtypes", []),
|
|
335
|
+
__metadata("design:returntype", Promise)
|
|
336
|
+
], ItemOperationsCommand.prototype, "init", null);
|
|
337
|
+
//# sourceMappingURL=ItemOperationsCommand.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ItemOperationsCommand.js","sourceRoot":"","sources":["../../../src/commands/ItemOperationsCommand.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
11
|
+
// Copyright (C) 2026 Jean-Philippe Steinmetz. All rights reserved.
|
|
12
|
+
// SPDX-License-Identifier: MPL-2.0
|
|
13
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
14
|
+
import { ApiError, ObjectDecorators } from "@rapidrest/core";
|
|
15
|
+
import { ACLAction, ACLUtils, ApiErrorMessages, ApiErrors, RepoUtils } from "@rapidrest/service-core";
|
|
16
|
+
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;
|
|
20
|
+
/** MS-ASCMD `UserResponse`: 1=Accepted, 2=Tentatively accepted, 3=Declined. */
|
|
21
|
+
const USER_RESPONSE_STATUS = {
|
|
22
|
+
"1": AttendeeResponseStatus.ACCEPTED,
|
|
23
|
+
"2": AttendeeResponseStatus.TENTATIVE,
|
|
24
|
+
"3": AttendeeResponseStatus.DECLINED,
|
|
25
|
+
};
|
|
26
|
+
const USER_RESPONSE_DECLINED = "3";
|
|
27
|
+
/**
|
|
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.
|
|
31
|
+
*
|
|
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.
|
|
41
|
+
*
|
|
42
|
+
* `calendarEventClass`/`mailboxClass` are supplied by the Mongo/SQL concrete subclasses.
|
|
43
|
+
*
|
|
44
|
+
* @author Jean-Philippe Steinmetz
|
|
45
|
+
*/
|
|
46
|
+
export class MeetingResponseCommand {
|
|
47
|
+
constructor() {
|
|
48
|
+
this.command = "MeetingResponse";
|
|
49
|
+
}
|
|
50
|
+
async init() {
|
|
51
|
+
this.calendarEventRepo = await this._objectFactory.newInstance(RecoverableRepoUtils, {
|
|
52
|
+
name: this.calendarEventClass.name,
|
|
53
|
+
args: [this.calendarEventClass],
|
|
54
|
+
});
|
|
55
|
+
this.mailboxRepo = await this._objectFactory.newInstance(RepoUtils, {
|
|
56
|
+
name: this.mailboxClass.name,
|
|
57
|
+
args: [this.mailboxClass],
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
async handle(ctx) {
|
|
61
|
+
if (!this.calendarEventRepo || !this.mailboxRepo) {
|
|
62
|
+
throw new ApiError(ApiErrors.INTERNAL_ERROR, 500, ApiErrorMessages.INTERNAL_ERROR);
|
|
63
|
+
}
|
|
64
|
+
const requestEl = ctx.request ? findChild(ctx.request, "Request") : undefined;
|
|
65
|
+
if (!requestEl) {
|
|
66
|
+
throw new ApiError(ApiErrors.INVALID_REQUEST, 400, ApiErrorMessages.INVALID_REQUEST);
|
|
67
|
+
}
|
|
68
|
+
const userResponse = childText(requestEl, "UserResponse");
|
|
69
|
+
const requestId = childText(requestEl, "RequestId");
|
|
70
|
+
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);
|
|
79
|
+
}
|
|
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);
|
|
83
|
+
}
|
|
84
|
+
const callerAddresses = new Set([mailbox.primarySmtpAddress.toLowerCase(), ...mailbox.aliasAddresses.map((a) => a.toLowerCase())]);
|
|
85
|
+
const attendeeIndex = event.attendees.findIndex((attendee) => callerAddresses.has(attendee.address.toLowerCase()));
|
|
86
|
+
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
|
+
]);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
__decorate([
|
|
111
|
+
Inject(ACLUtils),
|
|
112
|
+
__metadata("design:type", ACLUtils)
|
|
113
|
+
], MeetingResponseCommand.prototype, "aclUtils", void 0);
|
|
114
|
+
__decorate([
|
|
115
|
+
Init,
|
|
116
|
+
__metadata("design:type", Function),
|
|
117
|
+
__metadata("design:paramtypes", []),
|
|
118
|
+
__metadata("design:returntype", Promise)
|
|
119
|
+
], MeetingResponseCommand.prototype, "init", null);
|
|
120
|
+
//# sourceMappingURL=MeetingResponseCommand.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MeetingResponseCommand.js","sourceRoot":"","sources":["../../../src/commands/MeetingResponseCommand.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
11
|
+
// Copyright (C) 2026 Jean-Philippe Steinmetz. All rights reserved.
|
|
12
|
+
// SPDX-License-Identifier: MPL-2.0
|
|
13
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
14
|
+
import { ObjectDecorators } from "@rapidrest/core";
|
|
15
|
+
import { ACLAction, ACLUtils, RepoUtils } from "@rapidrest/service-core";
|
|
16
|
+
import { WbxmlCodePage } from "../codec/WbxmlCodePages.js";
|
|
17
|
+
import { childText, element, findChildren, textElement } from "../codec/WbxmlElement.js";
|
|
18
|
+
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";
|
|
25
|
+
/**
|
|
26
|
+
* Handles the standalone EAS `MoveItems` command: moves one or more `Message`s between folders in the caller's
|
|
27
|
+
* own mailbox by `ServerId`/`uid`.
|
|
28
|
+
*
|
|
29
|
+
* **Pragmatic subset**: `Message` only - `Contacts`/`Calendar`/`Tasks` moves are rare in practice (unlike
|
|
30
|
+
* `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
|
|
33
|
+
* library never mints a new identifier on move, unlike a real Exchange server, which sometimes does.
|
|
34
|
+
*
|
|
35
|
+
* `messageClass`/`folderClass` are supplied by the Mongo/SQL concrete subclasses.
|
|
36
|
+
*
|
|
37
|
+
* @author Jean-Philippe Steinmetz
|
|
38
|
+
*/
|
|
39
|
+
export class MoveItemsCommand {
|
|
40
|
+
constructor() {
|
|
41
|
+
this.command = "MoveItems";
|
|
42
|
+
}
|
|
43
|
+
async init() {
|
|
44
|
+
this.messageRepo = await this._objectFactory.newInstance(RepoUtils, {
|
|
45
|
+
name: this.messageClass.name,
|
|
46
|
+
args: [this.messageClass],
|
|
47
|
+
});
|
|
48
|
+
this.folderRepo = await this._objectFactory.newInstance(RepoUtils, {
|
|
49
|
+
name: this.folderClass.name,
|
|
50
|
+
args: [this.folderClass],
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
async handle(ctx) {
|
|
54
|
+
const moveEls = ctx.request ? findChildren(ctx.request, "Move") : [];
|
|
55
|
+
const responses = [];
|
|
56
|
+
for (const moveEl of moveEls) {
|
|
57
|
+
responses.push(await this.moveOne(ctx, moveEl));
|
|
58
|
+
}
|
|
59
|
+
return element(WbxmlCodePage.Move, "MoveItems", responses);
|
|
60
|
+
}
|
|
61
|
+
async moveOne(ctx, moveEl) {
|
|
62
|
+
const srcMsgId = childText(moveEl, "SrcMsgId");
|
|
63
|
+
const srcFldId = childText(moveEl, "SrcFldId");
|
|
64
|
+
const dstFldId = childText(moveEl, "DstFldId");
|
|
65
|
+
if (!srcMsgId || !srcFldId || !dstFldId) {
|
|
66
|
+
return this.responseElement(srcMsgId, STATUS_FAILURE, undefined);
|
|
67
|
+
}
|
|
68
|
+
const message = await this.messageRepo.findOne(srcMsgId, { ignoreACL: true });
|
|
69
|
+
// The client's claimed SrcFldId must match where the message actually lives - protects against a
|
|
70
|
+
// stale/mismatched client cache rather than trusting the claim outright.
|
|
71
|
+
if (!message || message.folderUid !== srcFldId) {
|
|
72
|
+
return this.responseElement(srcMsgId, STATUS_FAILURE, undefined);
|
|
73
|
+
}
|
|
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);
|
|
77
|
+
}
|
|
78
|
+
const destFolder = await this.folderRepo.findOne(dstFldId, { ignoreACL: true });
|
|
79
|
+
if (!destFolder || destFolder.mailboxUid !== ctx.mailboxUid) {
|
|
80
|
+
return this.responseElement(srcMsgId, STATUS_FAILURE, undefined);
|
|
81
|
+
}
|
|
82
|
+
await this.messageRepo.update({ uid: message.uid, version: message.version, folderUid: dstFldId }, message, { ignoreACL: true, user: ctx.user });
|
|
83
|
+
return this.responseElement(srcMsgId, STATUS_SUCCESS, message.uid);
|
|
84
|
+
}
|
|
85
|
+
responseElement(srcMsgId, status, dstMsgId) {
|
|
86
|
+
return element(WbxmlCodePage.Move, "Response", [
|
|
87
|
+
...(srcMsgId ? [textElement(WbxmlCodePage.Move, "SrcMsgId", srcMsgId)] : []),
|
|
88
|
+
textElement(WbxmlCodePage.Move, "Status", status),
|
|
89
|
+
...(dstMsgId ? [textElement(WbxmlCodePage.Move, "DstMsgId", dstMsgId)] : []),
|
|
90
|
+
]);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
__decorate([
|
|
94
|
+
Inject(ACLUtils),
|
|
95
|
+
__metadata("design:type", ACLUtils)
|
|
96
|
+
], MoveItemsCommand.prototype, "aclUtils", void 0);
|
|
97
|
+
__decorate([
|
|
98
|
+
Init,
|
|
99
|
+
__metadata("design:type", Function),
|
|
100
|
+
__metadata("design:paramtypes", []),
|
|
101
|
+
__metadata("design:returntype", Promise)
|
|
102
|
+
], MoveItemsCommand.prototype, "init", null);
|
|
103
|
+
//# sourceMappingURL=MoveItemsCommand.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MoveItemsCommand.js","sourceRoot":"","sources":["../../../src/commands/MoveItemsCommand.ts"],"names":[],"mappings":"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"}
|