@bumail/smtp 0.9.0 → 0.10.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.
Files changed (37) hide show
  1. package/README.md +6 -4
  2. package/dist/chunks/{index-wc25ywta.js → index-twt5myrr.js} +37 -3
  3. package/dist/chunks/{index-wc25ywta.js.map → index-twt5myrr.js.map} +4 -3
  4. package/dist/client/dsn.d.ts +21 -0
  5. package/dist/client/dsn.d.ts.map +1 -0
  6. package/dist/client/index.js +81 -5
  7. package/dist/client/index.js.map +6 -5
  8. package/dist/client/options.d.ts +21 -0
  9. package/dist/client/options.d.ts.map +1 -1
  10. package/dist/client/settings.d.ts +2 -0
  11. package/dist/client/settings.d.ts.map +1 -1
  12. package/dist/client/transaction.d.ts +2 -0
  13. package/dist/client/transaction.d.ts.map +1 -1
  14. package/dist/index.d.ts +1 -1
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +116 -15
  17. package/dist/index.js.map +10 -8
  18. package/dist/protocol/command.d.ts +2 -0
  19. package/dist/protocol/command.d.ts.map +1 -1
  20. package/dist/protocol/xtext.d.ts +14 -0
  21. package/dist/protocol/xtext.d.ts.map +1 -0
  22. package/dist/server/commands.d.ts.map +1 -1
  23. package/dist/server/dsn-params.d.ts +20 -0
  24. package/dist/server/dsn-params.d.ts.map +1 -0
  25. package/dist/server/envelope.d.ts +5 -0
  26. package/dist/server/envelope.d.ts.map +1 -0
  27. package/dist/server/intake.d.ts.map +1 -1
  28. package/dist/server/options.d.ts +28 -0
  29. package/dist/server/options.d.ts.map +1 -1
  30. package/dist/server/state.d.ts +4 -1
  31. package/dist/server/state.d.ts.map +1 -1
  32. package/dist/server/transaction.d.ts +2 -2
  33. package/dist/server/transaction.d.ts.map +1 -1
  34. package/docs/guide.md +100 -5
  35. package/docs/roadmap.md +11 -5
  36. package/docs/troubleshooting.md +54 -6
  37. package/package.json +3 -3
@@ -0,0 +1,20 @@
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`. The text of the `501` to
14
+ * answer when the value is wrong, else `undefined`.
15
+ */
16
+ export declare function readRcptDsn(into: {
17
+ notify?: 'NEVER' | readonly DsnNotify[];
18
+ orcpt?: EnvelopeRecipient['orcpt'];
19
+ }, key: string, value: string, repeated: readonly string[]): string | undefined;
20
+ //# 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":"AAGA,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;AA0CrD;;;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;;;GAGG;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,GACzB,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;AAgB/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;IA0E5D,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"}
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"}
@@ -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":"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"}
1
+ {"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../../src/server/options.ts"],"names":[],"mappings":"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"}
@@ -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;AAE1C,sDAAsD;AACtD,MAAM,WAAW,WAAW;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,EAAE,EAAE,MAAM,EAAE,CAAC;IACb,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,WAIlC,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>` (RFC 5321 §4.1.1.3), where relaying is refused without AUTH. */
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;AAS/C,iFAAiF;AACjF,wBAAsB,IAAI,CACzB,UAAU,EAAE,UAAU,EACtB,QAAQ,EAAE,MAAM,GACd,OAAO,CAAC,IAAI,CAAC,CAuDf;AAED,oFAAoF;AACpF,wBAAsB,IAAI,CACzB,UAAU,EAAE,UAAU,EACtB,QAAQ,EAAE,MAAM,GACd,OAAO,CAAC,IAAI,CAAC,CAyCf"}
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,CAoDf"}
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 AUTH on MAIL, or any parameter on RCPT |
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 from xtext. */
481
+ readonly orcpt?: { readonly type: string; readonly address: string };
466
482
  }
