imapflow 1.7.8 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/README.md +8 -2
- package/dist/cjs/charsets.d.ts +1 -0
- package/dist/cjs/charsets.js +294 -0
- package/dist/cjs/commands/append.d.ts +22 -0
- package/dist/cjs/commands/append.js +151 -0
- package/dist/cjs/commands/authenticate.d.ts +24 -0
- package/dist/cjs/commands/authenticate.js +223 -0
- package/dist/cjs/commands/capability.d.ts +8 -0
- package/dist/cjs/commands/capability.js +32 -0
- package/dist/cjs/commands/close.d.ts +8 -0
- package/dist/cjs/commands/close.js +39 -0
- package/dist/cjs/commands/compress.d.ts +8 -0
- package/dist/cjs/commands/compress.js +56 -0
- package/dist/cjs/commands/copy.d.ts +13 -0
- package/dist/cjs/commands/copy.js +44 -0
- package/dist/cjs/commands/copyuid-parser.d.ts +11 -0
- package/dist/cjs/commands/copyuid-parser.js +32 -0
- package/dist/cjs/commands/create.d.ts +11 -0
- package/dist/cjs/commands/create.js +80 -0
- package/dist/cjs/commands/delete.d.ts +11 -0
- package/dist/cjs/commands/delete.js +40 -0
- package/dist/cjs/commands/enable.d.ts +9 -0
- package/dist/cjs/commands/enable.js +61 -0
- package/dist/cjs/commands/esearch-parser.d.ts +17 -0
- package/dist/cjs/commands/esearch-parser.js +91 -0
- package/dist/cjs/commands/expunge.d.ts +12 -0
- package/dist/cjs/commands/expunge.js +60 -0
- package/dist/cjs/commands/fetch.d.ts +30 -0
- package/dist/cjs/commands/fetch.js +241 -0
- package/dist/cjs/commands/id.d.ts +10 -0
- package/dist/cjs/commands/id.js +80 -0
- package/dist/cjs/commands/idle.d.ts +9 -0
- package/dist/cjs/commands/idle.js +347 -0
- package/dist/cjs/commands/list.d.ts +16 -0
- package/dist/cjs/commands/list.js +518 -0
- package/dist/cjs/commands/login.d.ts +11 -0
- package/dist/cjs/commands/login.js +42 -0
- package/dist/cjs/commands/logout.d.ts +8 -0
- package/dist/cjs/commands/logout.js +47 -0
- package/dist/cjs/commands/move.d.ts +13 -0
- package/dist/cjs/commands/move.js +57 -0
- package/dist/cjs/commands/namespace.d.ts +25 -0
- package/dist/cjs/commands/namespace.js +139 -0
- package/dist/cjs/commands/noop.d.ts +8 -0
- package/dist/cjs/commands/noop.js +22 -0
- package/dist/cjs/commands/quota.d.ts +10 -0
- package/dist/cjs/commands/quota.js +119 -0
- package/dist/cjs/commands/rename.d.ts +12 -0
- package/dist/cjs/commands/rename.js +48 -0
- package/dist/cjs/commands/search.d.ts +15 -0
- package/dist/cjs/commands/search.js +228 -0
- package/dist/cjs/commands/select.d.ts +25 -0
- package/dist/cjs/commands/select.js +250 -0
- package/dist/cjs/commands/starttls.d.ts +8 -0
- package/dist/cjs/commands/starttls.js +30 -0
- package/dist/cjs/commands/status-fields.d.ts +14 -0
- package/dist/cjs/commands/status-fields.js +61 -0
- package/dist/cjs/commands/status.d.ts +12 -0
- package/dist/cjs/commands/status.js +108 -0
- package/dist/cjs/commands/store.d.ts +19 -0
- package/dist/cjs/commands/store.js +93 -0
- package/dist/cjs/commands/subscribe.d.ts +9 -0
- package/dist/cjs/commands/subscribe.js +31 -0
- package/dist/cjs/commands/unsubscribe.d.ts +9 -0
- package/dist/cjs/commands/unsubscribe.js +31 -0
- package/dist/cjs/connection-deadline.d.ts +49 -0
- package/dist/cjs/connection-deadline.js +91 -0
- package/dist/cjs/errors.d.ts +83 -0
- package/dist/cjs/errors.js +13 -0
- package/dist/cjs/handler/imap-compiler.d.ts +24 -0
- package/dist/cjs/handler/imap-compiler.js +285 -0
- package/dist/cjs/handler/imap-formal-syntax.d.ts +28 -0
- package/dist/cjs/handler/imap-formal-syntax.js +121 -0
- package/dist/cjs/handler/imap-handler.d.ts +9 -0
- package/dist/cjs/handler/imap-handler.js +10 -0
- package/dist/cjs/handler/imap-parser.d.ts +16 -0
- package/dist/cjs/handler/imap-parser.js +90 -0
- package/dist/cjs/handler/imap-stream.d.ts +181 -0
- package/dist/cjs/handler/imap-stream.js +446 -0
- package/dist/cjs/handler/limits.d.ts +25 -0
- package/dist/cjs/handler/limits.js +51 -0
- package/dist/cjs/handler/parser-instance.d.ts +68 -0
- package/dist/cjs/handler/parser-instance.js +223 -0
- package/dist/cjs/handler/token-parser.d.ts +91 -0
- package/dist/cjs/handler/token-parser.js +673 -0
- package/dist/cjs/handler/types.d.ts +91 -0
- package/dist/cjs/handler/types.js +4 -0
- package/dist/cjs/imap-commands.d.ts +16 -0
- package/dist/cjs/imap-commands.js +74 -0
- package/dist/cjs/imap-flow.d.ts +676 -0
- package/dist/cjs/imap-flow.js +3949 -0
- package/dist/cjs/jp-decoder.d.ts +12 -0
- package/dist/cjs/jp-decoder.js +79 -0
- package/dist/cjs/limited-passthrough.d.ts +25 -0
- package/dist/cjs/limited-passthrough.js +54 -0
- package/dist/cjs/logger.d.ts +3 -0
- package/dist/cjs/logger.js +11 -0
- package/dist/cjs/package-info.d.ts +3 -0
- package/dist/cjs/package-info.js +7 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/proxy-connection.d.ts +33 -0
- package/dist/cjs/proxy-connection.js +392 -0
- package/dist/cjs/search-compiler.d.ts +34 -0
- package/dist/cjs/search-compiler.js +476 -0
- package/dist/cjs/special-use.d.ts +22 -0
- package/dist/cjs/special-use.js +911 -0
- package/dist/cjs/tools.d.ts +427 -0
- package/dist/cjs/tools.js +1496 -0
- package/{lib/imap-flow.d.ts → dist/cjs/types.d.ts} +386 -516
- package/dist/cjs/types.js +5 -0
- package/dist/esm/charsets.d.ts +1 -0
- package/{lib → dist/esm}/charsets.js +1 -6
- package/dist/esm/commands/append.d.ts +22 -0
- package/{lib → dist/esm}/commands/append.js +22 -52
- package/dist/esm/commands/authenticate.d.ts +24 -0
- package/{lib → dist/esm}/commands/authenticate.js +62 -87
- package/dist/esm/commands/capability.d.ts +8 -0
- package/{lib → dist/esm}/commands/capability.js +6 -9
- package/dist/esm/commands/close.d.ts +8 -0
- package/{lib → dist/esm}/commands/close.js +6 -10
- package/dist/esm/commands/compress.d.ts +8 -0
- package/{lib → dist/esm}/commands/compress.js +7 -11
- package/dist/esm/commands/copy.d.ts +13 -0
- package/{lib → dist/esm}/commands/copy.js +12 -20
- package/dist/esm/commands/copyuid-parser.d.ts +11 -0
- package/{lib → dist/esm}/commands/copyuid-parser.js +9 -15
- package/dist/esm/commands/create.d.ts +11 -0
- package/{lib → dist/esm}/commands/create.js +13 -27
- package/dist/esm/commands/delete.d.ts +11 -0
- package/{lib → dist/esm}/commands/delete.js +9 -14
- package/dist/esm/commands/enable.d.ts +9 -0
- package/{lib → dist/esm}/commands/enable.js +23 -30
- package/dist/esm/commands/esearch-parser.d.ts +17 -0
- package/dist/esm/commands/esearch-parser.js +88 -0
- package/dist/esm/commands/expunge.d.ts +12 -0
- package/{lib → dist/esm}/commands/expunge.js +17 -22
- package/dist/esm/commands/fetch.d.ts +30 -0
- package/{lib → dist/esm}/commands/fetch.js +32 -64
- package/dist/esm/commands/id.d.ts +10 -0
- package/{lib → dist/esm}/commands/id.js +17 -23
- package/dist/esm/commands/idle.d.ts +9 -0
- package/{lib → dist/esm}/commands/idle.js +47 -81
- package/dist/esm/commands/list.d.ts +16 -0
- package/{lib → dist/esm}/commands/list.js +56 -121
- package/dist/esm/commands/login.d.ts +11 -0
- package/{lib → dist/esm}/commands/login.js +10 -15
- package/dist/esm/commands/logout.d.ts +8 -0
- package/{lib → dist/esm}/commands/logout.js +9 -11
- package/dist/esm/commands/move.d.ts +13 -0
- package/{lib → dist/esm}/commands/move.js +13 -21
- package/dist/esm/commands/namespace.d.ts +25 -0
- package/{lib → dist/esm}/commands/namespace.js +34 -44
- package/dist/esm/commands/noop.d.ts +8 -0
- package/{lib → dist/esm}/commands/noop.js +6 -7
- package/dist/esm/commands/quota.d.ts +10 -0
- package/{lib → dist/esm}/commands/quota.js +18 -36
- package/dist/esm/commands/rename.d.ts +12 -0
- package/{lib → dist/esm}/commands/rename.js +10 -15
- package/dist/esm/commands/search.d.ts +15 -0
- package/{lib → dist/esm}/commands/search.js +36 -135
- package/dist/esm/commands/select.d.ts +25 -0
- package/{lib → dist/esm}/commands/select.js +33 -64
- package/dist/esm/commands/starttls.d.ts +8 -0
- package/{lib → dist/esm}/commands/starttls.js +6 -8
- package/dist/esm/commands/status-fields.d.ts +14 -0
- package/{lib → dist/esm}/commands/status-fields.js +5 -16
- package/dist/esm/commands/status.d.ts +12 -0
- package/{lib → dist/esm}/commands/status.js +18 -29
- package/dist/esm/commands/store.d.ts +19 -0
- package/{lib → dist/esm}/commands/store.js +24 -37
- package/dist/esm/commands/subscribe.d.ts +9 -0
- package/{lib → dist/esm}/commands/subscribe.js +8 -12
- package/dist/esm/commands/unsubscribe.d.ts +9 -0
- package/{lib → dist/esm}/commands/unsubscribe.js +8 -12
- package/dist/esm/connection-deadline.d.ts +49 -0
- package/{lib → dist/esm}/connection-deadline.js +14 -25
- package/dist/esm/errors.d.ts +83 -0
- package/dist/esm/errors.js +9 -0
- package/dist/esm/handler/imap-compiler.d.ts +24 -0
- package/{lib → dist/esm}/handler/imap-compiler.js +22 -80
- package/dist/esm/handler/imap-formal-syntax.d.ts +28 -0
- package/dist/esm/handler/imap-formal-syntax.js +117 -0
- package/dist/esm/handler/imap-handler.d.ts +9 -0
- package/dist/esm/handler/imap-handler.js +9 -0
- package/dist/esm/handler/imap-parser.d.ts +16 -0
- package/{lib → dist/esm}/handler/imap-parser.js +31 -44
- package/dist/esm/handler/imap-stream.d.ts +181 -0
- package/{lib → dist/esm}/handler/imap-stream.js +29 -121
- package/dist/esm/handler/limits.d.ts +25 -0
- package/{lib → dist/esm}/handler/limits.js +13 -22
- package/dist/esm/handler/parser-instance.d.ts +68 -0
- package/{lib → dist/esm}/handler/parser-instance.js +19 -47
- package/dist/esm/handler/token-parser.d.ts +91 -0
- package/{lib → dist/esm}/handler/token-parser.js +71 -155
- package/dist/esm/handler/types.d.ts +91 -0
- package/dist/esm/handler/types.js +3 -0
- package/dist/esm/imap-commands.d.ts +16 -0
- package/dist/esm/imap-commands.js +67 -0
- package/dist/esm/imap-flow.d.ts +676 -0
- package/{lib → dist/esm}/imap-flow.js +761 -1789
- package/dist/esm/jp-decoder.d.ts +12 -0
- package/{lib → dist/esm}/jp-decoder.js +6 -21
- package/dist/esm/limited-passthrough.d.ts +25 -0
- package/{lib → dist/esm}/limited-passthrough.js +7 -20
- package/dist/esm/logger.d.ts +3 -0
- package/dist/esm/logger.js +4 -0
- package/dist/esm/package-info.d.ts +3 -0
- package/dist/esm/package-info.js +4 -0
- package/dist/esm/package.json +3 -0
- package/dist/esm/proxy-connection.d.ts +33 -0
- package/{lib → dist/esm}/proxy-connection.js +56 -127
- package/dist/esm/search-compiler.d.ts +34 -0
- package/{lib → dist/esm}/search-compiler.js +54 -110
- package/dist/esm/special-use.d.ts +22 -0
- package/dist/esm/special-use.js +907 -0
- package/dist/esm/tools.d.ts +427 -0
- package/dist/esm/tools.js +1446 -0
- package/dist/esm/types.d.ts +828 -0
- package/dist/esm/types.js +4 -0
- package/package.json +60 -20
- package/.gitattributes +0 -1
- package/.github/CODE_OF_CONDUCT.md +0 -76
- package/.github/FUNDING.yml +0 -4
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -40
- package/.github/ISSUE_TEMPLATE/feature_request.md +0 -19
- package/.github/contributing.md +0 -17
- package/.github/workflows/release.yaml +0 -36
- package/.github/workflows/stale.yml +0 -29
- package/.github/workflows/test.yml +0 -51
- package/.ncurc.js +0 -4
- package/.prettierignore +0 -4
- package/.prettierrc.js +0 -8
- package/.release-please-manifest.json +0 -3
- package/CLAUDE.md +0 -104
- package/Gruntfile.js +0 -23
- package/eslint.config.js +0 -45
- package/lib/handler/imap-formal-syntax.js +0 -189
- package/lib/handler/imap-handler.js +0 -17
- package/lib/imap-commands.js +0 -45
- package/lib/logger.js +0 -5
- package/lib/special-use.js +0 -923
- package/lib/tools.js +0 -1612
- package/release-please-config.json +0 -10
- package/test/authentication-test.js +0 -101
- package/test/auto-idle-test.js +0 -470
- package/test/bodystructure-test.js +0 -899
- package/test/charsets-test.js +0 -161
- package/test/commands-branches-test.js +0 -1095
- package/test/commands-integration-test.js +0 -11124
- package/test/commands-test.js +0 -73
- package/test/connection-edge-cases-test.js +0 -1828
- package/test/connection-test.js +0 -162
- package/test/copyuid-parser-test.js +0 -173
- package/test/fetch-generator-test.js +0 -218
- package/test/fixtures/fake-timers.js +0 -115
- package/test/fixtures/serialized-mimetorture.js +0 -2738
- package/test/fixtures/test-client.js +0 -101
- package/test/fixtures/test-tls.js +0 -8
- package/test/handler-branches-test.js +0 -310
- package/test/idle-polling-test.js +0 -518
- package/test/imap-compiler-test.js +0 -809
- package/test/imap-flow-compress-test.js +0 -166
- package/test/imap-flow-coverage-test.js +0 -612
- package/test/imap-flow-fetch-download-test.js +0 -909
- package/test/imap-flow-internals-test.js +0 -725
- package/test/imap-flow-methods-test.js +0 -889
- package/test/imap-flow-proxy-paths-test.js +0 -366
- package/test/imap-flow-secure-test.js +0 -573
- package/test/imap-flow-server-test.js +0 -1474
- package/test/imap-formal-syntax-test.js +0 -293
- package/test/imap-parser-test.js +0 -1474
- package/test/imap-stream-edge-cases-test.js +0 -666
- package/test/imap-stream-test.js +0 -177
- package/test/imapflow-test.js +0 -258
- package/test/integration/README.md +0 -52
- package/test/integration/dovecot-test.conf +0 -27
- package/test/integration/rev2-live-test.js +0 -431
- package/test/integration/run-rev2-tests.sh +0 -75
- package/test/integration-test.js +0 -83
- package/test/jp-decoder-test.js +0 -304
- package/test/limited-passthrough-test.js +0 -299
- package/test/memory-cleanup-test.js +0 -144
- package/test/memory-leak-test.js +0 -667
- package/test/parser-limits-test.js +0 -292
- package/test/proxy-connection-test.js +0 -738
- package/test/reliability-improvements-test.js +0 -548
- package/test/search-compiler-test.js +0 -1300
- package/test/search-test.js +0 -329
- package/test/special-use-test.js +0 -418
- package/test/starttls-injection-test.js +0 -181
- package/test/tag-correlation-test.js +0 -333
- package/test/timer-policy-test.js +0 -227
- package/test/token-parser-test.js +0 -456
- package/test/tools-test.js +0 -2013
- package/test/unhandled-rejection-test.js +0 -661
|
@@ -1,59 +1,26 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const logger = require('../logger');
|
|
5
|
-
const { MAX_LITERAL_SIZE, MAX_LINE_SIZE, MAX_RESPONSE_SIZE, normalizeLimit, createLiteralTooLargeError } = require('./limits');
|
|
6
|
-
|
|
1
|
+
import { Transform } from 'node:stream';
|
|
2
|
+
import logger from '../logger.js';
|
|
3
|
+
import { MAX_LITERAL_SIZE, MAX_LINE_SIZE, MAX_RESPONSE_SIZE, normalizeLimit, createLiteralTooLargeError } from './limits.js';
|
|
7
4
|
const LINE = 0x01;
|
|
8
5
|
const LITERAL = 0x02;
|
|
9
|
-
|
|
10
6
|
const LF = 0x0a;
|
|
11
7
|
const CR = 0x0d;
|
|
12
8
|
const NUM_0 = 0x30;
|
|
13
9
|
const NUM_9 = 0x39;
|
|
14
10
|
const CURLY_OPEN = 0x7b;
|
|
15
11
|
const CURLY_CLOSE = 0x7d;
|
|
16
|
-
|
|
17
12
|
/**
|
|
18
13
|
* A Transform stream that parses raw IMAP protocol data from a socket into structured
|
|
19
14
|
* command/response objects. Reads binary input, splits it into lines delimited by LF,
|
|
20
15
|
* extracts literal data blocks based on IMAP literal size markers (e.g., "{123}\r\n"),
|
|
21
16
|
* and emits each complete command as a readable object containing the payload Buffer
|
|
22
17
|
* and any associated literal Buffers. Enforces a maximum literal size of 1GB.
|
|
23
|
-
*
|
|
24
|
-
* @extends Transform
|
|
25
18
|
*/
|
|
26
|
-
class ImapStream extends Transform {
|
|
19
|
+
export class ImapStream extends Transform {
|
|
27
20
|
/**
|
|
28
21
|
* Creates a new ImapStream instance.
|
|
29
22
|
*
|
|
30
|
-
* @param
|
|
31
|
-
* @param {string} [options.cid] - Connection identifier used for logging.
|
|
32
|
-
* @param {Object} [options.logger] - A pino-compatible logger instance. If not provided, a default child logger is created.
|
|
33
|
-
* @param {boolean} [options.logRaw] - If true, logs raw socket data at trace level.
|
|
34
|
-
* @param {boolean} [options.secureConnection] - Whether the connection uses TLS.
|
|
35
|
-
* @param {number} [options.maxLineLength] - Maximum allowed length (in bytes) of a single
|
|
36
|
-
* line (a response without a literal). Defaults to MAX_LITERAL_SIZE (1GB). Guards against a
|
|
37
|
-
* malicious or broken server that never sends a line terminator, which would otherwise grow
|
|
38
|
-
* the internal line buffer without bound. The line terminator counts toward the limit, and a
|
|
39
|
-
* line exactly at the limit is accepted. Exceeding it is terminal: the stream is destroyed
|
|
40
|
-
* with a `LineTooLarge` error and no further input is parsed.
|
|
41
|
-
* @param {number} [options.maxLiteralSize] - Maximum allowed size (in bytes) of a single
|
|
42
|
-
* literal block. Defaults to MAX_LITERAL_SIZE (1GB). Lower it to bound peak memory
|
|
43
|
-
* allocation against a malicious or broken server announcing an oversized literal. A literal
|
|
44
|
-
* exactly at the limit is accepted. Exceeding it is terminal: the stream is destroyed with a
|
|
45
|
-
* `LiteralTooLarge` error, the marker line is not emitted, and no byte of the rejected
|
|
46
|
-
* literal body is parsed as protocol.
|
|
47
|
-
* @param {number} [options.maxResponseSize] - Maximum allowed total size (in bytes) of a
|
|
48
|
-
* single assembled response: every line segment and literal of one response combined.
|
|
49
|
-
* Defaults to MAX_RESPONSE_SIZE (2GB), which leaves room above the literal cap for a
|
|
50
|
-
* maximum-size literal plus its marker line. The per-line and per-literal caps alone
|
|
51
|
-
* cannot stop a server that spreads attacker-controlled bytes across an unbounded
|
|
52
|
-
* number of tokens of a single response. Declared literal sizes count when their
|
|
53
|
-
* marker is parsed, so an oversized total is rejected before the literal bytes arrive,
|
|
54
|
-
* and a line still being assembled counts against whatever budget is left.
|
|
55
|
-
* Exceeding the limit is terminal: the stream is destroyed with a `ResponseTooLarge`
|
|
56
|
-
* error and no further input is parsed.
|
|
23
|
+
* @param options - Stream options, see ImapStreamOptions.
|
|
57
24
|
*/
|
|
58
25
|
constructor(options) {
|
|
59
26
|
super({
|
|
@@ -61,30 +28,23 @@ class ImapStream extends Transform {
|
|
|
61
28
|
readableObjectMode: true,
|
|
62
29
|
writableObjectMode: false
|
|
63
30
|
});
|
|
64
|
-
|
|
65
31
|
this.options = options || {};
|
|
66
32
|
this.cid = this.options.cid;
|
|
67
|
-
|
|
68
33
|
this.log =
|
|
69
34
|
this.options.logger && typeof this.options.logger === 'object'
|
|
70
35
|
? this.options.logger
|
|
71
36
|
: logger.child({
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
37
|
+
component: 'imap-connection',
|
|
38
|
+
cid: this.cid
|
|
39
|
+
});
|
|
76
40
|
this.readBytesCounter = 0;
|
|
77
|
-
|
|
78
41
|
// Maximum length of a single line (response without a literal). Bounds the line buffer
|
|
79
42
|
// so a server that never sends a line terminator cannot exhaust memory.
|
|
80
43
|
this.maxLineLength = normalizeLimit(this.options.maxLineLength, MAX_LINE_SIZE);
|
|
81
|
-
|
|
82
44
|
// Maximum size of a single literal block. Bounds peak memory allocation so a server
|
|
83
45
|
// announcing an oversized literal cannot exhaust memory.
|
|
84
46
|
this.maxLiteralSize = normalizeLimit(this.options.maxLiteralSize, MAX_LITERAL_SIZE);
|
|
85
|
-
|
|
86
47
|
this.maxResponseSize = normalizeLimit(this.options.maxResponseSize, MAX_RESPONSE_SIZE);
|
|
87
|
-
|
|
88
48
|
this.state = LINE;
|
|
89
49
|
this.literalWaiting = 0;
|
|
90
50
|
this.inputBuffer = []; // lines
|
|
@@ -93,19 +53,15 @@ class ImapStream extends Transform {
|
|
|
93
53
|
this.literalBuffer = [];
|
|
94
54
|
this.literals = [];
|
|
95
55
|
this.responseBytes = 0; // bytes accumulated for the in-progress response (lines + declared literals)
|
|
96
|
-
|
|
97
56
|
this.compress = false;
|
|
98
57
|
this.secureConnection = this.options.secureConnection;
|
|
99
|
-
|
|
100
58
|
this.processingInput = false;
|
|
101
59
|
this.inputQueue = []; // unprocessed input chunks
|
|
102
60
|
this.activeInput = null; // chunk currently being processed (already shifted off inputQueue)
|
|
103
|
-
|
|
104
61
|
// Resolver of the in-flight push() backpressure promise, so destruction can settle it
|
|
105
62
|
// instead of leaving processInput() awaiting a consumer that will never read again.
|
|
106
63
|
this.pendingPush = null;
|
|
107
64
|
}
|
|
108
|
-
|
|
109
65
|
/**
|
|
110
66
|
* Terminally fails the stream. Used for response limit violations and for any other
|
|
111
67
|
* error raised while parsing.
|
|
@@ -119,8 +75,8 @@ class ImapStream extends Transform {
|
|
|
119
75
|
* `destroyed` (set synchronously by destroy()) is the single liveness flag every other path
|
|
120
76
|
* checks, so a second failure attempt is a no-op and nothing is parsed after the first.
|
|
121
77
|
*
|
|
122
|
-
* @param
|
|
123
|
-
* @returns
|
|
78
|
+
* @param err - The error to destroy the stream with.
|
|
79
|
+
* @returns Always false, so callers can `return this.failStream(err)`.
|
|
124
80
|
*/
|
|
125
81
|
failStream(err) {
|
|
126
82
|
if (this.destroyed) {
|
|
@@ -129,13 +85,12 @@ class ImapStream extends Transform {
|
|
|
129
85
|
this.destroy(err);
|
|
130
86
|
return false;
|
|
131
87
|
}
|
|
132
|
-
|
|
133
88
|
/**
|
|
134
89
|
* Releases a queued input chunk's transform callback exactly once, signalling the writable
|
|
135
90
|
* side that the chunk was consumed. The mirror image of ImapFlow's releaseStreamData(), which
|
|
136
91
|
* releases the readable items this stream pushes downstream.
|
|
137
92
|
*
|
|
138
|
-
* @param
|
|
93
|
+
* @param item - Queue entry holding the chunk and its transform callback.
|
|
139
94
|
*/
|
|
140
95
|
releaseInput(item) {
|
|
141
96
|
if (!item || item.released) {
|
|
@@ -146,37 +101,31 @@ class ImapStream extends Transform {
|
|
|
146
101
|
item.next();
|
|
147
102
|
}
|
|
148
103
|
}
|
|
149
|
-
|
|
150
104
|
/**
|
|
151
105
|
* Checks whether the given line buffer ends with an IMAP literal size marker
|
|
152
106
|
* (e.g., "{123}\r\n"). If a valid marker is found and the literal size is within
|
|
153
107
|
* the allowed maximum, switches the stream state to LITERAL mode and records
|
|
154
108
|
* the expected number of literal bytes.
|
|
155
109
|
*
|
|
156
|
-
* @param
|
|
157
|
-
* @returns
|
|
110
|
+
* @param line - The line buffer to check for a trailing literal marker.
|
|
111
|
+
* @returns True if a valid literal marker was found and literal state was activated, false otherwise.
|
|
158
112
|
*/
|
|
159
113
|
checkLiteralMarker(line) {
|
|
160
114
|
if (!line || !line.length) {
|
|
161
115
|
return false;
|
|
162
116
|
}
|
|
163
|
-
|
|
164
117
|
let pos = line.length - 1;
|
|
165
|
-
|
|
166
118
|
if (line[pos] !== LF) {
|
|
167
119
|
return false;
|
|
168
120
|
}
|
|
169
121
|
pos--;
|
|
170
|
-
|
|
171
122
|
if (pos >= 0 && line[pos] === CR) {
|
|
172
123
|
pos--;
|
|
173
124
|
}
|
|
174
|
-
|
|
175
125
|
if (pos < 0 || !pos || line[pos] !== CURLY_CLOSE) {
|
|
176
126
|
return false;
|
|
177
127
|
}
|
|
178
128
|
pos--;
|
|
179
|
-
|
|
180
129
|
// Scan backwards through the line to find an IMAP literal marker: {size}\r\n
|
|
181
130
|
// The format is: '{' followed by one or more ASCII digits followed by '}'.
|
|
182
131
|
// Only the digit run's bounds are tracked - a single linear pass, unlike
|
|
@@ -200,19 +149,15 @@ class ImapStream extends Transform {
|
|
|
200
149
|
while (digitsStart < digitsEnd && line[digitsStart] === NUM_0) {
|
|
201
150
|
digitsStart++;
|
|
202
151
|
}
|
|
203
|
-
|
|
204
152
|
// More significant digits than any number64 has cannot fit any
|
|
205
153
|
// permissible maxLiteralSize; fail closed without materializing them
|
|
206
154
|
if (digitsEnd + 1 - digitsStart > 19) {
|
|
207
155
|
return this.failStream(createLiteralTooLargeError(Infinity, this.maxLiteralSize, 'the widest permissible literal size (19 digits)'));
|
|
208
156
|
}
|
|
209
|
-
|
|
210
157
|
const literalSize = Number(line.toString('latin1', digitsStart, digitsEnd + 1));
|
|
211
|
-
|
|
212
158
|
if (literalSize > this.maxLiteralSize) {
|
|
213
159
|
return this.failStream(createLiteralTooLargeError(literalSize, this.maxLiteralSize));
|
|
214
160
|
}
|
|
215
|
-
|
|
216
161
|
this.state = LITERAL;
|
|
217
162
|
this.literalWaiting = literalSize;
|
|
218
163
|
return true;
|
|
@@ -221,14 +166,13 @@ class ImapStream extends Transform {
|
|
|
221
166
|
}
|
|
222
167
|
return false;
|
|
223
168
|
}
|
|
224
|
-
|
|
225
169
|
/**
|
|
226
170
|
* Enforces the configured line-length cap for a projected line length. The projected length
|
|
227
171
|
* covers every byte of the line, the line terminator included, whether or not the line was
|
|
228
172
|
* split across input chunks. A line exactly at the limit is accepted.
|
|
229
173
|
*
|
|
230
|
-
* @param
|
|
231
|
-
* @returns
|
|
174
|
+
* @param lineLength - Total length the current line would reach.
|
|
175
|
+
* @returns True if the line is within the limit, false if the stream was failed.
|
|
232
176
|
*/
|
|
233
177
|
checkLineLength(lineLength) {
|
|
234
178
|
if (lineLength <= this.maxLineLength) {
|
|
@@ -240,18 +184,17 @@ class ImapStream extends Transform {
|
|
|
240
184
|
err.maxSize = this.maxLineLength;
|
|
241
185
|
return this.failStream(err);
|
|
242
186
|
}
|
|
243
|
-
|
|
244
187
|
/**
|
|
245
188
|
* Enforces the configured per-response size cap: the cumulative bytes of every line
|
|
246
189
|
* segment and declared literal of the response currently being assembled. Counting
|
|
247
190
|
* declared literal sizes at marker time means an oversized total is rejected before
|
|
248
191
|
* the literal bytes even arrive. The counter is reset when a response is emitted.
|
|
249
192
|
*
|
|
250
|
-
* @param
|
|
251
|
-
* @param
|
|
193
|
+
* @param additionalBytes - Bytes the next token would add to the response.
|
|
194
|
+
* @param peek - Measure only, without committing the bytes to the counter.
|
|
252
195
|
* Used for a line that is still being assembled: its bytes are committed once, when the
|
|
253
196
|
* line completes.
|
|
254
|
-
* @returns
|
|
197
|
+
* @returns True if within the limit, false if the stream was failed.
|
|
255
198
|
*/
|
|
256
199
|
checkResponseSize(additionalBytes, peek) {
|
|
257
200
|
let total = this.responseBytes + additionalBytes;
|
|
@@ -267,23 +210,20 @@ class ImapStream extends Transform {
|
|
|
267
210
|
err.maxSize = this.maxResponseSize;
|
|
268
211
|
return this.failStream(err);
|
|
269
212
|
}
|
|
270
|
-
|
|
271
213
|
/**
|
|
272
214
|
* Processes a single input chunk of raw data. In LINE state, scans for LF-terminated
|
|
273
215
|
* lines and checks for literal markers. In LITERAL state, collects the expected number
|
|
274
216
|
* of literal bytes. When a complete command (with all its literals) is assembled, it is
|
|
275
217
|
* pushed downstream as a readable object.
|
|
276
218
|
*
|
|
277
|
-
* @param
|
|
278
|
-
* @param
|
|
279
|
-
* @returns {Promise<void>}
|
|
219
|
+
* @param chunk - The raw data chunk to process.
|
|
220
|
+
* @param startPos - The byte offset within the chunk to start processing from.
|
|
280
221
|
*/
|
|
281
222
|
async processInputChunk(chunk, startPos) {
|
|
282
223
|
startPos = startPos || 0;
|
|
283
224
|
if (this.destroyed || startPos >= chunk.length) {
|
|
284
225
|
return;
|
|
285
226
|
}
|
|
286
|
-
|
|
287
227
|
switch (this.state) {
|
|
288
228
|
case LINE: {
|
|
289
229
|
let lineStart = startPos;
|
|
@@ -296,15 +236,11 @@ class ImapStream extends Transform {
|
|
|
296
236
|
if (!this.checkLineLength(this.lineBytes + segment.length)) {
|
|
297
237
|
return;
|
|
298
238
|
}
|
|
299
|
-
|
|
300
239
|
this.lineBuffer.push(segment);
|
|
301
240
|
lineStart = i + 1;
|
|
302
|
-
|
|
303
241
|
let line = this.lineBuffer.length === 1 ? this.lineBuffer[0] : Buffer.concat(this.lineBuffer);
|
|
304
|
-
|
|
305
242
|
this.lineBuffer = [];
|
|
306
243
|
this.lineBytes = 0;
|
|
307
|
-
|
|
308
244
|
// try to detect if this is a literal start. An oversized literal fails the
|
|
309
245
|
// stream, so the marker line must not be buffered before the check - it
|
|
310
246
|
// would otherwise be emitted as part of the rejected command.
|
|
@@ -312,28 +248,23 @@ class ImapStream extends Transform {
|
|
|
312
248
|
if (this.destroyed) {
|
|
313
249
|
return;
|
|
314
250
|
}
|
|
315
|
-
|
|
316
251
|
// Count the line itself and, for a literal marker, the declared
|
|
317
252
|
// literal bytes against the cumulative per-response budget, so a
|
|
318
253
|
// response assembled from many tokens stays bounded as a whole
|
|
319
254
|
if (!this.checkResponseSize(line.length + (isLiteralMarker ? this.literalWaiting : 0))) {
|
|
320
255
|
return;
|
|
321
256
|
}
|
|
322
|
-
|
|
323
257
|
this.inputBuffer.push(line);
|
|
324
|
-
|
|
325
258
|
if (isLiteralMarker) {
|
|
326
259
|
// switch into literal mode and start over
|
|
327
260
|
return await this.processInputChunk(chunk, lineStart);
|
|
328
261
|
}
|
|
329
|
-
|
|
330
262
|
// reached end of command input, emit it
|
|
331
263
|
let payload = this.inputBuffer.length === 1 ? this.inputBuffer[0] : Buffer.concat(this.inputBuffer);
|
|
332
264
|
let literals = this.literals;
|
|
333
265
|
this.inputBuffer = [];
|
|
334
266
|
this.literals = [];
|
|
335
267
|
this.responseBytes = 0;
|
|
336
|
-
|
|
337
268
|
if (payload.length) {
|
|
338
269
|
// remove final line terminator (\n or \r\n)
|
|
339
270
|
if (payload[payload.length - 1] === LF) {
|
|
@@ -343,10 +274,9 @@ class ImapStream extends Transform {
|
|
|
343
274
|
}
|
|
344
275
|
payload = payload.slice(0, end);
|
|
345
276
|
}
|
|
346
|
-
|
|
347
277
|
if (payload.length) {
|
|
348
278
|
// Whether more buffered input already followed this command on the
|
|
349
|
-
// wire
|
|
279
|
+
// wire - more bytes in this chunk or another queued chunk. Captured
|
|
350
280
|
// per emitted command (immutable on the pushed object) so a later
|
|
351
281
|
// command cannot overwrite it; consumers that care about pipelining
|
|
352
282
|
// boundaries can read it from the pushed object.
|
|
@@ -356,10 +286,10 @@ class ImapStream extends Transform {
|
|
|
356
286
|
// this loop (and the chunk's transform callback) pending forever
|
|
357
287
|
// when the consumer stops reading.
|
|
358
288
|
this.pendingPush = resolve;
|
|
359
|
-
|
|
289
|
+
const item = { payload, literals, next: resolve, trailingAfterLine };
|
|
290
|
+
this.push(item);
|
|
360
291
|
});
|
|
361
292
|
this.pendingPush = null;
|
|
362
|
-
|
|
363
293
|
if (this.destroyed) {
|
|
364
294
|
return;
|
|
365
295
|
}
|
|
@@ -384,20 +314,16 @@ class ImapStream extends Transform {
|
|
|
384
314
|
}
|
|
385
315
|
break;
|
|
386
316
|
}
|
|
387
|
-
|
|
388
317
|
case LITERAL: {
|
|
389
318
|
const remainingInChunk = chunk.length - startPos;
|
|
390
319
|
const bytesToRead = Math.min(remainingInChunk, this.literalWaiting);
|
|
391
320
|
const partial = startPos === 0 && bytesToRead === chunk.length ? chunk : chunk.slice(startPos, startPos + bytesToRead);
|
|
392
|
-
|
|
393
321
|
this.literalBuffer.push(partial);
|
|
394
322
|
this.literalWaiting -= bytesToRead;
|
|
395
|
-
|
|
396
323
|
if (this.literalWaiting === 0) {
|
|
397
324
|
this.literals.push(Buffer.concat(this.literalBuffer));
|
|
398
325
|
this.literalBuffer = [];
|
|
399
326
|
this.state = LINE;
|
|
400
|
-
|
|
401
327
|
if (remainingInChunk > bytesToRead) {
|
|
402
328
|
return await this.processInputChunk(chunk, startPos + bytesToRead);
|
|
403
329
|
}
|
|
@@ -406,13 +332,10 @@ class ImapStream extends Transform {
|
|
|
406
332
|
}
|
|
407
333
|
}
|
|
408
334
|
}
|
|
409
|
-
|
|
410
335
|
/**
|
|
411
336
|
* Drains the input queue by processing each queued chunk sequentially.
|
|
412
337
|
* Yields to the event loop every 10 chunks to prevent CPU blocking on
|
|
413
338
|
* large bursts of incoming data.
|
|
414
|
-
*
|
|
415
|
-
* @returns {Promise<void>}
|
|
416
339
|
*/
|
|
417
340
|
async processInput() {
|
|
418
341
|
let data;
|
|
@@ -423,7 +346,6 @@ class ImapStream extends Transform {
|
|
|
423
346
|
this.activeInput = null;
|
|
424
347
|
// mark chunk as processed
|
|
425
348
|
this.releaseInput(data);
|
|
426
|
-
|
|
427
349
|
// Yield to event loop every 10 chunks to prevent CPU blocking
|
|
428
350
|
processedCount++;
|
|
429
351
|
if (processedCount % 10 === 0) {
|
|
@@ -431,27 +353,23 @@ class ImapStream extends Transform {
|
|
|
431
353
|
}
|
|
432
354
|
}
|
|
433
355
|
}
|
|
434
|
-
|
|
435
356
|
/**
|
|
436
357
|
* Transform stream implementation. Receives raw data chunks from the writable side,
|
|
437
358
|
* converts strings to Buffers, tracks total bytes read, optionally logs raw data,
|
|
438
359
|
* and queues the chunk for asynchronous processing.
|
|
439
360
|
*
|
|
440
|
-
* @param
|
|
441
|
-
* @param
|
|
442
|
-
* @param
|
|
361
|
+
* @param chunk - The incoming data chunk.
|
|
362
|
+
* @param encoding - The encoding if chunk is a string.
|
|
363
|
+
* @param next - Callback to signal that this chunk has been consumed.
|
|
443
364
|
*/
|
|
444
365
|
_transform(chunk, encoding, next) {
|
|
445
366
|
if (typeof chunk === 'string') {
|
|
446
367
|
chunk = Buffer.from(chunk, encoding);
|
|
447
368
|
}
|
|
448
|
-
|
|
449
369
|
if (!chunk || !chunk.length) {
|
|
450
370
|
return next();
|
|
451
371
|
}
|
|
452
|
-
|
|
453
372
|
this.readBytesCounter += chunk.length;
|
|
454
|
-
|
|
455
373
|
if (this.options.logRaw) {
|
|
456
374
|
this.log.trace({
|
|
457
375
|
src: 's',
|
|
@@ -462,18 +380,15 @@ class ImapStream extends Transform {
|
|
|
462
380
|
cid: this.cid
|
|
463
381
|
});
|
|
464
382
|
}
|
|
465
|
-
|
|
466
383
|
// A terminal parser failure must not accept any more protocol input, even if the
|
|
467
384
|
// transport delivers a chunk that was already in flight.
|
|
468
385
|
if (this.destroyed) {
|
|
469
386
|
return next();
|
|
470
387
|
}
|
|
471
|
-
|
|
472
388
|
// Queue the chunk for async processing. The 'next' callback serves as
|
|
473
389
|
// backpressure: it is called only after this chunk is fully processed,
|
|
474
390
|
// which signals the writable side that more data can be accepted.
|
|
475
391
|
this.inputQueue.push({ chunk, next });
|
|
476
|
-
|
|
477
392
|
if (!this.processingInput) {
|
|
478
393
|
this.processingInput = true;
|
|
479
394
|
this.processInput()
|
|
@@ -481,22 +396,20 @@ class ImapStream extends Transform {
|
|
|
481
396
|
.finally(() => (this.processingInput = false));
|
|
482
397
|
}
|
|
483
398
|
}
|
|
484
|
-
|
|
485
399
|
/**
|
|
486
400
|
* Flush implementation called when the writable side ends. Signals completion immediately.
|
|
487
401
|
*
|
|
488
|
-
* @param
|
|
402
|
+
* @param next - Callback to signal flush completion.
|
|
489
403
|
*/
|
|
490
404
|
_flush(next) {
|
|
491
405
|
next();
|
|
492
406
|
}
|
|
493
|
-
|
|
494
407
|
/**
|
|
495
408
|
* Destroy implementation for cleanup. Clears all internal buffers, drains the input queue
|
|
496
409
|
* by invoking pending callbacks, and forwards the error (if any) to the callback.
|
|
497
410
|
*
|
|
498
|
-
* @param
|
|
499
|
-
* @param
|
|
411
|
+
* @param err - The error that caused destruction, or null.
|
|
412
|
+
* @param callback - Callback to signal destruction completion.
|
|
500
413
|
*/
|
|
501
414
|
_destroy(err, callback) {
|
|
502
415
|
// Destruction is the single release point for parser-owned callbacks, so a terminal
|
|
@@ -507,14 +420,12 @@ class ImapStream extends Transform {
|
|
|
507
420
|
this.literalBuffer = [];
|
|
508
421
|
this.literals = [];
|
|
509
422
|
this.responseBytes = 0;
|
|
510
|
-
|
|
511
423
|
// Settle an in-flight push() wait so processInput() can unwind
|
|
512
424
|
if (typeof this.pendingPush === 'function') {
|
|
513
425
|
const resolve = this.pendingPush;
|
|
514
426
|
this.pendingPush = null;
|
|
515
427
|
resolve();
|
|
516
428
|
}
|
|
517
|
-
|
|
518
429
|
// Release the chunk currently being processed, then everything still queued.
|
|
519
430
|
// releaseInput() is idempotent, so the processing loop releasing the same chunk
|
|
520
431
|
// afterwards is a no-op.
|
|
@@ -523,9 +434,6 @@ class ImapStream extends Transform {
|
|
|
523
434
|
while (this.inputQueue.length) {
|
|
524
435
|
this.releaseInput(this.inputQueue.shift());
|
|
525
436
|
}
|
|
526
|
-
|
|
527
437
|
callback(err);
|
|
528
438
|
}
|
|
529
439
|
}
|
|
530
|
-
|
|
531
|
-
module.exports.ImapStream = ImapStream;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { ImapFlowError } from '../errors.js';
|
|
2
|
+
export declare const MAX_LITERAL_SIZE: number;
|
|
3
|
+
export declare const MAX_LINE_SIZE: number;
|
|
4
|
+
export declare const MAX_RESPONSE_SIZE: number;
|
|
5
|
+
/**
|
|
6
|
+
* Normalizes a configured size limit. A non-negative integer is honored as-is (including 0, which
|
|
7
|
+
* means "reject anything non-empty"), and `Infinity` disables the limit; anything else falls back
|
|
8
|
+
* to the default, so an explicit 0 is not silently swallowed the way `value || DEFAULT` would
|
|
9
|
+
* swallow it.
|
|
10
|
+
*
|
|
11
|
+
* @param value - The configured value.
|
|
12
|
+
* @param defaultValue - Fallback when the value is not a usable limit.
|
|
13
|
+
* @returns The normalized limit.
|
|
14
|
+
*/
|
|
15
|
+
export declare const normalizeLimit: (value: unknown, defaultValue: number) => number;
|
|
16
|
+
/**
|
|
17
|
+
* Builds the `LiteralTooLarge` error. One shape for every place a literal is refused, so callers
|
|
18
|
+
* can rely on `code`, `literalSize` and `maxSize` regardless of which parser rejected it.
|
|
19
|
+
*
|
|
20
|
+
* @param literalSize - The declared literal size.
|
|
21
|
+
* @param maxSize - The bound that was exceeded.
|
|
22
|
+
* @param reason - What the bound was, when it is not the configured maximum.
|
|
23
|
+
* @returns The error to emit or throw.
|
|
24
|
+
*/
|
|
25
|
+
export declare const createLiteralTooLargeError: (literalSize: number, maxSize: number, reason?: string | null | undefined) => ImapFlowError;
|
|
@@ -1,17 +1,12 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
1
|
// Shared response-size limits for the IMAP parser. Kept in one place so the streaming parser
|
|
4
2
|
// (ImapStream) and the standalone token parser cannot drift apart, and so the documented
|
|
5
|
-
// defaults in
|
|
6
|
-
|
|
3
|
+
// defaults in the ImapFlowOptions type describe both paths.
|
|
7
4
|
// Maximum allowed literal size: 1GB (1073741824 bytes)
|
|
8
|
-
const MAX_LITERAL_SIZE = 1024 * 1024 * 1024;
|
|
9
|
-
|
|
5
|
+
export const MAX_LITERAL_SIZE = 1024 * 1024 * 1024;
|
|
10
6
|
// Default maximum length of a single line (a response without a literal). Matches the literal cap:
|
|
11
7
|
// large literal-free responses (e.g. big SEARCH/LIST results) are legitimate, so this bound exists
|
|
12
8
|
// only to stop a server that never sends a line terminator, not to constrain normal traffic.
|
|
13
|
-
const MAX_LINE_SIZE = MAX_LITERAL_SIZE;
|
|
14
|
-
|
|
9
|
+
export const MAX_LINE_SIZE = MAX_LITERAL_SIZE;
|
|
15
10
|
// Default maximum total size of a single assembled response: every line segment and literal of
|
|
16
11
|
// one response combined. The per-line and per-literal caps alone cannot stop a server that
|
|
17
12
|
// spreads attacker-controlled bytes across an unbounded number of tokens of a single response
|
|
@@ -21,35 +16,31 @@ const MAX_LINE_SIZE = MAX_LITERAL_SIZE;
|
|
|
21
16
|
// and the rest of the response framing, so a cap equal to MAX_LITERAL_SIZE would make a literal
|
|
22
17
|
// of exactly the maximum permitted size impossible to receive. Configuring both limits calls for
|
|
23
18
|
// the same headroom - set maxResponseSize above maxLiteralSize, not equal to it.
|
|
24
|
-
const MAX_RESPONSE_SIZE = 2 * MAX_LITERAL_SIZE;
|
|
25
|
-
|
|
19
|
+
export const MAX_RESPONSE_SIZE = 2 * MAX_LITERAL_SIZE;
|
|
26
20
|
/**
|
|
27
21
|
* Normalizes a configured size limit. A non-negative integer is honored as-is (including 0, which
|
|
28
22
|
* means "reject anything non-empty"), and `Infinity` disables the limit; anything else falls back
|
|
29
23
|
* to the default, so an explicit 0 is not silently swallowed the way `value || DEFAULT` would
|
|
30
24
|
* swallow it.
|
|
31
25
|
*
|
|
32
|
-
* @param
|
|
33
|
-
* @param
|
|
34
|
-
* @returns
|
|
26
|
+
* @param value - The configured value.
|
|
27
|
+
* @param defaultValue - Fallback when the value is not a usable limit.
|
|
28
|
+
* @returns The normalized limit.
|
|
35
29
|
*/
|
|
36
|
-
const normalizeLimit = (value, defaultValue) => (
|
|
37
|
-
|
|
30
|
+
export const normalizeLimit = (value, defaultValue) => (Number.isInteger(value) || value === Infinity) && value >= 0 ? value : defaultValue;
|
|
38
31
|
/**
|
|
39
32
|
* Builds the `LiteralTooLarge` error. One shape for every place a literal is refused, so callers
|
|
40
33
|
* can rely on `code`, `literalSize` and `maxSize` regardless of which parser rejected it.
|
|
41
34
|
*
|
|
42
|
-
* @param
|
|
43
|
-
* @param
|
|
44
|
-
* @param
|
|
45
|
-
* @returns
|
|
35
|
+
* @param literalSize - The declared literal size.
|
|
36
|
+
* @param maxSize - The bound that was exceeded.
|
|
37
|
+
* @param reason - What the bound was, when it is not the configured maximum.
|
|
38
|
+
* @returns The error to emit or throw.
|
|
46
39
|
*/
|
|
47
|
-
const createLiteralTooLargeError = (literalSize, maxSize, reason) => {
|
|
40
|
+
export const createLiteralTooLargeError = (literalSize, maxSize, reason) => {
|
|
48
41
|
const err = new Error(`Literal size ${literalSize} exceeds ${reason || `maximum allowed size of ${maxSize} bytes`}`);
|
|
49
42
|
err.code = 'LiteralTooLarge';
|
|
50
43
|
err.literalSize = literalSize;
|
|
51
44
|
err.maxSize = maxSize;
|
|
52
45
|
return err;
|
|
53
46
|
};
|
|
54
|
-
|
|
55
|
-
module.exports = { MAX_LITERAL_SIZE, MAX_LINE_SIZE, MAX_RESPONSE_SIZE, normalizeLimit, createLiteralTooLargeError };
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { ImapAttributeList, ParserOptions } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Parses a single IMAP response line into its structural components: tag, command,
|
|
4
|
+
* and attributes. Handles status responses (OK, NO, BAD, PREAUTH, BYE) with their
|
|
5
|
+
* human-readable text and response codes, as well as continuation responses ("+").
|
|
6
|
+
*/
|
|
7
|
+
export declare class ParserInstance {
|
|
8
|
+
input: string;
|
|
9
|
+
options: ParserOptions;
|
|
10
|
+
remainder: string;
|
|
11
|
+
pos: number;
|
|
12
|
+
tag?: string | undefined;
|
|
13
|
+
command?: string | undefined;
|
|
14
|
+
humanReadable?: string | undefined;
|
|
15
|
+
/**
|
|
16
|
+
* Creates a new ParserInstance for parsing an IMAP response line.
|
|
17
|
+
*
|
|
18
|
+
* @param input - The raw IMAP response line to parse.
|
|
19
|
+
* @param options - Parser options passed through to the TokenParser for attribute parsing.
|
|
20
|
+
* @param options.literalPlus - Whether the LITERAL+ extension is in use.
|
|
21
|
+
* @param options.literals - Pre-parsed literal values from the stream.
|
|
22
|
+
*/
|
|
23
|
+
constructor(input?: Buffer | string | null | undefined, options?: ParserOptions | undefined);
|
|
24
|
+
/**
|
|
25
|
+
* Extracts and returns the IMAP tag from the beginning of the response.
|
|
26
|
+
* The tag is typically "*" for untagged responses, "+" for continuation requests,
|
|
27
|
+
* or a client-assigned command tag like "A1".
|
|
28
|
+
*
|
|
29
|
+
* @returns The parsed tag string.
|
|
30
|
+
* @throws {Error} If the tag contains invalid characters.
|
|
31
|
+
*/
|
|
32
|
+
getTag(): Promise<string>;
|
|
33
|
+
/**
|
|
34
|
+
* Extracts and returns the IMAP command or response name from the input.
|
|
35
|
+
* For continuation responses (tag "+"), returns an empty string and stores
|
|
36
|
+
* the remainder as human-readable text. For status responses (OK, NO, BAD,
|
|
37
|
+
* PREAUTH, BYE), separates the optional response code from the human-readable text.
|
|
38
|
+
*
|
|
39
|
+
* @returns The parsed command string.
|
|
40
|
+
* @throws {Error} If the command contains invalid characters or input ends unexpectedly.
|
|
41
|
+
*/
|
|
42
|
+
getCommand(): Promise<string>;
|
|
43
|
+
/**
|
|
44
|
+
* Extracts the next whitespace-delimited element from the input and validates it
|
|
45
|
+
* against the given syntax character set. Advances the parser position past the element.
|
|
46
|
+
*
|
|
47
|
+
* @param syntax - A string of allowed characters for the element (as returned by imap-formal-syntax methods).
|
|
48
|
+
* @returns The extracted element string.
|
|
49
|
+
* @throws {Error} If the element contains characters not in the syntax set, or if input ends unexpectedly.
|
|
50
|
+
*/
|
|
51
|
+
getElement(syntax: string): Promise<string>;
|
|
52
|
+
/**
|
|
53
|
+
* Consumes a single space character from the current position in the input.
|
|
54
|
+
* Advances the parser position by one.
|
|
55
|
+
*
|
|
56
|
+
* @throws {Error} If the current character is not a space, or if input has ended unexpectedly.
|
|
57
|
+
*/
|
|
58
|
+
getSpace(): Promise<void>;
|
|
59
|
+
/**
|
|
60
|
+
* Parses the remaining input as IMAP attributes using the TokenParser.
|
|
61
|
+
* This handles complex structures including nested lists, literals, strings,
|
|
62
|
+
* atoms, sections, sequences, and partial ranges.
|
|
63
|
+
*
|
|
64
|
+
* @returns A promise that resolves to an array of parsed attribute objects.
|
|
65
|
+
* @throws {Error} If the input contains unexpected whitespace, invalid characters, or ends unexpectedly.
|
|
66
|
+
*/
|
|
67
|
+
getAttributes(): Promise<ImapAttributeList>;
|
|
68
|
+
}
|