@bumail/smtp 0.1.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 (53) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +263 -0
  3. package/dist/errors.d.ts +13 -0
  4. package/dist/errors.d.ts.map +1 -0
  5. package/dist/index.d.ts +9 -0
  6. package/dist/index.d.ts.map +1 -0
  7. package/dist/index.js +1319 -0
  8. package/dist/index.js.map +29 -0
  9. package/dist/protocol/command.d.ts +20 -0
  10. package/dist/protocol/command.d.ts.map +1 -0
  11. package/dist/protocol/data.d.ts +24 -0
  12. package/dist/protocol/data.d.ts.map +1 -0
  13. package/dist/protocol/path.d.ts +20 -0
  14. package/dist/protocol/path.d.ts.map +1 -0
  15. package/dist/protocol/reply.d.ts +13 -0
  16. package/dist/protocol/reply.d.ts.map +1 -0
  17. package/dist/protocol/sasl.d.ts +16 -0
  18. package/dist/protocol/sasl.d.ts.map +1 -0
  19. package/dist/server/auth.d.ts +6 -0
  20. package/dist/server/auth.d.ts.map +1 -0
  21. package/dist/server/commands.d.ts +4 -0
  22. package/dist/server/commands.d.ts.map +1 -0
  23. package/dist/server/connection.d.ts +52 -0
  24. package/dist/server/connection.d.ts.map +1 -0
  25. package/dist/server/guard.d.ts +18 -0
  26. package/dist/server/guard.d.ts.map +1 -0
  27. package/dist/server/input.d.ts +31 -0
  28. package/dist/server/input.d.ts.map +1 -0
  29. package/dist/server/intake.d.ts +31 -0
  30. package/dist/server/intake.d.ts.map +1 -0
  31. package/dist/server/lines.d.ts +25 -0
  32. package/dist/server/lines.d.ts.map +1 -0
  33. package/dist/server/options.d.ts +151 -0
  34. package/dist/server/options.d.ts.map +1 -0
  35. package/dist/server/received.d.ts +11 -0
  36. package/dist/server/received.d.ts.map +1 -0
  37. package/dist/server/refusals.d.ts +16 -0
  38. package/dist/server/refusals.d.ts.map +1 -0
  39. package/dist/server/server.d.ts +25 -0
  40. package/dist/server/server.d.ts.map +1 -0
  41. package/dist/server/settings.d.ts +16 -0
  42. package/dist/server/settings.d.ts.map +1 -0
  43. package/dist/server/state.d.ts +24 -0
  44. package/dist/server/state.d.ts.map +1 -0
  45. package/dist/server/transaction.d.ts +6 -0
  46. package/dist/server/transaction.d.ts.map +1 -0
  47. package/dist/server/transport.d.ts +42 -0
  48. package/dist/server/transport.d.ts.map +1 -0
  49. package/docs/README.md +10 -0
  50. package/docs/guide.md +976 -0
  51. package/docs/roadmap.md +45 -0
  52. package/docs/troubleshooting.md +1308 -0
  53. package/package.json +55 -0