467
483
  ```
468
484
 
@@ -636,6 +652,83 @@ 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 the xtext and puts them on the `Envelope` `onData`
662
+ receives: `ret` and `envid`, and for each accepted recipient `notify` and
663
+ `orcpt` in `recipients`. `to` is unchanged.
664
+
665
+ The server does nothing else with them. Once it advertises DSN, the app that
666
+ receives these parameters must honour them, or the client is told something
667
+ untrue:
668
+
669
+ - An app that **relays** the message (a queue, a smarthost) passes them on
670
+ to the next hop, or generates the DSN itself when the next hop does not
671
+ advertise DSN.
672
+ - An app that **delivers locally** generates a DSN for a recipient that asked
673
+ for `SUCCESS` and was delivered, returning the message or only its
674
+ headers as `ret` says, and puts `envid` and `orcpt` in the report.
675
+ - `NOTIFY=NEVER` means no DSN for that recipient, a bounce included.
676
+
677
+ ```ts
678
+ import { createSmtpServer } from '@bumail/smtp';
679
+
680
+ createSmtpServer({
681
+ hostname: 'mx.example.com',
682
+ localDomains: ['example.com'],
683
+ async onData({ envelope }) {
684
+ for (const { address, notify, orcpt } of envelope.recipients) {
685
+ if (notify === 'NEVER') continue; // no DSN for this one, ever
686
+ // deliver to `address`; report to `envelope.from` as `notify` asks,
687
+ // quoting `envelope.envid` and `orcpt?.address`
688
+ }
689
+ },
690
+ });
691
+ ```
692
+
693
+ A malformed or repeated DSN parameter is answered `501 5.5.4`; a parameter
694
+ after `HELO` gets `555 <PARAM> needs EHLO`.
695
+
696
+ ### Sending DSN parameters
697
+
698
+ `sendMail` takes `ret`, `envid` and `recipients` (the shape of
699
+ `Envelope.recipients`: `address`, `notify`, `orcpt`, matched to `to` by
700
+ address), so a received envelope can be passed through when a relay hands the
701
+ message on. They are written only when the next hop's EHLO lists `DSN`, with
702
+ `envid` and the `orcpt` address xtext-encoded (RFC 3461 §4). An `orcpt` whose
703
+ type is not `rfc822`, or whose address is not ASCII (RFC 6533), is left out of
704
+ the command, not refused, so a relay never fails on what a received envelope
705
+ carries. When the next hop lists no `DSN` nothing is sent, the delivery goes
706
+ through as usual, and the result says `dsnOffered: false`: the relay must then
707
+ generate the DSNs itself (RFC 3461 §5.2.2).
708
+
709
+ ```ts
710
+ import { sendMail } from '@bumail/smtp/client';
711
+
712
+ const result = await sendMail(message, {
713
+ host: 'smtp.relay.example',
714
+ from: 'a@example.com',
715
+ to: ['b@example.net'],
716
+ ret: 'HDRS',
717
+ envid: 'q-1042',
718
+ recipients: [{ address: 'b@example.net', notify: ['FAILURE', 'DELAY'] }],
719
+ });
720
+ if (!result.dsnOffered) {
721
+ // the next hop never saw NOTIFY: send the 'relayed' DSN here
722
+ }
723
+ ```
724
+
725
+ `dsnOffered` is the server listing `DSN` in EHLO; it does not prove the next
726
+ hop will send a DSN. These are `INVALID_OPTION`: a wrong `ret`, `notify` or
727
+ `orcpt` shape; an `envid` over 100 characters once xtext-encoded (RFC 3461
728
+ §4.4); an `orcpt` over 500 characters once encoded (§4.2), whose type is not a
729
+ token, or whose address is empty; a `recipients` entry not in `to`, or named
730
+ twice.
731
+
639
732
  ## Delivering into @bumail/store
640
733
 
641
734
  [`@bumail/store`](https://github.com/softistx/bumail/tree/develop/packages/store) keeps
@@ -1893,6 +1986,7 @@ console.log(chunk.done, reader.bareLineBreaks);
1893
1986
  | SIZE | RFC 1870 |
1894
1987
  | PIPELINING | RFC 2920 |
1895
1988
  | 8BITMIME | RFC 6152 |
1989
+ | DSN parameters (`RET`, `ENVID`, `NOTIFY`, `ORCPT`) and xtext | RFC 3461 §4 |
1896
1990
  | SMTPUTF8 | RFC 6531 |
1897
1991
  | ENHANCEDSTATUSCODES and the codes themselves | RFC 2034, RFC 3463 |
1898
1992
  | STARTTLS | RFC 3207 |
@@ -1914,9 +2008,9 @@ each one comes from.
1914
2008
 
1915
2009
  ## What is not implemented
1916
2010
 
1917
- - **DSN** (RFC 3461). `RET`, `ENVID`, `NOTIFY` and `ORCPT` are refused with
1918
- `555 5.5.4 <PARAM> is not supported`; DSN is not advertised, so a
1919
- conforming client does not send them.
2011
+ - **Generating DSNs.** The server advertises DSN and hands the parameters to
2012
+ `onData` (see [Delivery status notifications](#delivery-status-notifications-dsn)),
2013
+ but it never writes a notification: honouring them is the app's job.
1920
2014
  - **CHUNKING** (RFC 3030). `BDAT` is answered `500 5.5.2 Command
