@rapidmx/mapi-plugin 1.0.0-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +382 -0
- package/README.md +60 -0
- package/dist/lib/BaseMapiEmsmdbRoute.js +297 -0
- package/dist/lib/BaseMapiEmsmdbRoute.js.map +1 -0
- package/dist/lib/BaseMapiNspiRoute.js +114 -0
- package/dist/lib/BaseMapiNspiRoute.js.map +1 -0
- package/dist/lib/MapiSessionManager.js +119 -0
- package/dist/lib/MapiSessionManager.js.map +1 -0
- package/dist/lib/RopDispatcher.js +34 -0
- package/dist/lib/RopDispatcher.js.map +1 -0
- package/dist/lib/codec/AppointmentRecurrence.js +314 -0
- package/dist/lib/codec/AppointmentRecurrence.js.map +1 -0
- package/dist/lib/codec/BufferCursor.js +213 -0
- package/dist/lib/codec/BufferCursor.js.map +1 -0
- package/dist/lib/codec/GlobalObjectId.js +92 -0
- package/dist/lib/codec/GlobalObjectId.js.map +1 -0
- package/dist/lib/codec/MapiGuid.js +41 -0
- package/dist/lib/codec/MapiGuid.js.map +1 -0
- package/dist/lib/codec/MapiTimeZone.js +93 -0
- package/dist/lib/codec/MapiTimeZone.js.map +1 -0
- package/dist/lib/codec/PropertyValue.js +214 -0
- package/dist/lib/codec/PropertyValue.js.map +1 -0
- package/dist/lib/codec/RopBuffer.js +26 -0
- package/dist/lib/codec/RopBuffer.js.map +1 -0
- package/dist/lib/codec/TypedString.js +54 -0
- package/dist/lib/codec/TypedString.js.map +1 -0
- package/dist/lib/index.js +99 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/mongo/MapiEmsmdbRouteMongo.js +84 -0
- package/dist/lib/mongo/MapiEmsmdbRouteMongo.js.map +1 -0
- package/dist/lib/mongo/MapiNspiRouteMongo.js +33 -0
- package/dist/lib/mongo/MapiNspiRouteMongo.js.map +1 -0
- package/dist/lib/mongo/index.js +7 -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/nspi/NspiBindHandler.js +54 -0
- package/dist/lib/nspi/NspiBindHandler.js.map +1 -0
- package/dist/lib/nspi/NspiCodec.js +106 -0
- package/dist/lib/nspi/NspiCodec.js.map +1 -0
- package/dist/lib/nspi/NspiGetMatchesHandler.js +142 -0
- package/dist/lib/nspi/NspiGetMatchesHandler.js.map +1 -0
- package/dist/lib/rop/CalendarEventTarget.js +27 -0
- package/dist/lib/rop/CalendarEventTarget.js.map +1 -0
- package/dist/lib/rop/CalendarNamedProperties.js +57 -0
- package/dist/lib/rop/CalendarNamedProperties.js.map +1 -0
- package/dist/lib/rop/ContactTarget.js +23 -0
- package/dist/lib/rop/ContactTarget.js.map +1 -0
- package/dist/lib/rop/FastTransferStream.js +98 -0
- package/dist/lib/rop/FastTransferStream.js.map +1 -0
- package/dist/lib/rop/FolderTarget.js +111 -0
- package/dist/lib/rop/FolderTarget.js.map +1 -0
- package/dist/lib/rop/MeetingMessageClassHandler.js +95 -0
- package/dist/lib/rop/MeetingMessageClassHandler.js.map +1 -0
- package/dist/lib/rop/MessageBodyStream.js +42 -0
- package/dist/lib/rop/MessageBodyStream.js.map +1 -0
- package/dist/lib/rop/MessageTarget.js +44 -0
- package/dist/lib/rop/MessageTarget.js.map +1 -0
- package/dist/lib/rop/NamedPropertyRegistry.js +66 -0
- package/dist/lib/rop/NamedPropertyRegistry.js.map +1 -0
- package/dist/lib/rop/PropertyResolvers.js +313 -0
- package/dist/lib/rop/PropertyResolvers.js.map +1 -0
- package/dist/lib/rop/RopCreateMessageHandler.js +53 -0
- package/dist/lib/rop/RopCreateMessageHandler.js.map +1 -0
- package/dist/lib/rop/RopDeleteFolderHandler.js +133 -0
- package/dist/lib/rop/RopDeleteFolderHandler.js.map +1 -0
- package/dist/lib/rop/RopDeleteMessagesHandler.js +70 -0
- package/dist/lib/rop/RopDeleteMessagesHandler.js.map +1 -0
- package/dist/lib/rop/RopFastTransferSourceCopyPropertiesHandler.js +61 -0
- package/dist/lib/rop/RopFastTransferSourceCopyPropertiesHandler.js.map +1 -0
- package/dist/lib/rop/RopFastTransferSourceCopyToHandler.js +61 -0
- package/dist/lib/rop/RopFastTransferSourceCopyToHandler.js.map +1 -0
- package/dist/lib/rop/RopFastTransferSourceGetBufferHandler.js +75 -0
- package/dist/lib/rop/RopFastTransferSourceGetBufferHandler.js.map +1 -0
- package/dist/lib/rop/RopGetContentsTableHandler.js +76 -0
- package/dist/lib/rop/RopGetContentsTableHandler.js.map +1 -0
- package/dist/lib/rop/RopGetHierarchyTableHandler.js +40 -0
- package/dist/lib/rop/RopGetHierarchyTableHandler.js.map +1 -0
- package/dist/lib/rop/RopGetPropertiesSpecificHandler.js +55 -0
- package/dist/lib/rop/RopGetPropertiesSpecificHandler.js.map +1 -0
- package/dist/lib/rop/RopGetPropertyIdsFromNamesHandler.js +92 -0
- package/dist/lib/rop/RopGetPropertyIdsFromNamesHandler.js.map +1 -0
- package/dist/lib/rop/RopHandler.js +2 -0
- package/dist/lib/rop/RopHandler.js.map +1 -0
- package/dist/lib/rop/RopLogonHandler.js +132 -0
- package/dist/lib/rop/RopLogonHandler.js.map +1 -0
- package/dist/lib/rop/RopOpenFolderHandler.js +46 -0
- package/dist/lib/rop/RopOpenFolderHandler.js.map +1 -0
- package/dist/lib/rop/RopOpenMessageHandler.js +81 -0
- package/dist/lib/rop/RopOpenMessageHandler.js.map +1 -0
- package/dist/lib/rop/RopOpenStreamHandler.js +81 -0
- package/dist/lib/rop/RopOpenStreamHandler.js.map +1 -0
- package/dist/lib/rop/RopQueryRowsHandler.js +91 -0
- package/dist/lib/rop/RopQueryRowsHandler.js.map +1 -0
- package/dist/lib/rop/RopReadStreamHandler.js +55 -0
- package/dist/lib/rop/RopReadStreamHandler.js.map +1 -0
- package/dist/lib/rop/RopReleaseHandler.js +20 -0
- package/dist/lib/rop/RopReleaseHandler.js.map +1 -0
- package/dist/lib/rop/RopSaveChangesMessageHandler.js +210 -0
- package/dist/lib/rop/RopSaveChangesMessageHandler.js.map +1 -0
- package/dist/lib/rop/RopSetColumnsHandler.js +52 -0
- package/dist/lib/rop/RopSetColumnsHandler.js.map +1 -0
- package/dist/lib/rop/RopSetPropertiesHandler.js +162 -0
- package/dist/lib/rop/RopSetPropertiesHandler.js.map +1 -0
- package/dist/lib/rop/RopSubmitMessageHandler.js +314 -0
- package/dist/lib/rop/RopSubmitMessageHandler.js.map +1 -0
- package/dist/lib/rop/RopWriteStreamHandler.js +45 -0
- package/dist/lib/rop/RopWriteStreamHandler.js.map +1 -0
- package/dist/lib/rop/TaskNamedProperties.js +29 -0
- package/dist/lib/rop/TaskNamedProperties.js.map +1 -0
- package/dist/lib/rop/TaskTarget.js +19 -0
- package/dist/lib/rop/TaskTarget.js.map +1 -0
- package/dist/lib/sql/MapiEmsmdbRouteSQL.js +84 -0
- package/dist/lib/sql/MapiEmsmdbRouteSQL.js.map +1 -0
- package/dist/lib/sql/MapiNspiRouteSQL.js +33 -0
- package/dist/lib/sql/MapiNspiRouteSQL.js.map +1 -0
- package/dist/lib/sql/index.js +7 -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/BaseMapiEmsmdbRoute.d.ts +82 -0
- package/dist/types/BaseMapiNspiRoute.d.ts +38 -0
- package/dist/types/MapiSessionManager.d.ts +130 -0
- package/dist/types/RopDispatcher.d.ts +17 -0
- package/dist/types/codec/AppointmentRecurrence.d.ts +14 -0
- package/dist/types/codec/BufferCursor.d.ts +78 -0
- package/dist/types/codec/GlobalObjectId.d.ts +11 -0
- package/dist/types/codec/MapiGuid.d.ts +6 -0
- package/dist/types/codec/MapiTimeZone.d.ts +7 -0
- package/dist/types/codec/PropertyValue.d.ts +77 -0
- package/dist/types/codec/RopBuffer.d.ts +25 -0
- package/dist/types/codec/TypedString.d.ts +17 -0
- package/dist/types/index.d.ts +94 -0
- package/dist/types/mongo/MapiEmsmdbRouteMongo.d.ts +19 -0
- package/dist/types/mongo/MapiNspiRouteMongo.d.ts +13 -0
- package/dist/types/mongo/index.d.ts +2 -0
- package/dist/types/mongo.d.ts +1 -0
- package/dist/types/nspi/NspiBindHandler.d.ts +22 -0
- package/dist/types/nspi/NspiCodec.d.ts +59 -0
- package/dist/types/nspi/NspiGetMatchesHandler.d.ts +24 -0
- package/dist/types/rop/CalendarEventTarget.d.ts +29 -0
- package/dist/types/rop/CalendarNamedProperties.d.ts +37 -0
- package/dist/types/rop/ContactTarget.d.ts +29 -0
- package/dist/types/rop/FastTransferStream.d.ts +18 -0
- package/dist/types/rop/FolderTarget.d.ts +63 -0
- package/dist/types/rop/MeetingMessageClassHandler.d.ts +36 -0
- package/dist/types/rop/MessageBodyStream.d.ts +26 -0
- package/dist/types/rop/MessageTarget.d.ts +38 -0
- package/dist/types/rop/NamedPropertyRegistry.d.ts +46 -0
- package/dist/types/rop/PropertyResolvers.d.ts +60 -0
- package/dist/types/rop/RopCreateMessageHandler.d.ts +23 -0
- package/dist/types/rop/RopDeleteFolderHandler.d.ts +38 -0
- package/dist/types/rop/RopDeleteMessagesHandler.d.ts +31 -0
- package/dist/types/rop/RopFastTransferSourceCopyPropertiesHandler.d.ts +24 -0
- package/dist/types/rop/RopFastTransferSourceCopyToHandler.d.ts +24 -0
- package/dist/types/rop/RopFastTransferSourceGetBufferHandler.d.ts +23 -0
- package/dist/types/rop/RopGetContentsTableHandler.d.ts +31 -0
- package/dist/types/rop/RopGetHierarchyTableHandler.d.ts +17 -0
- package/dist/types/rop/RopGetPropertiesSpecificHandler.d.ts +23 -0
- package/dist/types/rop/RopGetPropertyIdsFromNamesHandler.d.ts +23 -0
- package/dist/types/rop/RopHandler.d.ts +67 -0
- package/dist/types/rop/RopLogonHandler.d.ts +31 -0
- package/dist/types/rop/RopOpenFolderHandler.d.ts +19 -0
- package/dist/types/rop/RopOpenMessageHandler.d.ts +27 -0
- package/dist/types/rop/RopOpenStreamHandler.d.ts +27 -0
- package/dist/types/rop/RopQueryRowsHandler.d.ts +36 -0
- package/dist/types/rop/RopReadStreamHandler.d.ts +18 -0
- package/dist/types/rop/RopReleaseHandler.d.ts +14 -0
- package/dist/types/rop/RopSaveChangesMessageHandler.d.ts +44 -0
- package/dist/types/rop/RopSetColumnsHandler.d.ts +21 -0
- package/dist/types/rop/RopSetPropertiesHandler.d.ts +38 -0
- package/dist/types/rop/RopSubmitMessageHandler.d.ts +70 -0
- package/dist/types/rop/RopWriteStreamHandler.d.ts +21 -0
- package/dist/types/rop/TaskNamedProperties.d.ts +24 -0
- package/dist/types/rop/TaskTarget.d.ts +24 -0
- package/dist/types/sql/MapiEmsmdbRouteSQL.d.ts +19 -0
- package/dist/types/sql/MapiNspiRouteSQL.d.ts +13 -0
- package/dist/types/sql/index.d.ts +2 -0
- package/dist/types/sql.d.ts +1 -0
- package/package.json +104 -0
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
2
|
+
// Copyright (C) 2026 Jean-Philippe Steinmetz. All rights reserved.
|
|
3
|
+
// SPDX-License-Identifier: MPL-2.0
|
|
4
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
5
|
+
import * as crypto from "crypto";
|
|
6
|
+
import MailComposer from "nodemailer/lib/mail-composer/index.js";
|
|
7
|
+
import { findOrCreateWellKnownFolder, FolderType, MessageImportance, RecipientType, scanAndRelay, } from "@rapidmx/restapi";
|
|
8
|
+
import { submitMeetingResponse } from "./MeetingMessageClassHandler.js";
|
|
9
|
+
const ROP_ID_SUBMIT_MESSAGE = 0x32;
|
|
10
|
+
/** The well-known MAPI HRESULT `MAPI_E_INVALID_OBJECT`, reused for both "the referenced handle isn't a draft
|
|
11
|
+
* message" and "the draft has no resolvable recipients/sender" - the same constant reused throughout this
|
|
12
|
+
* pragmatic subset as a generic "can't do this" signal, not a claim of exact per-condition `[MS-OXCRPC]`
|
|
13
|
+
* return-value-table parity. */
|
|
14
|
+
const ERROR_INVALID_OBJECT = 0x80070005;
|
|
15
|
+
// The same well-known property IDs RopSetPropertiesHandler tracks - duplicated here (rather than imported)
|
|
16
|
+
// since importing them would only save a handful of literals; see that file for the real documentation of what
|
|
17
|
+
// each means.
|
|
18
|
+
const PID_TAG_SUBJECT = 0x0037;
|
|
19
|
+
const PID_TAG_MESSAGE_CLASS = 0x001a;
|
|
20
|
+
const PID_TAG_DISPLAY_BCC = 0x0e02;
|
|
21
|
+
const PID_TAG_DISPLAY_CC = 0x0e03;
|
|
22
|
+
const PID_TAG_DISPLAY_TO = 0x0e04;
|
|
23
|
+
const PID_TAG_BODY = 0x1000;
|
|
24
|
+
const PID_TAG_READ_RECEIPT_REQUESTED = 0x0029;
|
|
25
|
+
const PID_TAG_DEFERRED_SEND_TIME = 0x3fef;
|
|
26
|
+
/** `PidTagMessageClass` values starting with this prefix (`IPM.Appointment`, `IPM.Appointment.*`) route through
|
|
27
|
+
* `submitAppointment()` instead of the ordinary mail compose/send path below - see that method's own doc
|
|
28
|
+
* comment. */
|
|
29
|
+
const MESSAGE_CLASS_APPOINTMENT_PREFIX = "IPM.Appointment";
|
|
30
|
+
/** `PidTagMessageClass` values starting with this prefix (`IPM.Schedule.Meeting.Resp.{Pos,Neg,Tent}`) route
|
|
31
|
+
* through `MeetingMessageClassHandler.submitMeetingResponse()` instead of every other path here - see that
|
|
32
|
+
* function's own doc comment. */
|
|
33
|
+
const MESSAGE_CLASS_MEETING_RESPONSE_PREFIX = "IPM.Schedule.Meeting.Resp.";
|
|
34
|
+
/** Splits a `PidTagDisplayTo`/`Cc`/`Bcc`-style string on the semicolons real Outlook separates recipients
|
|
35
|
+
* with (also tolerating commas, in case a client or test harness uses that convention instead), trimming and
|
|
36
|
+
* dropping empty entries. Exported since `RopSaveChangesMessageHandler.ts` reads the same accumulated
|
|
37
|
+
* `PidTagDisplayTo`/`Cc` draft properties to build a Calendar item's attendee list. */
|
|
38
|
+
export function parseAddressList(value) {
|
|
39
|
+
if (!value) {
|
|
40
|
+
return [];
|
|
41
|
+
}
|
|
42
|
+
return value
|
|
43
|
+
.split(/[;,]/)
|
|
44
|
+
.map((s) => s.trim())
|
|
45
|
+
.filter((s) => s.length > 0);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* `RopSubmitMessage` (`[MS-OXOMSG]`/`[MS-OXCROPS]`): sends a composed message - the actual trigger point for
|
|
49
|
+
* this pragmatic subset's compose/send pipeline, since `RopSaveChangesMessage` itself does no real persistence
|
|
50
|
+
* (see its own doc comment). Builds a raw MIME buffer from whatever `RopSetProperties`/`RopWriteStream` calls
|
|
51
|
+
* accumulated on the draft handle (`Subject`, the `PidTagBody` stream or an inline `PidTagBody` property, and
|
|
52
|
+
* addressing - see below), via `nodemailer`'s `MailComposer` (already a dependency, used elsewhere in this
|
|
53
|
+
* library only to *send*, not build, MIME - this is its first use purely as a RFC 5322 serializer), then calls
|
|
54
|
+
* the exact same `scanAndRelay()` scan-then-relay pipeline `BaseMessageRoute.send()`/EAS's `ComposeMailCommand`
|
|
55
|
+
* already share, and persists a Sent Items copy (mirroring `ComposeMailCommand`'s own always-committed Sent
|
|
56
|
+
* Items behavior for MAPI submission specifically, since - unlike EAS's optional `SaveInSentItems` - a MAPI
|
|
57
|
+
* `RopSubmitMessage` call has no "don't keep a copy" option to honor).
|
|
58
|
+
*
|
|
59
|
+
* **Known, deliberate limitation - no `RopModifyRecipients` support**: real recipient rows (`[MS-OXCMSG]`
|
|
60
|
+
* §2.2.3.2, each with its own `PidTagEmailAddress`/`PidTagRecipientType`) are a substantial structure this
|
|
61
|
+
* pragmatic subset's ROP coverage doesn't include. Instead, addressing is read from `PidTagDisplayTo`/
|
|
62
|
+
* `DisplayCc`/`DisplayBcc` - the same semicolon-separated "cached recipient display string" properties real
|
|
63
|
+
* Outlook *also* always sets via `RopSetProperties` alongside `RopModifyRecipients` (as a display-only
|
|
64
|
+
* convenience cache). This works correctly for the common case of composing to bare, unresolved email
|
|
65
|
+
* addresses (nothing to resolve to a distinct display name) - the case this library's own integration tests
|
|
66
|
+
* exercise - but is **not** a substitute for real recipient rows: a client that resolves typed names against an
|
|
67
|
+
* address book before submitting would populate these fields with display names, not addresses, and this
|
|
68
|
+
* pragmatic subset has no way to recover a real SMTP address from a bare display name. Documented here rather
|
|
69
|
+
* than silently producing wrong addressing.
|
|
70
|
+
*
|
|
71
|
+
* `SubmitFlags` (`PreprocessOnly`, ...) is decoded to advance past it correctly but not honored - this
|
|
72
|
+
* pragmatic subset has no transport-agent preprocessing distinction to vary by flag.
|
|
73
|
+
*
|
|
74
|
+
* **`PidTagDeferredSendTime`** ("Do not deliver before"), if set to a future time via `RopSetProperties`,
|
|
75
|
+
* parks the message in Outbox with `Message.scheduledSendTime` set instead of relaying immediately - mirroring
|
|
76
|
+
* `BaseMessageRoute.send()`'s own identical branch - for `ScheduledSendJob` to relay later via this same
|
|
77
|
+
* `scanAndRelay()` pipeline. **`PidTagReadReceiptRequested`**, if `true`, attaches a real
|
|
78
|
+
* `Disposition-Notification-To` header (see `util/ReceiptUtils.ts`) and is recorded on the persisted message's
|
|
79
|
+
* own `requestReceipt` field - honored only when the client explicitly sets it, not this mailbox's
|
|
80
|
+
* `alwaysRequestReceiptInternal`/`External` defaults (those apply to the REST/webmail compose path only).
|
|
81
|
+
*
|
|
82
|
+
* **Calendar branches**: a draft whose `PidTagMessageClass` starts with `"IPM.Appointment"` is routed to
|
|
83
|
+
* `submitAppointment()` instead of the mail path below - see that method's own doc comment. One starting with
|
|
84
|
+
* `"IPM.Schedule.Meeting.Resp."` (an attendee's own accept/decline/tentative response to a meeting this server
|
|
85
|
+
* previously invited them to) is routed to `MeetingMessageClassHandler.submitMeetingResponse()` instead - see
|
|
86
|
+
* that function's own doc comment.
|
|
87
|
+
*
|
|
88
|
+
* @author Jean-Philippe Steinmetz
|
|
89
|
+
*/
|
|
90
|
+
export class RopSubmitMessageHandler {
|
|
91
|
+
constructor() {
|
|
92
|
+
this.ropId = ROP_ID_SUBMIT_MESSAGE;
|
|
93
|
+
}
|
|
94
|
+
async handle(reader, writer, context) {
|
|
95
|
+
reader.readUInt8(); // LogonId - this pragmatic subset doesn't track multiple concurrent logons per session
|
|
96
|
+
const inputHandleIndex = reader.readUInt8();
|
|
97
|
+
reader.readUInt8(); // SubmitFlags - see class doc comment
|
|
98
|
+
const handle = context.session.handles[inputHandleIndex];
|
|
99
|
+
if (!handle || handle.type !== "message") {
|
|
100
|
+
writer.writeUInt8(ROP_ID_SUBMIT_MESSAGE);
|
|
101
|
+
writer.writeUInt8(inputHandleIndex);
|
|
102
|
+
writer.writeUInt32LE(ERROR_INVALID_OBJECT);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const properties = handle.draftProperties ?? {};
|
|
106
|
+
const messageClass = properties[String(PID_TAG_MESSAGE_CLASS)] ?? "IPM.Note";
|
|
107
|
+
if (messageClass.startsWith(MESSAGE_CLASS_APPOINTMENT_PREFIX)) {
|
|
108
|
+
await this.submitAppointment(handle, context, writer, inputHandleIndex);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
if (messageClass.startsWith(MESSAGE_CLASS_MEETING_RESPONSE_PREFIX)) {
|
|
112
|
+
await submitMeetingResponse(messageClass, properties, context);
|
|
113
|
+
writer.writeUInt8(ROP_ID_SUBMIT_MESSAGE);
|
|
114
|
+
writer.writeUInt8(inputHandleIndex);
|
|
115
|
+
writer.writeUInt32LE(0); // ReturnValue - success, see MeetingMessageClassHandler's own doc comment
|
|
116
|
+
// on why every failure mode there is a silent no-op rather than surfaced here.
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
const to = parseAddressList(properties[String(PID_TAG_DISPLAY_TO)]);
|
|
120
|
+
const cc = parseAddressList(properties[String(PID_TAG_DISPLAY_CC)]);
|
|
121
|
+
const bcc = parseAddressList(properties[String(PID_TAG_DISPLAY_BCC)]);
|
|
122
|
+
const mailbox = await context.mailboxRepo.findOne(context.mailboxUid, { ignoreACL: true });
|
|
123
|
+
const envelopeFrom = mailbox?.primarySmtpAddress;
|
|
124
|
+
if (!envelopeFrom || to.length + cc.length + bcc.length === 0) {
|
|
125
|
+
writer.writeUInt8(ROP_ID_SUBMIT_MESSAGE);
|
|
126
|
+
writer.writeUInt8(inputHandleIndex);
|
|
127
|
+
writer.writeUInt32LE(ERROR_INVALID_OBJECT);
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
const subject = properties[String(PID_TAG_SUBJECT)] ?? "";
|
|
131
|
+
const bodyText = resolveDraftBody(context, inputHandleIndex, properties);
|
|
132
|
+
const requestReceipt = properties[String(PID_TAG_READ_RECEIPT_REQUESTED)] === "true";
|
|
133
|
+
// "Do not deliver before" - a real Outlook compose option, tracked as a plain ISO-8601 string by
|
|
134
|
+
// RopSetPropertiesHandler.stringifyValue()'s PtypTime encoding.
|
|
135
|
+
const deferredSendTimeRaw = properties[String(PID_TAG_DEFERRED_SEND_TIME)];
|
|
136
|
+
const deferredSendTime = deferredSendTimeRaw ? new Date(deferredSendTimeRaw) : undefined;
|
|
137
|
+
const isDeferred = deferredSendTime !== undefined && deferredSendTime.getTime() > Date.now();
|
|
138
|
+
const raw = await new MailComposer({
|
|
139
|
+
from: envelopeFrom,
|
|
140
|
+
to,
|
|
141
|
+
cc,
|
|
142
|
+
bcc,
|
|
143
|
+
subject,
|
|
144
|
+
text: bodyText,
|
|
145
|
+
// A real Disposition-Notification-To request, mirroring BaseMessageRoute.send()'s own explicit
|
|
146
|
+
// Message.requestReceipt handling (see util/ReceiptUtils.ts) - honored here only when the client
|
|
147
|
+
// itself asked for one via PidTagReadReceiptRequested, not this mailbox's own
|
|
148
|
+
// alwaysRequestReceiptInternal/External defaults (those apply to the REST/webmail compose path,
|
|
149
|
+
// which already goes through BaseMessageRoute.send() directly).
|
|
150
|
+
...(requestReceipt ? { headers: [{ key: "Disposition-Notification-To", value: envelopeFrom }] } : {}),
|
|
151
|
+
})
|
|
152
|
+
.compile()
|
|
153
|
+
.build();
|
|
154
|
+
const envelopeTo = [...to, ...cc, ...bcc];
|
|
155
|
+
const bodyBlobKey = `bodies/${crypto.randomUUID()}`;
|
|
156
|
+
const recipients = [
|
|
157
|
+
...to.map((address) => ({ address, type: RecipientType.TO })),
|
|
158
|
+
...cc.map((address) => ({ address, type: RecipientType.CC })),
|
|
159
|
+
...bcc.map((address) => ({ address, type: RecipientType.BCC })),
|
|
160
|
+
];
|
|
161
|
+
if (isDeferred) {
|
|
162
|
+
// Mirrors BaseMessageRoute.send()'s own scheduledSendTime branch: park the message in Outbox,
|
|
163
|
+
// unscanned/unrelayed, for the same ScheduledSendJob to pick up and relay (via this exact
|
|
164
|
+
// scanAndRelay() pipeline) once due - see that job's own doc comment.
|
|
165
|
+
await context.blobStore.put(bodyBlobKey, raw, { contentType: "message/rfc822" });
|
|
166
|
+
const outbox = await findOrCreateWellKnownFolder(context.folderRepo, context.folderClass, context.mailboxUid, FolderType.OUTBOX);
|
|
167
|
+
await context.messageRepo.create(new context.messageClass({
|
|
168
|
+
folderUid: outbox.uid,
|
|
169
|
+
mailboxUid: context.mailboxUid,
|
|
170
|
+
messageId: `${crypto.randomUUID()}@mapi`,
|
|
171
|
+
subject,
|
|
172
|
+
from: { address: envelopeFrom, type: RecipientType.TO },
|
|
173
|
+
recipients,
|
|
174
|
+
sentDate: new Date(),
|
|
175
|
+
receivedDate: new Date(),
|
|
176
|
+
bodyBlobKey,
|
|
177
|
+
bodyPreview: bodyText.slice(0, 200),
|
|
178
|
+
flags: { read: true, flagged: false, answered: false, forwarded: false },
|
|
179
|
+
importance: MessageImportance.NORMAL,
|
|
180
|
+
references: [],
|
|
181
|
+
hasAttachments: false,
|
|
182
|
+
scheduledSendTime: deferredSendTime,
|
|
183
|
+
requestReceipt,
|
|
184
|
+
}), { ignoreACL: true });
|
|
185
|
+
writer.writeUInt8(ROP_ID_SUBMIT_MESSAGE);
|
|
186
|
+
writer.writeUInt8(inputHandleIndex);
|
|
187
|
+
writer.writeUInt32LE(0); // ReturnValue - success
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
const { sanitizedHtmlBlobKey } = await scanAndRelay(raw, envelopeFrom, envelopeTo, context.scanPipeline, context.mailTransport, context.blobStore);
|
|
191
|
+
await context.blobStore.put(bodyBlobKey, raw, { contentType: "message/rfc822" });
|
|
192
|
+
const sentFolder = await findOrCreateWellKnownFolder(context.folderRepo, context.folderClass, context.mailboxUid, FolderType.SENT_ITEMS);
|
|
193
|
+
await context.messageRepo.create(new context.messageClass({
|
|
194
|
+
folderUid: sentFolder.uid,
|
|
195
|
+
mailboxUid: context.mailboxUid,
|
|
196
|
+
messageId: `${crypto.randomUUID()}@mapi`,
|
|
197
|
+
subject,
|
|
198
|
+
from: { address: envelopeFrom, type: RecipientType.TO },
|
|
199
|
+
recipients,
|
|
200
|
+
sentDate: new Date(),
|
|
201
|
+
receivedDate: new Date(),
|
|
202
|
+
bodyBlobKey,
|
|
203
|
+
sanitizedHtmlBlobKey,
|
|
204
|
+
bodyPreview: bodyText.slice(0, 200),
|
|
205
|
+
flags: { read: true, flagged: false, answered: false, forwarded: false },
|
|
206
|
+
importance: MessageImportance.NORMAL,
|
|
207
|
+
references: [],
|
|
208
|
+
hasAttachments: false,
|
|
209
|
+
requestReceipt,
|
|
210
|
+
}), { ignoreACL: true });
|
|
211
|
+
writer.writeUInt8(ROP_ID_SUBMIT_MESSAGE);
|
|
212
|
+
writer.writeUInt8(inputHandleIndex);
|
|
213
|
+
writer.writeUInt32LE(0); // ReturnValue - success
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Submits a Calendar item. The appointment itself is already persisted (`RopSaveChangesMessageHandler`
|
|
217
|
+
* writes the real `CalendarEvent` row at Save time, unlike a mail draft, which stays purely in-session until
|
|
218
|
+
* Submit) - this method's only job is notifying attendees, if any were set (via `PidTagDisplayTo`/`Cc`,
|
|
219
|
+
* decoded into `CalendarEvent.attendees` at Save time). No attendees means no invite to send (e.g. a private,
|
|
220
|
+
* non-meeting appointment) - a well-formed success response either way, since the appointment already exists.
|
|
221
|
+
*
|
|
222
|
+
* Unlike ordinary mail, there is no separate "Sent Items copy" to create - the appointment's durable copy
|
|
223
|
+
* *is* the `CalendarEvent` row already sitting in the organizer's own Calendar folder.
|
|
224
|
+
*
|
|
225
|
+
* The invite itself is a minimal iCalendar (`RFC 5545`) `VEVENT` with `METHOD:REQUEST` (`RFC 5546`),
|
|
226
|
+
* attached via `nodemailer`'s own `MailComposer` `icalEvent` option (which produces both a `text/calendar;
|
|
227
|
+
* method=REQUEST` MIME alternative and an `.ics` attachment - the standard dual form real invite emails use)
|
|
228
|
+
* - reusing the identical `scanAndRelay()` pipeline the mail path above already calls.
|
|
229
|
+
*/
|
|
230
|
+
async submitAppointment(handle, context, writer, inputHandleIndex) {
|
|
231
|
+
const uid = handle.entityUid.startsWith("calendarEvent:") ? handle.entityUid.slice("calendarEvent:".length) : undefined;
|
|
232
|
+
const event = uid ? await context.calendarEventRepo.findOne(uid, { ignoreACL: true }) : undefined;
|
|
233
|
+
if (!event) {
|
|
234
|
+
writer.writeUInt8(ROP_ID_SUBMIT_MESSAGE);
|
|
235
|
+
writer.writeUInt8(inputHandleIndex);
|
|
236
|
+
writer.writeUInt32LE(ERROR_INVALID_OBJECT);
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
if (event.attendees.length > 0) {
|
|
240
|
+
const mailbox = await context.mailboxRepo.findOne(context.mailboxUid, { ignoreACL: true });
|
|
241
|
+
const envelopeFrom = mailbox?.primarySmtpAddress;
|
|
242
|
+
if (envelopeFrom) {
|
|
243
|
+
const attendeeAddresses = event.attendees.map((attendee) => attendee.address);
|
|
244
|
+
const ics = buildMeetingRequestIcs(event, envelopeFrom);
|
|
245
|
+
const raw = await new MailComposer({
|
|
246
|
+
from: envelopeFrom,
|
|
247
|
+
to: attendeeAddresses,
|
|
248
|
+
subject: event.title,
|
|
249
|
+
text: `You have been invited to: ${event.title}`,
|
|
250
|
+
icalEvent: { method: "REQUEST", content: ics },
|
|
251
|
+
})
|
|
252
|
+
.compile()
|
|
253
|
+
.build();
|
|
254
|
+
await scanAndRelay(raw, envelopeFrom, attendeeAddresses, context.scanPipeline, context.mailTransport, context.blobStore);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
writer.writeUInt8(ROP_ID_SUBMIT_MESSAGE);
|
|
258
|
+
writer.writeUInt8(inputHandleIndex);
|
|
259
|
+
writer.writeUInt32LE(0); // ReturnValue - success
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
/** `YYYYMMDDTHHMMSSZ` - the `RFC 5545` "form 2" (UTC) `DATE-TIME` format every field below uses. */
|
|
263
|
+
function formatIcsDateUtc(date) {
|
|
264
|
+
return date.toISOString().replace(/[-:]/g, "").split(".")[0] + "Z";
|
|
265
|
+
}
|
|
266
|
+
/** Escapes the `RFC 5545` §3.3.11 `TEXT` value special characters (backslash, semicolon, comma, newline) - the
|
|
267
|
+
* only value types this minimal builder ever emits unescaped text into (`SUMMARY`/`LOCATION`). */
|
|
268
|
+
function escapeIcsText(value) {
|
|
269
|
+
return value.replace(/\\/g, "\\\\").replace(/;/g, "\\;").replace(/,/g, "\\,").replace(/\r?\n/g, "\\n");
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Builds a minimal `RFC 5545`/`RFC 5546` `METHOD:REQUEST` iCalendar document for `event`. Deliberately not a
|
|
273
|
+
* general-purpose iCalendar writer (see `AutodiscoverXml.ts`'s own doc comment for the same "hand-build just
|
|
274
|
+
* the fields this pass needs" precedent) - no `RRULE` (recurring meeting invites are a documented gap, same as
|
|
275
|
+
* this pragmatic subset's other recurrence-adjacent limitations), no line-folding at the 75-octet boundary
|
|
276
|
+
* `RFC 5545` §3.1 technically requires (every field emitted here is short enough in practice that folding would
|
|
277
|
+
* never trigger), no `VTIMEZONE` block (times are always emitted as UTC `Z`-suffixed values instead).
|
|
278
|
+
*/
|
|
279
|
+
function buildMeetingRequestIcs(event, organizerAddress) {
|
|
280
|
+
const lines = [
|
|
281
|
+
"BEGIN:VCALENDAR",
|
|
282
|
+
"PRODID:-//RapidREST//Mail//EN",
|
|
283
|
+
"VERSION:2.0",
|
|
284
|
+
"METHOD:REQUEST",
|
|
285
|
+
"BEGIN:VEVENT",
|
|
286
|
+
`UID:${event.icalUid}`,
|
|
287
|
+
`SEQUENCE:${event.sequence}`,
|
|
288
|
+
`DTSTAMP:${formatIcsDateUtc(new Date())}`,
|
|
289
|
+
`DTSTART:${formatIcsDateUtc(event.startDate)}`,
|
|
290
|
+
`DTEND:${formatIcsDateUtc(event.endDate)}`,
|
|
291
|
+
`SUMMARY:${escapeIcsText(event.title)}`,
|
|
292
|
+
...(event.location ? [`LOCATION:${escapeIcsText(event.location)}`] : []),
|
|
293
|
+
`ORGANIZER:mailto:${organizerAddress}`,
|
|
294
|
+
...event.attendees.map((attendee) => `ATTENDEE;RSVP=TRUE:mailto:${attendee.address}`),
|
|
295
|
+
"STATUS:CONFIRMED",
|
|
296
|
+
"END:VEVENT",
|
|
297
|
+
"END:VCALENDAR",
|
|
298
|
+
];
|
|
299
|
+
return lines.join("\r\n");
|
|
300
|
+
}
|
|
301
|
+
/** Prefers a `RopWriteStream`-accumulated body (the real path a large body takes) over an inline `PidTagBody`
|
|
302
|
+
* set directly via `RopSetProperties` (only realistic for a short body small enough to set inline) - see
|
|
303
|
+
* `RopOpenStreamHandler`'s write-mode branch for how `writeTargetHandleIndex` links a stream handle back to the
|
|
304
|
+
* draft message handle it was opened against. */
|
|
305
|
+
function resolveDraftBody(context, messageHandleIndex, properties) {
|
|
306
|
+
for (const candidate of Object.values(context.session.handles)) {
|
|
307
|
+
if (candidate.type === "stream" && candidate.writeTargetHandleIndex === messageHandleIndex && candidate.writeBufferBase64) {
|
|
308
|
+
const raw = Buffer.from(candidate.writeBufferBase64, "base64");
|
|
309
|
+
return raw.toString("utf16le").replace(/\0+$/, "");
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
return properties[String(PID_TAG_BODY)] ?? "";
|
|
313
|
+
}
|
|
314
|
+
//# sourceMappingURL=RopSubmitMessageHandler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RopSubmitMessageHandler.js","sourceRoot":"","sources":["../../../src/rop/RopSubmitMessageHandler.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
const ROP_ID_WRITE_STREAM = 0x2d;
|
|
2
|
+
/** The well-known MAPI HRESULT `MAPI_E_INVALID_OBJECT`, reused for "the referenced handle isn't a write-mode
|
|
3
|
+
* stream (or doesn't exist)" - the same constant `RopReadStreamHandler` uses for its own analogous check. */
|
|
4
|
+
const ERROR_INVALID_OBJECT = 0x80070005;
|
|
5
|
+
/**
|
|
6
|
+
* `RopWriteStream` (`[MS-OXCPRPT]`/`[MS-OXCROPS]`): writes bytes to a stream opened in `ReadWrite`/`Create`
|
|
7
|
+
* mode (`RopOpenStreamHandler`'s write-mode branch - the only kind of writable stream this pragmatic subset
|
|
8
|
+
* ever produces, always a `RopCreateMessage` draft's `PidTagBody`). Accumulates `Data` into the stream handle's
|
|
9
|
+
* `writeBufferBase64` field, decode-concat-reencode each call rather than a naive string concatenation of
|
|
10
|
+
* per-call base64 chunks - base64 is not concatenation-safe (a chunk's padding only belongs at the true end of
|
|
11
|
+
* the data), so reassembling the real bytes requires decoding what's accumulated so far, appending the new raw
|
|
12
|
+
* bytes, and re-encoding the whole thing. `RopSaveChangesMessageHandler`/`RopSubmitMessageHandler` decode this
|
|
13
|
+
* buffer back to the final body text once composing is complete.
|
|
14
|
+
*
|
|
15
|
+
* Like `RopReadStream`, `[MS-OXCROPS]` documents only one combined response-buffer shape for this ROP (no
|
|
16
|
+
* separate Success/Failure pages) - `WrittenSize` is always present, `0` standing in for the failure case.
|
|
17
|
+
*
|
|
18
|
+
* @author Jean-Philippe Steinmetz
|
|
19
|
+
*/
|
|
20
|
+
export class RopWriteStreamHandler {
|
|
21
|
+
constructor() {
|
|
22
|
+
this.ropId = ROP_ID_WRITE_STREAM;
|
|
23
|
+
}
|
|
24
|
+
handle(reader, writer, context) {
|
|
25
|
+
reader.readUInt8(); // LogonId - this pragmatic subset doesn't track multiple concurrent logons per session
|
|
26
|
+
const inputHandleIndex = reader.readUInt8();
|
|
27
|
+
const dataSize = reader.readUInt16LE();
|
|
28
|
+
const data = reader.readBytes(dataSize);
|
|
29
|
+
const handle = context.session.handles[inputHandleIndex];
|
|
30
|
+
if (!handle || handle.type !== "stream" || handle.writeTargetHandleIndex === undefined) {
|
|
31
|
+
writer.writeUInt8(ROP_ID_WRITE_STREAM);
|
|
32
|
+
writer.writeUInt8(inputHandleIndex);
|
|
33
|
+
writer.writeUInt32LE(ERROR_INVALID_OBJECT);
|
|
34
|
+
writer.writeUInt16LE(0); // WrittenSize - see class doc comment on why this is still written on failure
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const existing = handle.writeBufferBase64 ? Buffer.from(handle.writeBufferBase64, "base64") : Buffer.alloc(0);
|
|
38
|
+
handle.writeBufferBase64 = Buffer.concat([existing, data]).toString("base64");
|
|
39
|
+
writer.writeUInt8(ROP_ID_WRITE_STREAM);
|
|
40
|
+
writer.writeUInt8(inputHandleIndex);
|
|
41
|
+
writer.writeUInt32LE(0); // ReturnValue - success
|
|
42
|
+
writer.writeUInt16LE(data.length); // WrittenSize
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=RopWriteStreamHandler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RopWriteStreamHandler.js","sourceRoot":"","sources":["../../../src/rop/RopWriteStreamHandler.ts"],"names":[],"mappings":"AAOA,MAAM,mBAAmB,GAAG,IAAI,CAAC;AAEjC;6GAC6G;AAC7G,MAAM,oBAAoB,GAAG,UAAU,CAAC;AAExC;;;;;;;;;;;;;;GAcG;AACH,MAAM,OAAO,qBAAqB;IAAlC;QACoB,UAAK,GAAG,mBAAmB,CAAC;IAyBhD,CAAC;IAvBU,MAAM,CAAC,MAAoB,EAAE,MAAoB,EAAE,OAAmB;QACzE,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,uFAAuF;QAC3G,MAAM,gBAAgB,GAAW,MAAM,CAAC,SAAS,EAAE,CAAC;QACpD,MAAM,QAAQ,GAAW,MAAM,CAAC,YAAY,EAAE,CAAC;QAC/C,MAAM,IAAI,GAAW,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QAEhD,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC;QACzD,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC,IAAI,KAAK,QAAQ,IAAI,MAAM,CAAC,sBAAsB,KAAK,SAAS,EAAE,CAAC;YACrF,MAAM,CAAC,UAAU,CAAC,mBAAmB,CAAC,CAAC;YACvC,MAAM,CAAC,UAAU,CAAC,gBAAgB,CAAC,CAAC;YACpC,MAAM,CAAC,aAAa,CAAC,oBAAoB,CAAC,CAAC;YAC3C,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,8EAA8E;YACvG,OAAO;QACX,CAAC;QAED,MAAM,QAAQ,GAAW,MAAM,CAAC,iBAAiB,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,iBAAiB,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QACtH,MAAM,CAAC,iBAAiB,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAE9E,MAAM,CAAC,UAAU,CAAC,mBAAmB,CAAC,CAAC;QACvC,MAAM,CAAC,UAAU,CAAC,gBAAgB,CAAC,CAAC;QACpC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC,wBAAwB;QACjD,MAAM,CAAC,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,cAAc;IACrD,CAAC;CACJ"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
2
|
+
// Copyright (C) 2026 Jean-Philippe Steinmetz. All rights reserved.
|
|
3
|
+
// SPDX-License-Identifier: MPL-2.0
|
|
4
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
5
|
+
/**
|
|
6
|
+
* The Task named-property identity table (`(PropertySet GUID, LID)` pairs) this pragmatic subset supports, per
|
|
7
|
+
* `[MS-OXPROPS]`/`[MS-OXOTASK]` - the exact `CalendarNamedProperties.ts` precedent, adapted for `PSETID_Task`,
|
|
8
|
+
* shared by `PropertyResolvers.ts` (the read side: `taskValueFor`). Read-only in this pass - see
|
|
9
|
+
* `TaskTarget.ts`'s own doc comment for why there's no write-side (`RopSetProperties`/`RopSaveChangesMessage`)
|
|
10
|
+
* counterpart yet.
|
|
11
|
+
*/
|
|
12
|
+
export const PSETID_TASK = "00062003-0000-0000-c000-000000000046";
|
|
13
|
+
/** `PidLidTaskStatus` (`PT_LONG`) - `[MS-OXOTASK]` §2.2.2.1.5's own `olTaskNotStarted`(0)/`olTaskInProgress`(1)/
|
|
14
|
+
* `olTaskComplete`(2)/`olTaskWaiting`(3)/`olTaskDeferred`(4) values. This data model's `Task.completed` is a
|
|
15
|
+
* plain boolean with no in-progress/waiting/deferred distinction, so only `olTaskNotStarted`/`olTaskComplete`
|
|
16
|
+
* are ever produced - a documented, harmless approximation, the same category as `CalendarNamedProperties.ts`'s
|
|
17
|
+
* own `olWorkingElsewhere` gap. */
|
|
18
|
+
export const LID_TASK_STATUS = 0x8101;
|
|
19
|
+
export const TASK_STATUS_NOT_STARTED = 0;
|
|
20
|
+
export const TASK_STATUS_COMPLETE = 2;
|
|
21
|
+
/** `PidLidPercentComplete` (`PT_R8`, `[MS-OXOTASK]` §2.2.2.1.6) - `0.0`-`1.0`. No partial-progress tracking in
|
|
22
|
+
* this data model either, so this is always `1.0` (complete) or `0.0` (not started), mirroring
|
|
23
|
+
* `LID_TASK_STATUS`'s own two-value approximation. */
|
|
24
|
+
export const LID_PERCENT_COMPLETE = 0x8102;
|
|
25
|
+
/** `PidLidTaskDueDate` (`PT_SYSTIME`, `[MS-OXOTASK]` §2.2.2.2.4). */
|
|
26
|
+
export const LID_TASK_DUE_DATE = 0x8105;
|
|
27
|
+
/** `PidLidTaskComplete` (`PT_BOOLEAN`, `[MS-OXOTASK]` §2.2.2.1.9) - maps directly to `Task.completed`. */
|
|
28
|
+
export const LID_TASK_COMPLETE = 0x811c;
|
|
29
|
+
//# sourceMappingURL=TaskNamedProperties.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TaskNamedProperties.js","sourceRoot":"","sources":["../../../src/rop/TaskNamedProperties.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,mEAAmE;AACnE,mCAAmC;AACnC,+EAA+E;AAE/E;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,sCAAsC,CAAC;AAElE;;;;mCAImC;AACnC,MAAM,CAAC,MAAM,eAAe,GAAG,MAAM,CAAC;AACtC,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC;AACzC,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC;AAEtC;;sDAEsD;AACtD,MAAM,CAAC,MAAM,oBAAoB,GAAG,MAAM,CAAC;AAE3C,qEAAqE;AACrE,MAAM,CAAC,MAAM,iBAAiB,GAAG,MAAM,CAAC;AAExC,0GAA0G;AAC1G,MAAM,CAAC,MAAM,iBAAiB,GAAG,MAAM,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** Degrades to empty-looking values for a `"task:<uid>"` target whose real `Task` has since vanished
|
|
2
|
+
* (soft-deleted or otherwise) - the same "don't fail the whole ROP over one stale row" principle
|
|
3
|
+
* `MessageTarget.resolveMessageInfo`/`ContactTarget.resolveContactInfo` already apply. */
|
|
4
|
+
export async function resolveTaskInfo(target, taskRepo) {
|
|
5
|
+
const uid = target.slice("task:".length);
|
|
6
|
+
const task = await taskRepo.findOne(uid, { ignoreACL: true });
|
|
7
|
+
return {
|
|
8
|
+
title: task?.title ?? "",
|
|
9
|
+
completed: task?.completed ?? false,
|
|
10
|
+
dueDate: task?.dueDate,
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
/** Resolves the tasks directly in `folderUid`, as `"task:<uid>"` target strings, for `RopGetContentsTable`
|
|
14
|
+
* against a `TASKS`-type folder. */
|
|
15
|
+
export async function resolveFolderTasks(folderUid, taskRepo) {
|
|
16
|
+
const tasks = await taskRepo.find({ folderUid }, { ignoreACL: true });
|
|
17
|
+
return tasks.map((t) => `task:${t.uid}`);
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=TaskTarget.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TaskTarget.js","sourceRoot":"","sources":["../../../src/rop/TaskTarget.ts"],"names":[],"mappings":"AAwBA;;0FAE0F;AAC1F,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,MAAc,EAAE,QAAwB;IAC1E,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACzC,MAAM,IAAI,GAAqB,MAAM,QAAQ,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAChF,OAAO;QACH,KAAK,EAAE,IAAI,EAAE,KAAK,IAAI,EAAE;QACxB,SAAS,EAAE,IAAI,EAAE,SAAS,IAAI,KAAK;QACnC,OAAO,EAAE,IAAI,EAAE,OAAO;KACzB,CAAC;AACN,CAAC;AAED;oCACoC;AACpC,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,SAAiB,EAAE,QAAwB;IAChF,MAAM,KAAK,GAAW,MAAM,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9E,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;AAC7C,CAAC"}
|
|
@@ -0,0 +1,84 @@
|
|
|
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
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
8
|
+
// Copyright (C) 2026 Jean-Philippe Steinmetz. All rights reserved.
|
|
9
|
+
// SPDX-License-Identifier: MPL-2.0
|
|
10
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
11
|
+
import { CalendarEventSQL, ContactSQL, FolderSQL, LabelSQL, MailboxSQL, MessageSQL, TaskSQL } from "@rapidmx/restapi/sql";
|
|
12
|
+
import { BaseMapiEmsmdbRoute } from "../BaseMapiEmsmdbRoute.js";
|
|
13
|
+
import { RopLogonHandler } from "../rop/RopLogonHandler.js";
|
|
14
|
+
import { RopReleaseHandler } from "../rop/RopReleaseHandler.js";
|
|
15
|
+
import { RopOpenFolderHandler } from "../rop/RopOpenFolderHandler.js";
|
|
16
|
+
import { RopGetHierarchyTableHandler } from "../rop/RopGetHierarchyTableHandler.js";
|
|
17
|
+
import { RopGetContentsTableHandler } from "../rop/RopGetContentsTableHandler.js";
|
|
18
|
+
import { RopSetColumnsHandler } from "../rop/RopSetColumnsHandler.js";
|
|
19
|
+
import { RopQueryRowsHandler } from "../rop/RopQueryRowsHandler.js";
|
|
20
|
+
import { RopOpenMessageHandler } from "../rop/RopOpenMessageHandler.js";
|
|
21
|
+
import { RopGetPropertiesSpecificHandler } from "../rop/RopGetPropertiesSpecificHandler.js";
|
|
22
|
+
import { RopOpenStreamHandler } from "../rop/RopOpenStreamHandler.js";
|
|
23
|
+
import { RopReadStreamHandler } from "../rop/RopReadStreamHandler.js";
|
|
24
|
+
import { RopCreateMessageHandler } from "../rop/RopCreateMessageHandler.js";
|
|
25
|
+
import { RopSetPropertiesHandler } from "../rop/RopSetPropertiesHandler.js";
|
|
26
|
+
import { RopWriteStreamHandler } from "../rop/RopWriteStreamHandler.js";
|
|
27
|
+
import { RopSaveChangesMessageHandler } from "../rop/RopSaveChangesMessageHandler.js";
|
|
28
|
+
import { RopSubmitMessageHandler } from "../rop/RopSubmitMessageHandler.js";
|
|
29
|
+
import { RopGetPropertyIdsFromNamesHandler } from "../rop/RopGetPropertyIdsFromNamesHandler.js";
|
|
30
|
+
import { RopDeleteMessagesHandler } from "../rop/RopDeleteMessagesHandler.js";
|
|
31
|
+
import { RopDeleteFolderHandler } from "../rop/RopDeleteFolderHandler.js";
|
|
32
|
+
import { RopFastTransferSourceCopyToHandler } from "../rop/RopFastTransferSourceCopyToHandler.js";
|
|
33
|
+
import { RopFastTransferSourceCopyPropertiesHandler } from "../rop/RopFastTransferSourceCopyPropertiesHandler.js";
|
|
34
|
+
import { RopFastTransferSourceGetBufferHandler } from "../rop/RopFastTransferSourceGetBufferHandler.js";
|
|
35
|
+
import { RouteDecorators } from "@rapidrest/service-core";
|
|
36
|
+
const { Route } = RouteDecorators;
|
|
37
|
+
/**
|
|
38
|
+
* SQL-backed concrete `BaseMapiEmsmdbRoute`, mounted at `/mapi/emsmdb`. Exported from this plugin's
|
|
39
|
+
* `./sql` entry point, so the server host mounts it without a wrapper class of its
|
|
40
|
+
* own.
|
|
41
|
+
*
|
|
42
|
+
* @author Jean-Philippe Steinmetz
|
|
43
|
+
*/
|
|
44
|
+
let MapiEmsmdbRouteSQL = class MapiEmsmdbRouteSQL extends BaseMapiEmsmdbRoute {
|
|
45
|
+
constructor() {
|
|
46
|
+
super(...arguments);
|
|
47
|
+
this.mailboxClass = MailboxSQL;
|
|
48
|
+
this.folderClass = FolderSQL;
|
|
49
|
+
this.messageClass = MessageSQL;
|
|
50
|
+
this.calendarEventClass = CalendarEventSQL;
|
|
51
|
+
this.contactClass = ContactSQL;
|
|
52
|
+
this.taskClass = TaskSQL;
|
|
53
|
+
this.labelClass = LabelSQL;
|
|
54
|
+
this.ropHandlerClasses = [
|
|
55
|
+
RopLogonHandler,
|
|
56
|
+
RopReleaseHandler,
|
|
57
|
+
RopOpenFolderHandler,
|
|
58
|
+
RopGetHierarchyTableHandler,
|
|
59
|
+
RopGetContentsTableHandler,
|
|
60
|
+
RopSetColumnsHandler,
|
|
61
|
+
RopQueryRowsHandler,
|
|
62
|
+
RopOpenMessageHandler,
|
|
63
|
+
RopGetPropertiesSpecificHandler,
|
|
64
|
+
RopOpenStreamHandler,
|
|
65
|
+
RopReadStreamHandler,
|
|
66
|
+
RopCreateMessageHandler,
|
|
67
|
+
RopSetPropertiesHandler,
|
|
68
|
+
RopWriteStreamHandler,
|
|
69
|
+
RopSaveChangesMessageHandler,
|
|
70
|
+
RopSubmitMessageHandler,
|
|
71
|
+
RopGetPropertyIdsFromNamesHandler,
|
|
72
|
+
RopDeleteMessagesHandler,
|
|
73
|
+
RopDeleteFolderHandler,
|
|
74
|
+
RopFastTransferSourceCopyToHandler,
|
|
75
|
+
RopFastTransferSourceCopyPropertiesHandler,
|
|
76
|
+
RopFastTransferSourceGetBufferHandler,
|
|
77
|
+
];
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
MapiEmsmdbRouteSQL = __decorate([
|
|
81
|
+
Route("/mapi/emsmdb")
|
|
82
|
+
], MapiEmsmdbRouteSQL);
|
|
83
|
+
export { MapiEmsmdbRouteSQL };
|
|
84
|
+
//# sourceMappingURL=MapiEmsmdbRouteSQL.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MapiEmsmdbRouteSQL.js","sourceRoot":"","sources":["../../../src/sql/MapiEmsmdbRouteSQL.ts"],"names":[],"mappings":";;;;;;AAAA,+EAA+E;AAC/E,mEAAmE;AACnE,mCAAmC;AACnC,+EAA+E;AAC/E,OAAO,EAAE,gBAAgB,EAAE,UAAU,EAAE,SAAS,EAAE,QAAQ,EAAE,UAAU,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAC1H,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAChE,OAAO,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAC;AACtE,OAAO,EAAE,2BAA2B,EAAE,MAAM,uCAAuC,CAAC;AACpF,OAAO,EAAE,0BAA0B,EAAE,MAAM,sCAAsC,CAAC;AAClF,OAAO,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAC;AACtE,OAAO,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AACpE,OAAO,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AACxE,OAAO,EAAE,+BAA+B,EAAE,MAAM,2CAA2C,CAAC;AAC5F,OAAO,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAC;AACtE,OAAO,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAC;AACtE,OAAO,EAAE,uBAAuB,EAAE,MAAM,mCAAmC,CAAC;AAC5E,OAAO,EAAE,uBAAuB,EAAE,MAAM,mCAAmC,CAAC;AAC5E,OAAO,EAAE,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AACxE,OAAO,EAAE,4BAA4B,EAAE,MAAM,wCAAwC,CAAC;AACtF,OAAO,EAAE,uBAAuB,EAAE,MAAM,mCAAmC,CAAC;AAC5E,OAAO,EAAE,iCAAiC,EAAE,MAAM,6CAA6C,CAAC;AAChG,OAAO,EAAE,wBAAwB,EAAE,MAAM,oCAAoC,CAAC;AAC9E,OAAO,EAAE,sBAAsB,EAAE,MAAM,kCAAkC,CAAC;AAC1E,OAAO,EAAE,kCAAkC,EAAE,MAAM,8CAA8C,CAAC;AAClG,OAAO,EAAE,0CAA0C,EAAE,MAAM,sDAAsD,CAAC;AAClH,OAAO,EAAE,qCAAqC,EAAE,MAAM,iDAAiD,CAAC;AACxG,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,MAAM,EAAE,KAAK,EAAE,GAAG,eAAe,CAAC;AAElC;;;;;;GAMG;AAEI,IAAM,kBAAkB,GAAxB,MAAM,kBAAmB,SAAQ,mBAA+B;IAAhE;;QACO,iBAAY,GAAQ,UAAU,CAAC;QAC/B,gBAAW,GAAQ,SAAS,CAAC;QAC7B,iBAAY,GAAQ,UAAU,CAAC;QAC/B,uBAAkB,GAAQ,gBAAgB,CAAC;QAC3C,iBAAY,GAAQ,UAAU,CAAC;QAC/B,cAAS,GAAQ,OAAO,CAAC;QACzB,eAAU,GAAQ,QAAQ,CAAC;QAC3B,sBAAiB,GAAU;YACjC,eAAe;YACf,iBAAiB;YACjB,oBAAoB;YACpB,2BAA2B;YAC3B,0BAA0B;YAC1B,oBAAoB;YACpB,mBAAmB;YACnB,qBAAqB;YACrB,+BAA+B;YAC/B,oBAAoB;YACpB,oBAAoB;YACpB,uBAAuB;YACvB,uBAAuB;YACvB,qBAAqB;YACrB,4BAA4B;YAC5B,uBAAuB;YACvB,iCAAiC;YACjC,wBAAwB;YACxB,sBAAsB;YACtB,kCAAkC;YAClC,0CAA0C;YAC1C,qCAAqC;SACxC,CAAC;IACN,CAAC;CAAA,CAAA;AAhCY,kBAAkB;IAD9B,KAAK,CAAC,cAAc,CAAC;GACT,kBAAkB,CAgC9B"}
|
|
@@ -0,0 +1,33 @@
|
|
|
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
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
8
|
+
// Copyright (C) 2026 Jean-Philippe Steinmetz. All rights reserved.
|
|
9
|
+
// SPDX-License-Identifier: MPL-2.0
|
|
10
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
11
|
+
import { ContactSQL, MailboxSQL } from "@rapidmx/restapi/sql";
|
|
12
|
+
import { BaseMapiNspiRoute } from "../BaseMapiNspiRoute.js";
|
|
13
|
+
import { RouteDecorators } from "@rapidrest/service-core";
|
|
14
|
+
const { Route } = RouteDecorators;
|
|
15
|
+
/**
|
|
16
|
+
* SQL-backed concrete `BaseMapiNspiRoute`, mounted at `/mapi/nspi`. Exported from this plugin's
|
|
17
|
+
* `./sql` entry point, so the server host mounts it without a wrapper class of its
|
|
18
|
+
* own.
|
|
19
|
+
*
|
|
20
|
+
* @author Jean-Philippe Steinmetz
|
|
21
|
+
*/
|
|
22
|
+
let MapiNspiRouteSQL = class MapiNspiRouteSQL extends BaseMapiNspiRoute {
|
|
23
|
+
constructor() {
|
|
24
|
+
super(...arguments);
|
|
25
|
+
this.mailboxClass = MailboxSQL;
|
|
26
|
+
this.contactClass = ContactSQL;
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
MapiNspiRouteSQL = __decorate([
|
|
30
|
+
Route("/mapi/nspi")
|
|
31
|
+
], MapiNspiRouteSQL);
|
|
32
|
+
export { MapiNspiRouteSQL };
|
|
33
|
+
//# sourceMappingURL=MapiNspiRouteSQL.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MapiNspiRouteSQL.js","sourceRoot":"","sources":["../../../src/sql/MapiNspiRouteSQL.ts"],"names":[],"mappings":";;;;;;AAAA,+EAA+E;AAC/E,mEAAmE;AACnE,mCAAmC;AACnC,+EAA+E;AAC/E,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,MAAM,EAAE,KAAK,EAAE,GAAG,eAAe,CAAC;AAElC;;;;;;GAMG;AAEI,IAAM,gBAAgB,GAAtB,MAAM,gBAAiB,SAAQ,iBAA6B;IAA5D;;QACO,iBAAY,GAAQ,UAAU,CAAC;QAC/B,iBAAY,GAAQ,UAAU,CAAC;IAC7C,CAAC;CAAA,CAAA;AAHY,gBAAgB;IAD5B,KAAK,CAAC,YAAY,CAAC;GACP,gBAAgB,CAG5B"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
2
|
+
// Copyright (C) 2026 Jean-Philippe Steinmetz. All rights reserved.
|
|
3
|
+
// SPDX-License-Identifier: MPL-2.0
|
|
4
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
5
|
+
export * from "./MapiEmsmdbRouteSQL.js";
|
|
6
|
+
export * from "./MapiNspiRouteSQL.js";
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/sql/index.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,mEAAmE;AACnE,mCAAmC;AACnC,+EAA+E;AAC/E,cAAc,yBAAyB,CAAC;AACxC,cAAc,uBAAuB,CAAC"}
|
package/dist/lib/sql.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
2
|
+
// Copyright (C) 2026 Jean-Philippe Steinmetz. All rights reserved.
|
|
3
|
+
// SPDX-License-Identifier: MPL-2.0
|
|
4
|
+
///////////////////////////////////////////////////////////////////////////////
|
|
5
|
+
export * from "./sql/index.js";
|
|
6
|
+
//# sourceMappingURL=sql.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sql.js","sourceRoot":"","sources":["../../src/sql.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,mEAAmE;AACnE,mCAAmC;AACnC,+EAA+E;AAC/E,cAAc,gBAAgB,CAAC"}
|