@@ -0,0 +1,29 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/errors.ts", "../src/protocol/path.ts", "../src/protocol/command.ts", "../src/protocol/data.ts", "../src/protocol/reply.ts", "../src/protocol/sasl.ts", "../src/server/guard.ts", "../src/server/auth.ts", "../src/server/state.ts", "../src/server/transaction.ts", "../src/server/commands.ts", "../src/server/received.ts", "../src/server/refusals.ts", "../src/server/intake.ts", "../src/server/lines.ts", "../src/server/input.ts", "../src/server/connection.ts", "../src/server/settings.ts", "../src/server/transport.ts", "../src/server/server.ts"],
4
+ "sourcesContent": [
5
+ "/** Why the server refused an option, or why a message stream ended in error. */\nexport type SmtpErrorCode =\n\t| 'INVALID_OPTION'\n\t| 'ALREADY_LISTENING'\n\t| 'MESSAGE_TOO_BIG'\n\t| 'BARE_LINE_BREAK'\n\t| 'CONNECTION_LOST'\n\t| 'HOOK_TIMEOUT'\n\t| 'INVALID_HOOK_REPLY'\n\t| 'MESSAGE_NOT_READ';\n\n/**\n * Thrown by `createSmtpServer` and `listen` for a wrong option or call; the\n * error a message's `content` stream ends with when the message must not be\n * delivered; and what `onError` is given for a hook that broke a rule.\n */\nexport class SmtpError extends Error {\n\toverride readonly name = 'SmtpError';\n\treadonly code: SmtpErrorCode;\n\n\tconstructor(code: SmtpErrorCode, message: string) {\n\t\tsuper(message);\n\t\tthis.code = code;\n\t}\n}\n",
6
+ "/** An envelope address, as `MAIL FROM` and `RCPT TO` give it. */\nexport interface Path {\n\t/** `local@domain`; `''` for the null reverse-path `<>`. */\n\treadonly address: string;\n\treadonly local: string;\n\t/** Lower case. */\n\treadonly domain: string;\n}\n\nconst ATOM = /^[A-Za-z0-9!#$%&'*+\\-/=?^_`{|}~\\u0080-\\u{10ffff}]+$/u;\nconst LABEL =\n\t/^(?:[A-Za-z0-9\\u0080-\\u{10ffff}](?:[A-Za-z0-9\\-\\u0080-\\u{10ffff}]*[A-Za-z0-9\\u0080-\\u{10ffff}])?)$/u;\n\n/**\n * C1 controls, Unicode format characters (zero-width, bidi, BOM) and the\n * line and paragraph separators: none has a place in an address, and each\n * would land as it is in the Received field and the app's records.\n */\nconst INVISIBLE = /[\\u0080-\\u009f\\u2028\\u2029\\p{Cf}]/u;\n\nfunction isDotString(text: string): boolean {\n\treturn text.split('.').every((atom) => ATOM.test(atom));\n}\n\nfunction isQuotedString(text: string): boolean {\n\treturn /^\"(?:[\\x20\\x21\\x23-\\x5b\\x5d-\\x7e\\u0080-\\u{10ffff}]|\\\\[\\x20-\\x7e])*\"$/u.test(\n\t\ttext,\n\t);\n}\n\nfunction isDomain(domain: string): boolean {\n\tif (/^\\[(?:IPv6:[0-9A-Fa-f:.]+|\\d{1,3}(?:\\.\\d{1,3}){3})\\]$/.test(domain))\n\t\treturn true;\n\treturn (\n\t\tdomain.length <= 255 &&\n\t\tdomain.split('.').every((label) => label.length <= 63 && LABEL.test(label))\n\t);\n}\n\n/**\n * Parses the `<path>` of RFC 5321 §4.1.2: `<local@domain>`, with a source\n * route to ignore (`<@a,@b:local@domain>`, §C), or `<>` when `allowNull`.\n * Non-ASCII is allowed: whether the session may use it is SMTPUTF8's\n * question (RFC 6531), not the grammar's. `undefined` when it is not a path.\n *\n * The local part is kept as written, quotes included: `\"v@x.example\"` and\n * `v%x.example` are local parts of the domain after the last `@`, not\n * addresses elsewhere. Code that delivers must not split on the first `@`.\n */\nexport function parsePath(text: string, allowNull: boolean): Path | undefined {\n\tif (!text.startsWith('<') || !text.endsWith('>')) return undefined;\n\tlet inner = text.slice(1, -1);\n\tif (INVISIBLE.test(inner)) return undefined;\n\tif (inner === '')\n\t\treturn allowNull ? { address: '', local: '', domain: '' } : undefined;\n\tconst route = /^@[^:]+:/.exec(inner);\n\tif (route) inner = inner.slice(route[0].length);\n\tconst at = inner.lastIndexOf('@');\n\tif (at <= 0) return undefined;\n\tconst local = inner.slice(0, at);\n\tconst domain = inner.slice(at + 1);\n\tif (local.length > 64 || !(isDotString(local) || isQuotedString(local)))\n\t\treturn undefined;\n\tif (!isDomain(domain)) return undefined;\n\treturn {\n\t\taddress: `${local}@${domain.toLowerCase()}`,\n\t\tlocal,\n\t\tdomain: domain.toLowerCase(),\n\t};\n}\n",
7
+ "import { type Path, parsePath } from './path';\n\n/** A command line, split into its verb (upper case) and the rest. */\nexport interface Command {\n\treadonly verb: string;\n\treadonly argument: string;\n}\n\nexport function parseCommand(line: string): Command {\n\tconst space = line.indexOf(' ');\n\tif (space < 0) return { verb: line.toUpperCase(), argument: '' };\n\treturn {\n\t\tverb: line.slice(0, space).toUpperCase(),\n\t\targument: line.slice(space + 1).trim(),\n\t};\n}\n\n/** `MAIL FROM:<path> params` or `RCPT TO:<path> params`, parsed. */\nexport interface PathCommand {\n\treadonly path: Path;\n\t/** ESMTP parameters (RFC 5321 §4.1.2), keys in upper case; a key with no `=` has the value `''`. */\n\treadonly parameters: Readonly<Record<string, string>>;\n}\n\n/**\n * Parses the argument of `MAIL` or `RCPT`: `FROM:<path> params` (or `TO:`).\n * Tolerates a space after the colon, which many clients send. `undefined`\n * when the syntax is wrong.\n */\nexport function parsePathCommand(\n\targument: string,\n\tkeyword: 'FROM' | 'TO',\n): PathCommand | undefined {\n\tconst match = new RegExp(`^${keyword}:\\\\s*(<[^>]*>)(.*)$`, 'i').exec(\n\t\targument,\n\t);\n\tif (!match) return undefined;\n\tconst path = parsePath(match[1] as string, keyword === 'FROM');\n\tif (!path) return undefined;\n\tconst parameters: Record<string, string> = {};\n\tfor (const item of (match[2] as string).trim().split(/\\s+/)) {\n\t\tif (item === '') continue;\n\t\tconst equals = item.indexOf('=');\n\t\tconst key = (equals < 0 ? item : item.slice(0, equals)).toUpperCase();\n\t\tif (!/^[A-Z0-9][A-Z0-9-]*$/.test(key)) return undefined;\n\t\tparameters[key] = equals < 0 ? '' : item.slice(equals + 1);\n\t}\n\treturn { path, parameters };\n}\n",
8
+ "const CR = 0x0d;\nconst LF = 0x0a;\nconst DOT = 0x2e;\n\n/** The result of one chunk of DATA. */\nexport interface DataChunk {\n\t/** The message bytes in this chunk, dot-unstuffed. */\n\treadonly data: Uint8Array;\n\t/** The terminating `<CRLF>.<CRLF>` was read. */\n\treadonly done: boolean;\n\t/** What followed the terminator: the next pipelined commands. */\n\treadonly rest: Uint8Array;\n}\n\n/**\n * Reads the content of `DATA` (RFC 5321 §4.1.1.4): removes the leading dot\n * that §4.5.2 adds to lines starting with one, and stops at `<CRLF>.<CRLF>`\n * — that exact sequence and no other, so a bare LF before a dot cannot end\n * the message (SMTP smuggling). It counts the bare CRs and LFs it meets,\n * for the server to refuse the message.\n */\nexport class DataReader {\n\t/** The next byte starts a line: it follows CRLF, or opens the message. */\n\t#lineStart = true;\n\t/** Bytes held back: a CR, or a dot at a line start, whose meaning depends on what follows. */\n\t#held: number[] = [];\n\t#size = 0;\n\tbareLineBreaks = 0;\n\n\t/** The bytes of message read so far, after unstuffing. */\n\tget size(): number {\n\t\treturn this.#size;\n\t}\n\n\twrite(chunk: Uint8Array): DataChunk {\n\t\t// The output never outgrows the chunk and the two bytes held before it.\n\t\tconst out = new Uint8Array(chunk.length + 2);\n\t\tlet n = 0;\n\t\tfor (let i = 0; i < chunk.length; i++) {\n\t\t\tconst byte = chunk[i] as number;\n\t\t\tconst held = this.#held;\n\t\t\tif (held.length > 0) {\n\t\t\t\t// Held: [CR], [DOT], [DOT, CR].\n\t\t\t\tif (held[0] === DOT && held.length === 1) {\n\t\t\t\t\tif (byte === CR) {\n\t\t\t\t\t\theld.push(CR);\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\t// A stuffed dot: drop it, and read this byte as the line's first.\n\t\t\t\t\tthis.#held = [];\n\t\t\t\t\tthis.#lineStart = false;\n\t\t\t\t\ti--;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tif (held[0] === DOT && held.length === 2) {\n\t\t\t\t\tthis.#held = [];\n\t\t\t\t\tif (byte === LF) {\n\t\t\t\t\t\tthis.#size += n;\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tdata: out.subarray(0, n),\n\t\t\t\t\t\t\tdone: true,\n\t\t\t\t\t\t\trest: chunk.slice(i + 1),\n\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t\t// `.` CR x: the dot was stuffing; the CR is bare.\n\t\t\t\t\tthis.bareLineBreaks++;\n\t\t\t\t\tout[n++] = CR;\n\t\t\t\t\tthis.#lineStart = false;\n\t\t\t\t\ti--;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t// Held a CR.\n\t\t\t\tthis.#held = [];\n\t\t\t\tif (byte === LF) {\n\t\t\t\t\tout[n++] = CR;\n\t\t\t\t\tout[n++] = LF;\n\t\t\t\t\tthis.#lineStart = true;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tthis.bareLineBreaks++;\n\t\t\t\tout[n++] = CR;\n\t\t\t\tthis.#lineStart = false;\n\t\t\t\ti--;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (byte === CR) {\n\t\t\t\tthis.#held = [CR];\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (byte === DOT && this.#lineStart) {\n\t\t\t\tthis.#held = [DOT];\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (byte === LF) this.bareLineBreaks++;\n\t\t\tout[n++] = byte;\n\t\t\tthis.#lineStart = false;\n\t\t}\n\t\tthis.#size += n;\n\t\treturn { data: out.subarray(0, n), done: false, rest: new Uint8Array(0) };\n\t}\n}\n",
9
+ "/** An SMTP reply (RFC 5321 §4.2), with its enhanced status code (RFC 3463) when it has one. */\nexport interface Reply {\n\treadonly code: number;\n\t/** `x.y.z`, written before the text of each line (RFC 2034 §4). */\n\treadonly status?: string;\n\t/** One line or more; a multi-line reply joins them with `code-`. */\n\treadonly text: string | readonly string[];\n}\n\n/** The reply as sent: every line but the last as `code-`, the last as `code `, each ending in CRLF. */\nexport function formatReply(reply: Reply, enhanced = true): string {\n\tconst lines = typeof reply.text === 'string' ? [reply.text] : reply.text;\n\tconst status = enhanced && reply.status ? `${reply.status} ` : '';\n\treturn lines\n\t\t.map((line, i) => {\n\t\t\tconst clean = line.replace(/[\\r\\n]+/g, ' ');\n\t\t\treturn `${reply.code}${i === lines.length - 1 ? ' ' : '-'}${status}${clean}\\r\\n`;\n\t\t})\n\t\t.join('');\n}\n\n/** Builds a reply. */\nexport const reply = (\n\tcode: number,\n\tstatus: string | undefined,\n\ttext: string | readonly string[],\n): Reply => (status === undefined ? { code, text } : { code, status, text });\n",
10
+ "/** Credentials a client gave with `AUTH`. */\nexport interface Credentials {\n\treadonly mechanism: 'PLAIN' | 'LOGIN';\n\treadonly username: string;\n\treadonly password: string;\n\t/**\n\t * PLAIN's authorization identity, when it differs from the username.\n\t * The server refuses such credentials before `authenticate` is asked.\n\t */\n\treadonly authorizationId?: string;\n}\n\nfunction base64ToText(text: string): string | undefined {\n\tif (!/^[A-Za-z0-9+/]*={0,2}$/.test(text) || text.length % 4 !== 0)\n\t\treturn undefined;\n\ttry {\n\t\treturn new TextDecoder('utf-8', { fatal: true }).decode(\n\t\t\tUint8Array.fromBase64(text),\n\t\t);\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\n/** PLAIN (RFC 4616): `authzid NUL authcid NUL passwd`, in base64. */\nexport function decodePlain(response: string): Credentials | undefined {\n\tconst text = base64ToText(response);\n\tif (text === undefined) return undefined;\n\tconst parts = text.split('\\0');\n\tif (parts.length !== 3) return undefined;\n\tconst [authorizationId = '', username = '', password = ''] = parts;\n\tif (username === '' || password === '') return undefined;\n\treturn {\n\t\tmechanism: 'PLAIN',\n\t\tusername,\n\t\tpassword,\n\t\t...(authorizationId === '' || authorizationId === username\n\t\t\t? {}\n\t\t\t: { authorizationId }),\n\t};\n}\n\n/** One step of LOGIN: a base64 line to its text. */\nexport function decodeLoginStep(response: string): string | undefined {\n\treturn base64ToText(response);\n}\n",
11
+ "import { SmtpError } from '../errors';\nimport { type Reply, reply } from '../protocol/reply';\n\n/** The reply when a hook throws, times out or answers what is not a refusal. */\nexport const LOCAL_ERROR = reply(451, '4.3.0', 'Local error in processing');\n\nconst TIMED_OUT = Symbol('timed out');\n\n/** `promise`, or `TIMED_OUT` once `seconds` have passed. */\nexport async function within<T>(\n\tpromise: Promise<T>,\n\tseconds: number,\n): Promise<T | typeof TIMED_OUT> {\n\tlet timer: ReturnType<typeof setTimeout> | undefined;\n\tconst deadline = new Promise<typeof TIMED_OUT>((resolve) => {\n\t\ttimer = setTimeout(() => resolve(TIMED_OUT), seconds * 1000);\n\t});\n\ttry {\n\t\treturn await Promise.race([promise, deadline]);\n\t} finally {\n\t\tclearTimeout(timer);\n\t}\n}\n\nexport function timedOut(value: unknown): value is typeof TIMED_OUT {\n\treturn value === TIMED_OUT;\n}\n\nexport function hookTimeout(name: string, seconds: number): SmtpError {\n\treturn new SmtpError(\n\t\t'HOOK_TIMEOUT',\n\t\t`${name} did not settle within hookTimeout (${seconds} s)`,\n\t);\n}\n\n/**\n * What a hook answered, as the reply to send: `undefined` to go on, or the\n * refusal. A reply under 400 is not a refusal — sending it would tell the\n * client yes while the server did not take the command — so it is reported\n * and answered as a local error.\n */\nexport function refusalOf(\n\tname: string,\n\tanswer: unknown,\n\treport: (error: unknown) => void,\n): Reply | undefined {\n\tif (answer === undefined) return undefined;\n\tconst code = (answer as Partial<Reply> | null)?.code;\n\tif (typeof code === 'number' && code >= 400 && code < 600) {\n\t\treturn answer as Reply;\n\t}\n\treport(\n\t\tnew SmtpError(\n\t\t\t'INVALID_HOOK_REPLY',\n\t\t\t`${name} answered ${JSON.stringify(answer)}, which is not a refusal: a hook refuses with a 4xx or 5xx reply, and accepts with undefined`,\n\t\t),\n\t);\n\treturn LOCAL_ERROR;\n}\n",
12
+ "import { reply } from '../protocol/reply';\nimport {\n\ttype Credentials,\n\tdecodeLoginStep,\n\tdecodePlain,\n} from '../protocol/sasl';\nimport type { Connection } from './connection';\n\n/** Failed AUTH attempts before the server hangs up. */\nconst MAX_AUTH_FAILURES = 3;\n\nasync function check(\n\tconnection: Connection,\n\tcredentials: Credentials | undefined,\n): Promise<void> {\n\tconst { state, settings } = connection;\n\tstate.waiting = 'command';\n\tstate.loginUser = undefined;\n\tif (credentials === undefined) {\n\t\treturn connection.fail(reply(501, '5.5.2', 'Cannot decode the response'));\n\t}\n\tif (\n\t\tcredentials.authorizationId !== undefined &&\n\t\tcredentials.authorizationId !== credentials.username\n\t) {\n\t\t// A session acts as the user it authenticated as, never as another.\n\t\treturn refuse(connection);\n\t}\n\tconst { authenticate } = settings.options;\n\tconst answer = await connection.check('authenticate', () =>\n\t\tauthenticate?.(credentials, connection.session),\n\t);\n\tif (answer.failed) {\n\t\t// RFC 4954 §6: a failure of the server's own, not of the credentials.\n\t\treturn connection.send(\n\t\t\treply(454, '4.7.0', 'Temporary authentication failure'),\n\t\t);\n\t}\n\tconst ok = answer.value === true;\n\tif (ok) {\n\t\tstate.user = credentials.username;\n\t\treturn connection.send(reply(235, '2.7.0', 'Authentication successful'));\n\t}\n\trefuse(connection);\n}\n\nfunction refuse(connection: Connection): void {\n\tconst { state, settings } = connection;\n\tstate.authFailures++;\n\tif (state.authFailures >= MAX_AUTH_FAILURES) {\n\t\tconst { hostname } = settings.options;\n\t\tconnection.close(\n\t\t\treply(\n\t\t\t\t421,\n\t\t\t\t'4.7.0',\n\t\t\t\t`${hostname} Too many failed authentications, closing`,\n\t\t\t),\n\t\t);\n\t\treturn;\n\t}\n\tconnection.fail(reply(535, '5.7.8', 'Authentication credentials invalid'));\n}\n\n/** `AUTH PLAIN [initial]` or `AUTH LOGIN [initial]` (RFC 4954), over TLS only. */\nexport async function authenticate(\n\tconnection: Connection,\n\targument: string,\n): Promise<void> {\n\tconst { state, settings } = connection;\n\tif (!settings.options.authenticate)\n\t\treturn connection.fail(reply(502, '5.5.1', 'AUTH not available'));\n\tif (!state.secure)\n\t\treturn connection.fail(\n\t\t\treply(\n\t\t\t\t538,\n\t\t\t\t'5.7.11',\n\t\t\t\t'Encryption required for requested authentication mechanism',\n\t\t\t),\n\t\t);\n\tif (state.helo === undefined || !state.esmtp)\n\t\treturn connection.fail(reply(503, '5.5.1', 'Send EHLO first'));\n\tif (state.user !== undefined)\n\t\treturn connection.fail(reply(503, '5.5.1', 'Already authenticated'));\n\tif (state.transaction.from !== undefined)\n\t\treturn connection.fail(\n\t\t\treply(503, '5.5.1', 'AUTH not allowed during a transaction'),\n\t\t);\n\tconst [mechanism = '', initial, extra] = argument.split(' ');\n\tif (extra !== undefined)\n\t\treturn connection.fail(\n\t\t\treply(501, '5.5.4', 'Syntax: AUTH mechanism [initial-response]'),\n\t\t);\n\tswitch (mechanism.toUpperCase()) {\n\t\tcase 'PLAIN':\n\t\t\tif (initial === undefined) {\n\t\t\t\tstate.waiting = 'auth-plain';\n\t\t\t\treturn connection.send(reply(334, undefined, ''));\n\t\t\t}\n\t\t\treturn check(connection, decodePlain(initial === '=' ? '' : initial));\n\t\tcase 'LOGIN':\n\t\t\tif (initial === undefined) {\n\t\t\t\tstate.waiting = 'auth-login-user';\n\t\t\t\treturn connection.send(reply(334, undefined, 'VXNlcm5hbWU6'));\n\t\t\t}\n\t\t\treturn loginUser(connection, initial);\n\t\tdefault:\n\t\t\treturn connection.fail(\n\t\t\t\treply(504, '5.5.4', 'Unrecognized authentication type'),\n\t\t\t);\n\t}\n}\n\nfunction loginUser(\n\tconnection: Connection,\n\tresponse: string,\n): void | Promise<void> {\n\tconst user = decodeLoginStep(response);\n\tif (!user) return check(connection, undefined);\n\tconnection.state.loginUser = user;\n\tconnection.state.waiting = 'auth-login-password';\n\tconnection.send(reply(334, undefined, 'UGFzc3dvcmQ6'));\n}\n\n/** The client's line after a 334 challenge. */\nexport async function continueAuth(\n\tconnection: Connection,\n\tline: string,\n): Promise<void> {\n\tconst { state } = connection;\n\tif (line === '*') {\n\t\tstate.waiting = 'command';\n\t\tstate.loginUser = undefined;\n\t\treturn connection.fail(reply(501, '5.0.0', 'Authentication cancelled'));\n\t}\n\tswitch (state.waiting) {\n\t\tcase 'auth-plain':\n\t\t\treturn check(connection, decodePlain(line));\n\t\tcase 'auth-login-user':\n\t\t\treturn loginUser(connection, line);\n\t\tdefault: {\n\t\t\tconst password = decodeLoginStep(line);\n\t\t\tconst username = state.loginUser ?? '';\n\t\t\treturn check(\n\t\t\t\tconnection,\n\t\t\t\tpassword ? { mechanism: 'LOGIN', username, password } : undefined,\n\t\t\t);\n\t\t}\n\t}\n}\n",
13
+ "import type { Envelope } from './options';\n\n/** A mail transaction in progress (RFC 5321 §3.3). */\nexport interface Transaction {\n\tfrom?: string;\n\tto: string[];\n\tsmtputf8: boolean;\n\tbody: Envelope['body'];\n}\n\n/** What the next line from the client is. */\nexport type Waiting =\n\t| 'command'\n\t| 'data'\n\t| 'auth-plain'\n\t| 'auth-login-user'\n\t| 'auth-login-password';\n\n/** The state of a session the commands change. */\nexport interface State {\n\thelo?: string | undefined;\n\tesmtp: boolean;\n\tuser?: string | undefined;\n\tsecure: boolean;\n\ttransaction: Transaction;\n\twaiting: Waiting;\n\tloginUser?: string | undefined;\n\terrors: number;\n\tauthFailures: number;\n}\n\nexport const emptyTransaction = (): Transaction => ({\n\tto: [],\n\tsmtputf8: false,\n\tbody: '7BIT',\n});\n",
14
+ "import { parsePathCommand } from '../protocol/command';\nimport { reply } from '../protocol/reply';\nimport type { Connection } from './connection';\nimport { LOCAL_ERROR } from './guard';\n\nfunction nonAscii(text: string): boolean {\n\tfor (let i = 0; i < text.length; i++)\n\t\tif (text.charCodeAt(i) > 0x7f) return true;\n\treturn false;\n}\n\n/** `MAIL FROM:<path> [SIZE=] [BODY=] [SMTPUTF8] [AUTH=]` (RFC 5321 §4.1.1.2). */\nexport async function mail(\n\tconnection: Connection,\n\targument: string,\n): Promise<void> {\n\tconst { state, settings } = connection;\n\tif (state.helo === undefined)\n\t\treturn connection.fail(reply(503, '5.5.1', 'Send EHLO first'));\n\tif (state.transaction.from !== undefined) {\n\t\treturn connection.fail(reply(503, '5.5.1', 'Nested MAIL command'));\n\t}\n\tif (settings.mode === 'submission' && state.user === undefined) {\n\t\treturn connection.fail(reply(530, '5.7.0', 'Authentication required'));\n\t}\n\tconst command = parsePathCommand(argument, 'FROM');\n\tif (!command)\n\t\treturn connection.fail(reply(501, '5.5.4', 'Syntax: MAIL FROM:<address>'));\n\tconst { path, parameters } = command;\n\tlet body: '7BIT' | '8BITMIME' = '7BIT';\n\tlet smtputf8 = false;\n\tfor (const [key, value] of Object.entries(parameters)) {\n\t\tif (!state.esmtp)\n\t\t\treturn connection.fail(reply(555, '5.5.4', `${key} needs EHLO`));\n\t\tif (key === 'SIZE') {\n\t\t\tif (!/^\\d+$/.test(value))\n\t\t\t\treturn connection.fail(reply(501, '5.5.4', 'Syntax: SIZE=<bytes>'));\n\t\t\tif (Number(value) > settings.maxMessageSize) {\n\t\t\t\treturn connection.fail(\n\t\t\t\t\treply(552, '5.3.4', 'Message too big for system'),\n\t\t\t\t);\n\t\t\t}\n\t\t} else if (key === 'BODY') {\n\t\t\tconst kind = value.toUpperCase();\n\t\t\tif (kind !== '7BIT' && kind !== '8BITMIME') {\n\t\t\t\treturn connection.fail(reply(501, '5.5.4', 'BODY is 7BIT or 8BITMIME'));\n\t\t\t}\n\t\t\tbody = kind;\n\t\t} else if (key === 'SMTPUTF8' && value === '') {\n\t\t\tsmtputf8 = true;\n\t\t} else if (key === 'AUTH') {\n\t\t\t// RFC 4954 §5: the original submitter; taken and not passed on.\n\t\t} else {\n\t\t\treturn connection.fail(reply(555, '5.5.4', `${key} is not supported`));\n\t\t}\n\t}\n\tif (!smtputf8 && nonAscii(path.address)) {\n\t\treturn connection.fail(\n\t\t\treply(553, '5.6.7', 'A non-ASCII address needs SMTPUTF8'),\n\t\t);\n\t}\n\tconst options = settings.options;\n\tconst refused = options.onMailFrom\n\t\t? await connection.hook('onMailFrom', () =>\n\t\t\t\toptions.onMailFrom?.(path, connection.session),\n\t\t\t)\n\t\t: undefined;\n\tif (refused) return connection.send(refused);\n\tstate.transaction = { from: path.address, to: [], smtputf8, body };\n\tconnection.send(reply(250, '2.1.0', 'OK'));\n}\n\n/** `RCPT TO:<path>` (RFC 5321 §4.1.1.3), where relaying is refused without AUTH. */\nexport async function rcpt(\n\tconnection: Connection,\n\targument: string,\n): Promise<void> {\n\tconst { state, settings } = connection;\n\tif (state.transaction.from === undefined)\n\t\treturn connection.fail(reply(503, '5.5.1', 'Send MAIL first'));\n\tconst command = parsePathCommand(argument, 'TO');\n\tif (!command)\n\t\treturn connection.fail(reply(501, '5.5.4', 'Syntax: RCPT TO:<address>'));\n\tconst { path, parameters } = command;\n\tif (Object.keys(parameters).length > 0) {\n\t\treturn connection.fail(\n\t\t\treply(555, '5.5.4', `${Object.keys(parameters)[0]} is not supported`),\n\t\t);\n\t}\n\tif (!state.transaction.smtputf8 && nonAscii(path.address)) {\n\t\treturn connection.fail(\n\t\t\treply(553, '5.6.7', 'A non-ASCII address needs SMTPUTF8'),\n\t\t);\n\t}\n\tif (state.transaction.to.length >= settings.maxRecipients) {\n\t\treturn connection.send(reply(452, '4.5.3', 'Too many recipients'));\n\t}\n\t// Never an open relay: a domain this server does not host takes AUTH.\n\tif (state.user === undefined) {\n\t\tconst local = await connection.check('localDomains', () =>\n\t\t\tsettings.isLocal(path.domain),\n\t\t);\n\t\tif (local.failed) return connection.send(LOCAL_ERROR);\n\t\tif (local.value !== true) {\n\t\t\treturn connection.fail(reply(554, '5.7.1', 'Relay access denied'));\n\t\t}\n\t}\n\tconst options = settings.options;\n\tconst refused = options.onRcptTo\n\t\t? await connection.hook('onRcptTo', () =>\n\t\t\t\toptions.onRcptTo?.(path, connection.session),\n\t\t\t)\n\t\t: undefined;\n\tif (refused) return connection.send(refused);\n\tstate.transaction.to.push(path.address);\n\tconnection.send(reply(250, '2.1.5', 'OK'));\n}\n",
15
+ "import { parseCommand } from '../protocol/command';\nimport { reply } from '../protocol/reply';\nimport { authenticate, continueAuth } from './auth';\nimport type { Connection } from './connection';\nimport { emptyTransaction } from './state';\nimport { mail, rcpt } from './transaction';\n\nfunction ehloLines(connection: Connection): string[] {\n\tconst { settings, state } = connection;\n\tconst lines = [\n\t\t`${settings.options.hostname} greets ${state.helo}`,\n\t\t'PIPELINING',\n\t\t`SIZE ${settings.maxMessageSize}`,\n\t\t'8BITMIME',\n\t\t'SMTPUTF8',\n\t\t'ENHANCEDSTATUSCODES',\n\t];\n\tif (settings.options.tls && !state.secure) lines.push('STARTTLS');\n\tif (\n\t\tsettings.options.authenticate &&\n\t\tstate.secure &&\n\t\tstate.user === undefined\n\t) {\n\t\tlines.push('AUTH PLAIN LOGIN');\n\t}\n\treturn lines;\n}\n\nasync function hello(\n\tconnection: Connection,\n\targument: string,\n\tesmtp: boolean,\n): Promise<void> {\n\tif (\n\t\t!/^(?:[A-Za-z0-9-]+(?:\\.[A-Za-z0-9-]+)*|\\[[0-9A-Fa-f:.Iv]+\\])$/.test(\n\t\t\targument,\n\t\t)\n\t) {\n\t\tconnection.fail(\n\t\t\treply(501, '5.5.4', `Syntax: ${esmtp ? 'EHLO' : 'HELO'} hostname`),\n\t\t);\n\t\treturn;\n\t}\n\tconst { state } = connection;\n\tstate.helo = argument;\n\tstate.esmtp = esmtp;\n\t// RFC 5321 §4.1.4: EHLO or HELO resets a transaction in progress.\n\tstate.transaction = emptyTransaction();\n\tconnection.send(\n\t\tesmtp\n\t\t\t? reply(250, undefined, ehloLines(connection))\n\t\t\t: reply(\n\t\t\t\t\t250,\n\t\t\t\t\tundefined,\n\t\t\t\t\t`${connection.settings.options.hostname} greets ${argument}`,\n\t\t\t\t),\n\t);\n}\n\nfunction data(connection: Connection, argument: string): void {\n\tconst { state } = connection;\n\tconst refusal =\n\t\targument !== ''\n\t\t\t? reply(501, '5.5.4', 'Syntax: DATA')\n\t\t\t: state.transaction.from === undefined\n\t\t\t\t? reply(503, '5.5.1', 'Send MAIL first')\n\t\t\t\t: state.transaction.to.length === 0\n\t\t\t\t\t? reply(554, '5.5.1', 'No valid recipients')\n\t\t\t\t\t: undefined;\n\tif (refusal) {\n\t\tconnection.fail(refusal);\n\t\t// What the client pipelined after a refused DATA is message content,\n\t\t// not commands: running it would let a message smuggle commands in.\n\t\tconnection.input.drop();\n\t\treturn;\n\t}\n\tconnection.send(reply(354, undefined, 'End data with <CR><LF>.<CR><LF>'));\n\tconnection.input.beginData();\n}\n\nasync function startTls(\n\tconnection: Connection,\n\targument: string,\n): Promise<void> {\n\tconst { state, settings } = connection;\n\tconst refusal = !settings.options.tls\n\t\t? reply(454, '4.7.0', 'TLS not available')\n\t\t: state.secure\n\t\t\t? reply(503, '5.5.1', 'TLS already active')\n\t\t\t: argument !== ''\n\t\t\t\t? reply(501, '5.5.4', 'Syntax: STARTTLS')\n\t\t\t\t: undefined;\n\tif (refusal) {\n\t\tconnection.fail(refusal);\n\t\treturn;\n\t}\n\tconnection.send(reply(220, '2.0.0', 'Ready to start TLS'));\n\tconnection.input.ignore();\n\t// The 220 must reach the client in clear before the handshake starts.\n\tawait connection.transport.drained();\n\tif (connection.closed) return;\n\tconnection.transport.startTls();\n\tconnection.input.accept();\n\t// RFC 3207 §4.2: everything learnt before TLS is forgotten.\n\tstate.secure = true;\n\tstate.helo = undefined;\n\tstate.esmtp = false;\n\tstate.transaction = emptyTransaction();\n}\n\n/** Runs one command line, or the client's answer to an AUTH challenge. */\nexport async function runCommand(\n\tconnection: Connection,\n\tline: string,\n): Promise<void> {\n\tconst { state } = connection;\n\tif (state.waiting !== 'command') return continueAuth(connection, line);\n\tconst { verb, argument } = parseCommand(line);\n\tswitch (verb) {\n\t\tcase 'EHLO':\n\t\t\treturn hello(connection, argument, true);\n\t\tcase 'HELO':\n\t\t\treturn hello(connection, argument, false);\n\t\tcase 'MAIL':\n\t\t\treturn mail(connection, argument);\n\t\tcase 'RCPT':\n\t\t\treturn rcpt(connection, argument);\n\t\tcase 'DATA':\n\t\t\treturn data(connection, argument);\n\t\tcase 'RSET':\n\t\t\tstate.transaction = emptyTransaction();\n\t\t\treturn connection.send(reply(250, '2.0.0', 'OK'));\n\t\tcase 'NOOP':\n\t\t\treturn connection.send(reply(250, '2.0.0', 'OK'));\n\t\tcase 'VRFY':\n\t\t\treturn connection.send(\n\t\t\t\treply(\n\t\t\t\t\t252,\n\t\t\t\t\t'2.5.0',\n\t\t\t\t\t'Cannot VRFY user; send the message and it will be tried',\n\t\t\t\t),\n\t\t\t);\n\t\tcase 'HELP':\n\t\t\treturn connection.send(reply(214, '2.0.0', 'See RFC 5321'));\n\t\tcase 'QUIT':\n\t\t\treturn connection.close(\n\t\t\t\treply(\n\t\t\t\t\t221,\n\t\t\t\t\t'2.0.0',\n\t\t\t\t\t`${connection.settings.options.hostname} closing connection`,\n\t\t\t\t),\n\t\t\t);\n\t\tcase 'STARTTLS':\n\t\t\treturn startTls(connection, argument);\n\t\tcase 'AUTH':\n\t\t\treturn authenticate(connection, argument);\n\t\tdefault:\n\t\t\treturn connection.fail(reply(500, '5.5.2', 'Command unrecognized'));\n\t}\n}\n",
16
+ "import type { Connection } from './connection';\n\nconst DAYS = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];\nconst MONTHS = [\n\t'Jan',\n\t'Feb',\n\t'Mar',\n\t'Apr',\n\t'May',\n\t'Jun',\n\t'Jul',\n\t'Aug',\n\t'Sep',\n\t'Oct',\n\t'Nov',\n\t'Dec',\n];\n\nconst two = (n: number) => String(n).padStart(2, '0');\n\n/** An RFC 5322 §3.3 date-time, in UTC. */\nexport function dateTime(date: Date): string {\n\treturn `${DAYS[date.getUTCDay()]}, ${date.getUTCDate()} ${MONTHS[date.getUTCMonth()]} ${date.getUTCFullYear()} ${two(date.getUTCHours())}:${two(date.getUTCMinutes())}:${two(date.getUTCSeconds())} +0000`;\n}\n\n/** RFC 3848: how the message came — SMTP, ESMTP, then S for TLS and A for AUTH. */\nexport function protocolOf(\n\tesmtp: boolean,\n\tsecure: boolean,\n\tauthenticated: boolean,\n): string {\n\tif (!esmtp) return 'SMTP';\n\treturn `ESMTP${secure ? 'S' : ''}${authenticated ? 'A' : ''}`;\n}\n\n/** Keeps a client-given name printable in a header: anything else becomes `?`. */\nfunction printable(text: string): string {\n\tlet out = '';\n\tfor (const char of text) {\n\t\tconst code = char.charCodeAt(0);\n\t\tout += code > 0x20 && code < 0x7f ? char : '?';\n\t}\n\treturn out;\n}\n\n/** Whether an address can go into the `for` clause as it is: printable, and no `;` to end the clause early. */\nfunction plain(address: string): boolean {\n\treturn printable(address) === address && !address.includes(';');\n}\n\n/**\n * The Received field the server puts on top (RFC 5321 §4.4): who sent the\n * message, from where, how, to whom when there is one recipient, and when.\n */\nexport function receivedField(\n\tconnection: Connection,\n\trecipients: readonly string[],\n\tid: string,\n\tnow = new Date(),\n): string {\n\tconst { state, transport, settings } = connection;\n\tconst lines = [\n\t\t`Received: from ${printable(state.helo ?? 'unknown')} ([${printable(transport.remoteAddress)}])`,\n\t\t`\\tby ${settings.options.hostname} with ${protocolOf(state.esmtp, state.secure, state.user !== undefined)} id ${id}`,\n\t];\n\tconst [recipient] = recipients;\n\tif (recipients.length === 1 && recipient !== undefined && plain(recipient)) {\n\t\tlines.push(`\\tfor <${recipient}>`);\n\t}\n\tlines[lines.length - 1] += `; ${dateTime(now)}`;\n\treturn `${lines.join('\\r\\n')}\\r\\n`;\n}\n",
17
+ "import { SmtpError } from '../errors';\nimport { reply } from '../protocol/reply';\n\n/** Why a message on its way in is refused, as the reply and the error `onData` and `onError` see. */\n\nexport const TOO_BIG = reply(552, '5.3.4', 'Message too big for system');\n\nexport const BARE_LINE_BREAK = reply(\n\t550,\n\t'5.6.11',\n\t'Bare CR or LF is not allowed in a message',\n);\n\nexport function tooBig(maxMessageSize: number): SmtpError {\n\treturn new SmtpError(\n\t\t'MESSAGE_TOO_BIG',\n\t\t`The message is larger than maxMessageSize (${maxMessageSize} bytes); do not deliver it`,\n\t);\n}\n\n/** A bare CR or LF is how SMTP smuggling hides a second message. */\nexport function bareLineBreak(): SmtpError {\n\treturn new SmtpError(\n\t\t'BARE_LINE_BREAK',\n\t\t'The message holds a bare CR or LF (SMTP smuggling); do not deliver it',\n\t);\n}\n\n/**\n * The connection closed mid-message, or after its end but before the reply:\n * the client hung up, or the server closed it (idle timeout, socket error).\n */\nexport function connectionLost(ended: boolean): SmtpError {\n\treturn new SmtpError(\n\t\t'CONNECTION_LOST',\n\t\tended\n\t\t\t? 'The connection closed before the reply to the message; do not deliver it'\n\t\t\t: 'The connection closed before the end of the message; do not deliver it',\n\t);\n}\n\nexport function notAnswered(seconds: number): SmtpError {\n\treturn new SmtpError(\n\t\t'HOOK_TIMEOUT',\n\t\t`onData did not answer within hookTimeout (${seconds} s); do not deliver it`,\n\t);\n}\n\nexport function notReadInTime(seconds: number): SmtpError {\n\treturn new SmtpError(\n\t\t'HOOK_TIMEOUT',\n\t\t`onData did not read the message within hookTimeout (${seconds} s); do not deliver it`,\n\t);\n}\n\nexport function notRead(): SmtpError {\n\treturn new SmtpError(\n\t\t'MESSAGE_NOT_READ',\n\t\t'onData answered without reading the message to its end; it was not taken',\n\t);\n}\n",
18
+ "import type { SmtpError } from '../errors';\nimport { DataReader } from '../protocol/data';\nimport { type Reply, reply } from '../protocol/reply';\nimport type { Connection } from './connection';\nimport { hookTimeout, LOCAL_ERROR, refusalOf, timedOut, within } from './guard';\nimport type { ReceivedMessage } from './options';\nimport { receivedField } from './received';\nimport {\n\tBARE_LINE_BREAK,\n\tbareLineBreak,\n\tconnectionLost,\n\tnotAnswered,\n\tnotRead,\n\tnotReadInTime,\n\tTOO_BIG,\n\ttooBig,\n} from './refusals';\nimport type { Transaction } from './state';\n\n/** Bytes of message held for `onData` before the server stops reading the client. */\nconst HIGH_WATER_MARK = 64 * 1024;\n\n/**\n * One message on its way in: the content of DATA, unstuffed, fed to\n * `onData` as a stream while the client sends it. At most\n * `HIGH_WATER_MARK` bytes wait to be read; past that the server waits for\n * `onData` to read, and so stops reading the client.\n *\n * The stream ends in an `SmtpError` when the message must not be\n * delivered, and the reply to DATA is then that refusal, whatever `onData`\n * answered. The 250 goes out only when `onData` read the stream to its\n * clean end and accepted; a stream left unread errors with\n * `MESSAGE_NOT_READ`. When the message's `signal` aborts is set out on\n * `ReceivedMessage.signal`.\n */\nexport class Intake {\n\treadonly id = crypto.getRandomValues(new Uint8Array(10)).toHex();\n\treadonly #connection: Connection;\n\treadonly #reader = new DataReader();\n\t#controller!: ReadableStreamDefaultController<Uint8Array>;\n\t/** What waits for `onData` to pull. */\n\t#queue: Uint8Array[] = [];\n\t#queued = 0;\n\t/** The end of DATA came, cleanly. */\n\t#ended = false;\n\t/** `onData` pulled the last byte and saw the stream close. */\n\t#read = false;\n\t#state: 'open' | 'closed' | 'errored' | 'cancelled' = 'open';\n\t#failure: Reply | undefined;\n\t#wakers: (() => void)[] = [];\n\treadonly #delivery: Promise<Reply | undefined>;\n\t/** Whether `onData` had read to the end when it answered. */\n\t#readWhenAnswered = false;\n\t/** Aborts when the server refuses the message after `onData` had it. */\n\treadonly #refusal = new AbortController();\n\n\tconstructor(connection: Connection, transaction: Transaction) {\n\t\tthis.#connection = connection;\n\t\t// High-water mark 0: a byte leaves the queue only when onData asks\n\t\t// for it, so the server knows whether it read to the end.\n\t\tconst content = new ReadableStream<Uint8Array>(\n\t\t\t{\n\t\t\t\tstart: (controller) => {\n\t\t\t\t\tthis.#controller = controller;\n\t\t\t\t},\n\t\t\t\tpull: (controller) => this.#pull(controller),\n\t\t\t\tcancel: () => {\n\t\t\t\t\tif (this.#state === 'open') this.#state = 'cancelled';\n\t\t\t\t\tthis.#queue = [];\n\t\t\t\t\tthis.#queued = 0;\n\t\t\t\t\tthis.#wake();\n\t\t\t\t},\n\t\t\t},\n\t\t\t{ highWaterMark: 0 },\n\t\t);\n\t\tthis.#push(\n\t\t\tnew TextEncoder().encode(\n\t\t\t\treceivedField(connection, transaction.to, this.id),\n\t\t\t),\n\t\t);\n\t\tconst message: ReceivedMessage = {\n\t\t\tid: this.id,\n\t\t\tenvelope: {\n\t\t\t\tfrom: transaction.from ?? '',\n\t\t\t\tto: [...transaction.to],\n\t\t\t\tsmtputf8: transaction.smtputf8,\n\t\t\t\tbody: transaction.body,\n\t\t\t},\n\t\t\tcontent,\n\t\t\tsignal: this.#refusal.signal,\n\t\t};\n\t\tthis.#delivery = this.#deliver(message);\n\t}\n\n\t/**\n\t * `onData`'s answer as the reply: `undefined`, its refusal, or 451 when\n\t * it threw or answered what is not a refusal — which also aborts the\n\t * signal, since the message was not taken.\n\t */\n\tasync #deliver(message: ReceivedMessage): Promise<Reply | undefined> {\n\t\tconst connection = this.#connection;\n\t\t/** onData refused the message itself, with a valid 4xx or 5xx reply. */\n\t\tlet ownRefusal = false;\n\t\tconst refuse = (error: unknown) => {\n\t\t\tthis.#refusal.abort(error);\n\t\t\tconnection.report(error);\n\t\t};\n\t\ttry {\n\t\t\t// Keeps onData starting after Input has set up the transaction, as\n\t\t\t// connection.hook did; nothing observable depends on it today.\n\t\t\tawait Promise.resolve();\n\t\t\tconst answer = await connection.settings.options.onData(\n\t\t\t\tmessage,\n\t\t\t\tconnection.session,\n\t\t\t);\n\t\t\tconst refusal = refusalOf('onData', answer, refuse);\n\t\t\townRefusal = refusal !== undefined && refusal === answer;\n\t\t\treturn refusal;\n\t\t} catch (error) {\n\t\t\trefuse(error);\n\t\t\treturn LOCAL_ERROR;\n\t\t} finally {\n\t\t\tthis.#readWhenAnswered = this.#read;\n\t\t\t// Answered before the end: a reader left running must not reach a\n\t\t\t// clean end. The signal aborts only if the server refuses for it.\n\t\t\tif (!this.#read) this.#stop(!ownRefusal);\n\t\t\tthis.#wake();\n\t\t}\n\t}\n\n\t/** A chunk of DATA; resolves once there is room for the next. */\n\tasync write(chunk: Uint8Array): Promise<{ done: boolean; rest: Uint8Array }> {\n\t\tconst { data, done, rest } = this.#reader.write(chunk);\n\t\tconst { maxMessageSize } = this.#connection.settings;\n\t\tif (this.#failure) return { done, rest };\n\t\tif (this.#reader.size > maxMessageSize) {\n\t\t\tthis.#fail(TOO_BIG, tooBig(maxMessageSize));\n\t\t} else if (this.#reader.bareLineBreaks > 0) {\n\t\t\t// A bare CR or LF is how SMTP smuggling hides a second message.\n\t\t\tthis.#fail(BARE_LINE_BREAK, bareLineBreak());\n\t\t} else if (this.#state === 'open' && data.length > 0) {\n\t\t\tthis.#push(data);\n\t\t\tawait this.#room();\n\t\t}\n\t\treturn { done, rest };\n\t}\n\n\t/** The end of DATA: the reply to send. */\n\tasync finish(): Promise<Reply> {\n\t\tif (this.#failure) return this.#failure;\n\t\tthis.#ended = true;\n\t\tthis.#wake();\n\t\tconst { hookTimeout: seconds } = this.#connection.settings;\n\t\tconst answer = await within(this.#delivery, seconds);\n\t\tif (timedOut(answer)) {\n\t\t\tthis.#connection.report(hookTimeout('onData', seconds));\n\t\t\t// The client is told 451 and will try again: a late read must not\n\t\t\t// take it, and a read already finished learns it through the signal.\n\t\t\tthis.#fail(LOCAL_ERROR, notAnswered(seconds));\n\t\t\treturn LOCAL_ERROR;\n\t\t}\n\t\tif (answer) return answer;\n\t\tif (!this.#readWhenAnswered) {\n\t\t\tconst error = notRead();\n\t\t\tthis.#refusal.abort(error);\n\t\t\tthis.#connection.report(error);\n\t\t\treturn LOCAL_ERROR;\n\t\t}\n\t\treturn reply(250, '2.0.0', `OK queued as ${this.id}`);\n\t}\n\n\t/** The connection closed, mid-message or before the reply was sent. */\n\tabort(): void {\n\t\tthis.#fail(LOCAL_ERROR, connectionLost(this.#ended));\n\t}\n\n\t#push(bytes: Uint8Array): void {\n\t\tthis.#queue.push(bytes);\n\t\tthis.#queued += bytes.length;\n\t\tthis.#wake();\n\t}\n\n\t/** onData asks for more: the next chunk, the end, or a wait for either. */\n\tasync #pull(\n\t\tcontroller: ReadableStreamDefaultController<Uint8Array>,\n\t): Promise<void> {\n\t\tfor (;;) {\n\t\t\tif (this.#state !== 'open') return;\n\t\t\tconst next = this.#queue.shift();\n\t\t\tif (next) {\n\t\t\t\tthis.#queued -= next.length;\n\t\t\t\tcontroller.enqueue(next);\n\t\t\t\tthis.#wake();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (this.#ended) {\n\t\t\t\tthis.#read = true;\n\t\t\t\tthis.#state = 'closed';\n\t\t\t\tcontroller.close();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tawait new Promise<void>((wake) => this.#wakers.push(wake));\n\t\t}\n\t}\n\n\t/**\n\t * onData answered without reading to the end: the stream errors, and\n\t * takes no more. The signal aborts too, unless onData refused itself.\n\t */\n\t#stop(abortSignal: boolean): void {\n\t\tif (this.#state !== 'open') return;\n\t\tconst error = notRead();\n\t\tif (abortSignal) this.#refusal.abort(error);\n\t\tthis.#controller.error(error);\n\t\tthis.#state = 'cancelled';\n\t\tthis.#queue = [];\n\t\tthis.#queued = 0;\n\t}\n\n\t#fail(answer: Reply, error: SmtpError): void {\n\t\tthis.#failure ??= answer;\n\t\tthis.#refusal.abort(error);\n\t\tif (this.#state === 'open') this.#controller.error(error);\n\t\tthis.#state = 'errored';\n\t\tthis.#queue = [];\n\t\tthis.#queued = 0;\n\t\tthis.#wake();\n\t}\n\n\t/** Waits while the queue is full; gives up on an `onData` that stopped reading. */\n\tasync #room(): Promise<void> {\n\t\tconst { hookTimeout: seconds } = this.#connection.settings;\n\t\twhile (this.#state === 'open' && this.#queued > HIGH_WATER_MARK) {\n\t\t\tconst woken = await within(\n\t\t\t\tnew Promise<void>((wake) => this.#wakers.push(wake)),\n\t\t\t\tseconds,\n\t\t\t);\n\t\t\tif (timedOut(woken)) {\n\t\t\t\tthis.#connection.report(hookTimeout('onData', seconds));\n\t\t\t\tthis.#fail(LOCAL_ERROR, notReadInTime(seconds));\n\t\t\t}\n\t\t}\n\t}\n\n\t#wake(): void {\n\t\tfor (const wake of this.#wakers.splice(0)) wake();\n\t}\n}\n",
19
+ "/** The longest command line taken, CRLF included; RFC 5321 §4.5.3.1.4 asks for 512 at least. */\nexport const MAX_LINE = 2048;\n\n/** One line from the client, or a line that was too long and was skipped. */\nexport type LineEvent = { line: string } | { tooLong: true };\n\n/**\n * Cuts the client's bytes into command lines, ending at LF with or without\n * CR. It holds one partial line at most: past `MAX_LINE`, the line is\n * skipped to its end and reported once.\n */\nexport class LineSplitter {\n\t#held: Uint8Array = new Uint8Array(0);\n\t#skipping = false;\n\t#rest: Uint8Array = new Uint8Array(0);\n\n\t/** Starts on a chunk; read its lines with `next`. */\n\tpush(chunk: Uint8Array): void {\n\t\tthis.#rest = this.#held.length > 0 ? concat(this.#held, chunk) : chunk;\n\t\tthis.#held = new Uint8Array(0);\n\t}\n\n\t/** The next line of the current chunk, or `undefined` when it has no more. */\n\tnext(): LineEvent | undefined {\n\t\tfor (;;) {\n\t\t\tconst data = this.#rest;\n\t\t\tconst lf = data.indexOf(0x0a);\n\t\t\tif (lf < 0) {\n\t\t\t\tthis.#rest = new Uint8Array(0);\n\t\t\t\tif (this.#skipping) return undefined;\n\t\t\t\tif (data.length > MAX_LINE) {\n\t\t\t\t\tthis.#skipping = true;\n\t\t\t\t\treturn { tooLong: true };\n\t\t\t\t}\n\t\t\t\tthis.#held = data.slice();\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t\tconst end = lf > 0 && data[lf - 1] === 0x0d ? lf - 1 : lf;\n\t\t\tthis.#rest = data.subarray(lf + 1);\n\t\t\tif (this.#skipping) {\n\t\t\t\tthis.#skipping = false;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (end > MAX_LINE) return { tooLong: true };\n\t\t\treturn { line: new TextDecoder().decode(data.subarray(0, end)) };\n\t\t}\n\t}\n\n\t/** What is left of the current chunk: message content pipelined after DATA. */\n\ttakeRest(): Uint8Array {\n\t\tconst rest = this.#rest;\n\t\tthis.#rest = new Uint8Array(0);\n\t\treturn rest;\n\t}\n\n\t/** Forgets everything held: after STARTTLS, or after a refused DATA. */\n\tclear(): void {\n\t\tthis.#held = new Uint8Array(0);\n\t\tthis.#rest = new Uint8Array(0);\n\t\tthis.#skipping = false;\n\t}\n}\n\nfunction concat(a: Uint8Array, b: Uint8Array): Uint8Array {\n\tconst out = new Uint8Array(a.length + b.length);\n\tout.set(a, 0);\n\tout.set(b, a.length);\n\treturn out;\n}\n",
20
+ "import { reply } from '../protocol/reply';\nimport { runCommand } from './commands';\nimport type { Connection } from './connection';\nimport { Intake } from './intake';\nimport { LineSplitter } from './lines';\nimport { emptyTransaction } from './state';\n\n/** Bytes waiting to be read past which the server stops reading the client. */\nconst INPUT_LIMIT = 64 * 1024;\n\n/**\n * What a client sends, read in order: command lines one at a time, each\n * answered before the next runs, and the message after DATA. It holds a\n * bounded amount: past `INPUT_LIMIT` waiting bytes it pauses the socket,\n * and it waits for the replies to leave before reading on.\n */\nexport class Input {\n\treadonly #connection: Connection;\n\t#input: Uint8Array[] = [];\n\t#queued = 0;\n\t#paused = false;\n\treadonly #lines = new LineSplitter();\n\t#intake: Intake | undefined;\n\t/** A message whole, while `onData` answers it and before the reply is sent. */\n\t#answering: Intake | undefined;\n\t/** The work in progress on what the client sent; settled when all of it is answered. */\n\t#pumping: Promise<void> | undefined;\n\t/** The 220 went out: input before it breaks RFC 5321 §4.3.1. */\n\t#greeted = false;\n\t/** The client talked before the greeting. */\n\tearly = false;\n\t/** Set while the 220 to STARTTLS leaves: clear text then is never read. */\n\t#ignoring = false;\n\t/** Set by `drop`: the line loop drops what it still holds. */\n\t#dropped = false;\n\n\tconstructor(connection: Connection) {\n\t\tthis.#connection = connection;\n\t}\n\n\tgreeted(): void {\n\t\tthis.#greeted = true;\n\t}\n\n\treceive(chunk: Uint8Array): void {\n\t\tif (this.#ignoring) return;\n\t\tif (!this.#greeted) {\n\t\t\tthis.early = true;\n\t\t\treturn;\n\t\t}\n\t\tthis.#input.push(chunk.slice());\n\t\tthis.#queued += chunk.length;\n\t\tif (this.#queued > INPUT_LIMIT && !this.#paused) {\n\t\t\tthis.#paused = true;\n\t\t\tthis.#connection.transport.pause();\n\t\t}\n\t\tthis.#kick();\n\t}\n\n\t/** The connection closed: a message on its way in must not be delivered. */\n\tabort(): void {\n\t\tthis.#intake?.abort();\n\t\tthis.#intake = undefined;\n\t\t// The connection closed before the reply, so the client will send the\n\t\t// message again: onData learns it through the signal.\n\t\tthis.#answering?.abort();\n\t\tthis.#answering = undefined;\n\t}\n\n\t/**\n\t * Forgets what the client sent ahead and reads nothing until `accept`:\n\t * what comes in clear before TLS never counts (RFC 3207 §4.2,\n\t * CVE-2011-0411).\n\t */\n\tignore(): void {\n\t\tthis.drop();\n\t\tthis.#input = [];\n\t\tthis.#queued = 0;\n\t\tthis.#ignoring = true;\n\t\tthis.#resume();\n\t}\n\n\taccept(): void {\n\t\tthis.#ignoring = false;\n\t}\n\n\t/** Drops the rest of the chunk being read: after a refused DATA, it is message content, not commands. */\n\tdrop(): void {\n\t\tthis.#lines.clear();\n\t\tthis.#dropped = true;\n\t}\n\n\t/** Starts receiving a message: `onData` is called, and gets it as it comes. */\n\tbeginData(): void {\n\t\tconst connection = this.#connection;\n\t\tthis.#intake = new Intake(connection, connection.state.transaction);\n\t\tconnection.state.waiting = 'data';\n\t}\n\n\t/** Resolves once everything received so far is answered. */\n\tasync idle(): Promise<void> {\n\t\twhile (this.#pumping) await this.#pumping;\n\t}\n\n\t#kick(): void {\n\t\tif (this.#pumping) return;\n\t\tconst connection = this.#connection;\n\t\tthis.#pumping = this.#pump()\n\t\t\t.catch((error) => {\n\t\t\t\tconnection.report(error);\n\t\t\t\tconnection.close(reply(421, '4.3.0', 'Local error, closing'));\n\t\t\t})\n\t\t\t.finally(() => {\n\t\t\t\tthis.#pumping = undefined;\n\t\t\t\t// Bytes that came after the loop's last look.\n\t\t\t\tif (!connection.closed && this.#input.length > 0) this.#kick();\n\t\t\t});\n\t}\n\n\t#resume(): void {\n\t\tif (this.#paused && this.#queued <= INPUT_LIMIT / 2) {\n\t\t\tthis.#paused = false;\n\t\t\tthis.#connection.transport.resume();\n\t\t}\n\t}\n\n\tasync #pump(): Promise<void> {\n\t\twhile (!this.#connection.closed && this.#input.length > 0) {\n\t\t\tconst chunk = this.#input.shift() as Uint8Array;\n\t\t\tthis.#queued -= chunk.length;\n\t\t\tthis.#resume();\n\t\t\tif (this.#connection.state.waiting === 'data') await this.#data(chunk);\n\t\t\telse await this.#commands(chunk);\n\t\t}\n\t}\n\n\tasync #commands(chunk: Uint8Array): Promise<void> {\n\t\tthis.#lines.push(chunk);\n\t\twhile (!this.#connection.closed) {\n\t\t\t// The replies so far leave before the next command runs.\n\t\t\tawait this.#connection.transport.drained();\n\t\t\tconst event = this.#lines.next();\n\t\t\tif (!event) return;\n\t\t\tif (!('line' in event)) {\n\t\t\t\tthis.#connection.fail(reply(500, '5.5.6', 'Line too long'));\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tthis.#dropped = false;\n\t\t\tawait runCommand(this.#connection, event.line);\n\t\t\tif (this.#dropped) return;\n\t\t\tif (this.#connection.state.waiting === 'data') {\n\t\t\t\t// Message content pipelined right after DATA.\n\t\t\t\tconst rest = this.#lines.takeRest();\n\t\t\t\tif (rest.length > 0) this.#unshift(rest.slice());\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t}\n\n\tasync #data(chunk: Uint8Array): Promise<void> {\n\t\tconst intake = this.#intake as Intake;\n\t\tconst { done, rest } = await intake.write(chunk);\n\t\tif (!done || this.#connection.closed) return;\n\t\tthis.#intake = undefined;\n\t\tthis.#answering = intake;\n\t\tthis.#connection.state.waiting = 'command';\n\t\tthis.#connection.state.transaction = emptyTransaction();\n\t\tconst answer = await intake.finish();\n\t\tthis.#answering = undefined;\n\t\tthis.#connection.send(answer);\n\t\tif (rest.length > 0) this.#unshift(rest);\n\t}\n\n\t/** Puts bytes back at the front, counted again: the pump took them off the count. */\n\t#unshift(bytes: Uint8Array): void {\n\t\tthis.#input.unshift(bytes);\n\t\tthis.#queued += bytes.length;\n\t\tif (this.#queued > INPUT_LIMIT && !this.#paused) {\n\t\t\tthis.#paused = true;\n\t\t\tthis.#connection.transport.pause();\n\t\t}\n\t}\n}\n",
21
+ "import { formatReply, type Reply, reply } from '../protocol/reply';\nimport { hookTimeout, LOCAL_ERROR, refusalOf, timedOut, within } from './guard';\nimport { Input } from './input';\nimport type { HookResult, Session } from './options';\nimport type { Settings } from './settings';\nimport { emptyTransaction, type State } from './state';\nimport type { Transport } from './transport';\n\n/**\n * One SMTP session (RFC 5321): reads commands, one at a time and in order —\n * PIPELINING sends several at once, and each reply waits for the hooks of\n * the commands before it — and the message after DATA.\n */\nexport class Connection {\n\treadonly settings: Settings;\n\t#transport: Transport;\n\treadonly id = crypto.getRandomValues(new Uint8Array(8)).toHex();\n\treadonly data: Record<string, unknown> = {};\n\treadonly state: State;\n\t/** What the client sends: commands, then message content after DATA. */\n\treadonly input: Input = new Input(this);\n\t#closed = false;\n\n\tconstructor(settings: Settings, transport: Transport) {\n\t\tthis.settings = settings;\n\t\tthis.#transport = transport;\n\t\tthis.state = {\n\t\t\tesmtp: false,\n\t\t\tsecure: transport.secure,\n\t\t\ttransaction: emptyTransaction(),\n\t\t\twaiting: 'command',\n\t\t\terrors: 0,\n\t\t\tauthFailures: 0,\n\t\t};\n\t}\n\n\tget transport(): Transport {\n\t\treturn this.#transport;\n\t}\n\n\t/** After STARTTLS: writes and hang-ups go through the encrypted socket. */\n\tuseTransport(transport: Transport): void {\n\t\tthis.#transport = transport;\n\t}\n\n\tget session(): Session {\n\t\tconst { helo, user } = this.state;\n\t\treturn {\n\t\t\tid: this.id,\n\t\t\tremoteAddress: this.transport.remoteAddress,\n\t\t\tsecure: this.state.secure,\n\t\t\tesmtp: this.state.esmtp,\n\t\t\t...(helo === undefined ? {} : { helo }),\n\t\t\t...(user === undefined ? {} : { user }),\n\t\t\tdata: this.data,\n\t\t};\n\t}\n\n\tget closed(): boolean {\n\t\treturn this.#closed;\n\t}\n\n\tsend(answer: Reply): void {\n\t\tif (this.#closed) return;\n\t\tthis.transport.write(formatReply(answer, this.state.esmtp));\n\t}\n\n\tclose(answer?: Reply): void {\n\t\tif (this.#closed) return;\n\t\tif (answer) this.send(answer);\n\t\tthis.#closed = true;\n\t\tthis.input.abort();\n\t\tthis.transport.end();\n\t}\n\n\t/** Counts a failed command; past the limit, hangs up. */\n\tfail(answer: Reply): void {\n\t\tthis.state.errors++;\n\t\tif (this.state.errors >= this.settings.maxErrors) {\n\t\t\tconst { hostname } = this.settings.options;\n\t\t\tthis.close(reply(421, '4.7.0', `${hostname} Too many errors, closing`));\n\t\t} else {\n\t\t\tthis.send(answer);\n\t\t}\n\t}\n\n\t/** Hands an error to `onError`, which may not throw back. */\n\treport(error: unknown): void {\n\t\ttry {\n\t\t\tthis.settings.options.onError?.(error, this.session);\n\t\t} catch {\n\t\t\t// The app's error handler failing is not the client's business.\n\t\t}\n\t}\n\n\t/**\n\t * Runs a hook: its refusal, or `undefined`. A throw, a reply that is not\n\t * a refusal, or — with `deadline` — no answer within `hookTimeout` is\n\t * reported and answered `451 4.3.0`.\n\t */\n\tasync hook(\n\t\tname: string,\n\t\trun: () => HookResult | Promise<HookResult>,\n\t\tdeadline = true,\n\t): Promise<Reply | undefined> {\n\t\tconst answer = await this.check(name, run, deadline);\n\t\tif (answer.failed) return LOCAL_ERROR;\n\t\treturn refusalOf(name, answer.value, (error) => this.report(error));\n\t}\n\n\t/** Runs app code that answers a value: `failed` when it threw or timed out, which is reported. */\n\tasync check<T>(\n\t\tname: string,\n\t\trun: () => T | Promise<T>,\n\t\tdeadline = true,\n\t): Promise<{ failed: false; value: T } | { failed: true }> {\n\t\tconst seconds = this.settings.hookTimeout;\n\t\ttry {\n\t\t\tconst running = Promise.resolve().then(run);\n\t\t\tconst value = deadline ? await within(running, seconds) : await running;\n\t\t\tif (timedOut(value)) {\n\t\t\t\tthis.report(hookTimeout(name, seconds));\n\t\t\t\treturn { failed: true };\n\t\t\t}\n\t\t\treturn { failed: false, value };\n\t\t} catch (error) {\n\t\t\tthis.report(error);\n\t\t\treturn { failed: true };\n\t\t}\n\t}\n\n\t/** The greeting, or the refusal of `onConnect`, or of a client that talked first. */\n\tasync open(): Promise<void> {\n\t\tconst { options } = this.settings;\n\t\tconst refused = options.onConnect\n\t\t\t? await this.hook('onConnect', () => options.onConnect?.(this.session))\n\t\t\t: undefined;\n\t\tif (refused) return this.close(refused);\n\t\tconst { greetingDelay } = this.settings;\n\t\tif (greetingDelay > 0) await Bun.sleep(greetingDelay * 1000);\n\t\tif (this.#closed) return;\n\t\tif (this.input.early) {\n\t\t\t// RFC 5321 §4.3.1: the client waits for the greeting; a spammer often does not.\n\t\t\treturn this.close(\n\t\t\t\treply(554, '5.5.0', `${options.hostname} Talked before the greeting`),\n\t\t\t);\n\t\t}\n\t\tthis.send(reply(220, undefined, `${options.hostname} ESMTP ready`));\n\t\t// The client's idle time starts at the greeting, not at connect: what\n\t\t// greetingDelay and onConnect took is not taken from it.\n\t\tthis.transport.restartIdle(this.settings.timeout);\n\t\tthis.input.greeted();\n\t}\n\n\t/** Bytes from the client; processed in order, after what came before. */\n\treceive(chunk: Uint8Array): void {\n\t\tif (!this.#closed) this.input.receive(chunk);\n\t}\n\n\t/** Resolves once everything received so far is answered. */\n\tidle(): Promise<void> {\n\t\treturn this.input.idle();\n\t}\n}\n",
22
+ "import { SmtpError } from '../errors';\nimport type { SmtpServerOptions } from './options';\n\n/** The options with their defaults, checked once. */\nexport interface Settings {\n\treadonly options: SmtpServerOptions;\n\treadonly mode: 'mx' | 'submission';\n\treadonly maxMessageSize: number;\n\treadonly maxRecipients: number;\n\treadonly maxConnections: number;\n\treadonly maxErrors: number;\n\treadonly timeout: number;\n\treadonly hookTimeout: number;\n\treadonly greetingDelay: number;\n\tisLocal(domain: string): boolean | Promise<boolean>;\n}\n\nconst invalid = (message: string) =>\n\tnew SmtpError('INVALID_OPTION', `createSmtpServer(): ${message}`);\n\nfunction positive(\n\tname: string,\n\tvalue: number | undefined,\n\tfallback: number,\n): number {\n\tif (value === undefined) return fallback;\n\tif (!Number.isSafeInteger(value) || value < 1) {\n\t\tthrow invalid(`${name} must be a positive integer, not ${value}`);\n\t}\n\treturn value;\n}\n\nfunction nonNegative(\n\tname: string,\n\tvalue: number | undefined,\n\tfallback: number,\n): number {\n\tif (value === undefined) return fallback;\n\tif (!Number.isFinite(value) || value < 0) {\n\t\tthrow invalid(\n\t\t\t`${name} must be a number of seconds, 0 or more, not ${value}`,\n\t\t);\n\t}\n\treturn value;\n}\n\n/** `localDomains` as a lookup; the domain it is given is lower case. */\nfunction localLookup(\n\tlocal: SmtpServerOptions['localDomains'],\n): (domain: string) => boolean | Promise<boolean> {\n\tif (typeof local === 'function') return local;\n\tif (\n\t\tArray.isArray(local) &&\n\t\tlocal.every((domain: unknown) => typeof domain === 'string')\n\t) {\n\t\tconst domains = new Set(\n\t\t\t(local as readonly string[]).map((domain) => domain.toLowerCase()),\n\t\t);\n\t\treturn (domain) => domains.has(domain);\n\t}\n\tthrow invalid('localDomains must be an array of domains or a function');\n}\n\nexport function settingsOf(options: SmtpServerOptions): Settings {\n\tif (\n\t\ttypeof options.hostname !== 'string' ||\n\t\t!/^[A-Za-z0-9.-]+$/.test(options.hostname)\n\t) {\n\t\tthrow invalid(`\"${options.hostname}\" is not a host name`);\n\t}\n\tif (typeof options.onData !== 'function') {\n\t\tthrow invalid('onData must be a function: it is where messages go');\n\t}\n\tif (options.implicitTls && !options.tls) {\n\t\tthrow invalid('implicitTls needs tls: { key, cert }');\n\t}\n\tif (options.mode === 'submission' && !options.authenticate) {\n\t\tthrow invalid(\n\t\t\t'submission takes mail only from authenticated users, so it needs authenticate',\n\t\t);\n\t}\n\tif (options.authenticate && !options.tls) {\n\t\tthrow invalid(\n\t\t\t'authenticate needs tls: { key, cert }, since AUTH is offered only once encrypted',\n\t\t);\n\t}\n\tconst isLocal = localLookup(options.localDomains);\n\tconst timeout = positive('timeout', options.timeout, 300);\n\tconst greetingDelay = nonNegative('greetingDelay', options.greetingDelay, 0);\n\tif (greetingDelay >= timeout) {\n\t\tthrow invalid(\n\t\t\t`greetingDelay (${greetingDelay} s) must be shorter than timeout (${timeout} s), or every client times out before the greeting`,\n\t\t);\n\t}\n\treturn {\n\t\toptions,\n\t\tmode: options.mode ?? 'mx',\n\t\tmaxMessageSize: positive(\n\t\t\t'maxMessageSize',\n\t\t\toptions.maxMessageSize,\n\t\t\t25 * 1024 * 1024,\n\t\t),\n\t\tmaxRecipients: positive('maxRecipients', options.maxRecipients, 100),\n\t\tmaxConnections: positive('maxConnections', options.maxConnections, 1000),\n\t\tmaxErrors: positive('maxErrors', options.maxErrors, 10),\n\t\ttimeout,\n\t\thookTimeout: positive('hookTimeout', options.hookTimeout, 60),\n\t\tgreetingDelay,\n\t\tisLocal: (domain) => isLocal(domain.toLowerCase()),\n\t};\n}\n",
23
+ "import type { Socket } from 'bun';\n\n/** What a connection needs from its socket: Bun's, or a fake one in specs. */\nexport interface Transport {\n\treadonly remoteAddress: string;\n\treadonly secure: boolean;\n\t/** Queues text for the client; what the socket cannot take now is kept for `drain`. */\n\twrite(text: string): void;\n\t/** Resolves once everything written has left. */\n\tdrained(): Promise<void>;\n\t/** Hangs up once everything written has left. */\n\tend(): void;\n\t/** Stops reading from the client, while the server catches up. */\n\tpause(): void;\n\tresume(): void;\n\t/** Starts TLS on the connection, once the 220 to STARTTLS has left. */\n\tstartTls(): void;\n\t/** Starts the idle time again, as a byte from the client does. */\n\trestartIdle(seconds: number): void;\n}\n\n/**\n * A Bun socket as a Transport. Bun's sockets do not buffer: `write` takes\n * what the kernel takes and says how much. The rest waits here for the\n * `drain` event, so no reply is lost to a client that reads slowly.\n */\nexport class SocketTransport implements Transport {\n\treadonly #socket: Socket<unknown>;\n\treadonly remoteAddress: string;\n\treadonly secure: boolean;\n\treadonly #startTls: () => void;\n\t#queue: Uint8Array[] = [];\n\t#waiters: (() => void)[] = [];\n\t#ending = false;\n\n\tconstructor(\n\t\tsocket: Socket<unknown>,\n\t\tsecure: boolean,\n\t\tstartTls: () => void,\n\t\tremoteAddress = socket.remoteAddress,\n\t) {\n\t\tthis.#socket = socket;\n\t\tthis.secure = secure;\n\t\tthis.#startTls = startTls;\n\t\tthis.remoteAddress = remoteAddress;\n\t}\n\n\twrite(text: string): void {\n\t\tconst bytes = new TextEncoder().encode(text);\n\t\tif (this.#queue.length > 0) {\n\t\t\tthis.#queue.push(bytes);\n\t\t\treturn;\n\t\t}\n\t\tconst written = this.#socket.write(bytes);\n\t\tif (written < bytes.length) this.#queue.push(bytes.subarray(written));\n\t}\n\n\t/** Bun's `drain`: the socket can take more. */\n\tdrain(): void {\n\t\twhile (this.#queue.length > 0) {\n\t\t\tconst bytes = this.#queue[0] as Uint8Array;\n\t\t\tconst written = this.#socket.write(bytes);\n\t\t\tif (written < bytes.length) {\n\t\t\t\tthis.#queue[0] = bytes.subarray(written);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthis.#queue.shift();\n\t\t}\n\t\tfor (const wake of this.#waiters.splice(0)) wake();\n\t\tif (this.#ending) this.#socket.end();\n\t}\n\n\tdrained(): Promise<void> {\n\t\tif (this.#queue.length === 0) return Promise.resolve();\n\t\treturn new Promise((wake) => this.#waiters.push(wake));\n\t}\n\n\t/** The socket closed: nothing more will leave. */\n\tclosed(): void {\n\t\tthis.#queue = [];\n\t\tfor (const wake of this.#waiters.splice(0)) wake();\n\t}\n\n\tend(): void {\n\t\tif (this.#queue.length === 0) this.#socket.end();\n\t\telse this.#ending = true;\n\t}\n\n\tpause(): void {\n\t\tthis.#socket.pause();\n\t}\n\n\tresume(): void {\n\t\tthis.#socket.resume();\n\t}\n\n\tstartTls(): void {\n\t\tthis.#startTls();\n\t}\n\n\trestartIdle(seconds: number): void {\n\t\tthis.#socket.timeout(seconds);\n\t}\n}\n",
24
+ "import type { Socket, SocketHandler, TCPSocketListener } from 'bun';\nimport { SmtpError } from '../errors';\nimport { reply } from '../protocol/reply';\nimport { Connection } from './connection';\nimport type { SmtpServerOptions } from './options';\nimport { type Settings, settingsOf } from './settings';\nimport { SocketTransport } from './transport';\n\ninterface SocketState {\n\tconnection?: Connection;\n\ttransport?: SocketTransport;\n\t/** Bytes on the raw socket after STARTTLS are the TLS stream itself: ignored. */\n\tupgraded: boolean;\n}\n\nexport interface SmtpServer {\n\t/** Starts listening; resolves once the port is bound. Once only: a second call throws. */\n\tlisten(options: {\n\t\tport: number;\n\t\thostname?: string;\n\t}): Promise<{ port: number; hostname: string }>;\n\t/** Stops listening; `closeConnections` hangs up on every client too. */\n\tstop(closeConnections?: boolean): void;\n\t/** Open connections. */\n\treadonly connections: number;\n}\n\n/** The handlers both the clear socket and the TLS one share: input, drain, idle time. */\nfunction handlers(settings: Settings): SocketHandler<SocketState> {\n\tconst idle = reply(\n\t\t421,\n\t\t'4.4.2',\n\t\t`${settings.options.hostname} Idle too long, closing`,\n\t);\n\treturn {\n\t\tdata(socket, chunk) {\n\t\t\tif (socket.data.upgraded) return;\n\t\t\t// Any byte from the client starts the idle time again.\n\t\t\tsocket.timeout(settings.timeout);\n\t\t\tsocket.data.connection?.receive(chunk);\n\t\t},\n\t\tdrain(socket) {\n\t\t\tif (!socket.data.upgraded) socket.data.transport?.drain();\n\t\t},\n\t\terror(socket) {\n\t\t\tsocket.data.connection?.close();\n\t\t},\n\t\ttimeout(socket) {\n\t\t\tif (socket.data.upgraded) return;\n\t\t\tsocket.data.connection?.close(idle);\n\t\t},\n\t};\n}\n\n/** Wraps a socket; STARTTLS swaps it for the encrypted one. */\nfunction transportOf(\n\tsocket: Socket<SocketState>,\n\tsettings: Settings,\n\tsecure: boolean,\n): SocketTransport {\n\tconst transport = new SocketTransport(\n\t\tsocket as Socket<unknown>,\n\t\tsecure,\n\t\t() => {\n\t\t\tconst tls = settings.options.tls;\n\t\t\tconst connection = socket.data.connection;\n\t\t\tif (!tls || !connection) return;\n\t\t\tsocket.data.upgraded = true;\n\t\t\tconst [, encrypted] = socket.upgradeTLS<SocketState>({\n\t\t\t\ttls: { key: tls.key, cert: tls.cert },\n\t\t\t\tdata: { connection, upgraded: false },\n\t\t\t\tsocket: {\n\t\t\t\t\t...handlers(settings),\n\t\t\t\t\tclose: (s) => {\n\t\t\t\t\t\ts.data.transport?.closed();\n\t\t\t\t\t\tconnection.close();\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t});\n\t\t\tencrypted.timeout(settings.timeout);\n\t\t\tconst next = new SocketTransport(\n\t\t\t\tencrypted as Socket<unknown>,\n\t\t\t\ttrue,\n\t\t\t\t() => {},\n\t\t\t\tsocket.remoteAddress,\n\t\t\t);\n\t\t\tencrypted.data.transport = next;\n\t\t\tconnection.useTransport(next);\n\t\t},\n\t);\n\tsocket.data.transport = transport;\n\treturn transport;\n}\n\n/**\n * An SMTP server (RFC 5321) on `Bun.listen`: EHLO with PIPELINING, SIZE,\n * 8BITMIME, SMTPUTF8 and ENHANCEDSTATUSCODES; STARTTLS (RFC 3207) or\n * implicit TLS (RFC 8314); AUTH PLAIN and LOGIN over TLS only (RFC 4954);\n * policy through hooks. It never relays without AUTH: a recipient outside\n * `localDomains` is refused to an unauthenticated session, whatever the\n * hooks say.\n */\nexport function createSmtpServer(options: SmtpServerOptions): SmtpServer {\n\tconst settings = settingsOf(options);\n\tlet listener: TCPSocketListener<SocketState> | undefined;\n\tlet open = 0;\n\tconst secure = options.implicitTls === true;\n\treturn {\n\t\tget connections() {\n\t\t\treturn open;\n\t\t},\n\t\tasync listen({ port, hostname = '0.0.0.0' }) {\n\t\t\tif (listener) {\n\t\t\t\tthrow new SmtpError(\n\t\t\t\t\t'ALREADY_LISTENING',\n\t\t\t\t\t`listen(): the server is already listening on ${listener.hostname}:${listener.port}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tlistener = Bun.listen<SocketState>({\n\t\t\t\thostname,\n\t\t\t\tport,\n\t\t\t\t...(secure && options.tls\n\t\t\t\t\t? { tls: { key: options.tls.key, cert: options.tls.cert } }\n\t\t\t\t\t: {}),\n\t\t\t\tsocket: {\n\t\t\t\t\t...handlers(settings),\n\t\t\t\t\topen(socket) {\n\t\t\t\t\t\tsocket.data = { upgraded: false };\n\t\t\t\t\t\tsocket.timeout(settings.timeout);\n\t\t\t\t\t\tif (open >= settings.maxConnections) {\n\t\t\t\t\t\t\tsocket.end(\n\t\t\t\t\t\t\t\t`421 4.3.2 ${options.hostname} Too many connections, try later\\r\\n`,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\treturn;\n\t\t\t\t\t\t}\n\t\t\t\t\t\topen++;\n\t\t\t\t\t\tconst connection = new Connection(\n\t\t\t\t\t\t\tsettings,\n\t\t\t\t\t\t\ttransportOf(socket, settings, secure),\n\t\t\t\t\t\t);\n\t\t\t\t\t\tsocket.data.connection = connection;\n\t\t\t\t\t\tvoid connection.open();\n\t\t\t\t\t},\n\t\t\t\t\tclose(socket) {\n\t\t\t\t\t\tif (socket.data.connection) open--;\n\t\t\t\t\t\tsocket.data.transport?.closed();\n\t\t\t\t\t\tsocket.data.connection?.close();\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t});\n\t\t\treturn { port: listener.port, hostname: listener.hostname };\n\t\t},\n\t\tstop(closeConnections = false) {\n\t\t\tlistener?.stop(closeConnections);\n\t\t\tlistener = undefined;\n\t\t},\n\t};\n}\n"
25
+ ],
26
+ "mappings": "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",
27
+ "debugId": "DE4D9903D05BDADC64756E2164756E21",
28
+ "names": []
29
+ }
@@ -0,0 +1,20 @@
1
+ import { type Path } from './path';
2
+ /** A command line, split into its verb (upper case) and the rest. */
3
+ export interface Command {
4
+ readonly verb: string;
5
+ readonly argument: string;
6
+ }
7
+ export declare function parseCommand(line: string): Command;
8
+ /** `MAIL FROM:<path> params` or `RCPT TO:<path> params`, parsed. */
9
+ export interface PathCommand {
10
+ readonly path: Path;
11
+ /** ESMTP parameters (RFC 5321 §4.1.2), keys in upper case; a key with no `=` has the value `''`. */
12
+ readonly parameters: Readonly<Record<string, string>>;
13
+ }
14
+ /**
15
+ * Parses the argument of `MAIL` or `RCPT`: `FROM:<path> params` (or `TO:`).
16
+ * Tolerates a space after the colon, which many clients send. `undefined`
17
+ * when the syntax is wrong.
18
+ */
19
+ export declare function parsePathCommand(argument: string, keyword: 'FROM' | 'TO'): PathCommand | undefined;
20
+ //# sourceMappingURL=command.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../../src/protocol/command.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,IAAI,EAAa,MAAM,QAAQ,CAAC;AAE9C,qEAAqE;AACrE,MAAM,WAAW,OAAO;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC1B;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAOlD;AAED,oEAAoE;AACpE,MAAM,WAAW,WAAW;IAC3B,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC;IACpB,oGAAoG;IACpG,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CACtD;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC/B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,GAAG,IAAI,GACpB,WAAW,GAAG,SAAS,CAgBzB"}
@@ -0,0 +1,24 @@
1
+ /** The result of one chunk of DATA. */
2
+ export interface DataChunk {
3
+ /** The message bytes in this chunk, dot-unstuffed. */
4
+ readonly data: Uint8Array;
5
+ /** The terminating `<CRLF>.<CRLF>` was read. */
6
+ readonly done: boolean;
7
+ /** What followed the terminator: the next pipelined commands. */
8
+ readonly rest: Uint8Array;
9
+ }
10
+ /**
11
+ * Reads the content of `DATA` (RFC 5321 §4.1.1.4): removes the leading dot
12
+ * that §4.5.2 adds to lines starting with one, and stops at `<CRLF>.<CRLF>`
13
+ * — that exact sequence and no other, so a bare LF before a dot cannot end
14
+ * the message (SMTP smuggling). It counts the bare CRs and LFs it meets,
15
+ * for the server to refuse the message.
16
+ */
17
+ export declare class DataReader {
18
+ #private;
19
+ bareLineBreaks: number;
20
+ /** The bytes of message read so far, after unstuffing. */
21
+ get size(): number;
22
+ write(chunk: Uint8Array): DataChunk;
23
+ }
24
+ //# sourceMappingURL=data.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"data.d.ts","sourceRoot":"","sources":["../../src/protocol/data.ts"],"names":[],"mappings":"AAIA,uCAAuC;AACvC,MAAM,WAAW,SAAS;IACzB,sDAAsD;IACtD,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,gDAAgD;IAChD,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,iEAAiE;IACjE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;CAC1B;AAED;;;;;;GAMG;AACH,qBAAa,UAAU;;IAMtB,cAAc,SAAK;IAEnB,0DAA0D;IAC1D,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,KAAK,CAAC,KAAK,EAAE,UAAU,GAAG,SAAS;CAkEnC"}
@@ -0,0 +1,20 @@
1
+ /** An envelope address, as `MAIL FROM` and `RCPT TO` give it. */
2
+ export interface Path {
3
+ /** `local@domain`; `''` for the null reverse-path `<>`. */
4
+ readonly address: string;
5
+ readonly local: string;
6
+ /** Lower case. */
7
+ readonly domain: string;
8
+ }
9
+ /**
10
+ * Parses the `<path>` of RFC 5321 §4.1.2: `<local@domain>`, with a source
11
+ * route to ignore (`<@a,@b:local@domain>`, §C), or `<>` when `allowNull`.
12
+ * Non-ASCII is allowed: whether the session may use it is SMTPUTF8's
13
+ * question (RFC 6531), not the grammar's. `undefined` when it is not a path.
14
+ *
15
+ * The local part is kept as written, quotes included: `"v@x.example"` and
16
+ * `v%x.example` are local parts of the domain after the last `@`, not
17
+ * addresses elsewhere. Code that delivers must not split on the first `@`.
18
+ */
19
+ export declare function parsePath(text: string, allowNull: boolean): Path | undefined;
20
+ //# sourceMappingURL=path.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path.d.ts","sourceRoot":"","sources":["../../src/protocol/path.ts"],"names":[],"mappings":"AAAA,iEAAiE;AACjE,MAAM,WAAW,IAAI;IACpB,2DAA2D;IAC3D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,kBAAkB;IAClB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACxB;AAgCD;;;;;;;;;GASG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,GAAG,IAAI,GAAG,SAAS,CAoB5E"}
@@ -0,0 +1,13 @@
1
+ /** An SMTP reply (RFC 5321 §4.2), with its enhanced status code (RFC 3463) when it has one. */
2
+ export interface Reply {
3
+ readonly code: number;
4
+ /** `x.y.z`, written before the text of each line (RFC 2034 §4). */
5
+ readonly status?: string;
6
+ /** One line or more; a multi-line reply joins them with `code-`. */
7
+ readonly text: string | readonly string[];
8
+ }
9
+ /** The reply as sent: every line but the last as `code-`, the last as `code `, each ending in CRLF. */
10
+ export declare function formatReply(reply: Reply, enhanced?: boolean): string;
11
+ /** Builds a reply. */
12
+ export declare const reply: (code: number, status: string | undefined, text: string | readonly string[]) => Reply;
13
+ //# sourceMappingURL=reply.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reply.d.ts","sourceRoot":"","sources":["../../src/protocol/reply.ts"],"names":[],"mappings":"AAAA,+FAA+F;AAC/F,MAAM,WAAW,KAAK;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,mEAAmE;IACnE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,oEAAoE;IACpE,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAAC;CAC1C;AAED,uGAAuG;AACvG,wBAAgB,WAAW,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,UAAO,GAAG,MAAM,CASjE;AAED,sBAAsB;AACtB,eAAO,MAAM,KAAK,GACjB,MAAM,MAAM,EACZ,QAAQ,MAAM,GAAG,SAAS,EAC1B,MAAM,MAAM,GAAG,SAAS,MAAM,EAAE,KAC9B,KAAyE,CAAC"}
@@ -0,0 +1,16 @@
1
+ /** Credentials a client gave with `AUTH`. */
2
+ export interface Credentials {
3
+ readonly mechanism: 'PLAIN' | 'LOGIN';
4
+ readonly username: string;
5
+ readonly password: string;
6
+ /**
7
+ * PLAIN's authorization identity, when it differs from the username.
8
+ * The server refuses such credentials before `authenticate` is asked.
9
+ */
10
+ readonly authorizationId?: string;
11
+ }
12
+ /** PLAIN (RFC 4616): `authzid NUL authcid NUL passwd`, in base64. */
13
+ export declare function decodePlain(response: string): Credentials | undefined;
14
+ /** One step of LOGIN: a base64 line to its text. */
15
+ export declare function decodeLoginStep(response: string): string | undefined;
16
+ //# sourceMappingURL=sasl.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sasl.d.ts","sourceRoot":"","sources":["../../src/protocol/sasl.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAC7C,MAAM,WAAW,WAAW;IAC3B,QAAQ,CAAC,SAAS,EAAE,OAAO,GAAG,OAAO,CAAC;IACtC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B;;;OAGG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;CAClC;AAcD,qEAAqE;AACrE,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS,CAerE;AAED,oDAAoD;AACpD,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAEpE"}
@@ -0,0 +1,6 @@
1
+ import type { Connection } from './connection';
2
+ /** `AUTH PLAIN [initial]` or `AUTH LOGIN [initial]` (RFC 4954), over TLS only. */
3
+ export declare function authenticate(connection: Connection, argument: string): Promise<void>;
4
+ /** The client's line after a 334 challenge. */
5
+ export declare function continueAuth(connection: Connection, line: string): Promise<void>;
6
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/server/auth.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAyD/C,kFAAkF;AAClF,wBAAsB,YAAY,CACjC,UAAU,EAAE,UAAU,EACtB,QAAQ,EAAE,MAAM,GACd,OAAO,CAAC,IAAI,CAAC,CA2Cf;AAaD,+CAA+C;AAC/C,wBAAsB,YAAY,CACjC,UAAU,EAAE,UAAU,EACtB,IAAI,EAAE,MAAM,GACV,OAAO,CAAC,IAAI,CAAC,CAqBf"}
@@ -0,0 +1,4 @@
1
+ import type { Connection } from './connection';
2
+ /** Runs one command line, or the client's answer to an AUTH challenge. */
3
+ export declare function runCommand(connection: Connection, line: string): Promise<void>;
4
+ //# sourceMappingURL=commands.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../src/server/commands.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AA2G/C,0EAA0E;AAC1E,wBAAsB,UAAU,CAC/B,UAAU,EAAE,UAAU,EACtB,IAAI,EAAE,MAAM,GACV,OAAO,CAAC,IAAI,CAAC,CA6Cf"}
@@ -0,0 +1,52 @@
1
+ import { type Reply } from '../protocol/reply';
2
+ import { Input } from './input';
3
+ import type { HookResult, Session } from './options';
4
+ import type { Settings } from './settings';
5
+ import { type State } from './state';
6
+ import type { Transport } from './transport';
7
+ /**
8
+ * One SMTP session (RFC 5321): reads commands, one at a time and in order —
9
+ * PIPELINING sends several at once, and each reply waits for the hooks of
10
+ * the commands before it — and the message after DATA.
11
+ */
12
+ export declare class Connection {
13
+ #private;
14
+ readonly settings: Settings;
15
+ readonly id: string;
16
+ readonly data: Record<string, unknown>;
17
+ readonly state: State;
18
+ /** What the client sends: commands, then message content after DATA. */
19
+ readonly input: Input;
20
+ constructor(settings: Settings, transport: Transport);
21
+ get transport(): Transport;
22
+ /** After STARTTLS: writes and hang-ups go through the encrypted socket. */
23
+ useTransport(transport: Transport): void;
24
+ get session(): Session;
25
+ get closed(): boolean;
26
+ send(answer: Reply): void;
27
+ close(answer?: Reply): void;
28
+ /** Counts a failed command; past the limit, hangs up. */
29
+ fail(answer: Reply): void;
30
+ /** Hands an error to `onError`, which may not throw back. */
31
+ report(error: unknown): void;
32
+ /**
33
+ * Runs a hook: its refusal, or `undefined`. A throw, a reply that is not
34
+ * a refusal, or — with `deadline` — no answer within `hookTimeout` is
35
+ * reported and answered `451 4.3.0`.
36
+ */
37
+ hook(name: string, run: () => HookResult | Promise<HookResult>, deadline?: boolean): Promise<Reply | undefined>;
38
+ /** Runs app code that answers a value: `failed` when it threw or timed out, which is reported. */
39
+ check<T>(name: string, run: () => T | Promise<T>, deadline?: boolean): Promise<{
40
+ failed: false;
41
+ value: T;
42
+ } | {
43
+ failed: true;
44
+ }>;
45
+ /** The greeting, or the refusal of `onConnect`, or of a client that talked first. */
46
+ open(): Promise<void>;
47
+ /** Bytes from the client; processed in order, after what came before. */
48
+ receive(chunk: Uint8Array): void;
49
+ /** Resolves once everything received so far is answered. */
50
+ idle(): Promise<void>;
51
+ }
52
+ //# sourceMappingURL=connection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../../src/server/connection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,KAAK,KAAK,EAAS,MAAM,mBAAmB,CAAC;AAEnE,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACrD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAC3C,OAAO,EAAoB,KAAK,KAAK,EAAE,MAAM,SAAS,CAAC;AACvD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C;;;;GAIG;AACH,qBAAa,UAAU;;IACtB,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAE5B,QAAQ,CAAC,EAAE,SAAqD;IAChE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAM;IAC5C,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IACtB,wEAAwE;IACxE,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAmB;gBAG5B,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS;IAapD,IAAI,SAAS,IAAI,SAAS,CAEzB;IAED,2EAA2E;IAC3E,YAAY,CAAC,SAAS,EAAE,SAAS,GAAG,IAAI;IAIxC,IAAI,OAAO,IAAI,OAAO,CAWrB;IAED,IAAI,MAAM,IAAI,OAAO,CAEpB;IAED,IAAI,CAAC,MAAM,EAAE,KAAK,GAAG,IAAI;IAKzB,KAAK,CAAC,MAAM,CAAC,EAAE,KAAK,GAAG,IAAI;IAQ3B,yDAAyD;IACzD,IAAI,CAAC,MAAM,EAAE,KAAK,GAAG,IAAI;IAUzB,6DAA6D;IAC7D,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IAQ5B;;;;OAIG;IACG,IAAI,CACT,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,EAC3C,QAAQ,UAAO,GACb,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC;IAM7B,kGAAkG;IAC5F,KAAK,CAAC,CAAC,EACZ,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,EACzB,QAAQ,UAAO,GACb,OAAO,CAAC;QAAE,MAAM,EAAE,KAAK,CAAC;QAAC,KAAK,EAAE,CAAC,CAAA;KAAE,GAAG;QAAE,MAAM,EAAE,IAAI,CAAA;KAAE,CAAC;IAgB1D,qFAAqF;IAC/E,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAsB3B,yEAAyE;IACzE,OAAO,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IAIhC,4DAA4D;IAC5D,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAGrB"}
@@ -0,0 +1,18 @@
1
+ import { SmtpError } from '../errors';
2
+ import { type Reply } from '../protocol/reply';
3
+ /** The reply when a hook throws, times out or answers what is not a refusal. */
4
+ export declare const LOCAL_ERROR: Reply;
5
+ declare const TIMED_OUT: unique symbol;
6
+ /** `promise`, or `TIMED_OUT` once `seconds` have passed. */
7
+ export declare function within<T>(promise: Promise<T>, seconds: number): Promise<T | typeof TIMED_OUT>;
8
+ export declare function timedOut(value: unknown): value is typeof TIMED_OUT;
9
+ export declare function hookTimeout(name: string, seconds: number): SmtpError;
10
+ /**
11
+ * What a hook answered, as the reply to send: `undefined` to go on, or the
12
+ * refusal. A reply under 400 is not a refusal — sending it would tell the
13
+ * client yes while the server did not take the command — so it is reported
14
+ * and answered as a local error.
15
+ */
16
+ export declare function refusalOf(name: string, answer: unknown, report: (error: unknown) => void): Reply | undefined;
17
+ export {};
18
+ //# sourceMappingURL=guard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"guard.d.ts","sourceRoot":"","sources":["../../src/server/guard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,KAAK,KAAK,EAAS,MAAM,mBAAmB,CAAC;AAEtD,gFAAgF;AAChF,eAAO,MAAM,WAAW,OAAmD,CAAC;AAE5E,QAAA,MAAM,SAAS,eAAsB,CAAC;AAEtC,4DAA4D;AAC5D,wBAAsB,MAAM,CAAC,CAAC,EAC7B,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EACnB,OAAO,EAAE,MAAM,GACb,OAAO,CAAC,CAAC,GAAG,OAAO,SAAS,CAAC,CAU/B;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,OAAO,SAAS,CAElE;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS,CAKpE;AAED;;;;;GAKG;AACH,wBAAgB,SAAS,CACxB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,OAAO,EACf,MAAM,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,GAC9B,KAAK,GAAG,SAAS,CAanB"}
@@ -0,0 +1,31 @@
1
+ import type { Connection } from './connection';
2
+ /**
3
+ * What a client sends, read in order: command lines one at a time, each
4
+ * answered before the next runs, and the message after DATA. It holds a
5
+ * bounded amount: past `INPUT_LIMIT` waiting bytes it pauses the socket,
6
+ * and it waits for the replies to leave before reading on.
7
+ */
8
+ export declare class Input {
9
+ #private;
10
+ /** The client talked before the greeting. */
11
+ early: boolean;
12
+ constructor(connection: Connection);
13
+ greeted(): void;
14
+ receive(chunk: Uint8Array): void;
15
+ /** The connection closed: a message on its way in must not be delivered. */
16
+ abort(): void;
17
+ /**
18
+ * Forgets what the client sent ahead and reads nothing until `accept`:
19
+ * what comes in clear before TLS never counts (RFC 3207 §4.2,
20
+ * CVE-2011-0411).
21
+ */
22
+ ignore(): void;
23
+ accept(): void;
24
+ /** Drops the rest of the chunk being read: after a refused DATA, it is message content, not commands. */
25
+ drop(): void;
26
+ /** Starts receiving a message: `onData` is called, and gets it as it comes. */
27
+ beginData(): void;
28
+ /** Resolves once everything received so far is answered. */
29
+ idle(): Promise<void>;
30
+ }
31
+ //# sourceMappingURL=input.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"input.d.ts","sourceRoot":"","sources":["../../src/server/input.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAQ/C;;;;;GAKG;AACH,qBAAa,KAAK;;IAajB,6CAA6C;IAC7C,KAAK,UAAS;gBAMF,UAAU,EAAE,UAAU;IAIlC,OAAO,IAAI,IAAI;IAIf,OAAO,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IAehC,4EAA4E;IAC5E,KAAK,IAAI,IAAI;IASb;;;;OAIG;IACH,MAAM,IAAI,IAAI;IAQd,MAAM,IAAI,IAAI;IAId,yGAAyG;IACzG,IAAI,IAAI,IAAI;IAKZ,+EAA+E;IAC/E,SAAS,IAAI,IAAI;IAMjB,4DAA4D;IACtD,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAkF3B"}
@@ -0,0 +1,31 @@
1
+ import { type Reply } from '../protocol/reply';
2
+ import type { Connection } from './connection';
3
+ import type { Transaction } from './state';
4
+ /**
5
+ * One message on its way in: the content of DATA, unstuffed, fed to
6
+ * `onData` as a stream while the client sends it. At most
7
+ * `HIGH_WATER_MARK` bytes wait to be read; past that the server waits for
8
+ * `onData` to read, and so stops reading the client.
9
+ *
10
+ * The stream ends in an `SmtpError` when the message must not be
11
+ * delivered, and the reply to DATA is then that refusal, whatever `onData`
12
+ * answered. The 250 goes out only when `onData` read the stream to its
13
+ * clean end and accepted; a stream left unread errors with
14
+ * `MESSAGE_NOT_READ`. When the message's `signal` aborts is set out on
15
+ * `ReceivedMessage.signal`.
16
+ */
17
+ export declare class Intake {
18
+ #private;
19
+ readonly id: string;
20
+ constructor(connection: Connection, transaction: Transaction);
21
+ /** A chunk of DATA; resolves once there is room for the next. */
22
+ write(chunk: Uint8Array): Promise<{
23
+ done: boolean;
24
+ rest: Uint8Array;
25
+ }>;
26
+ /** The end of DATA: the reply to send. */
27
+ finish(): Promise<Reply>;
28
+ /** The connection closed, mid-message or before the reply was sent. */
29
+ abort(): void;
30
+ }
31
+ //# sourceMappingURL=intake.d.ts.map
@@ -0,0 +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;AAc/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;IAgB5E,0CAA0C;IACpC,MAAM,IAAI,OAAO,CAAC,KAAK,CAAC;IAuB9B,uEAAuE;IACvE,KAAK,IAAI,IAAI;CA2Eb"}
@@ -0,0 +1,25 @@
1
+ /** The longest command line taken, CRLF included; RFC 5321 §4.5.3.1.4 asks for 512 at least. */
2
+ export declare const MAX_LINE = 2048;
3
+ /** One line from the client, or a line that was too long and was skipped. */
4
+ export type LineEvent = {
5
+ line: string;
6
+ } | {
7
+ tooLong: true;
8
+ };
9
+ /**
10
+ * Cuts the client's bytes into command lines, ending at LF with or without
11
+ * CR. It holds one partial line at most: past `MAX_LINE`, the line is
12
+ * skipped to its end and reported once.
13
+ */
14
+ export declare class LineSplitter {
15
+ #private;
16
+ /** Starts on a chunk; read its lines with `next`. */
17
+ push(chunk: Uint8Array): void;
18
+ /** The next line of the current chunk, or `undefined` when it has no more. */
19
+ next(): LineEvent | undefined;
20
+ /** What is left of the current chunk: message content pipelined after DATA. */
21
+ takeRest(): Uint8Array;
22
+ /** Forgets everything held: after STARTTLS, or after a refused DATA. */
23
+ clear(): void;
24
+ }
25
+ //# sourceMappingURL=lines.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lines.d.ts","sourceRoot":"","sources":["../../src/server/lines.ts"],"names":[],"mappings":"AAAA,gGAAgG;AAChG,eAAO,MAAM,QAAQ,OAAO,CAAC;AAE7B,6EAA6E;AAC7E,MAAM,MAAM,SAAS,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,OAAO,EAAE,IAAI,CAAA;CAAE,CAAC;AAE7D;;;;GAIG;AACH,qBAAa,YAAY;;IAKxB,qDAAqD;IACrD,IAAI,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IAK7B,8EAA8E;IAC9E,IAAI,IAAI,SAAS,GAAG,SAAS;IAyB7B,+EAA+E;IAC/E,QAAQ,IAAI,UAAU;IAMtB,wEAAwE;IACxE,KAAK,IAAI,IAAI;CAKb"}