imapflow 1.7.7 → 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 +27 -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 +785 -1802
- 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 -57
- 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 -873
- 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 -593
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import type { ImapResponse } from './handler/types.js';
|
|
2
|
+
/**
|
|
3
|
+
* An Error raised by ImapFlow, with the extra properties the library attaches to describe
|
|
4
|
+
* the failure. Every property is optional: which ones are present depends on where the
|
|
5
|
+
* error came from.
|
|
6
|
+
*/
|
|
7
|
+
export interface ImapFlowError extends Error {
|
|
8
|
+
/** Error code, e.g. 'NoConnection', 'ETIMEOUT', 'LockTimeout' or a parser error code */
|
|
9
|
+
code?: string | undefined;
|
|
10
|
+
/** Connection id the error belongs to */
|
|
11
|
+
cid?: string | undefined;
|
|
12
|
+
/** Connection id, stamped by emitError() */
|
|
13
|
+
_connId?: string | undefined;
|
|
14
|
+
/** Which internal site rejected with this error */
|
|
15
|
+
rejectedFrom?: string | undefined;
|
|
16
|
+
/** The command that was affected */
|
|
17
|
+
command?: string | undefined;
|
|
18
|
+
/** The mailbox path that was affected */
|
|
19
|
+
path?: string | undefined;
|
|
20
|
+
/** Status of the tagged response that failed the command: 'NO' or 'BAD' */
|
|
21
|
+
responseStatus?: string | undefined;
|
|
22
|
+
/** Human readable text of the failed tagged response */
|
|
23
|
+
responseText?: string | undefined;
|
|
24
|
+
/** The server response: the parsed response, or its text once enhanceCommandError() ran */
|
|
25
|
+
response?: ImapResponse | string | false | undefined;
|
|
26
|
+
/** Response code of the failed tagged response, e.g. 'AUTHENTICATIONFAILED' */
|
|
27
|
+
serverResponseCode?: string | undefined;
|
|
28
|
+
/** The command as it was sent, for logging */
|
|
29
|
+
executedCommand?: string | undefined;
|
|
30
|
+
/** Set when authentication failed */
|
|
31
|
+
authenticationFailed?: boolean | undefined;
|
|
32
|
+
/** Set when a TLS or STARTTLS upgrade failed */
|
|
33
|
+
tlsFailed?: boolean | undefined;
|
|
34
|
+
/** Server suggested back-off in milliseconds for an ETHROTTLE error */
|
|
35
|
+
throttleReset?: number | undefined;
|
|
36
|
+
/** Additional details, e.g. the timeouts that applied */
|
|
37
|
+
details?: {
|
|
38
|
+
[key: string]: any;
|
|
39
|
+
} | undefined;
|
|
40
|
+
/** The underlying error */
|
|
41
|
+
_err?: Error | undefined;
|
|
42
|
+
/** Server BYE reason */
|
|
43
|
+
reason?: string | undefined;
|
|
44
|
+
/** Set when a mailbox could not be selected because it does not exist */
|
|
45
|
+
mailboxMissing?: boolean | undefined;
|
|
46
|
+
/** Id of the mailbox lock that timed out */
|
|
47
|
+
lockId?: number | undefined;
|
|
48
|
+
/** The parser error that failed a command completion */
|
|
49
|
+
parserError?: ImapFlowError | undefined;
|
|
50
|
+
/** Parser diagnostics */
|
|
51
|
+
parserContext?: {
|
|
52
|
+
[key: string]: any;
|
|
53
|
+
} | undefined;
|
|
54
|
+
/** The tag the parser had already read before it failed */
|
|
55
|
+
parsedTag?: string | undefined;
|
|
56
|
+
/** The declared size of a rejected literal */
|
|
57
|
+
literalSize?: number | undefined;
|
|
58
|
+
/** The length of a rejected line */
|
|
59
|
+
lineLength?: number | undefined;
|
|
60
|
+
/** The size of a rejected response */
|
|
61
|
+
responseSize?: number | undefined;
|
|
62
|
+
/** The bound that was exceeded */
|
|
63
|
+
maxSize?: number | undefined;
|
|
64
|
+
/** OAuth error details from the server, for XOAUTH2 authentication failures */
|
|
65
|
+
oauthError?: any;
|
|
66
|
+
/** The IMAP string that could not be parsed */
|
|
67
|
+
_imapStr?: string | undefined;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* The fields a connection error is stamped with to say where it was rejected, see
|
|
71
|
+
* buildConnectionError() in tools.ts
|
|
72
|
+
*/
|
|
73
|
+
export type ConnectionErrorSite = Pick<ImapFlowError, 'rejectedFrom' | 'command' | 'path'>;
|
|
74
|
+
/**
|
|
75
|
+
* Error subclass thrown when IMAP authentication fails.
|
|
76
|
+
*/
|
|
77
|
+
export declare class AuthenticationFailure extends Error implements ImapFlowError {
|
|
78
|
+
authenticationFailed: true;
|
|
79
|
+
serverResponseCode?: string | undefined;
|
|
80
|
+
/** Text of the server's error response */
|
|
81
|
+
response?: string | undefined;
|
|
82
|
+
oauthError?: any;
|
|
83
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { CompilerOptions, ImapCompileInput } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Compiles an input object into a sequence of Buffers representing an IMAP protocol response string.
|
|
4
|
+
* Handles various node types including literals, strings, atoms, sections, sequences, and nested lists.
|
|
5
|
+
*
|
|
6
|
+
* @param response - The response object to compile.
|
|
7
|
+
* @param response.tag - The IMAP command tag (e.g., "*" or a sequence number).
|
|
8
|
+
* @param response.command - The IMAP command name.
|
|
9
|
+
* @param response.attributes - The response attributes to compile into IMAP format.
|
|
10
|
+
* @param options - Compilation options.
|
|
11
|
+
* @param options.asArray - If true, returns an array of Buffers (one per literal segment); otherwise returns a single concatenated Buffer.
|
|
12
|
+
* @param options.isLogging - If true, redacts sensitive values and truncates long strings/literals for logging purposes.
|
|
13
|
+
* @param options.literalPlus - If true, uses the LITERAL+ extension (appends "+" to literal length markers).
|
|
14
|
+
* @param options.literalMinus - If true, uses the LITERAL- extension for literals up to 4096 bytes.
|
|
15
|
+
* @returns A promise that resolves to an array of Buffers (if asArray is true) or a single concatenated Buffer.
|
|
16
|
+
*/
|
|
17
|
+
declare function compiler(response: ImapCompileInput, options: CompilerOptions & {
|
|
18
|
+
asArray: true;
|
|
19
|
+
}): Promise<Buffer[]>;
|
|
20
|
+
declare function compiler(response: ImapCompileInput, options?: (CompilerOptions & {
|
|
21
|
+
asArray?: false | undefined;
|
|
22
|
+
}) | undefined): Promise<Buffer>;
|
|
23
|
+
declare function compiler(response: ImapCompileInput, options?: CompilerOptions | undefined): Promise<Buffer | Buffer[]>;
|
|
24
|
+
export default compiler;
|
|
@@ -1,50 +1,41 @@
|
|
|
1
1
|
/* eslint no-console: 0, new-cap: 0 */
|
|
2
|
-
|
|
3
|
-
'use strict';
|
|
4
|
-
|
|
5
|
-
const imapFormalSyntax = require('./imap-formal-syntax');
|
|
6
|
-
|
|
2
|
+
import imapFormalSyntax from './imap-formal-syntax.js';
|
|
7
3
|
// A single element of a sequence-set as defined by the RFC 9051 grammar: a number
|
|
8
4
|
// or a range, where "*" stands for the largest number in use. Digit strings are not
|
|
9
5
|
// range-checked here (a server rejects "0" or an overlong number on its own); the
|
|
10
6
|
// point of the check is that nothing outside this alphabet can reach the wire.
|
|
11
7
|
const SEQ_RANGE = /^(\d+|\*)(:(\d+|\*))?$/;
|
|
12
|
-
|
|
13
8
|
// Validates a full sequence-set: comma-separated SEQ_RANGE elements, or "$"
|
|
14
9
|
// (RFC 5182 SEARCHRES), which references the previous SEARCH result and is only
|
|
15
10
|
// valid as the entire set. Split into per-element tests on purpose - a whole-set
|
|
16
11
|
// regex with an unbounded repeat group overflows the regex engine's backtrack
|
|
17
12
|
// stack with an uncoded RangeError on valid sets in the million-element range,
|
|
18
13
|
// while the per-element regex is bounded.
|
|
19
|
-
const isValidSequenceSet = value => value === '$' || value.split(',').every(part => SEQ_RANGE.test(part));
|
|
20
|
-
|
|
14
|
+
const isValidSequenceSet = (value) => value === '$' || value.split(',').every(part => SEQ_RANGE.test(part));
|
|
21
15
|
// Numeric tokens may only put the digit alphabet on the wire. Anything that does
|
|
22
16
|
// not round to a bounded non-negative integer (NaN, Infinity, negatives, unsafe
|
|
23
17
|
// magnitudes) degrades to 0 - the fallback the NaN coercion has always used.
|
|
24
|
-
const safeNumber = value => {
|
|
18
|
+
const safeNumber = (value) => {
|
|
25
19
|
let num = Math.round(Number(value));
|
|
26
20
|
return Number.isSafeInteger(num) && num >= 0 ? num : 0;
|
|
27
21
|
};
|
|
28
|
-
|
|
29
22
|
// Characters that cannot appear in an IMAP quoted string: CR and LF terminate a
|
|
30
23
|
// command line, and NUL is outside the CHAR production entirely. A value carrying
|
|
31
24
|
// any of them has to be sent as a literal, so quoting it is never correct.
|
|
32
25
|
const NOT_QUOTABLE = /[\r\n\0]/;
|
|
33
|
-
|
|
34
26
|
// A line terminator ends an IMAP command, so no token may carry one to the wire.
|
|
35
27
|
const CRLF = /[\r\n]/;
|
|
36
|
-
|
|
37
28
|
/**
|
|
38
29
|
* Quotes a value as an IMAP quoted string. Only DQUOTE and backslash are escaped -
|
|
39
30
|
* the IMAP grammar defines no other escape sequence, so JSON-style escaping (which
|
|
40
31
|
* turns a tab into a literal backslash-t and a control character into \\uXXXX) would
|
|
41
32
|
* silently change the value the server receives.
|
|
42
33
|
*
|
|
43
|
-
* @param
|
|
44
|
-
* @returns
|
|
34
|
+
* @param value - The value to quote.
|
|
35
|
+
* @returns The quoted string, ready to be written to the wire.
|
|
45
36
|
* @throws {Error} If the value contains CR, LF or NUL, which a quoted string cannot carry.
|
|
46
37
|
*/
|
|
47
|
-
const quoteString = value => {
|
|
38
|
+
const quoteString = (value) => {
|
|
48
39
|
if (NOT_QUOTABLE.test(value)) {
|
|
49
40
|
let error = new Error('Unquotable character in IMAP string value');
|
|
50
41
|
error.code = 'InvalidStringValue';
|
|
@@ -52,26 +43,9 @@ const quoteString = value => {
|
|
|
52
43
|
}
|
|
53
44
|
return '"' + value.replace(/["\\]/g, char => '\\' + char) + '"';
|
|
54
45
|
};
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Compiles an input object into a sequence of Buffers representing an IMAP protocol response string.
|
|
58
|
-
* Handles various node types including literals, strings, atoms, sections, sequences, and nested lists.
|
|
59
|
-
*
|
|
60
|
-
* @param {Object} response - The response object to compile.
|
|
61
|
-
* @param {string} [response.tag] - The IMAP command tag (e.g., "*" or a sequence number).
|
|
62
|
-
* @param {string} [response.command] - The IMAP command name.
|
|
63
|
-
* @param {Array|Object} [response.attributes] - The response attributes to compile into IMAP format.
|
|
64
|
-
* @param {Object} [options] - Compilation options.
|
|
65
|
-
* @param {boolean} [options.asArray] - If true, returns an array of Buffers (one per literal segment); otherwise returns a single concatenated Buffer.
|
|
66
|
-
* @param {boolean} [options.isLogging] - If true, redacts sensitive values and truncates long strings/literals for logging purposes.
|
|
67
|
-
* @param {boolean} [options.literalPlus] - If true, uses the LITERAL+ extension (appends "+" to literal length markers).
|
|
68
|
-
* @param {boolean} [options.literalMinus] - If true, uses the LITERAL- extension for literals up to 4096 bytes.
|
|
69
|
-
* @returns {Promise<Buffer[]|Buffer>} A promise that resolves to an array of Buffers (if asArray is true) or a single concatenated Buffer.
|
|
70
|
-
*/
|
|
71
|
-
module.exports = async (response, options) => {
|
|
46
|
+
async function compiler(response, options) {
|
|
72
47
|
let { asArray, isLogging, literalPlus, literalMinus } = options || {};
|
|
73
48
|
const respParts = [];
|
|
74
|
-
|
|
75
49
|
// Formats an entry (string, number or Buffer) into the Buffer that is written to
|
|
76
50
|
// the wire, and is the choke point every emission passes through: a line
|
|
77
51
|
// terminator ends an IMAP command, so no token - the tag and command name
|
|
@@ -88,33 +62,27 @@ module.exports = async (response, options) => {
|
|
|
88
62
|
error.code = 'InvalidTokenValue';
|
|
89
63
|
throw error;
|
|
90
64
|
}
|
|
91
|
-
|
|
92
65
|
if (typeof entry === 'string') {
|
|
93
66
|
return Buffer.from(entry);
|
|
94
67
|
}
|
|
95
|
-
|
|
96
68
|
if (typeof entry === 'number') {
|
|
97
69
|
return Buffer.from(entry.toString());
|
|
98
70
|
}
|
|
99
|
-
|
|
100
71
|
if (Buffer.isBuffer(entry)) {
|
|
101
72
|
return entry;
|
|
102
73
|
}
|
|
103
|
-
|
|
104
74
|
if (returnEmpty) {
|
|
105
75
|
return null;
|
|
106
76
|
}
|
|
107
|
-
|
|
108
77
|
return Buffer.alloc(0);
|
|
109
78
|
};
|
|
110
|
-
|
|
111
|
-
|
|
79
|
+
let resp = []
|
|
80
|
+
.concat(emitEntry(response.tag, { returnEmpty: true }) || [])
|
|
81
|
+
.concat(response.command ? emitEntry(' ' + response.command) : []);
|
|
112
82
|
let val;
|
|
113
83
|
let lastType;
|
|
114
|
-
|
|
115
84
|
let walk = async (node, options) => {
|
|
116
85
|
options = options || {};
|
|
117
|
-
|
|
118
86
|
// Determine whether a space separator is needed before this node.
|
|
119
87
|
// Inspect the last byte written to decide context.
|
|
120
88
|
let lastRespEntry = resp.length && resp[resp.length - 1];
|
|
@@ -122,7 +90,6 @@ module.exports = async (response, options) => {
|
|
|
122
90
|
if (typeof lastRespByte === 'number') {
|
|
123
91
|
lastRespByte = String.fromCharCode(lastRespByte);
|
|
124
92
|
}
|
|
125
|
-
|
|
126
93
|
// Add a space separator when:
|
|
127
94
|
// - The previous token was a LITERAL. Literal data ends exactly at its declared length, so
|
|
128
95
|
// a following token always needs an explicit separator, even though the last written byte
|
|
@@ -136,65 +103,55 @@ module.exports = async (response, options) => {
|
|
|
136
103
|
resp.push(emitEntry(' '));
|
|
137
104
|
}
|
|
138
105
|
}
|
|
139
|
-
|
|
140
106
|
if (node && node.buffer && !Buffer.isBuffer(node)) {
|
|
141
107
|
// mongodb binary
|
|
142
108
|
node = node.buffer;
|
|
143
109
|
}
|
|
144
|
-
|
|
145
110
|
if (Array.isArray(node)) {
|
|
146
111
|
lastType = 'LIST';
|
|
147
112
|
resp.push(emitEntry('('));
|
|
148
|
-
|
|
149
113
|
// check if we need to skip separator WS between two arrays
|
|
150
114
|
let subArray = node.length > 1 && Array.isArray(node[0]);
|
|
151
|
-
|
|
152
115
|
for (let child of node) {
|
|
153
116
|
if (subArray && !Array.isArray(child)) {
|
|
154
117
|
subArray = false;
|
|
155
118
|
}
|
|
156
119
|
await walk(child, { subArray });
|
|
157
120
|
}
|
|
158
|
-
|
|
159
121
|
resp.push(emitEntry(')'));
|
|
160
122
|
return;
|
|
161
123
|
}
|
|
162
|
-
|
|
163
124
|
if (!node && typeof node !== 'string' && typeof node !== 'number' && !Buffer.isBuffer(node)) {
|
|
164
125
|
resp.push(emitEntry('NIL'));
|
|
165
126
|
return;
|
|
166
127
|
}
|
|
167
|
-
|
|
168
128
|
if (typeof node === 'string' || Buffer.isBuffer(node)) {
|
|
169
129
|
if (isLogging && node.length > 100) {
|
|
170
130
|
resp.push(emitEntry('"(* ' + node.length + 'B string *)"'));
|
|
171
|
-
}
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
172
133
|
resp.push(emitEntry(isLogging ? JSON.stringify(node.toString()) : quoteString(node.toString())));
|
|
173
134
|
}
|
|
174
135
|
return;
|
|
175
136
|
}
|
|
176
|
-
|
|
177
137
|
if (typeof node === 'number') {
|
|
178
138
|
resp.push(emitEntry(safeNumber(node))); // Only bounded non-negative integers allowed
|
|
179
139
|
return;
|
|
180
140
|
}
|
|
181
|
-
|
|
182
141
|
lastType = node.type;
|
|
183
|
-
|
|
184
142
|
if (isLogging && node.sensitive) {
|
|
185
143
|
resp.push(emitEntry('"(* value hidden *)"'));
|
|
186
144
|
return;
|
|
187
145
|
}
|
|
188
|
-
|
|
189
146
|
switch (node.type.toUpperCase()) {
|
|
190
147
|
case 'LITERAL':
|
|
191
148
|
if (isLogging) {
|
|
192
149
|
resp.push(emitEntry('"(* ' + node.value.length + 'B literal *)"'));
|
|
193
|
-
}
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
194
152
|
// The literal size marker counts octets - string values are written as
|
|
195
153
|
// UTF-8, so their UTF-16 .length would undercount multi-byte characters
|
|
196
154
|
let literalLength = !node.value ? 0 : Buffer.isBuffer(node.value) ? node.value.length : Buffer.byteLength(node.value.toString());
|
|
197
|
-
|
|
198
155
|
// Append '+' to the size marker only when the extension actually permits a
|
|
199
156
|
// non-synchronizing literal of this size (RFC 7888): LITERAL+ always,
|
|
200
157
|
// LITERAL- only up to 4096 bytes
|
|
@@ -203,16 +160,15 @@ module.exports = async (response, options) => {
|
|
|
203
160
|
// non-synchronizing literals always, and everything in single-buffer mode
|
|
204
161
|
// (asArray false), which has no continuation flow
|
|
205
162
|
let canAppend = !asArray || usePlus;
|
|
206
|
-
|
|
207
163
|
// Emit the literal header: optional '~' prefix for literal8, then {size[+]}\r\n
|
|
208
164
|
resp.push(emitEntry(`${node.isLiteral8 ? '~' : ''}{${literalLength}${usePlus ? '+' : ''}}\r\n`, { raw: true }));
|
|
209
|
-
|
|
210
165
|
if (canAppend) {
|
|
211
166
|
// Literal data follows immediately in the same buffer segment
|
|
212
167
|
if (node.value && node.value.length) {
|
|
213
168
|
resp.push(emitEntry(node.value, { raw: true }));
|
|
214
169
|
}
|
|
215
|
-
}
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
216
172
|
// For synchronizing literals in asArray mode, split output into separate
|
|
217
173
|
// parts. The caller must send each part and wait for a continuation
|
|
218
174
|
// response from the server before sending the next.
|
|
@@ -221,16 +177,15 @@ module.exports = async (response, options) => {
|
|
|
221
177
|
}
|
|
222
178
|
}
|
|
223
179
|
break;
|
|
224
|
-
|
|
225
180
|
case 'STRING':
|
|
226
181
|
if (isLogging && node.value.length > 100) {
|
|
227
182
|
resp.push(emitEntry('"(* ' + node.value.length + 'B string *)"'));
|
|
228
|
-
}
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
229
185
|
val = (node.value || '').toString();
|
|
230
186
|
resp.push(emitEntry(isLogging ? JSON.stringify(val) : quoteString(val)));
|
|
231
187
|
}
|
|
232
188
|
break;
|
|
233
|
-
|
|
234
189
|
case 'SEQUENCE':
|
|
235
190
|
// Sequence sets are written verbatim - they are the one token type with
|
|
236
191
|
// no quoting to fall back on. Callers build them from user-supplied
|
|
@@ -255,7 +210,6 @@ module.exports = async (response, options) => {
|
|
|
255
210
|
resp.push(emitEntry(node.value, { raw: true }));
|
|
256
211
|
}
|
|
257
212
|
break;
|
|
258
|
-
|
|
259
213
|
case 'TEXT':
|
|
260
214
|
// Response text is written verbatim. Only the parser produces it today, for
|
|
261
215
|
// incoming lines, so this is a re-encoding path rather than a command-building
|
|
@@ -270,18 +224,15 @@ module.exports = async (response, options) => {
|
|
|
270
224
|
resp.push(emitEntry(node.value));
|
|
271
225
|
}
|
|
272
226
|
break;
|
|
273
|
-
|
|
274
227
|
case 'NUMBER':
|
|
275
228
|
// Coerced rather than written through: formatRespEntry passes a string or
|
|
276
229
|
// Buffer straight to the wire, so a numeric token carrying a string value
|
|
277
230
|
// would be another verbatim channel
|
|
278
231
|
resp.push(emitEntry(safeNumber(node.value)));
|
|
279
232
|
break;
|
|
280
|
-
|
|
281
233
|
case 'ATOM':
|
|
282
234
|
case 'SECTION':
|
|
283
235
|
val = (node.value || '').toString();
|
|
284
|
-
|
|
285
236
|
if (!node.section || val) {
|
|
286
237
|
// Verify the value contains only valid ATOM-CHAR characters.
|
|
287
238
|
// Strip a leading backslash before checking (system flags like \Seen start with '\').
|
|
@@ -290,19 +241,15 @@ module.exports = async (response, options) => {
|
|
|
290
241
|
if (node.value === '' || imapFormalSyntax.verify(val.charAt(0) === '\\' ? val.substr(1) : val, imapFormalSyntax['ATOM-CHAR']()) >= 0) {
|
|
291
242
|
val = isLogging ? JSON.stringify(val) : quoteString(val);
|
|
292
243
|
}
|
|
293
|
-
|
|
294
244
|
resp.push(emitEntry(val));
|
|
295
245
|
}
|
|
296
|
-
|
|
297
246
|
// Section bracket handling: emit [section-contents] after the ATOM value
|
|
298
247
|
// e.g., BODY[HEADER.FIELDS (Subject)] or BODY[1.MIME]
|
|
299
248
|
if (node.section) {
|
|
300
249
|
resp.push(emitEntry('['));
|
|
301
|
-
|
|
302
250
|
for (let child of node.section) {
|
|
303
251
|
await walk(child);
|
|
304
252
|
}
|
|
305
|
-
|
|
306
253
|
resp.push(emitEntry(']'));
|
|
307
254
|
}
|
|
308
255
|
// Partial range: emit <origin.length> after the section brackets. Coerced
|
|
@@ -316,21 +263,16 @@ module.exports = async (response, options) => {
|
|
|
316
263
|
break;
|
|
317
264
|
}
|
|
318
265
|
};
|
|
319
|
-
|
|
320
266
|
if (response.attributes) {
|
|
321
267
|
let attributes = Array.isArray(response.attributes) ? response.attributes : [].concat(response.attributes);
|
|
322
268
|
for (let child of attributes) {
|
|
323
269
|
await walk(child);
|
|
324
270
|
}
|
|
325
271
|
}
|
|
326
|
-
|
|
327
272
|
if (resp.length) {
|
|
328
273
|
respParts.push(resp);
|
|
329
274
|
}
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
return asArray ? respParts : respParts.flatMap(entry => entry);
|
|
336
|
-
};
|
|
275
|
+
const compiled = respParts.map(part => Buffer.concat(part));
|
|
276
|
+
return asArray ? compiled : compiled.flatMap(entry => entry);
|
|
277
|
+
}
|
|
278
|
+
export default compiler;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Verifies that every character in the given string is within the set of allowed characters.
|
|
3
|
+
*
|
|
4
|
+
* @param str - The string to validate.
|
|
5
|
+
* @param allowedChars - A string containing all allowed characters.
|
|
6
|
+
* @returns The index of the first disallowed character, or -1 if all characters are valid.
|
|
7
|
+
*/
|
|
8
|
+
declare function verify(str: string, allowedChars: string): number;
|
|
9
|
+
declare const imapFormalSyntax: {
|
|
10
|
+
CHAR: () => string;
|
|
11
|
+
CHAR8: () => string;
|
|
12
|
+
SP: () => string;
|
|
13
|
+
CTL: () => string;
|
|
14
|
+
DQUOTE: () => string;
|
|
15
|
+
ALPHA: () => string;
|
|
16
|
+
DIGIT: () => string;
|
|
17
|
+
'ATOM-CHAR': () => string;
|
|
18
|
+
'ASTRING-CHAR': () => string;
|
|
19
|
+
'TEXT-CHAR': () => string;
|
|
20
|
+
'atom-specials': () => string;
|
|
21
|
+
'list-wildcards': () => string;
|
|
22
|
+
'quoted-specials': () => string;
|
|
23
|
+
'resp-specials': () => string;
|
|
24
|
+
tag: () => string;
|
|
25
|
+
command: () => string;
|
|
26
|
+
verify: typeof verify;
|
|
27
|
+
};
|
|
28
|
+
export default imapFormalSyntax;
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/* eslint new-cap: 0, no-useless-concat: 0 */
|
|
2
|
+
/**
|
|
3
|
+
* Defines the IMAP formal syntax character classes and validation rules as specified
|
|
4
|
+
* in RFC 3501 Section 9 (http://tools.ietf.org/html/rfc3501#section-9).
|
|
5
|
+
*
|
|
6
|
+
* Each exported method returns a string of allowed characters for a given IMAP grammar
|
|
7
|
+
* production rule (e.g., ATOM-CHAR, ASTRING-CHAR, TEXT-CHAR). Results are computed once,
|
|
8
|
+
* on first use, and cached at module level.
|
|
9
|
+
*
|
|
10
|
+
* Also exports a `verify` function for validating strings against a set of allowed characters.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Generates a string containing all characters in the given Unicode code point range (inclusive).
|
|
14
|
+
*
|
|
15
|
+
* @param start - The starting character code point.
|
|
16
|
+
* @param end - The ending character code point.
|
|
17
|
+
* @returns A string containing all characters from start to end.
|
|
18
|
+
*/
|
|
19
|
+
function expandRange(start, end) {
|
|
20
|
+
let chars = [];
|
|
21
|
+
for (let i = start; i <= end; i++) {
|
|
22
|
+
chars.push(i);
|
|
23
|
+
}
|
|
24
|
+
return String.fromCharCode(...chars);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Returns a new string with all characters from the exclude string removed from the source string.
|
|
28
|
+
*
|
|
29
|
+
* @param source - The source string to filter.
|
|
30
|
+
* @param exclude - A string of characters to exclude from the source.
|
|
31
|
+
* @returns The source string with excluded characters removed.
|
|
32
|
+
*/
|
|
33
|
+
function excludeChars(source, exclude) {
|
|
34
|
+
return Array.prototype.filter.call(source, (ch) => exclude.indexOf(ch) < 0).join('');
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Wraps a computation so that it runs once, on first call, and the result is reused afterwards.
|
|
38
|
+
*
|
|
39
|
+
* @param compute - Produces the character set.
|
|
40
|
+
* @returns A zero-argument function returning the cached character set.
|
|
41
|
+
*/
|
|
42
|
+
function memo(compute) {
|
|
43
|
+
let value = null;
|
|
44
|
+
return () => {
|
|
45
|
+
if (value === null) {
|
|
46
|
+
value = compute();
|
|
47
|
+
}
|
|
48
|
+
return value;
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/** All 7-bit US-ASCII characters excluding NUL (0x01-0x7F). */
|
|
52
|
+
const CHAR = memo(() => expandRange(0x01, 0x7f));
|
|
53
|
+
/** All 8-bit characters excluding NUL (0x01-0xFF). */
|
|
54
|
+
const CHAR8 = memo(() => expandRange(0x01, 0xff));
|
|
55
|
+
/** The space character (0x20). */
|
|
56
|
+
const SP = () => ' ';
|
|
57
|
+
/** All control characters (0x00-0x1F and 0x7F). */
|
|
58
|
+
const CTL = memo(() => expandRange(0x00, 0x1f) + '\x7F');
|
|
59
|
+
/** The double-quote character. */
|
|
60
|
+
const DQUOTE = () => '"';
|
|
61
|
+
/** All uppercase and lowercase ASCII alphabetic characters (A-Z, a-z). */
|
|
62
|
+
const ALPHA = memo(() => expandRange(0x41, 0x5a) + expandRange(0x61, 0x7a));
|
|
63
|
+
/** All ASCII digit characters (0-9). */
|
|
64
|
+
const DIGIT = memo(() => expandRange(0x30, 0x39));
|
|
65
|
+
/** The LIST wildcard characters ("%" and "*"). */
|
|
66
|
+
const listWildcards = () => '%' + '*';
|
|
67
|
+
/** Characters that are special inside quoted strings (DQUOTE and backslash). */
|
|
68
|
+
const quotedSpecials = memo(() => DQUOTE() + '\\');
|
|
69
|
+
/** The response-special character ("]"). */
|
|
70
|
+
const respSpecials = () => ']';
|
|
71
|
+
/** Characters that are special in ATOMs and must be excluded: "(", ")", "{", SP, CTL, list-wildcards, quoted-specials, resp-specials. */
|
|
72
|
+
const atomSpecials = memo(() => '(' + ')' + '{' + SP() + CTL() + listWildcards() + quotedSpecials() + respSpecials());
|
|
73
|
+
/** Characters allowed in an IMAP ATOM (CHAR minus atom-specials). */
|
|
74
|
+
const atomChar = memo(() => excludeChars(CHAR(), atomSpecials()));
|
|
75
|
+
/** Characters allowed in an IMAP ASTRING (ATOM-CHAR plus resp-specials). */
|
|
76
|
+
const astringChar = memo(() => atomChar() + respSpecials());
|
|
77
|
+
/** Characters allowed in IMAP text (CHAR minus CR and LF). */
|
|
78
|
+
const textChar = memo(() => excludeChars(CHAR(), '\r\n'));
|
|
79
|
+
/** Characters allowed in an IMAP tag (ASTRING-CHAR minus "+"). */
|
|
80
|
+
const tag = memo(() => excludeChars(astringChar(), '+'));
|
|
81
|
+
/** Characters allowed in an IMAP command name (ALPHA, DIGIT, and hyphen). */
|
|
82
|
+
const command = memo(() => ALPHA() + DIGIT() + '-');
|
|
83
|
+
/**
|
|
84
|
+
* Verifies that every character in the given string is within the set of allowed characters.
|
|
85
|
+
*
|
|
86
|
+
* @param str - The string to validate.
|
|
87
|
+
* @param allowedChars - A string containing all allowed characters.
|
|
88
|
+
* @returns The index of the first disallowed character, or -1 if all characters are valid.
|
|
89
|
+
*/
|
|
90
|
+
function verify(str, allowedChars) {
|
|
91
|
+
for (let i = 0, len = str.length; i < len; i++) {
|
|
92
|
+
if (allowedChars.indexOf(str.charAt(i)) < 0) {
|
|
93
|
+
return i;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return -1;
|
|
97
|
+
}
|
|
98
|
+
const imapFormalSyntax = {
|
|
99
|
+
CHAR,
|
|
100
|
+
CHAR8,
|
|
101
|
+
SP,
|
|
102
|
+
CTL,
|
|
103
|
+
DQUOTE,
|
|
104
|
+
ALPHA,
|
|
105
|
+
DIGIT,
|
|
106
|
+
'ATOM-CHAR': atomChar,
|
|
107
|
+
'ASTRING-CHAR': astringChar,
|
|
108
|
+
'TEXT-CHAR': textChar,
|
|
109
|
+
'atom-specials': atomSpecials,
|
|
110
|
+
'list-wildcards': listWildcards,
|
|
111
|
+
'quoted-specials': quotedSpecials,
|
|
112
|
+
'resp-specials': respSpecials,
|
|
113
|
+
tag,
|
|
114
|
+
command,
|
|
115
|
+
verify
|
|
116
|
+
};
|
|
117
|
+
export default imapFormalSyntax;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import parser from './imap-parser.js';
|
|
2
|
+
import compiler from './imap-compiler.js';
|
|
3
|
+
/**
|
|
4
|
+
* Re-exports the IMAP protocol parser and compiler as a single module.
|
|
5
|
+
*
|
|
6
|
+
* - `parser` parses raw IMAP command/response buffers into structured objects.
|
|
7
|
+
* - `compiler` compiles structured response objects into IMAP protocol Buffers.
|
|
8
|
+
*/
|
|
9
|
+
export { parser, compiler };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import parser from './imap-parser.js';
|
|
2
|
+
import compiler from './imap-compiler.js';
|
|
3
|
+
/**
|
|
4
|
+
* Re-exports the IMAP protocol parser and compiler as a single module.
|
|
5
|
+
*
|
|
6
|
+
* - `parser` parses raw IMAP command/response buffers into structured objects.
|
|
7
|
+
* - `compiler` compiles structured response objects into IMAP protocol Buffers.
|
|
8
|
+
*/
|
|
9
|
+
export { parser, compiler };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ImapResponse, ParserOptions } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Parses a raw IMAP command or response buffer into a structured object.
|
|
4
|
+
* Handles edge cases such as null-byte-padded responses from buggy servers and
|
|
5
|
+
* multi-word commands like UID and AUTHENTICATE.
|
|
6
|
+
*
|
|
7
|
+
* @param command - The raw IMAP command or response data to parse.
|
|
8
|
+
* @param options - Parser options passed through to the underlying ParserInstance and TokenParser.
|
|
9
|
+
* @param options.literalPlus - Whether the LITERAL+ extension is in use.
|
|
10
|
+
* @param options.literals - Pre-parsed literal values extracted from the input stream.
|
|
11
|
+
* @returns A promise that resolves to a parsed response object with `tag` (the IMAP tag, e.g. "*",
|
|
12
|
+
* "+", or a command tag like "A1"), `command` (the IMAP command or response name, e.g. "OK",
|
|
13
|
+
* "FETCH"), `attributes` (parsed attributes of the response) and `nullBytesRemoved` (number of
|
|
14
|
+
* leading null bytes removed, if any).
|
|
15
|
+
*/
|
|
16
|
+
export default function parser(command: Buffer | string, options?: ParserOptions | undefined): Promise<ImapResponse>;
|