1921
2015
  unrecognized`; a client sends DATA instead.
1922
2016
  - **A queue.** The client tries a destination once and reports;
@@ -1924,7 +2018,8 @@ each one comes from.
1924
2018
  - **Connection reuse.** The client opens one connection per message, and
1925
2019
  sends no `RSET`. Several messages to one destination over one session is
1926
2020
  on the [roadmap](roadmap.md).
1927
- - **DSN in the client.** It sends no `RET`, `ENVID`, `NOTIFY` or `ORCPT`.
2021
+ - **`ORCPT` of an RFC 6533 type (`utf-8`) or with a non-ASCII address in the
2022
+ client**: it is left out of `RCPT TO`, not sent.
1928
2023
  - **CHUNKING in the client.** It sends DATA, never `BDAT`.
1929
2024
  - **DANE and MTA-STS.** By MX, TLS is opportunistic; a policy that makes
1930
2025
  it required for a domain is for later.
package/docs/roadmap.md CHANGED
@@ -6,22 +6,23 @@ number on it.
6
6
 
7
7
  ## Now
8
8
 
9
- Nothing in progress.
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
+ - **UTF-8 address types in DSNs** (RFC 6533) — internationalized addresses
24
+ in the `ORCPT` parameter and in the reports built from it.
25
+
25
26
  ## Not planned
26
27
 
27
28
  - **Relaying without AUTH** — not as a default, not as an option. A
@@ -34,6 +35,11 @@ Nothing in progress.
34
35
 
35
36
  ### Unreleased — merged, not yet published
36
37
 
38
+ - **DSN requests (RFC 3461)**: the server accepts `RET`, `ENVID`, `NOTIFY`
39
+ and `ORCPT` and hands them to the app, and always lists `DSN` in its
40
+ `EHLO` reply, so these parameters are no longer refused with `555 5.5.4`
41
+ (a client that relied on that refusal has to adapt). The client sends
42
+ them when the next hop lists `DSN`, and its result reports `dsnOffered`.
37
43
  - **The envelope on an `afterData` error**: `SmtpError.accepted` and
38
44
  `rejected` list the recipients the server took and refused at `RCPT TO`
39
45
  when the session failed after the final dot.
@@ -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,60 @@ 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` and `AUTH`, such as `RET` or `ENVID`; or `RCPT TO` with any
1140
- parameter, such as `NOTIFY` or `ORCPT`; or `SMTPUTF8` given a value.
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. Delivery
1143
- status notifications (DSN, RFC 3461) are not among them.
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
- turn off DSN options (`NOTIFY`, `RET`, `ENVID`, `ORCPT`) for this server.
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.
1161
+
1162
+ **Fix**: shorten the value, or give the type as letters, digits and hyphens.
1163
+
1164
+ ```ts
1165
+ await sendMail(message, { host, from, to, envid: 'q-1042' }); // not 'q 1042 …' past 100 encoded
1166
+ ```
1167
+
1168
+ ### `501 5.5.4 Syntax: RET=…`, `ENVID=…`, `NOTIFY=…`, `ORCPT=…`
1169
+
1170
+ **When**: a DSN parameter has a value the RFC does not allow: `RET` other
1171
+ than `FULL` or `HDRS`; `ENVID` empty, longer than 100 characters as sent,
1172
+ not printable ASCII or not xtext; `NOTIFY` mixing `NEVER` with another
1173
+ event, naming an event twice or an unknown one; `ORCPT` with no
1174
+ `addr-type;` prefix, an address that is not xtext, or more than 500
1175
+ characters as sent (RFC 3461 §4.2).
1176
+
1177
+ **Why**: RFC 3461 §4. The server refuses the command rather than hand a
1178
+ wrong value to `onData`.
1179
+
1180
+ **Fix**, as a client: encode the value as xtext (`+` and `=`, spaces and
1181
+ non-ASCII bytes as `+XX`), and send `NOTIFY=NEVER` alone.
1182
+
1183
+ ```text
1184
+ C: RCPT TO:<bob@example.com> NOTIFY=SUCCESS,FAILURE ORCPT=rfc822;bob+2Btag@example.com
1185
+ S: 250 2.1.5 OK
1186
+ ```
1187
+
1188
+ ### `501 5.5.4 … given twice`
1189
+
1190
+ **When**: `RET`, `ENVID`, `NOTIFY` or `ORCPT` appears twice on one command.
1191
+
1192
+ **Why**: each DSN parameter may be given once (RFC 3461 §4).
1193
+
1194
+ **Fix**, as a client: send it once; join `NOTIFY` events in one list.
1147
1195
 
1148
1196
  ### `555 … needs EHLO`
1149
1197
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bumail/smtp",
3
- "version": "0.9.0",
3
+ "version": "0.10.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.4.1",
50
+ "@bumail/auth": "^0.5.0",
51
51
  "@bumail/dns": "^0.2.1",
52
- "@bumail/store": "^0.4.2",
52
+ "@bumail/store": "^0.6.0",
53
53
  "@types/bun": "^1.4.2"
54
54
  },
55
55
  "peerDependencies": {