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,23 +1,16 @@
|
|
|
1
1
|
/* eslint new-cap: 0 */
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const imapFormalSyntax = require('./imap-formal-syntax');
|
|
6
|
-
const { MAX_LITERAL_SIZE, normalizeLimit, createLiteralTooLargeError } = require('./limits');
|
|
7
|
-
|
|
2
|
+
import imapFormalSyntax from './imap-formal-syntax.js';
|
|
3
|
+
import { MAX_LITERAL_SIZE, normalizeLimit, createLiteralTooLargeError } from './limits.js';
|
|
8
4
|
const STATE_ATOM = 0x001;
|
|
9
5
|
const STATE_LITERAL = 0x002;
|
|
10
6
|
const STATE_NORMAL = 0x003;
|
|
11
7
|
const STATE_PARTIAL = 0x004;
|
|
12
8
|
const STATE_SEQUENCE = 0x005;
|
|
13
9
|
const STATE_STRING = 0x006;
|
|
14
|
-
|
|
15
10
|
const RE_DIGITS = /^\d+$/;
|
|
16
11
|
const RE_SINGLE_DIGIT = /^\d$/;
|
|
17
|
-
|
|
18
12
|
// Prevents stack overflow from maliciously crafted deeply-nested IMAP input (e.g., (((((...))))))
|
|
19
13
|
const MAX_NODE_DEPTH = 25;
|
|
20
|
-
|
|
21
14
|
/**
|
|
22
15
|
* Tokenizes an IMAP attribute string into a tree of typed nodes.
|
|
23
16
|
* Handles all IMAP data types: atoms, quoted strings, literals (including literal8),
|
|
@@ -25,61 +18,52 @@ const MAX_NODE_DEPTH = 25;
|
|
|
25
18
|
* Enforces a maximum nesting depth of {@link MAX_NODE_DEPTH} to prevent stack overflow
|
|
26
19
|
* from malicious input.
|
|
27
20
|
*/
|
|
28
|
-
class TokenParser {
|
|
21
|
+
export class TokenParser {
|
|
29
22
|
/**
|
|
30
23
|
* Creates a new TokenParser.
|
|
31
24
|
*
|
|
32
|
-
* @param
|
|
33
|
-
* @param
|
|
34
|
-
* @param
|
|
35
|
-
* @param
|
|
36
|
-
* @param
|
|
37
|
-
* @param
|
|
38
|
-
* @param
|
|
25
|
+
* @param parent - The parent ParserInstance that owns this token parser. Used to access the parsed command for context-sensitive parsing.
|
|
26
|
+
* @param startPos - The starting position offset in the original input, used for error reporting.
|
|
27
|
+
* @param str - The attribute string to tokenize.
|
|
28
|
+
* @param options - Parser options.
|
|
29
|
+
* @param options.literalPlus - Whether the LITERAL+ extension is in use.
|
|
30
|
+
* @param options.literals - Pre-parsed literal values from the input stream.
|
|
31
|
+
* @param options.maxLiteralSize - Maximum size (in bytes) of a literal parsed inline
|
|
39
32
|
* from the input, i.e. when no pre-parsed literal buffers were supplied. Defaults to 1GB.
|
|
40
33
|
*/
|
|
41
34
|
constructor(parent, startPos, str, options) {
|
|
42
35
|
this.str = (str || '').toString();
|
|
43
36
|
this.options = options || {};
|
|
44
37
|
this.parent = parent;
|
|
45
|
-
|
|
46
38
|
// Same normalization and default as the streaming parser, so a direct user of this parser
|
|
47
39
|
// gets the same bound (an explicit 0 means "reject any non-empty inline literal").
|
|
48
40
|
this.maxLiteralSize = normalizeLimit(this.options.maxLiteralSize, MAX_LITERAL_SIZE);
|
|
49
|
-
|
|
50
41
|
this.tree = this.currentNode = this.createNode();
|
|
51
42
|
this.pos = startPos || 0;
|
|
52
|
-
|
|
53
43
|
this.currentNode.type = 'TREE';
|
|
54
|
-
|
|
55
44
|
this.state = STATE_NORMAL;
|
|
56
45
|
}
|
|
57
|
-
|
|
58
46
|
/**
|
|
59
47
|
* Processes the input string and returns the parsed attributes as a flat array of typed objects.
|
|
60
48
|
* Each attribute is an object with a `type` (e.g., "ATOM", "STRING", "LITERAL", "SEQUENCE")
|
|
61
49
|
* and a `value` property. Lists are represented as nested arrays. Sections and partials are
|
|
62
50
|
* attached as properties on the preceding attribute object.
|
|
63
51
|
*
|
|
64
|
-
* @returns
|
|
52
|
+
* @returns A promise that resolves to an array of parsed attribute objects and nested arrays.
|
|
65
53
|
* @throws {Error} If the input contains syntax errors or unclosed nodes.
|
|
66
54
|
*/
|
|
67
55
|
async getAttributes() {
|
|
68
56
|
await this.processString();
|
|
69
|
-
|
|
70
57
|
const attributes = [];
|
|
71
58
|
let branch = attributes;
|
|
72
|
-
|
|
73
|
-
let walk = async node => {
|
|
59
|
+
let walk = async (node) => {
|
|
74
60
|
let curBranch = branch;
|
|
75
61
|
let elm;
|
|
76
62
|
let partial;
|
|
77
|
-
|
|
78
63
|
if (!node.isClosed && node.type === 'SEQUENCE' && node.value === '*') {
|
|
79
64
|
node.isClosed = true;
|
|
80
65
|
node.type = 'ATOM';
|
|
81
66
|
}
|
|
82
|
-
|
|
83
67
|
// If the node was never closed, throw it
|
|
84
68
|
if (!node.isClosed) {
|
|
85
69
|
let error = new Error(`Unexpected end of input at position ${this.pos + this.str.length - 1} [E9]`);
|
|
@@ -87,9 +71,7 @@ class TokenParser {
|
|
|
87
71
|
error.parserContext = { input: this.str, pos: this.pos + this.str.length - 1 };
|
|
88
72
|
throw error;
|
|
89
73
|
}
|
|
90
|
-
|
|
91
74
|
let type = (node.type || '').toString().toUpperCase();
|
|
92
|
-
|
|
93
75
|
switch (type) {
|
|
94
76
|
case 'LITERAL':
|
|
95
77
|
case 'STRING':
|
|
@@ -100,7 +82,6 @@ class TokenParser {
|
|
|
100
82
|
};
|
|
101
83
|
branch.push(elm);
|
|
102
84
|
break;
|
|
103
|
-
|
|
104
85
|
case 'ATOM':
|
|
105
86
|
if (node.value.toUpperCase() === 'NIL') {
|
|
106
87
|
branch.push(null);
|
|
@@ -112,43 +93,35 @@ class TokenParser {
|
|
|
112
93
|
};
|
|
113
94
|
branch.push(elm);
|
|
114
95
|
break;
|
|
115
|
-
|
|
116
96
|
case 'SECTION':
|
|
117
97
|
branch = branch[branch.length - 1].section = [];
|
|
118
98
|
break;
|
|
119
|
-
|
|
120
99
|
case 'LIST':
|
|
121
100
|
elm = [];
|
|
122
101
|
branch.push(elm);
|
|
123
102
|
branch = elm;
|
|
124
103
|
break;
|
|
125
|
-
|
|
126
104
|
case 'PARTIAL':
|
|
127
105
|
partial = node.value.split('.').map(Number);
|
|
128
106
|
branch[branch.length - 1].partial = partial;
|
|
129
107
|
break;
|
|
130
108
|
}
|
|
131
|
-
|
|
132
109
|
for (let childNode of node.childNodes) {
|
|
133
110
|
await walk(childNode);
|
|
134
111
|
}
|
|
135
|
-
|
|
136
112
|
branch = curBranch;
|
|
137
113
|
};
|
|
138
|
-
|
|
139
114
|
await walk(this.tree);
|
|
140
|
-
|
|
141
115
|
return attributes;
|
|
142
116
|
}
|
|
143
|
-
|
|
144
117
|
/**
|
|
145
118
|
* Creates a new node in the parse tree. Each node represents a token or structural
|
|
146
119
|
* element (e.g., atom, string, literal, list, section, partial). The node is automatically
|
|
147
120
|
* appended to the parent's childNodes array if a parent is provided.
|
|
148
121
|
*
|
|
149
|
-
* @param
|
|
150
|
-
* @param
|
|
151
|
-
* @returns
|
|
122
|
+
* @param parentNode - The parent node to attach this node to. If omitted, creates a root node.
|
|
123
|
+
* @param startPos - The starting position of this node in the original input string.
|
|
124
|
+
* @returns The newly created node with childNodes, type, value, and isClosed properties.
|
|
152
125
|
* @throws {Error} If the nesting depth exceeds MAX_NODE_DEPTH.
|
|
153
126
|
*/
|
|
154
127
|
createNode(parentNode, startPos) {
|
|
@@ -156,56 +129,66 @@ class TokenParser {
|
|
|
156
129
|
childNodes: [],
|
|
157
130
|
type: false,
|
|
158
131
|
value: '',
|
|
159
|
-
isClosed: true
|
|
132
|
+
isClosed: true,
|
|
133
|
+
depth: 0
|
|
160
134
|
};
|
|
161
|
-
|
|
162
135
|
if (parentNode) {
|
|
163
136
|
node.parentNode = parentNode;
|
|
164
137
|
node.depth = parentNode.depth + 1;
|
|
165
|
-
}
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
166
140
|
node.depth = 0;
|
|
167
141
|
}
|
|
168
|
-
|
|
169
142
|
if (node.depth > MAX_NODE_DEPTH) {
|
|
170
143
|
let error = new Error('Too much nesting in IMAP string');
|
|
171
144
|
error.code = 'MAX_IMAP_NESTING_REACHED';
|
|
172
145
|
error._imapStr = this.str;
|
|
173
146
|
throw error;
|
|
174
147
|
}
|
|
175
|
-
|
|
176
148
|
if (typeof startPos === 'number') {
|
|
177
149
|
node.startPos = startPos;
|
|
178
150
|
}
|
|
179
|
-
|
|
180
151
|
if (parentNode) {
|
|
181
152
|
parentNode.childNodes.push(node);
|
|
182
153
|
}
|
|
183
|
-
|
|
184
154
|
return node;
|
|
185
155
|
}
|
|
186
|
-
|
|
187
156
|
/**
|
|
188
157
|
* Processes the entire input string character by character using a state machine.
|
|
189
158
|
* Transitions between states (NORMAL, ATOM, STRING, LITERAL, SEQUENCE, PARTIAL, TEXT)
|
|
190
159
|
* based on the current character and builds the parse tree. This is the main parsing
|
|
191
160
|
* loop that drives the tokenization.
|
|
192
161
|
*
|
|
193
|
-
* @returns {Promise<void>}
|
|
194
162
|
* @throws {Error} If the input contains unexpected characters, unclosed structures, or other syntax errors.
|
|
195
163
|
*/
|
|
196
164
|
async processString() {
|
|
197
165
|
let chr, i, len;
|
|
198
|
-
|
|
199
166
|
const checkSP = () => {
|
|
200
167
|
// jump to the next non whitespace pos
|
|
201
168
|
while (this.str.charAt(i + 1) === ' ') {
|
|
202
169
|
i++;
|
|
203
170
|
}
|
|
204
171
|
};
|
|
205
|
-
|
|
172
|
+
// Any ATOM supported char starts a new Atom sequence, otherwise throw an error
|
|
173
|
+
// Allow \ as the first char for atom to support system flags
|
|
174
|
+
// Allow % to support LIST '' %
|
|
175
|
+
// Allow 8bit characters (presumably unicode)
|
|
176
|
+
// Shared by the default branch and by a '[' that is not a response code section, which
|
|
177
|
+
// used to fall through into the default branch
|
|
178
|
+
const startAtom = () => {
|
|
179
|
+
if (!imapFormalSyntax['ATOM-CHAR']().includes(chr) && chr !== '\\' && chr !== '%' && chr.charCodeAt(0) < 0x80) {
|
|
180
|
+
let error = new Error(`Unexpected char at position ${this.pos + i} [E13: ${JSON.stringify(chr)}]`);
|
|
181
|
+
error.code = 'ParserError13';
|
|
182
|
+
error.parserContext = { input: this.str, pos: this.pos + i, chr };
|
|
183
|
+
throw error;
|
|
184
|
+
}
|
|
185
|
+
this.currentNode = this.createNode(this.currentNode, this.pos + i);
|
|
186
|
+
this.currentNode.type = 'ATOM';
|
|
187
|
+
this.currentNode.value = chr;
|
|
188
|
+
this.state = STATE_ATOM;
|
|
189
|
+
};
|
|
206
190
|
for (i = 0, len = this.str.length; i < len; i++) {
|
|
207
191
|
chr = this.str.charAt(i);
|
|
208
|
-
|
|
209
192
|
switch (this.state) {
|
|
210
193
|
case STATE_NORMAL:
|
|
211
194
|
switch (chr) {
|
|
@@ -216,14 +199,12 @@ class TokenParser {
|
|
|
216
199
|
this.state = STATE_STRING;
|
|
217
200
|
this.currentNode.isClosed = false;
|
|
218
201
|
break;
|
|
219
|
-
|
|
220
202
|
// ( starts a new list
|
|
221
203
|
case '(':
|
|
222
204
|
this.currentNode = this.createNode(this.currentNode, this.pos + i);
|
|
223
205
|
this.currentNode.type = 'LIST';
|
|
224
206
|
this.currentNode.isClosed = false;
|
|
225
207
|
break;
|
|
226
|
-
|
|
227
208
|
// ) closes a list
|
|
228
209
|
case ')':
|
|
229
210
|
if (this.currentNode.type !== 'LIST') {
|
|
@@ -232,14 +213,11 @@ class TokenParser {
|
|
|
232
213
|
error.parserContext = { input: this.str, pos: this.pos + i, chr };
|
|
233
214
|
throw error;
|
|
234
215
|
}
|
|
235
|
-
|
|
236
216
|
this.currentNode.isClosed = true;
|
|
237
217
|
this.currentNode.endPos = this.pos + i;
|
|
238
218
|
this.currentNode = this.currentNode.parentNode;
|
|
239
|
-
|
|
240
219
|
checkSP();
|
|
241
220
|
break;
|
|
242
|
-
|
|
243
221
|
// ] closes section group
|
|
244
222
|
case ']':
|
|
245
223
|
if (this.currentNode.type !== 'SECTION') {
|
|
@@ -251,10 +229,8 @@ class TokenParser {
|
|
|
251
229
|
this.currentNode.isClosed = true;
|
|
252
230
|
this.currentNode.endPos = this.pos + i;
|
|
253
231
|
this.currentNode = this.currentNode.parentNode;
|
|
254
|
-
|
|
255
232
|
checkSP();
|
|
256
233
|
break;
|
|
257
|
-
|
|
258
234
|
// < starts a new partial byte range (e.g., BODY[]<0.1024>)
|
|
259
235
|
case '<':
|
|
260
236
|
// '<' is only a partial range marker when it immediately follows ']',
|
|
@@ -265,14 +241,14 @@ class TokenParser {
|
|
|
265
241
|
this.currentNode.type = 'ATOM';
|
|
266
242
|
this.currentNode.value = chr;
|
|
267
243
|
this.state = STATE_ATOM;
|
|
268
|
-
}
|
|
244
|
+
}
|
|
245
|
+
else {
|
|
269
246
|
this.currentNode = this.createNode(this.currentNode, this.pos + i);
|
|
270
247
|
this.currentNode.type = 'PARTIAL';
|
|
271
248
|
this.state = STATE_PARTIAL;
|
|
272
249
|
this.currentNode.isClosed = false;
|
|
273
250
|
}
|
|
274
251
|
break;
|
|
275
|
-
|
|
276
252
|
// literal8 (RFC 3516): uses ~{size} prefix instead of {size}
|
|
277
253
|
// literal8 allows binary data containing NUL bytes, unlike regular literals
|
|
278
254
|
case '~': {
|
|
@@ -286,7 +262,6 @@ class TokenParser {
|
|
|
286
262
|
this.state = STATE_ATOM;
|
|
287
263
|
break;
|
|
288
264
|
}
|
|
289
|
-
|
|
290
265
|
let error = new Error(`Unexpected literal8 marker at position ${this.pos + i} [E12]`);
|
|
291
266
|
error.code = 'ParserError12';
|
|
292
267
|
error.parserContext = { input: this.str, pos: this.pos + i, chr };
|
|
@@ -296,7 +271,6 @@ class TokenParser {
|
|
|
296
271
|
this.expectedLiteralType = 'literal8';
|
|
297
272
|
break;
|
|
298
273
|
}
|
|
299
|
-
|
|
300
274
|
// { starts a new literal (regular {size}\r\n or literal8 ~{size}\r\n)
|
|
301
275
|
case '{':
|
|
302
276
|
this.currentNode = this.createNode(this.currentNode, this.pos + i);
|
|
@@ -307,7 +281,6 @@ class TokenParser {
|
|
|
307
281
|
this.state = STATE_LITERAL;
|
|
308
282
|
this.currentNode.isClosed = false;
|
|
309
283
|
break;
|
|
310
|
-
|
|
311
284
|
// * starts a new sequence
|
|
312
285
|
case '*':
|
|
313
286
|
this.currentNode = this.createNode(this.currentNode, this.pos + i);
|
|
@@ -316,27 +289,23 @@ class TokenParser {
|
|
|
316
289
|
this.currentNode.isClosed = false;
|
|
317
290
|
this.state = STATE_SEQUENCE;
|
|
318
291
|
break;
|
|
319
|
-
|
|
320
292
|
// normally a space should never occur
|
|
321
293
|
case ' ':
|
|
322
294
|
// just ignore
|
|
323
295
|
break;
|
|
324
|
-
|
|
325
296
|
// [ starts section
|
|
326
297
|
case '[':
|
|
327
298
|
// If it is the *first* element after response command, then process as a response argument list
|
|
328
299
|
// Status responses (OK/NO/BAD/BYE/PREAUTH) use [code] for response codes
|
|
329
|
-
if (['OK', 'NO', 'BAD', 'BYE', 'PREAUTH'].includes(this.parent.command.toUpperCase()) &&
|
|
300
|
+
if (['OK', 'NO', 'BAD', 'BYE', 'PREAUTH'].includes(this.parent.command.toUpperCase()) &&
|
|
301
|
+
this.currentNode === this.tree) {
|
|
330
302
|
this.currentNode.endPos = this.pos + i;
|
|
331
|
-
|
|
332
303
|
this.currentNode = this.createNode(this.currentNode, this.pos + i);
|
|
333
304
|
this.currentNode.type = 'ATOM';
|
|
334
|
-
|
|
335
305
|
this.currentNode = this.createNode(this.currentNode, this.pos + i);
|
|
336
306
|
this.currentNode.type = 'SECTION';
|
|
337
307
|
this.currentNode.isClosed = false;
|
|
338
308
|
this.state = STATE_NORMAL;
|
|
339
|
-
|
|
340
309
|
// RFC 2221 REFERRAL special case: the payload is an RFC 2192/RFC 5092
|
|
341
310
|
// IMAP URL (e.g., imap://user@host/mailbox) which contains characters
|
|
342
311
|
// that would break normal ATOM parsing (colons, slashes, etc.).
|
|
@@ -348,7 +317,6 @@ class TokenParser {
|
|
|
348
317
|
this.currentNode.endPos = this.pos + i + 8;
|
|
349
318
|
this.currentNode.value = 'REFERRAL';
|
|
350
319
|
this.currentNode = this.currentNode.parentNode;
|
|
351
|
-
|
|
352
320
|
// eat all the way through the ] to be the IMAPURL token.
|
|
353
321
|
this.currentNode = this.createNode(this.currentNode, this.pos + i + 10);
|
|
354
322
|
// just call this an ATOM, even though IMAPURL might be more correct
|
|
@@ -365,38 +333,21 @@ class TokenParser {
|
|
|
365
333
|
this.currentNode.endPos = this.pos + i - 1;
|
|
366
334
|
this.currentNode.value = this.str.substring(this.currentNode.startPos - this.pos, this.currentNode.endPos - this.pos + 1);
|
|
367
335
|
this.currentNode = this.currentNode.parentNode;
|
|
368
|
-
|
|
369
336
|
// close out the SECTION
|
|
370
337
|
this.currentNode.isClosed = true;
|
|
371
338
|
this.currentNode = this.currentNode.parentNode;
|
|
372
|
-
|
|
373
339
|
checkSP();
|
|
374
340
|
}
|
|
375
|
-
|
|
376
341
|
break;
|
|
377
342
|
}
|
|
378
|
-
|
|
379
|
-
|
|
343
|
+
// Not a response code section: handled like any other atom start
|
|
344
|
+
startAtom();
|
|
345
|
+
break;
|
|
380
346
|
default:
|
|
381
|
-
|
|
382
|
-
// Allow \ as the first char for atom to support system flags
|
|
383
|
-
// Allow % to support LIST '' %
|
|
384
|
-
// Allow 8bit characters (presumably unicode)
|
|
385
|
-
if (!imapFormalSyntax['ATOM-CHAR']().includes(chr) && chr !== '\\' && chr !== '%' && chr.charCodeAt(0) < 0x80) {
|
|
386
|
-
let error = new Error(`Unexpected char at position ${this.pos + i} [E13: ${JSON.stringify(chr)}]`);
|
|
387
|
-
error.code = 'ParserError13';
|
|
388
|
-
error.parserContext = { input: this.str, pos: this.pos + i, chr };
|
|
389
|
-
throw error;
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
this.currentNode = this.createNode(this.currentNode, this.pos + i);
|
|
393
|
-
this.currentNode.type = 'ATOM';
|
|
394
|
-
this.currentNode.value = chr;
|
|
395
|
-
this.state = STATE_ATOM;
|
|
347
|
+
startAtom();
|
|
396
348
|
break;
|
|
397
349
|
}
|
|
398
350
|
break;
|
|
399
|
-
|
|
400
351
|
case STATE_ATOM:
|
|
401
352
|
// An atom is terminated by: space, closing delimiter of parent node,
|
|
402
353
|
// or encountering a '[' that starts a section for BODY/BINARY commands.
|
|
@@ -407,24 +358,18 @@ class TokenParser {
|
|
|
407
358
|
this.state = STATE_NORMAL;
|
|
408
359
|
break;
|
|
409
360
|
}
|
|
410
|
-
|
|
411
361
|
// ')' or ']' terminates the atom AND closes the enclosing LIST or SECTION
|
|
412
|
-
if (
|
|
413
|
-
this.currentNode.parentNode &&
|
|
414
|
-
((chr === ')' && this.currentNode.parentNode.type === 'LIST') || (chr === ']' && this.currentNode.parentNode.type === 'SECTION'))
|
|
415
|
-
) {
|
|
362
|
+
if (this.currentNode.parentNode &&
|
|
363
|
+
((chr === ')' && this.currentNode.parentNode.type === 'LIST') || (chr === ']' && this.currentNode.parentNode.type === 'SECTION'))) {
|
|
416
364
|
this.currentNode.endPos = this.pos + i - 1;
|
|
417
365
|
this.currentNode = this.currentNode.parentNode;
|
|
418
|
-
|
|
419
366
|
this.currentNode.isClosed = true;
|
|
420
367
|
this.currentNode.endPos = this.pos + i;
|
|
421
368
|
this.currentNode = this.currentNode.parentNode;
|
|
422
369
|
this.state = STATE_NORMAL;
|
|
423
370
|
checkSP();
|
|
424
|
-
|
|
425
371
|
break;
|
|
426
372
|
}
|
|
427
|
-
|
|
428
373
|
// If the atom so far is all digits and we see ',' or ':', it is actually
|
|
429
374
|
// a sequence set (e.g., "1:5" or "1,3,5"), so reclassify and switch state
|
|
430
375
|
if ((chr === ',' || chr === ':') && RE_DIGITS.test(this.currentNode.value)) {
|
|
@@ -432,7 +377,6 @@ class TokenParser {
|
|
|
432
377
|
this.currentNode.isClosed = true;
|
|
433
378
|
this.state = STATE_SEQUENCE;
|
|
434
379
|
}
|
|
435
|
-
|
|
436
380
|
// [ starts a section group for this element
|
|
437
381
|
// Allowed only for selected elements, otherwise falls through to regular ATOM processing
|
|
438
382
|
if (chr === '[' && ['BODY', 'BODY.PEEK', 'BINARY', 'BINARY.PEEK'].includes(this.currentNode.value.toUpperCase())) {
|
|
@@ -443,29 +387,25 @@ class TokenParser {
|
|
|
443
387
|
this.state = STATE_NORMAL;
|
|
444
388
|
break;
|
|
445
389
|
}
|
|
446
|
-
|
|
447
390
|
// if the char is not ATOM compatible, throw. Allow \* as an exception
|
|
448
|
-
if (
|
|
449
|
-
!imapFormalSyntax['ATOM-CHAR']().includes(chr) &&
|
|
391
|
+
if (!imapFormalSyntax['ATOM-CHAR']().includes(chr) &&
|
|
450
392
|
chr.charCodeAt(0) < 0x80 && // allow 8bit (presumably unicode) bytes
|
|
451
393
|
chr !== ']' &&
|
|
452
394
|
!(chr === '*' && this.currentNode.value === '\\') &&
|
|
453
|
-
(!this.parent || !this.parent.command || !['NO', 'BAD', 'OK'].includes(this.parent.command))
|
|
454
|
-
) {
|
|
395
|
+
(!this.parent || !this.parent.command || !['NO', 'BAD', 'OK'].includes(this.parent.command))) {
|
|
455
396
|
let error = new Error(`Unexpected char at position ${this.pos + i} [E16: ${JSON.stringify(chr)}]`);
|
|
456
397
|
error.code = 'ParserError16';
|
|
457
398
|
error.parserContext = { input: this.str, pos: this.pos + i, chr };
|
|
458
399
|
throw error;
|
|
459
|
-
}
|
|
400
|
+
}
|
|
401
|
+
else if (this.currentNode.value === '\\*') {
|
|
460
402
|
let error = new Error(`Unexpected char at position ${this.pos + i} [E17: ${JSON.stringify(chr)}]`);
|
|
461
403
|
error.code = 'ParserError17';
|
|
462
404
|
error.parserContext = { input: this.str, pos: this.pos + i, chr };
|
|
463
405
|
throw error;
|
|
464
406
|
}
|
|
465
|
-
|
|
466
407
|
this.currentNode.value += chr;
|
|
467
408
|
break;
|
|
468
|
-
|
|
469
409
|
case STATE_STRING:
|
|
470
410
|
// DQUOTE ends the string sequence
|
|
471
411
|
if (chr === '"') {
|
|
@@ -473,11 +413,9 @@ class TokenParser {
|
|
|
473
413
|
this.currentNode.isClosed = true;
|
|
474
414
|
this.currentNode = this.currentNode.parentNode;
|
|
475
415
|
this.state = STATE_NORMAL;
|
|
476
|
-
|
|
477
416
|
checkSP();
|
|
478
417
|
break;
|
|
479
418
|
}
|
|
480
|
-
|
|
481
419
|
// \ Escapes the following char
|
|
482
420
|
if (chr === '\\') {
|
|
483
421
|
i++;
|
|
@@ -489,10 +427,8 @@ class TokenParser {
|
|
|
489
427
|
}
|
|
490
428
|
chr = this.str.charAt(i);
|
|
491
429
|
}
|
|
492
|
-
|
|
493
430
|
this.currentNode.value += chr;
|
|
494
431
|
break;
|
|
495
|
-
|
|
496
432
|
case STATE_PARTIAL:
|
|
497
433
|
if (chr === '>') {
|
|
498
434
|
if (this.currentNode.value.at(-1) === '.') {
|
|
@@ -508,35 +444,29 @@ class TokenParser {
|
|
|
508
444
|
checkSP();
|
|
509
445
|
break;
|
|
510
446
|
}
|
|
511
|
-
|
|
512
447
|
if (chr === '.' && (this.currentNode.value === '' || this.currentNode.value.includes('.'))) {
|
|
513
448
|
let error = new Error(`Unexpected partial separator . at position ${this.pos + i} [E20]`);
|
|
514
449
|
error.code = 'ParserError20';
|
|
515
450
|
error.parserContext = { input: this.str, pos: this.pos + i, chr };
|
|
516
451
|
throw error;
|
|
517
452
|
}
|
|
518
|
-
|
|
519
453
|
if (!imapFormalSyntax.DIGIT().includes(chr) && chr !== '.') {
|
|
520
454
|
let error = new Error(`Unexpected char at position ${this.pos + i} [E21: ${JSON.stringify(chr)}]`);
|
|
521
455
|
error.code = 'ParserError21';
|
|
522
456
|
error.parserContext = { input: this.str, pos: this.pos + i, chr };
|
|
523
457
|
throw error;
|
|
524
458
|
}
|
|
525
|
-
|
|
526
459
|
if ((this.currentNode.value === '0' || this.currentNode.value.endsWith('.0')) && chr !== '.') {
|
|
527
460
|
let error = new Error(`Invalid partial at position ${this.pos + i} [E22: ${JSON.stringify(chr)}]`);
|
|
528
461
|
error.code = 'ParserError22';
|
|
529
462
|
error.parserContext = { input: this.str, pos: this.pos + i, chr };
|
|
530
463
|
throw error;
|
|
531
464
|
}
|
|
532
|
-
|
|
533
465
|
this.currentNode.value += chr;
|
|
534
466
|
break;
|
|
535
|
-
|
|
536
467
|
case STATE_LITERAL:
|
|
537
468
|
if (this.currentNode.started) {
|
|
538
469
|
// only relevant if literals are not already parsed out from input
|
|
539
|
-
|
|
540
470
|
// Disabled NULL byte check
|
|
541
471
|
// See https://github.com/emailjs/emailjs-imap-handler/commit/f11b2822bedabe492236e8263afc630134a3c41c
|
|
542
472
|
/*
|
|
@@ -544,9 +474,7 @@ class TokenParser {
|
|
|
544
474
|
throw new Error('Unexpected \\x00 at position ' + (this.pos + i));
|
|
545
475
|
}
|
|
546
476
|
*/
|
|
547
|
-
|
|
548
477
|
this.currentNode.chBuffer[this.currentNode.chPos++] = chr.charCodeAt(0);
|
|
549
|
-
|
|
550
478
|
if (this.currentNode.chPos >= this.currentNode.literalLength) {
|
|
551
479
|
this.currentNode.endPos = this.pos + i;
|
|
552
480
|
this.currentNode.isClosed = true;
|
|
@@ -558,12 +486,10 @@ class TokenParser {
|
|
|
558
486
|
}
|
|
559
487
|
break;
|
|
560
488
|
}
|
|
561
|
-
|
|
562
489
|
if (chr === '+' && this.options.literalPlus) {
|
|
563
490
|
this.currentNode.literalPlus = true;
|
|
564
491
|
break;
|
|
565
492
|
}
|
|
566
|
-
|
|
567
493
|
if (chr === '}') {
|
|
568
494
|
if (!('literalLength' in this.currentNode)) {
|
|
569
495
|
let error = new Error(`Unexpected literal prefix end char } at position ${this.pos + i} [E23]`);
|
|
@@ -573,17 +499,17 @@ class TokenParser {
|
|
|
573
499
|
}
|
|
574
500
|
if (this.str.charAt(i + 1) === '\n') {
|
|
575
501
|
i++;
|
|
576
|
-
}
|
|
502
|
+
}
|
|
503
|
+
else if (this.str.charAt(i + 1) === '\r' && this.str.charAt(i + 2) === '\n') {
|
|
577
504
|
i += 2;
|
|
578
|
-
}
|
|
505
|
+
}
|
|
506
|
+
else {
|
|
579
507
|
let error = new Error(`Unexpected char at position ${this.pos + i} [E24: ${JSON.stringify(chr)}]`);
|
|
580
508
|
error.code = 'ParserError24';
|
|
581
509
|
error.parserContext = { input: this.str, pos: this.pos + i, chr };
|
|
582
510
|
throw error;
|
|
583
511
|
}
|
|
584
|
-
|
|
585
512
|
this.currentNode.literalLength = Number(this.currentNode.literalLength);
|
|
586
|
-
|
|
587
513
|
if (!this.currentNode.literalLength) {
|
|
588
514
|
// special case where literal content length is 0
|
|
589
515
|
// close the node right away, do not wait for additional input
|
|
@@ -599,25 +525,24 @@ class TokenParser {
|
|
|
599
525
|
this.currentNode = this.currentNode.parentNode;
|
|
600
526
|
this.state = STATE_NORMAL;
|
|
601
527
|
checkSP();
|
|
602
|
-
}
|
|
528
|
+
}
|
|
529
|
+
else if (this.options.literals) {
|
|
603
530
|
// use the next precached literal values
|
|
604
531
|
this.currentNode.value = this.options.literals.shift();
|
|
605
|
-
|
|
606
532
|
// only APPEND arguments are kept as Buffers
|
|
607
533
|
/*
|
|
608
534
|
if ((this.parent.command || '').toString().toUpperCase() !== 'APPEND') {
|
|
609
535
|
this.currentNode.value = this.currentNode.value.toString('binary');
|
|
610
536
|
}
|
|
611
537
|
*/
|
|
612
|
-
|
|
613
538
|
this.currentNode.endPos = this.pos + i + this.currentNode.value.length;
|
|
614
|
-
|
|
615
539
|
this.currentNode.started = false;
|
|
616
540
|
this.currentNode.isClosed = true;
|
|
617
541
|
this.currentNode = this.currentNode.parentNode;
|
|
618
542
|
this.state = STATE_NORMAL;
|
|
619
543
|
checkSP();
|
|
620
|
-
}
|
|
544
|
+
}
|
|
545
|
+
else {
|
|
621
546
|
// No pre-parsed literal buffers were supplied, so the literal is read
|
|
622
547
|
// inline from this input and its declared length decides an
|
|
623
548
|
// allocation. ImapStream always supplies buffers (and has already
|
|
@@ -632,15 +557,10 @@ class TokenParser {
|
|
|
632
557
|
let literalLength = this.currentNode.literalLength;
|
|
633
558
|
if (literalLength > this.maxLiteralSize || literalLength > available) {
|
|
634
559
|
let overMax = literalLength > this.maxLiteralSize;
|
|
635
|
-
let error = createLiteralTooLargeError(
|
|
636
|
-
literalLength,
|
|
637
|
-
overMax ? this.maxLiteralSize : available,
|
|
638
|
-
overMax ? null : `the ${available} bytes available in the input`
|
|
639
|
-
);
|
|
560
|
+
let error = createLiteralTooLargeError(literalLength, overMax ? this.maxLiteralSize : available, overMax ? null : `the ${available} bytes available in the input`);
|
|
640
561
|
error.parserContext = { input: this.str, pos: this.pos + i, chr };
|
|
641
562
|
throw error;
|
|
642
563
|
}
|
|
643
|
-
|
|
644
564
|
this.currentNode.started = true;
|
|
645
565
|
// Allocate expected size buffer.
|
|
646
566
|
// Maybe should use allocUnsafe instead?
|
|
@@ -663,7 +583,6 @@ class TokenParser {
|
|
|
663
583
|
}
|
|
664
584
|
this.currentNode.literalLength = (this.currentNode.literalLength || '') + chr;
|
|
665
585
|
break;
|
|
666
|
-
|
|
667
586
|
case STATE_SEQUENCE:
|
|
668
587
|
// space finishes the sequence set
|
|
669
588
|
if (chr === ' ') {
|
|
@@ -673,32 +592,30 @@ class TokenParser {
|
|
|
673
592
|
error.parserContext = { input: this.str, pos: this.pos + i, chr };
|
|
674
593
|
throw error;
|
|
675
594
|
}
|
|
676
|
-
|
|
677
|
-
|
|
595
|
+
if (this.currentNode.value !== '*' &&
|
|
596
|
+
this.currentNode.value.at(-1) === '*' &&
|
|
597
|
+
this.currentNode.value.at(-2) !== ':') {
|
|
678
598
|
let error = new Error(`Unexpected whitespace at position ${this.pos + i} [E28]`);
|
|
679
599
|
error.code = 'ParserError28';
|
|
680
600
|
error.parserContext = { input: this.str, pos: this.pos + i, chr };
|
|
681
601
|
throw error;
|
|
682
602
|
}
|
|
683
|
-
|
|
684
603
|
this.currentNode.isClosed = true;
|
|
685
604
|
this.currentNode.endPos = this.pos + i - 1;
|
|
686
605
|
this.currentNode = this.currentNode.parentNode;
|
|
687
606
|
this.state = STATE_NORMAL;
|
|
688
607
|
break;
|
|
689
|
-
}
|
|
608
|
+
}
|
|
609
|
+
else if (this.currentNode.parentNode && chr === ']' && this.currentNode.parentNode.type === 'SECTION') {
|
|
690
610
|
this.currentNode.endPos = this.pos + i - 1;
|
|
691
611
|
this.currentNode = this.currentNode.parentNode;
|
|
692
|
-
|
|
693
612
|
this.currentNode.isClosed = true;
|
|
694
613
|
this.currentNode.endPos = this.pos + i;
|
|
695
614
|
this.currentNode = this.currentNode.parentNode;
|
|
696
615
|
this.state = STATE_NORMAL;
|
|
697
|
-
|
|
698
616
|
checkSP();
|
|
699
617
|
break;
|
|
700
618
|
}
|
|
701
|
-
|
|
702
619
|
if (chr === ':') {
|
|
703
620
|
if (!RE_SINGLE_DIGIT.test(this.currentNode.value.at(-1)) && this.currentNode.value.at(-1) !== '*') {
|
|
704
621
|
let error = new Error(`Unexpected range separator : at position ${this.pos + i} [E29]`);
|
|
@@ -706,14 +623,16 @@ class TokenParser {
|
|
|
706
623
|
error.parserContext = { input: this.str, pos: this.pos + i, chr };
|
|
707
624
|
throw error;
|
|
708
625
|
}
|
|
709
|
-
}
|
|
626
|
+
}
|
|
627
|
+
else if (chr === '*') {
|
|
710
628
|
if (![',', ':'].includes(this.currentNode.value.at(-1))) {
|
|
711
629
|
let error = new Error(`Unexpected range wildcard at position ${this.pos + i} [E30]`);
|
|
712
630
|
error.code = 'ParserError30';
|
|
713
631
|
error.parserContext = { input: this.str, pos: this.pos + i, chr };
|
|
714
632
|
throw error;
|
|
715
633
|
}
|
|
716
|
-
}
|
|
634
|
+
}
|
|
635
|
+
else if (chr === ',') {
|
|
717
636
|
if (!RE_SINGLE_DIGIT.test(this.currentNode.value.at(-1)) && this.currentNode.value.at(-1) !== '*') {
|
|
718
637
|
let error = new Error(`Unexpected sequence separator , at position ${this.pos + i} [E31]`);
|
|
719
638
|
error.code = 'ParserError31';
|
|
@@ -726,25 +645,22 @@ class TokenParser {
|
|
|
726
645
|
error.parserContext = { input: this.str, pos: this.pos + i, chr };
|
|
727
646
|
throw error;
|
|
728
647
|
}
|
|
729
|
-
}
|
|
648
|
+
}
|
|
649
|
+
else if (!RE_SINGLE_DIGIT.test(chr)) {
|
|
730
650
|
let error = new Error(`Unexpected char at position ${this.pos + i} [E33: ${JSON.stringify(chr)}]`);
|
|
731
651
|
error.code = 'ParserError33';
|
|
732
652
|
error.parserContext = { input: this.str, pos: this.pos + i, chr };
|
|
733
653
|
throw error;
|
|
734
654
|
}
|
|
735
|
-
|
|
736
655
|
if (RE_SINGLE_DIGIT.test(chr) && this.currentNode.value.at(-1) === '*') {
|
|
737
656
|
let error = new Error(`Unexpected number at position ${this.pos + i} [E34: ${JSON.stringify(chr)}]`);
|
|
738
657
|
error.code = 'ParserError34';
|
|
739
658
|
error.parserContext = { input: this.str, pos: this.pos + i, chr };
|
|
740
659
|
throw error;
|
|
741
660
|
}
|
|
742
|
-
|
|
743
661
|
this.currentNode.value += chr;
|
|
744
662
|
break;
|
|
745
663
|
}
|
|
746
664
|
}
|
|
747
665
|
}
|
|
748
666
|
}
|
|
749
|
-
|
|
750
|
-
module.exports.TokenParser = TokenParser;
|