@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,230 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
11
|
+
// Copyright (C) 2026 Jean-Philippe Steinmetz. All rights reserved.
|
|
12
|
+
// SPDX-License-Identifier: MPL-2.0
|
|
13
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
14
|
+
import * as crypto from "crypto";
|
|
15
|
+
import { ObjectDecorators } from "@rapidrest/core";
|
|
16
|
+
import { WbxmlCodePage } from "../codec/WbxmlCodePages.js";
|
|
17
|
+
import { childText, element, findChild, opaqueElement, textElement } from "../codec/WbxmlElement.js";
|
|
18
|
+
import { MessageImportance, RecipientType } from "@rapidmx/restapi";
|
|
19
|
+
const { Inject } = ObjectDecorators;
|
|
20
|
+
/** MS-ASEMAIL `Importance`: 0=Low, 1=Normal, 2=High. */
|
|
21
|
+
const IMPORTANCE_CODES = {
|
|
22
|
+
[MessageImportance.LOW]: "0",
|
|
23
|
+
[MessageImportance.NORMAL]: "1",
|
|
24
|
+
[MessageImportance.HIGH]: "2",
|
|
25
|
+
};
|
|
26
|
+
const IMPORTANCE_BY_CODE = {
|
|
27
|
+
"0": MessageImportance.LOW,
|
|
28
|
+
"1": MessageImportance.NORMAL,
|
|
29
|
+
"2": MessageImportance.HIGH,
|
|
30
|
+
};
|
|
31
|
+
/** MS-ASAIRSYNCBASE `Body.Type`: 1 = plain text, 2 = HTML, 3 = RTF, 4 = MIME. */
|
|
32
|
+
const BODY_TYPE_PLAIN_TEXT = "1";
|
|
33
|
+
/**
|
|
34
|
+
* Maps `Message` to/from the EAS `Sync` `Email` collection class (MS-ASEMAIL). Only a plain-text preview of the
|
|
35
|
+
* body is included here (`Message.bodyPreview`, always already loaded on the entity, `Truncated: 1`) rather
|
|
36
|
+
* than the full sanitized HTML body from the `BlobStore` - a real device fetches the full body on demand via
|
|
37
|
+
* `ItemOperations`' `Fetch` (see the architecture plan's command table), the same two-step "list, then fetch
|
|
38
|
+
* body" flow every real EAS client already implements for exactly this reason (bodies can be large; a sync
|
|
39
|
+
* window's Add/Change list shouldn't have to pull every one of them from blob storage up front).
|
|
40
|
+
*
|
|
41
|
+
* Also handles client-originated `Add`/`Change` for Drafts (`SyncCommand`'s own doc comment covers why this is
|
|
42
|
+
* the only `Email` write EAS itself allows) - a plain-text-only pragmatic subset: no HTML body, no attachments
|
|
43
|
+
* (mirrors `ComposeMailCommand`'s own already-documented attachment gap). `To`/`Cc`/`Bcc` (the latter MS-ASEMAIL2's
|
|
44
|
+
* own `Bcc` tag) are ghosted independently per recipient type, not as one combined group - a `Change` touching
|
|
45
|
+
* only one of them leaves the others untouched, carried over from `existing.recipients`.
|
|
46
|
+
*
|
|
47
|
+
* Emits MS-ASEMAIL2's `Email2:ConversationId` (read-only - no `fromApplicationData` handling, since EAS itself
|
|
48
|
+
* never lets a client set it) whenever `Message.conversationId` is populated, so a device's threaded-view UI can
|
|
49
|
+
* group messages the same way `BaseMessageRoute.conversations()` does server-side. See `encodeConversationId`'s
|
|
50
|
+
* own doc comment for the wire encoding, and `ItemOperationsCommand`'s `Move` handling for the one place this
|
|
51
|
+
* gets decoded back.
|
|
52
|
+
*
|
|
53
|
+
* @author Jean-Philippe Steinmetz
|
|
54
|
+
*/
|
|
55
|
+
export class EmailSyncAdapter {
|
|
56
|
+
constructor() {
|
|
57
|
+
this.collectionClass = "Email";
|
|
58
|
+
}
|
|
59
|
+
toApplicationData(message) {
|
|
60
|
+
const to = message.recipients.filter((r) => r.type === RecipientType.TO).map((r) => r.address);
|
|
61
|
+
const cc = message.recipients.filter((r) => r.type === RecipientType.CC).map((r) => r.address);
|
|
62
|
+
const bcc = message.recipients.filter((r) => r.type === RecipientType.BCC).map((r) => r.address);
|
|
63
|
+
return element(WbxmlCodePage.AirSync, "ApplicationData", [
|
|
64
|
+
textElement(WbxmlCodePage.Email, "Subject", message.subject),
|
|
65
|
+
textElement(WbxmlCodePage.Email, "From", formatAddress(message.from.address, message.from.displayName)),
|
|
66
|
+
...(to.length > 0 ? [textElement(WbxmlCodePage.Email, "To", to.join("; "))] : []),
|
|
67
|
+
...(cc.length > 0 ? [textElement(WbxmlCodePage.Email, "Cc", cc.join("; "))] : []),
|
|
68
|
+
...(bcc.length > 0 ? [textElement(WbxmlCodePage.Email2, "Bcc", bcc.join("; "))] : []),
|
|
69
|
+
textElement(WbxmlCodePage.Email, "DateReceived", message.receivedDate.toISOString()),
|
|
70
|
+
textElement(WbxmlCodePage.Email, "Importance", IMPORTANCE_CODES[message.importance]),
|
|
71
|
+
textElement(WbxmlCodePage.Email, "Read", message.flags.read ? "1" : "0"),
|
|
72
|
+
textElement(WbxmlCodePage.Email, "Flag", message.flags.flagged ? "1" : "0"),
|
|
73
|
+
element(WbxmlCodePage.AirSyncBase, "Body", [
|
|
74
|
+
textElement(WbxmlCodePage.AirSyncBase, "Type", BODY_TYPE_PLAIN_TEXT),
|
|
75
|
+
textElement(WbxmlCodePage.AirSyncBase, "EstimatedDataSize", String(Buffer.byteLength(message.bodyPreview, "utf8"))),
|
|
76
|
+
textElement(WbxmlCodePage.AirSyncBase, "Truncated", "1"),
|
|
77
|
+
textElement(WbxmlCodePage.AirSyncBase, "Data", message.bodyPreview),
|
|
78
|
+
]),
|
|
79
|
+
...(message.conversationId ? [opaqueElement(WbxmlCodePage.Email2, "ConversationId", encodeConversationId(message.conversationId))] : []),
|
|
80
|
+
]);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* `Message.bodyBlobKey` is documented (see the `Message` interface itself) as holding raw MIME "unmodified
|
|
84
|
+
* from ingestion/send" - `ItemOperationsCommand.fetchMessage` parses it with `simpleParser` unconditionally
|
|
85
|
+
* for every message, Draft or not. A Draft's plain-text body is therefore wrapped in a minimal valid
|
|
86
|
+
* RFC 5322 message here (via `buildPlainTextMime`) rather than stored as bare text, so that contract holds
|
|
87
|
+
* for every consumer, not just this write path - a Draft created/edited via `Sync` must `Fetch` correctly
|
|
88
|
+
* the same way any other message does.
|
|
89
|
+
*/
|
|
90
|
+
async fromApplicationData(el, existing) {
|
|
91
|
+
const partial = {};
|
|
92
|
+
const subject = childText(el, "Subject");
|
|
93
|
+
if (subject !== undefined)
|
|
94
|
+
partial.subject = subject;
|
|
95
|
+
const to = childText(el, "To");
|
|
96
|
+
const cc = childText(el, "Cc");
|
|
97
|
+
const bcc = childText(el, "Bcc");
|
|
98
|
+
if (to !== undefined || cc !== undefined || bcc !== undefined) {
|
|
99
|
+
// Ghosted per-recipient-type, not as one combined group: a `Change` touching only `To` must leave
|
|
100
|
+
// any existing `Cc`/`Bcc` recipients alone, so untouched types are carried over from `existing`
|
|
101
|
+
// rather than the whole `recipients` array being rebuilt from just what's present in `el`.
|
|
102
|
+
const untouched = (existing?.recipients ?? []).filter((r) => (r.type !== RecipientType.TO || to === undefined) &&
|
|
103
|
+
(r.type !== RecipientType.CC || cc === undefined) &&
|
|
104
|
+
(r.type !== RecipientType.BCC || bcc === undefined));
|
|
105
|
+
partial.recipients = [
|
|
106
|
+
...untouched,
|
|
107
|
+
...(to !== undefined ? parseAddressList(to, RecipientType.TO) : []),
|
|
108
|
+
...(cc !== undefined ? parseAddressList(cc, RecipientType.CC) : []),
|
|
109
|
+
...(bcc !== undefined ? parseAddressList(bcc, RecipientType.BCC) : []),
|
|
110
|
+
];
|
|
111
|
+
}
|
|
112
|
+
const importance = childText(el, "Importance");
|
|
113
|
+
if (importance !== undefined) {
|
|
114
|
+
partial.importance = IMPORTANCE_BY_CODE[importance] ?? MessageImportance.NORMAL;
|
|
115
|
+
}
|
|
116
|
+
const read = childText(el, "Read");
|
|
117
|
+
const flag = childText(el, "Flag");
|
|
118
|
+
if (read !== undefined || flag !== undefined) {
|
|
119
|
+
const baseFlags = existing?.flags ?? { read: false, flagged: false, answered: false, forwarded: false };
|
|
120
|
+
partial.flags = {
|
|
121
|
+
...baseFlags,
|
|
122
|
+
...(read !== undefined ? { read: read === "1" } : {}),
|
|
123
|
+
...(flag !== undefined ? { flagged: flag === "1" } : {}),
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
const bodyEl = findChild(el, "Body");
|
|
127
|
+
if (bodyEl) {
|
|
128
|
+
const text = childText(bodyEl, "Data") ?? "";
|
|
129
|
+
// Reuse the existing blob key on a Change (overwriting its content) rather than minting a new one
|
|
130
|
+
// - a fresh key is only needed the first time a body is set (a bare `existing.bodyBlobKey` of ""
|
|
131
|
+
// means an earlier Add never included a Body element at all).
|
|
132
|
+
const bodyBlobKey = existing?.bodyBlobKey || `bodies/${crypto.randomUUID()}`;
|
|
133
|
+
const mime = buildPlainTextMime({
|
|
134
|
+
subject: partial.subject ?? existing?.subject ?? "",
|
|
135
|
+
from: existing?.from,
|
|
136
|
+
recipients: partial.recipients ?? existing?.recipients ?? [],
|
|
137
|
+
date: existing?.sentDate ?? new Date(),
|
|
138
|
+
text,
|
|
139
|
+
});
|
|
140
|
+
await this.blobStore.put(bodyBlobKey, Buffer.from(mime, "utf-8"), { contentType: "message/rfc822" });
|
|
141
|
+
partial.bodyBlobKey = bodyBlobKey;
|
|
142
|
+
partial.bodyPreview = text.slice(0, 200);
|
|
143
|
+
}
|
|
144
|
+
return partial;
|
|
145
|
+
}
|
|
146
|
+
/** Defaults for a brand-new Draft created via a client-originated `Add` - `from` is the caller's own
|
|
147
|
+
* mailbox address, per `ComposeMailCommand`'s identical `{ address, type: RecipientType.TO }` shape
|
|
148
|
+
* convention for a `from` field (the `Recipient` struct's `type` is only meaningful for real recipients;
|
|
149
|
+
* it's reused here as a harmless placeholder). */
|
|
150
|
+
newEntityDefaults(mailbox) {
|
|
151
|
+
return {
|
|
152
|
+
messageId: `<${crypto.randomUUID()}@eas>`,
|
|
153
|
+
subject: "",
|
|
154
|
+
from: { address: mailbox.primarySmtpAddress, displayName: mailbox.displayName, type: RecipientType.TO },
|
|
155
|
+
recipients: [],
|
|
156
|
+
sentDate: new Date(),
|
|
157
|
+
receivedDate: new Date(),
|
|
158
|
+
bodyBlobKey: "",
|
|
159
|
+
bodyPreview: "",
|
|
160
|
+
flags: { read: true, flagged: false, answered: false, forwarded: false },
|
|
161
|
+
importance: MessageImportance.NORMAL,
|
|
162
|
+
references: [],
|
|
163
|
+
hasAttachments: false,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
__decorate([
|
|
168
|
+
Inject("BlobStore"),
|
|
169
|
+
__metadata("design:type", Object)
|
|
170
|
+
], EmailSyncAdapter.prototype, "blobStore", void 0);
|
|
171
|
+
/** Encodes `Message.conversationId` (an internal string uid) into the opaque binary blob MS-ASEMAIL2's
|
|
172
|
+
* `Email2:ConversationId` carries on the wire. The spec never mandates any particular binary format for this
|
|
173
|
+
* value - a real Exchange server mints a GUID, but a client only ever compares/echoes it byte-for-byte, never
|
|
174
|
+
* interprets it - so encoding the uid's own UTF-8 bytes directly (rather than hashing into a 16-byte GUID
|
|
175
|
+
* shape) is a valid, simpler choice that `decodeConversationId` can invert exactly, which `ItemOperationsCommand`
|
|
176
|
+
* relies on to resolve an `ItemOperations` `Move`'s `ConversationId` back into this same uid. */
|
|
177
|
+
export function encodeConversationId(conversationId) {
|
|
178
|
+
return Buffer.from(conversationId, "utf8");
|
|
179
|
+
}
|
|
180
|
+
/** Reverse of `encodeConversationId`. */
|
|
181
|
+
export function decodeConversationId(opaque) {
|
|
182
|
+
return opaque.toString("utf8");
|
|
183
|
+
}
|
|
184
|
+
function formatAddress(address, displayName) {
|
|
185
|
+
return displayName ? `${displayName} <${address}>` : address;
|
|
186
|
+
}
|
|
187
|
+
/** Parses a `;`/`,`-separated address list (`"Name <a@x.com>; b@y.com"`, or the bare-address-only form this
|
|
188
|
+
* adapter's own `toApplicationData` emits) into `Recipient`s of `type`. */
|
|
189
|
+
function parseAddressList(value, type) {
|
|
190
|
+
return value
|
|
191
|
+
.split(/[;,]/)
|
|
192
|
+
.map((part) => part.trim())
|
|
193
|
+
.filter((part) => part.length > 0)
|
|
194
|
+
.map((part) => {
|
|
195
|
+
const match = /^(.*)<(.+)>$/.exec(part);
|
|
196
|
+
return match
|
|
197
|
+
? { address: match[2].trim(), displayName: match[1].trim() || undefined, type }
|
|
198
|
+
: { address: part, type };
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
/** Strips CR/LF from a value about to be interpolated into a single RFC 5322 header line. `Subject`/`To`/`Cc`
|
|
202
|
+
* arrive from client-controlled WBXML inline strings (`WbxmlDecoder.readCString()`, which terminates only on a
|
|
203
|
+
* NUL byte - literal `\r`/`\n` bytes pass through untouched), so without this a crafted value like
|
|
204
|
+
* `"Hi\r\nBcc: attacker@evil.com"` would inject an arbitrary extra header line (or, via a blank line, a forged
|
|
205
|
+
* second message) into the constructed MIME below. Folds onto a single line rather than rejecting outright -
|
|
206
|
+
* a real device is never expected to send this, but a header value silently losing its embedded newlines is
|
|
207
|
+
* safer than the request failing outright over what a client will never notice either way. */
|
|
208
|
+
function sanitizeHeaderValue(value) {
|
|
209
|
+
return value.replace(/[\r\n]+/g, " ");
|
|
210
|
+
}
|
|
211
|
+
/** Builds a minimal, valid RFC 5322 plain-text message - just enough structure for `simpleParser` (used by
|
|
212
|
+
* `ItemOperationsCommand.fetchMessage`) to read it back correctly. No multipart/HTML/attachments - matches this
|
|
213
|
+
* adapter's own documented pragmatic-subset scope. */
|
|
214
|
+
function buildPlainTextMime(parts) {
|
|
215
|
+
const to = parts.recipients.filter((r) => r.type === RecipientType.TO).map((r) => formatAddress(r.address, r.displayName));
|
|
216
|
+
const cc = parts.recipients.filter((r) => r.type === RecipientType.CC).map((r) => formatAddress(r.address, r.displayName));
|
|
217
|
+
const bcc = parts.recipients.filter((r) => r.type === RecipientType.BCC).map((r) => formatAddress(r.address, r.displayName));
|
|
218
|
+
const headers = [
|
|
219
|
+
...(parts.from ? [`From: ${sanitizeHeaderValue(formatAddress(parts.from.address, parts.from.displayName))}`] : []),
|
|
220
|
+
...(to.length > 0 ? [`To: ${sanitizeHeaderValue(to.join(", "))}`] : []),
|
|
221
|
+
...(cc.length > 0 ? [`Cc: ${sanitizeHeaderValue(cc.join(", "))}`] : []),
|
|
222
|
+
...(bcc.length > 0 ? [`Bcc: ${sanitizeHeaderValue(bcc.join(", "))}`] : []),
|
|
223
|
+
`Subject: ${sanitizeHeaderValue(parts.subject)}`,
|
|
224
|
+
`Date: ${parts.date.toUTCString()}`,
|
|
225
|
+
"MIME-Version: 1.0",
|
|
226
|
+
"Content-Type: text/plain; charset=utf-8",
|
|
227
|
+
];
|
|
228
|
+
return `${headers.join("\r\n")}\r\n\r\n${parts.text}`;
|
|
229
|
+
}
|
|
230
|
+
//# sourceMappingURL=EmailSyncAdapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EmailSyncAdapter.js","sourceRoot":"","sources":["../../../src/adapters/EmailSyncAdapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
2
|
+
// Copyright (C) 2026 Jean-Philippe Steinmetz. All rights reserved.
|
|
3
|
+
// SPDX-License-Identifier: MPL-2.0
|
|
4
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
5
|
+
import { WbxmlCodePage } from "../codec/WbxmlCodePages.js";
|
|
6
|
+
import { childText, element, findChild, textElement } from "../codec/WbxmlElement.js";
|
|
7
|
+
import { fromCompactDateTime, toCompactDateTime } from "../CompactDateTime.js";
|
|
8
|
+
import { TaskPriority } from "@rapidmx/restapi";
|
|
9
|
+
/** MS-ASTASK `Importance` reuses the same 0/1/2 scale as MS-ASEMAIL's `Importance` (see `EmailSyncAdapter`'s
|
|
10
|
+
* own identical table) - Low/Normal/High. */
|
|
11
|
+
const IMPORTANCE_CODES = {
|
|
12
|
+
[TaskPriority.LOW]: "0",
|
|
13
|
+
[TaskPriority.NORMAL]: "1",
|
|
14
|
+
[TaskPriority.HIGH]: "2",
|
|
15
|
+
};
|
|
16
|
+
const IMPORTANCE_FROM_CODE = Object.fromEntries(Object.entries(IMPORTANCE_CODES).map(([k, v]) => [v, k]));
|
|
17
|
+
/**
|
|
18
|
+
* Maps `Task` to/from the EAS `Sync` `Tasks` collection class (MS-ASTASK).
|
|
19
|
+
*
|
|
20
|
+
* **Pragmatic subset**: only the `Utc*` (UTC Compact DateTime) variant of each date field is emitted, not the
|
|
21
|
+
* paired local-time variant MS-ASTASK also defines (`DueDate`/`StartDate` alongside `UtcDueDate`/
|
|
22
|
+
* `UtcStartDate`) - this library doesn't track a per-task timezone to correctly localize the non-UTC variant,
|
|
23
|
+
* and a real device already treats the `Utc*` field as authoritative. Recurring tasks are not synced (a task's
|
|
24
|
+
* `Recurrence` element mirrors Calendar's, itself already a pragmatic subset there - deferred further here).
|
|
25
|
+
*
|
|
26
|
+
* @author Jean-Philippe Steinmetz
|
|
27
|
+
*/
|
|
28
|
+
export class TasksSyncAdapter {
|
|
29
|
+
constructor() {
|
|
30
|
+
this.collectionClass = "Tasks";
|
|
31
|
+
}
|
|
32
|
+
toApplicationData(task) {
|
|
33
|
+
return element(WbxmlCodePage.AirSync, "ApplicationData", [
|
|
34
|
+
textElement(WbxmlCodePage.Tasks, "Subject", task.title),
|
|
35
|
+
textElement(WbxmlCodePage.Tasks, "Complete", task.completed ? "1" : "0"),
|
|
36
|
+
...(task.completed ? [textElement(WbxmlCodePage.Tasks, "DateCompleted", toCompactDateTime(task.dateModified))] : []),
|
|
37
|
+
...(task.dueDate ? [textElement(WbxmlCodePage.Tasks, "UtcDueDate", toCompactDateTime(task.dueDate))] : []),
|
|
38
|
+
textElement(WbxmlCodePage.Tasks, "Importance", IMPORTANCE_CODES[task.priority]),
|
|
39
|
+
textElement(WbxmlCodePage.Tasks, "Sensitivity", "0"),
|
|
40
|
+
textElement(WbxmlCodePage.Tasks, "ReminderSet", task.reminderDate ? "1" : "0"),
|
|
41
|
+
...(task.reminderDate ? [textElement(WbxmlCodePage.Tasks, "ReminderTime", toCompactDateTime(task.reminderDate))] : []),
|
|
42
|
+
...(task.body
|
|
43
|
+
? [
|
|
44
|
+
element(WbxmlCodePage.AirSyncBase, "Body", [
|
|
45
|
+
textElement(WbxmlCodePage.AirSyncBase, "Type", "1"),
|
|
46
|
+
textElement(WbxmlCodePage.AirSyncBase, "EstimatedDataSize", String(Buffer.byteLength(task.body, "utf8"))),
|
|
47
|
+
textElement(WbxmlCodePage.AirSyncBase, "Data", task.body),
|
|
48
|
+
]),
|
|
49
|
+
]
|
|
50
|
+
: []),
|
|
51
|
+
]);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Reverse of `toApplicationData`. `DateCompleted` is never parsed back - it's `task.dateModified` echoed
|
|
55
|
+
* out, not an independent field this library's own `Task` model has room to store separately, so a client
|
|
56
|
+
* setting `Complete` is enough on its own. `ReminderSet="0"` (with no `ReminderTime`) is treated as an
|
|
57
|
+
* explicit "clear the reminder" signal (`reminderDate: null` in the returned partial, which - unlike
|
|
58
|
+
* simply omitting the key - does override an existing reminder when merged onto `existing` for a `Change`)
|
|
59
|
+
* since MS-ASTASK gives no other way to express removing a reminder; `UtcDueDate` has no equivalent
|
|
60
|
+
* explicit-clear signal and so can only be set, never cleared, via `Sync` - a real, narrower gap than
|
|
61
|
+
* `reminderDate`'s, documented here rather than silently accepted. `null`, not `undefined`: TypeORM's
|
|
62
|
+
* `UpdateQueryBuilder` silently drops any `undefined`-valued key from the generated SQL `SET` clause
|
|
63
|
+
* (confirmed by reading its source), so `undefined` here would leave a stale `reminderDate` in place on
|
|
64
|
+
* the SQL backend while correctly clearing it on Mongo - `null` clears it on both.
|
|
65
|
+
*/
|
|
66
|
+
fromApplicationData(el) {
|
|
67
|
+
const partial = {};
|
|
68
|
+
const subject = childText(el, "Subject");
|
|
69
|
+
if (subject !== undefined)
|
|
70
|
+
partial.title = subject;
|
|
71
|
+
const complete = childText(el, "Complete");
|
|
72
|
+
if (complete !== undefined)
|
|
73
|
+
partial.completed = complete === "1";
|
|
74
|
+
const utcDueDate = childText(el, "UtcDueDate");
|
|
75
|
+
if (utcDueDate !== undefined)
|
|
76
|
+
partial.dueDate = fromCompactDateTime(utcDueDate);
|
|
77
|
+
const importance = childText(el, "Importance");
|
|
78
|
+
if (importance !== undefined) {
|
|
79
|
+
const mapped = IMPORTANCE_FROM_CODE[importance];
|
|
80
|
+
if (!mapped) {
|
|
81
|
+
throw new Error(`Unrecognized Importance value: '${importance}'`);
|
|
82
|
+
}
|
|
83
|
+
partial.priority = mapped;
|
|
84
|
+
}
|
|
85
|
+
const reminderSet = childText(el, "ReminderSet");
|
|
86
|
+
const reminderTime = childText(el, "ReminderTime");
|
|
87
|
+
if (reminderTime !== undefined) {
|
|
88
|
+
partial.reminderDate = fromCompactDateTime(reminderTime);
|
|
89
|
+
}
|
|
90
|
+
else if (reminderSet === "0") {
|
|
91
|
+
partial.reminderDate = null;
|
|
92
|
+
}
|
|
93
|
+
const bodyEl = findChild(el, "Body");
|
|
94
|
+
const body = bodyEl ? childText(bodyEl, "Data") : undefined;
|
|
95
|
+
if (body !== undefined)
|
|
96
|
+
partial.body = body;
|
|
97
|
+
return partial;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=TasksSyncAdapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TasksSyncAdapter.js","sourceRoot":"","sources":["../../../src/adapters/TasksSyncAdapter.ts"],"names":[],"mappings":"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"}
|