@bumail/smtp 0.9.0 → 0.11.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/README.md +6 -4
- package/dist/chunks/{index-wc25ywta.js → index-byaw0g4k.js} +88 -3
- package/dist/chunks/{index-wc25ywta.js.map → index-byaw0g4k.js.map} +5 -3
- package/dist/client/dsn.d.ts +21 -0
- package/dist/client/dsn.d.ts.map +1 -0
- package/dist/client/index.js +98 -5
- package/dist/client/index.js.map +6 -5
- package/dist/client/options.d.ts +21 -0
- package/dist/client/options.d.ts.map +1 -1
- package/dist/client/settings.d.ts +2 -0
- package/dist/client/settings.d.ts.map +1 -1
- package/dist/client/transaction.d.ts +2 -0
- package/dist/client/transaction.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +120 -15
- package/dist/index.js.map +10 -8
- package/dist/protocol/command.d.ts +2 -0
- package/dist/protocol/command.d.ts.map +1 -1
- package/dist/protocol/orcpt.d.ts +27 -0
- package/dist/protocol/orcpt.d.ts.map +1 -0
- package/dist/protocol/xtext.d.ts +14 -0
- package/dist/protocol/xtext.d.ts.map +1 -0
- package/dist/server/commands.d.ts.map +1 -1
- package/dist/server/dsn-params.d.ts +21 -0
- package/dist/server/dsn-params.d.ts.map +1 -0
- package/dist/server/envelope.d.ts +5 -0
- package/dist/server/envelope.d.ts.map +1 -0
- package/dist/server/intake.d.ts.map +1 -1
- package/dist/server/options.d.ts +28 -0
- package/dist/server/options.d.ts.map +1 -1
- package/dist/server/state.d.ts +4 -1
- package/dist/server/state.d.ts.map +1 -1
- package/dist/server/transaction.d.ts +2 -2
- package/dist/server/transaction.d.ts.map +1 -1
- package/docs/guide.md +122 -5
- package/docs/roadmap.md +15 -5
- package/docs/troubleshooting.md +61 -6
- package/package.json +3 -3
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Encodes text as `xtext` (RFC 3461 §4), the form DSN parameter values take
|
|
3
|
+
* on the wire: every byte of its UTF-8 form outside `!` to `~` (33 to 126),
|
|
4
|
+
* and `+` and `=`, becomes `+` and two upper-case hex digits.
|
|
5
|
+
*/
|
|
6
|
+
export declare function encodeXtext(text: string): string;
|
|
7
|
+
/**
|
|
8
|
+
* Decodes `xtext` (RFC 3461 §4) read as UTF-8, or `undefined` when it is
|
|
9
|
+
* not xtext: a `+` not followed by two upper-case hex digits, a character
|
|
10
|
+
* outside `!` to `~` that is not encoded, a bare `=`, or bytes that are not
|
|
11
|
+
* valid UTF-8.
|
|
12
|
+
*/
|
|
13
|
+
export declare function decodeXtext(value: string): string | undefined;
|
|
14
|
+
//# sourceMappingURL=xtext.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"xtext.d.ts","sourceRoot":"","sources":["../../src/protocol/xtext.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAShD;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAwB7D"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../src/server/commands.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../src/server/commands.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAwG/C,0EAA0E;AAC1E,wBAAsB,UAAU,CAC/B,UAAU,EAAE,UAAU,EACtB,IAAI,EAAE,MAAM,GACV,OAAO,CAAC,IAAI,CAAC,CA6Cf"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { DsnNotify, EnvelopeRecipient } from './options';
|
|
2
|
+
import type { Transaction } from './state';
|
|
3
|
+
/** The DSN parameters (RFC 3461 §4) MAIL FROM takes. */
|
|
4
|
+
export declare const MAIL_DSN: Set<string>;
|
|
5
|
+
/** The DSN parameters (RFC 3461 §4) RCPT TO takes. */
|
|
6
|
+
export declare const RCPT_DSN: Set<string>;
|
|
7
|
+
/**
|
|
8
|
+
* Reads one DSN parameter of MAIL FROM into the transaction under way.
|
|
9
|
+
* The text of the `501` to answer when the value is wrong, else `undefined`.
|
|
10
|
+
*/
|
|
11
|
+
export declare function readMailDsn(into: Pick<Transaction, 'ret' | 'envid'>, key: string, value: string, repeated: readonly string[]): string | undefined;
|
|
12
|
+
/**
|
|
13
|
+
* Reads one DSN parameter of RCPT TO into `into`; `smtputf8` is whether the
|
|
14
|
+
* transaction uses SMTPUTF8 (the form of a `utf-8` ORCPT). The text of the `501` to
|
|
15
|
+
* answer when the value is wrong, else `undefined`.
|
|
16
|
+
*/
|
|
17
|
+
export declare function readRcptDsn(into: {
|
|
18
|
+
notify?: 'NEVER' | readonly DsnNotify[];
|
|
19
|
+
orcpt?: EnvelopeRecipient['orcpt'];
|
|
20
|
+
}, key: string, value: string, repeated: readonly string[], smtputf8?: boolean): string | undefined;
|
|
21
|
+
//# sourceMappingURL=dsn-params.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dsn-params.d.ts","sourceRoot":"","sources":["../../src/server/dsn-params.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAC9D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE3C,wDAAwD;AACxD,eAAO,MAAM,QAAQ,aAA4B,CAAC;AAClD,sDAAsD;AACtD,eAAO,MAAM,QAAQ,aAA+B,CAAC;AAoDrD;;;GAGG;AACH,wBAAgB,WAAW,CAC1B,IAAI,EAAE,IAAI,CAAC,WAAW,EAAE,KAAK,GAAG,OAAO,CAAC,EACxC,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,SAAS,MAAM,EAAE,GACzB,MAAM,GAAG,SAAS,CAYpB;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAC1B,IAAI,EAAE;IACL,MAAM,CAAC,EAAE,OAAO,GAAG,SAAS,SAAS,EAAE,CAAC;IACxC,KAAK,CAAC,EAAE,iBAAiB,CAAC,OAAO,CAAC,CAAC;CACnC,EACD,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,SAAS,MAAM,EAAE,EAC3B,QAAQ,UAAQ,GACd,MAAM,GAAG,SAAS,CAYpB"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { ReceivedMessage } from './options';
|
|
2
|
+
import type { Transaction } from './state';
|
|
3
|
+
/** The envelope of a message as `onData` is told it, from the transaction that ended in DATA. */
|
|
4
|
+
export declare function envelopeOf(transaction: Transaction): ReceivedMessage['envelope'];
|
|
5
|
+
//# sourceMappingURL=envelope.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"envelope.d.ts","sourceRoot":"","sources":["../../src/server/envelope.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAE3C,iGAAiG;AACjG,wBAAgB,UAAU,CACzB,WAAW,EAAE,WAAW,GACtB,eAAe,CAAC,UAAU,CAAC,CAU7B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"intake.d.ts","sourceRoot":"","sources":["../../src/server/intake.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,KAAK,EAAS,MAAM,mBAAmB,CAAC;AACtD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"intake.d.ts","sourceRoot":"","sources":["../../src/server/intake.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,KAAK,EAAS,MAAM,mBAAmB,CAAC;AACtD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAiB/C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAK3C;;;;;;;;;;;;GAYG;AACH,qBAAa,MAAM;;IAClB,QAAQ,CAAC,EAAE,SAAsD;gBAoBrD,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW;IAqE5D,iEAAiE;IAC3D,KAAK,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,OAAO,CAAC;QAAC,IAAI,EAAE,UAAU,CAAA;KAAE,CAAC;IAmB5E,0CAA0C;IACpC,MAAM,IAAI,OAAO,CAAC,KAAK,CAAC;IAuB9B,uEAAuE;IACvE,KAAK,IAAI,IAAI;CA2Eb"}
|
package/dist/server/options.d.ts
CHANGED
|
@@ -20,6 +20,28 @@ export interface Session {
|
|
|
20
20
|
*/
|
|
21
21
|
readonly data: Record<string, unknown>;
|
|
22
22
|
}
|
|
23
|
+
/** What a recipient asked to be told (RFC 3461 §4.1): `NEVER`, or the events among these. */
|
|
24
|
+
export type DsnNotify = 'SUCCESS' | 'FAILURE' | 'DELAY';
|
|
25
|
+
/**
|
|
26
|
+
* One recipient of a message, with the DSN parameters of its `RCPT TO`
|
|
27
|
+
* (RFC 3461 §4.1, §4.2). The server only passes them on: the app honours
|
|
28
|
+
* them when it relays the message or generates a DSN.
|
|
29
|
+
*/
|
|
30
|
+
export interface EnvelopeRecipient {
|
|
31
|
+
/** The same address as the matching entry of `Envelope.to`. */
|
|
32
|
+
readonly address: string;
|
|
33
|
+
/**
|
|
34
|
+
* `NOTIFY=`: `'NEVER'`, or the events, in the order given, with no
|
|
35
|
+
* duplicate. Absent when the client sent none: the default is a DSN on
|
|
36
|
+
* failure, delay and success never (RFC 3461 §4.1).
|
|
37
|
+
*/
|
|
38
|
+
readonly notify?: 'NEVER' | readonly DsnNotify[];
|
|
39
|
+
/** `ORCPT=`: the original recipient, its `type` as sent (`rfc822`) and its `address` decoded from xtext. */
|
|
40
|
+
readonly orcpt?: {
|
|
41
|
+
readonly type: string;
|
|
42
|
+
readonly address: string;
|
|
43
|
+
};
|
|
44
|
+
}
|
|
23
45
|
/** The sender and recipients of one message. */
|
|
24
46
|
export interface Envelope {
|
|
25
47
|
/** `''` for the null reverse-path `<>`: a bounce. */
|
|
@@ -30,6 +52,12 @@ export interface Envelope {
|
|
|
30
52
|
readonly smtputf8: boolean;
|
|
31
53
|
/** `7BIT` or `8BITMIME`, as MAIL FROM's BODY= said; `7BIT` when it said nothing. */
|
|
32
54
|
readonly body: '7BIT' | '8BITMIME';
|
|
55
|
+
/** `RET=` of MAIL FROM (RFC 3461 §4.3), upper case: whether a DSN returns the whole message or its headers. Absent when not sent. */
|
|
56
|
+
readonly ret?: 'FULL' | 'HDRS';
|
|
57
|
+
/** `ENVID=` of MAIL FROM (RFC 3461 §4.4), decoded from xtext: at most 100 printable ASCII characters. Absent when not sent. */
|
|
58
|
+
readonly envid?: string;
|
|
59
|
+
/** The recipients accepted, in the order of `to`, each with its `NOTIFY` and `ORCPT`. */
|
|
60
|
+
readonly recipients: readonly EnvelopeRecipient[];
|
|
33
61
|
}
|
|
34
62
|
/** A message on its way in, handed to `onData` as the client sends it. */
|
|
35
63
|
export interface ReceivedMessage {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../../src/server/options.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,sGAAsG;AACtG,MAAM,WAAW,OAAO;IACvB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,8CAA8C;IAC9C,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,gDAAgD;IAChD,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,sCAAsC;IACtC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,gDAAgD;IAChD,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB;;;;OAIG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACvC;AAED,gDAAgD;AAChD,MAAM,WAAW,QAAQ;IACxB,qDAAqD;IACrD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,uFAAuF;IACvF,QAAQ,CAAC,EAAE,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/B,gDAAgD;IAChD,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,oFAAoF;IACpF,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../../src/server/options.ts"],"names":[],"mappings":"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"}
|
package/dist/server/state.d.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
import type { Envelope } from './options';
|
|
1
|
+
import type { Envelope, EnvelopeRecipient } from './options';
|
|
2
2
|
/** A mail transaction in progress (RFC 5321 §3.3). */
|
|
3
3
|
export interface Transaction {
|
|
4
4
|
from?: string;
|
|
5
5
|
to: string[];
|
|
6
|
+
recipients: EnvelopeRecipient[];
|
|
7
|
+
ret?: Envelope['ret'];
|
|
8
|
+
envid?: string;
|
|
6
9
|
smtputf8: boolean;
|
|
7
10
|
body: Envelope['body'];
|
|
8
11
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../src/server/state.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../../src/server/state.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAE7D,sDAAsD;AACtD,MAAM,WAAW,WAAW;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,EAAE,EAAE,MAAM,EAAE,CAAC;IACb,UAAU,EAAE,iBAAiB,EAAE,CAAC;IAChC,GAAG,CAAC,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;CACvB;AAED,6CAA6C;AAC7C,MAAM,MAAM,OAAO,GAChB,SAAS,GACT,MAAM,GACN,YAAY,GACZ,iBAAiB,GACjB,qBAAqB,CAAC;AAEzB,kDAAkD;AAClD,MAAM,WAAW,KAAK;IACrB,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,KAAK,EAAE,OAAO,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,MAAM,EAAE,OAAO,CAAC;IAChB,WAAW,EAAE,WAAW,CAAC;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;CACrB;AAED,eAAO,MAAM,gBAAgB,QAAO,WAKlC,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Connection } from './connection';
|
|
2
|
-
/** `MAIL FROM:<path> [SIZE=] [BODY=] [SMTPUTF8] [AUTH=]` (RFC 5321 §4.1.1.2). */
|
|
2
|
+
/** `MAIL FROM:<path> [SIZE=] [BODY=] [SMTPUTF8] [AUTH=] [RET=] [ENVID=]` (RFC 5321 §4.1.1.2, RFC 3461 §4.3, §4.4). */
|
|
3
3
|
export declare function mail(connection: Connection, argument: string): Promise<void>;
|
|
4
|
-
/** `RCPT TO:<path
|
|
4
|
+
/** `RCPT TO:<path> [NOTIFY=] [ORCPT=]` (RFC 5321 §4.1.1.3, RFC 3461 §4.1, §4.2), where relaying is refused without AUTH. */
|
|
5
5
|
export declare function rcpt(connection: Connection, argument: string): Promise<void>;
|
|
6
6
|
//# sourceMappingURL=transaction.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"transaction.d.ts","sourceRoot":"","sources":["../../src/server/transaction.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"transaction.d.ts","sourceRoot":"","sources":["../../src/server/transaction.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAY/C,sHAAsH;AACtH,wBAAsB,IAAI,CACzB,UAAU,EAAE,UAAU,EACtB,QAAQ,EAAE,MAAM,GACd,OAAO,CAAC,IAAI,CAAC,CAkEf;AAED,4HAA4H;AAC5H,wBAAsB,IAAI,CACzB,UAAU,EAAE,UAAU,EACtB,QAAQ,EAAE,MAAM,GACd,OAAO,CAAC,IAAI,CAAC,CA0Df"}
|
package/docs/guide.md
CHANGED
|
@@ -192,7 +192,9 @@ Every reply the server sends of its own:
|
|
|
192
192
|
| `503 5.5.1 Send MAIL first` | RCPT or DATA before MAIL |
|
|
193
193
|
| `554 5.5.1 No valid recipients` | DATA with no recipient accepted; what the client pipelined behind it in the same write is dropped, not run as commands |
|
|
194
194
|
| `501 5.5.4 Syntax: MAIL FROM:<address>` / `RCPT TO:<address>` / `DATA` | the command's syntax is wrong |
|
|
195
|
-
| `555 5.5.4 <PARAM> is not supported` | an ESMTP parameter other than SIZE, BODY, SMTPUTF8 and
|
|
195
|
+
| `555 5.5.4 <PARAM> is not supported` | an ESMTP parameter other than SIZE, BODY, SMTPUTF8, AUTH, RET and ENVID on MAIL, or other than NOTIFY and ORCPT on RCPT |
|
|
196
|
+
| `501 5.5.4 Syntax: RET=…` / `ENVID=…` / `NOTIFY=…` / `ORCPT=…` | a DSN parameter with a malformed value (RFC 3461 §4) |
|
|
197
|
+
| `501 5.5.4 <PARAM> given twice` | a DSN parameter repeated on one command |
|
|
196
198
|
| `555 5.5.4 <PARAM> needs EHLO` | a parameter after HELO |
|
|
197
199
|
| `501 5.5.4 Syntax: SIZE=<bytes>` | SIZE is not a number |
|
|
198
200
|
| `501 5.5.4 BODY is 7BIT or 8BITMIME` | another BODY value |
|
|
@@ -463,6 +465,20 @@ export interface Envelope {
|
|
|
463
465
|
readonly smtputf8: boolean;
|
|
464
466
|
/** `7BIT` or `8BITMIME`, as MAIL FROM's BODY= said; `7BIT` when it said nothing. */
|
|
465
467
|
readonly body: '7BIT' | '8BITMIME';
|
|
468
|
+
/** MAIL FROM's RET= (RFC 3461 §4.3); absent when not sent. */
|
|
469
|
+
readonly ret?: 'FULL' | 'HDRS';
|
|
470
|
+
/** MAIL FROM's ENVID=, decoded from xtext (RFC 3461 §4.4); absent when not sent. */
|
|
471
|
+
readonly envid?: string;
|
|
472
|
+
/** The recipients accepted, in the order of `to`, with their DSN parameters. */
|
|
473
|
+
readonly recipients: readonly EnvelopeRecipient[];
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
export interface EnvelopeRecipient {
|
|
477
|
+
readonly address: string; // the matching entry of `to`
|
|
478
|
+
/** NOTIFY= (RFC 3461 §4.1): `'NEVER'` or the events given; absent when not sent. */
|
|
479
|
+
readonly notify?: 'NEVER' | readonly ('SUCCESS' | 'FAILURE' | 'DELAY')[];
|
|
480
|
+
/** ORCPT= (RFC 3461 §4.2): the type as sent and the address decoded (xtext, or RFC 6533 for `utf-8`). */
|
|
481
|
+
readonly orcpt?: { readonly type: string; readonly address: string };
|
|
466
482
|
}
|
|
467
483
|
```
|
|
468
484
|
|
|
@@ -636,6 +652,103 @@ const server = createSmtpServer({
|
|
|
636
652
|
await server.listen({ port: 25 });
|
|
637
653
|
```
|
|
638
654
|
|
|
655
|
+
## Delivery status notifications (DSN)
|
|
656
|
+
|
|
657
|
+
The server advertises `DSN` (RFC 3461) in its EHLO reply, always (no option to
|
|
658
|
+
turn it off). A client may then send `RET=FULL|HDRS` and `ENVID=<xtext>` on
|
|
659
|
+
`MAIL FROM`, and `NOTIFY=NEVER` (or a comma list of `SUCCESS`, `FAILURE`,
|
|
660
|
+
`DELAY`) and `ORCPT=<addr-type>;<xtext>` on `RCPT TO`. The server checks
|
|
661
|
+
their syntax, decodes them (`rfc822` as xtext, `utf-8` per RFC 6533, below)
|
|
662
|
+
and puts them on the `Envelope` `onData`
|
|
663
|
+
receives: `ret` and `envid`, and for each accepted recipient `notify` and
|
|
664
|
+
`orcpt` in `recipients`. `to` is unchanged.
|
|
665
|
+
|
|
666
|
+
`ORCPT` keeps the type as sent. `utf-8`, matched in any case, is read per RFC
|
|
667
|
+
6533; every other type, `rfc822` included, is xtext (RFC 3461 §4, §5.1(b)). `utf-8`
|
|
668
|
+
(RFC 6533 §3) is `utf-8-addr-xtext`: printable ASCII as itself, and
|
|
669
|
+
`\x{HEX}` (2 to 6 hexadecimal digits) for a non-ASCII character and for
|
|
670
|
+
space, `\`, `+` and `=`; when the transaction used `SMTPUTF8` on `MAIL FROM`
|
|
671
|
+
it may also be `utf-8-addr-unitext`, with the non-ASCII characters as raw
|
|
672
|
+
UTF-8. The address in `orcpt.address` is decoded in both cases. A raw
|
|
673
|
+
non-ASCII address without `SMTPUTF8`, or a malformed or out-of-range escape
|
|
674
|
+
(or one for NUL or a control character RFC 6533's grammar has no escape for,
|
|
675
|
+
`0A` to `0F` and `1A` to `1F`) is `501 5.5.4`. Escapes are accepted leniently:
|
|
676
|
+
leading zeros and escaped printable ASCII, which RFC 6533's HEXPOINT does not
|
|
677
|
+
produce. The 500 characters of a `utf-8` value count as sent on the wire.
|
|
678
|
+
|
|
679
|
+
The server does nothing else with them. Once it advertises DSN, the app that
|
|
680
|
+
receives these parameters must honour them, or the client is told something
|
|
681
|
+
untrue:
|
|
682
|
+
|
|
683
|
+
- An app that **relays** the message (a queue, a smarthost) passes them on
|
|
684
|
+
to the next hop, or generates the DSN itself when the next hop does not
|
|
685
|
+
advertise DSN.
|
|
686
|
+
- An app that **delivers locally** generates a DSN for a recipient that asked
|
|
687
|
+
for `SUCCESS` and was delivered, returning the message or only its
|
|
688
|
+
headers as `ret` says, and puts `envid` and `orcpt` in the report.
|
|
689
|
+
- `NOTIFY=NEVER` means no DSN for that recipient, a bounce included.
|
|
690
|
+
|
|
691
|
+
```ts
|
|
692
|
+
import { createSmtpServer } from '@bumail/smtp';
|
|
693
|
+
|
|
694
|
+
createSmtpServer({
|
|
695
|
+
hostname: 'mx.example.com',
|
|
696
|
+
localDomains: ['example.com'],
|
|
697
|
+
async onData({ envelope }) {
|
|
698
|
+
for (const { address, notify, orcpt } of envelope.recipients) {
|
|
699
|
+
if (notify === 'NEVER') continue; // no DSN for this one, ever
|
|
700
|
+
// deliver to `address`; report to `envelope.from` as `notify` asks,
|
|
701
|
+
// quoting `envelope.envid` and `orcpt?.address`
|
|
702
|
+
}
|
|
703
|
+
},
|
|
704
|
+
});
|
|
705
|
+
```
|
|
706
|
+
|
|
707
|
+
A malformed or repeated DSN parameter is answered `501 5.5.4`; a parameter
|
|
708
|
+
after `HELO` gets `555 <PARAM> needs EHLO`.
|
|
709
|
+
|
|
710
|
+
### Sending DSN parameters
|
|
711
|
+
|
|
712
|
+
`sendMail` takes `ret`, `envid` and `recipients` (the shape of
|
|
713
|
+
`Envelope.recipients`: `address`, `notify`, `orcpt`, matched to `to` by
|
|
714
|
+
address), so a received envelope can be passed through when a relay hands the
|
|
715
|
+
message on. They are written only when the next hop's EHLO lists `DSN`, with
|
|
716
|
+
`envid` and the `orcpt` address xtext-encoded (RFC 3461 §4). A `utf-8`
|
|
717
|
+
`orcpt` is encoded per RFC 6533 §3: `utf-8-addr-unitext` (non-ASCII kept as
|
|
718
|
+
UTF-8) when the transaction uses `SMTPUTF8`, else `utf-8-addr-xtext`
|
|
719
|
+
(`\x{HEX}`). An `orcpt` of another type is relayed as `type;xtext(address)`, the
|
|
720
|
+
type's case kept (RFC 3461 §5.2(d)). An `orcpt` whose address is not ASCII (type `rfc822`), holds a
|
|
721
|
+
lone surrogate (any type) or has no RFC 6533 escape (`utf-8`), or a `utf-8` one
|
|
722
|
+
over 500 characters as sent, is left out of the command, not refused. One of
|
|
723
|
+
another type over 500 characters as sent is refused with `INVALID_OPTION`.
|
|
724
|
+
A relay never fails on what a received envelope carries. The type is written as given. When the next hop lists no `DSN` nothing is sent, the delivery goes
|
|
725
|
+
through as usual, and the result says `dsnOffered: false`: the relay must then
|
|
726
|
+
generate the DSNs itself (RFC 3461 §5.2.2).
|
|
727
|
+
|
|
728
|
+
```ts
|
|
729
|
+
import { sendMail } from '@bumail/smtp/client';
|
|
730
|
+
|
|
731
|
+
const result = await sendMail(message, {
|
|
732
|
+
host: 'smtp.relay.example',
|
|
733
|
+
from: 'a@example.com',
|
|
734
|
+
to: ['b@example.net'],
|
|
735
|
+
ret: 'HDRS',
|
|
736
|
+
envid: 'q-1042',
|
|
737
|
+
recipients: [{ address: 'b@example.net', notify: ['FAILURE', 'DELAY'] }],
|
|
738
|
+
});
|
|
739
|
+
if (!result.dsnOffered) {
|
|
740
|
+
// the next hop never saw NOTIFY: send the 'relayed' DSN here
|
|
741
|
+
}
|
|
742
|
+
```
|
|
743
|
+
|
|
744
|
+
`dsnOffered` is the server listing `DSN` in EHLO; it does not prove the next
|
|
745
|
+
hop will send a DSN. These are `INVALID_OPTION`: a wrong `ret`, `notify` or
|
|
746
|
+
`orcpt` shape; an `envid` over 100 characters once xtext-encoded (RFC 3461
|
|
747
|
+
§4.4); an `orcpt` over 500 characters once encoded (§4.2; for a `utf-8` `orcpt`, the
|
|
748
|
+
unitext form: raw UTF-8 octets, `\x{HH}` = 6), whose type is not a
|
|
749
|
+
token, or whose address is empty; a `recipients` entry not in `to`, or named
|
|
750
|
+
twice.
|
|
751
|
+
|
|
639
752
|
## Delivering into @bumail/store
|
|
640
753
|
|
|
641
754
|
[`@bumail/store`](https://github.com/softistx/bumail/tree/develop/packages/store) keeps
|
|
@@ -1893,6 +2006,8 @@ console.log(chunk.done, reader.bareLineBreaks);
|
|
|
1893
2006
|
| SIZE | RFC 1870 |
|
|
1894
2007
|
| PIPELINING | RFC 2920 |
|
|
1895
2008
|
| 8BITMIME | RFC 6152 |
|
|
2009
|
+
| DSN parameters (`RET`, `ENVID`, `NOTIFY`, `ORCPT`) and xtext | RFC 3461 §4 |
|
|
2010
|
+
| The `utf-8` `ORCPT` address type | RFC 6533 §3 |
|
|
1896
2011
|
| SMTPUTF8 | RFC 6531 |
|
|
1897
2012
|
| ENHANCEDSTATUSCODES and the codes themselves | RFC 2034, RFC 3463 |
|
|
1898
2013
|
| STARTTLS | RFC 3207 |
|
|
@@ -1914,9 +2029,9 @@ each one comes from.
|
|
|
1914
2029
|
|
|
1915
2030
|
## What is not implemented
|
|
1916
2031
|
|
|
1917
|
-
- **
|
|
1918
|
-
`
|
|
1919
|
-
|
|
2032
|
+
- **Generating DSNs.** The server advertises DSN and hands the parameters to
|
|
2033
|
+
`onData` (see [Delivery status notifications](#delivery-status-notifications-dsn)),
|
|
2034
|
+
but it never writes a notification: honouring them is the app's job.
|
|
1920
2035
|
- **CHUNKING** (RFC 3030). `BDAT` is answered `500 5.5.2 Command
|
|
1921
2036
|
unrecognized`; a client sends DATA instead.
|
|
1922
2037
|
- **A queue.** The client tries a destination once and reports;
|
|
@@ -1924,7 +2039,9 @@ each one comes from.
|
|
|
1924
2039
|
- **Connection reuse.** The client opens one connection per message, and
|
|
1925
2040
|
sends no `RSET`. Several messages to one destination over one session is
|
|
1926
2041
|
on the [roadmap](roadmap.md).
|
|
1927
|
-
- **
|
|
2042
|
+
- **A non-ASCII `ORCPT` of type `rfc822` in the client**: RFC 6533 defines
|
|
2043
|
+
`utf-8` for it, which both sides handle; the client leaves a non-ASCII
|
|
2044
|
+
`rfc822` address out of `RCPT TO`. Any other type is relayed as xtext.
|
|
1928
2045
|
- **CHUNKING in the client.** It sends DATA, never `BDAT`.
|
|
1929
2046
|
- **DANE and MTA-STS.** By MX, TLS is opportunistic; a policy that makes
|
|
1930
2047
|
it required for a domain is for later.
|
package/docs/roadmap.md
CHANGED
|
@@ -6,22 +6,22 @@ number on it.
|
|
|
6
6
|
|
|
7
7
|
## Now
|
|
8
8
|
|
|
9
|
-
Nothing
|
|
9
|
+
Nothing is under way besides what is marked shipped below.
|
|
10
10
|
|
|
11
11
|
## Next
|
|
12
12
|
|
|
13
13
|
- **Connection reuse in the client** — several messages to one
|
|
14
14
|
destination over one session (`RSET` between them), for a queue that
|
|
15
15
|
delivers in batches. Today each `sendMail` opens its own connection.
|
|
16
|
-
- **DSN** (RFC 3461) — `RET`, `ENVID`, `NOTIFY` and `ORCPT` accepted and
|
|
17
|
-
handed to the app, so a sender can ask for delivery status
|
|
18
|
-
notifications. Today they are refused with `555 5.5.4`, and the client
|
|
19
|
-
sends none.
|
|
20
16
|
- **Delivery into `@bumail/store`, ready-made** — an `onData` that puts
|
|
21
17
|
each message in its recipients' inboxes, instead of the few lines the
|
|
22
18
|
[guide](guide.md#delivering-into-bumailstore) shows today. The store
|
|
23
19
|
itself has landed; only the helper is to come.
|
|
24
20
|
|
|
21
|
+
## Later
|
|
22
|
+
|
|
23
|
+
Nothing yet.
|
|
24
|
+
|
|
25
25
|
## Not planned
|
|
26
26
|
|
|
27
27
|
- **Relaying without AUTH** — not as a default, not as an option. A
|
|
@@ -34,6 +34,16 @@ Nothing in progress.
|
|
|
34
34
|
|
|
35
35
|
### Unreleased — merged, not yet published
|
|
36
36
|
|
|
37
|
+
- **UTF-8 `ORCPT` (RFC 6533)**: the server decodes `ORCPT=utf-8;…`
|
|
38
|
+
(`utf-8-addr-xtext` with `\x{HEX}`, and raw UTF-8 under `SMTPUTF8`); the
|
|
39
|
+
client sends a
|
|
40
|
+
`utf-8` `ORCPT` in the form the transaction allows instead of leaving it
|
|
41
|
+
out. The reports built from it, in `@bumail/queue`, are not part of this.
|
|
42
|
+
- **DSN requests (RFC 3461)**: the server accepts `RET`, `ENVID`, `NOTIFY`
|
|
43
|
+
and `ORCPT` and hands them to the app, and always lists `DSN` in its
|
|
44
|
+
`EHLO` reply, so these parameters are no longer refused with `555 5.5.4`
|
|
45
|
+
(a client that relied on that refusal has to adapt). The client sends
|
|
46
|
+
them when the next hop lists `DSN`, and its result reports `dsnOffered`.
|
|
37
47
|
- **The envelope on an `afterData` error**: `SmtpError.accepted` and
|
|
38
48
|
`rejected` list the recipients the server took and refused at `RCPT TO`
|
|
39
49
|
when the session failed after the final dot.
|
package/docs/troubleshooting.md
CHANGED
|
@@ -63,6 +63,8 @@ A reply carries its enhanced status code (`5.7.1`, RFC 3463) only after
|
|
|
63
63
|
- [`503 5.5.1 TLS already active`](#503-551-tls-already-active)
|
|
64
64
|
- [`501 5.5.4 BODY is 7BIT or 8BITMIME`](#501-554-body-is-7bit-or-8bitmime)
|
|
65
65
|
- [`555 5.5.4 … is not supported`](#555-554--is-not-supported)
|
|
66
|
+
- [`501 5.5.4 Syntax: RET=…`, `ENVID=…`, `NOTIFY=…`, `ORCPT=…`](#501-554-syntax-ret-envid-notify-orcpt)
|
|
67
|
+
- [`501 5.5.4 … given twice`](#501-554--given-twice)
|
|
66
68
|
- [`555 … needs EHLO`](#555--needs-ehlo)
|
|
67
69
|
- [`553 5.6.7 A non-ASCII address needs SMTPUTF8`](#553-567-a-non-ascii-address-needs-smtputf8)
|
|
68
70
|
- [`500 5.5.6 Line too long`](#500-556-line-too-long)
|
|
@@ -1136,14 +1138,67 @@ S: 250 2.1.0 OK
|
|
|
1136
1138
|
### `555 5.5.4 … is not supported`
|
|
1137
1139
|
|
|
1138
1140
|
**When**: `MAIL FROM` with a parameter other than `SIZE`, `BODY`,
|
|
1139
|
-
`SMTPUTF8
|
|
1140
|
-
|
|
1141
|
+
`SMTPUTF8`, `AUTH`, `RET` and `ENVID`; or `RCPT TO` with one other than
|
|
1142
|
+
`NOTIFY` and `ORCPT`; or `SMTPUTF8` given a value.
|
|
1141
1143
|
|
|
1142
|
-
**Why**: the server supports only the extensions its EHLO lists.
|
|
1143
|
-
|
|
1144
|
+
**Why**: the server supports only the extensions its EHLO lists. The DSN
|
|
1145
|
+
parameters (RFC 3461) are accepted, each on its own command: `RET` on
|
|
1146
|
+
`RCPT TO`, or `NOTIFY` on `MAIL FROM`, is refused here.
|
|
1144
1147
|
|
|
1145
|
-
**Fix**, as a client: send only the parameters the EHLO reply announces
|
|
1146
|
-
|
|
1148
|
+
**Fix**, as a client: send only the parameters the EHLO reply announces,
|
|
1149
|
+
each on the command it belongs to.
|
|
1150
|
+
|
|
1151
|
+
### `INVALID_OPTION` from `sendMail`: `envid …` or `orcpt …`
|
|
1152
|
+
|
|
1153
|
+
**When**: `sendMail` is given a DSN option it refuses, before any connection.
|
|
1154
|
+
The messages: `envid must be 1 to 100 printable ASCII characters, at most 100
|
|
1155
|
+
once encoded as xtext`; `orcpt of <address> must be { type, address }, the type
|
|
1156
|
+
a token`; `orcpt.address of <address> must be a non-empty string`; `orcpt of
|
|
1157
|
+
<address> is over 500 characters once encoded`.
|
|
1158
|
+
|
|
1159
|
+
**Why**: RFC 3461 §4.4 and §4.2 cap the parameters as sent, so a character that
|
|
1160
|
+
xtext writes as `+XX` counts three (for a `utf-8` `orcpt`, the unitext form:
|
|
1161
|
+
raw UTF-8 octets, `\x{HH}` = 6; for another type, the type, the `;` and the xtext form).
|
|
1162
|
+
|
|
1163
|
+
**Fix**: shorten the value, or give the type as letters, digits and hyphens.
|
|
1164
|
+
|
|
1165
|
+
```ts
|
|
1166
|
+
await sendMail(message, { host, from, to, envid: 'q-1042' }); // not 'q 1042 …' past 100 encoded
|
|
1167
|
+
```
|
|
1168
|
+
|
|
1169
|
+
### `501 5.5.4 Syntax: RET=…`, `ENVID=…`, `NOTIFY=…`, `ORCPT=…`
|
|
1170
|
+
|
|
1171
|
+
**When**: a DSN parameter has a value the RFC does not allow: `RET` other
|
|
1172
|
+
than `FULL` or `HDRS`; `ENVID` empty, longer than 100 characters as sent,
|
|
1173
|
+
not printable ASCII or not xtext; `NOTIFY` mixing `NEVER` with another
|
|
1174
|
+
event, naming an event twice or an unknown one; `ORCPT` with no
|
|
1175
|
+
`addr-type;` prefix, an address that does not decode (not xtext for
|
|
1176
|
+
`rfc822`; for `utf-8`, RFC 6533's `\x{HEX}` form, with raw UTF-8 only when
|
|
1177
|
+
the transaction used `SMTPUTF8`), or more than 500 characters as sent (RFC
|
|
1178
|
+
3461 §4.2).
|
|
1179
|
+
|
|
1180
|
+
**Why**: RFC 3461 §4. The server refuses the command rather than hand a
|
|
1181
|
+
wrong value to `onData`.
|
|
1182
|
+
|
|
1183
|
+
**Fix**, as a client: encode the value as xtext (`+` and `=`, spaces and
|
|
1184
|
+
non-ASCII bytes as `+XX`), and send `NOTIFY=NEVER` alone. For
|
|
1185
|
+
`ORCPT=utf-8;` use RFC 6533 instead: `\x{E9}` for `é`, and `\x{2B}` for `+`
|
|
1186
|
+
(not `+2B`), or raw UTF-8 after `MAIL FROM … SMTPUTF8`.
|
|
1187
|
+
|
|
1188
|
+
```text
|
|
1189
|
+
C: RCPT TO:<bob@example.com> NOTIFY=SUCCESS,FAILURE ORCPT=rfc822;bob+2Btag@example.com
|
|
1190
|
+
S: 250 2.1.5 OK
|
|
1191
|
+
C: RCPT TO:<bob@example.com> ORCPT=utf-8;\x{E9}l\x{E8}ve@example.com
|
|
1192
|
+
S: 250 2.1.5 OK
|
|
1193
|
+
```
|
|
1194
|
+
|
|
1195
|
+
### `501 5.5.4 … given twice`
|
|
1196
|
+
|
|
1197
|
+
**When**: `RET`, `ENVID`, `NOTIFY` or `ORCPT` appears twice on one command.
|
|
1198
|
+
|
|
1199
|
+
**Why**: each DSN parameter may be given once (RFC 3461 §4).
|
|
1200
|
+
|
|
1201
|
+
**Fix**, as a client: send it once; join `NOTIFY` events in one list.
|
|
1147
1202
|
|
|
1148
1203
|
### `555 … needs EHLO`
|
|
1149
1204
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bumail/smtp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"description": "An SMTP server on Bun.listen — RFC 5321 with PIPELINING, SIZE, 8BITMIME, SMTPUTF8, STARTTLS and AUTH after TLS, policy hooks, and no open relay — and a client that delivers a message to a smarthost or by MX",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -47,9 +47,9 @@
|
|
|
47
47
|
]
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
|
-
"@bumail/auth": "^0.
|
|
50
|
+
"@bumail/auth": "^0.5.0",
|
|
51
51
|
"@bumail/dns": "^0.2.1",
|
|
52
|
-
"@bumail/store": "^0.
|
|
52
|
+
"@bumail/store": "^0.6.0",
|
|
53
53
|
"@types/bun": "^1.4.2"
|
|
54
54
|
},
|
|
55
55
|
"peerDependencies": {
|