imapflow 1.7.8 → 2.0.1
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 +520 -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 +3956 -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} +387 -517
- 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 +60 -123
- 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 +769 -1790
- 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
package/test/imap-stream-test.js
DELETED
|
@@ -1,177 +0,0 @@
|
|
|
1
|
-
/*eslint no-unused-expressions: 0, prefer-arrow-callback: 0 */
|
|
2
|
-
|
|
3
|
-
'use strict';
|
|
4
|
-
|
|
5
|
-
const { ImapStream } = require('../lib/handler/imap-stream');
|
|
6
|
-
const { parser } = require('../lib/handler/imap-handler');
|
|
7
|
-
|
|
8
|
-
module.exports['Full input'] = test => {
|
|
9
|
-
let input = Buffer.from(
|
|
10
|
-
`A CAPABILITY
|
|
11
|
-
A LOGIN "aaa" "bbb"
|
|
12
|
-
A APPEND INBOX {5}
|
|
13
|
-
12345
|
|
14
|
-
A LOGIN {5}
|
|
15
|
-
12345 {11}
|
|
16
|
-
12345678901 "another"
|
|
17
|
-
A LOGOUT
|
|
18
|
-
`.replace(/\r?\n/g, '\r\n')
|
|
19
|
-
);
|
|
20
|
-
|
|
21
|
-
let expecting = [
|
|
22
|
-
{ command: 'A CAPABILITY', literals: [] },
|
|
23
|
-
{ command: 'A LOGIN "aaa" "bbb"', literals: [] },
|
|
24
|
-
{ command: 'A APPEND INBOX {5}\r\n', literals: ['12345'] },
|
|
25
|
-
{ command: 'A LOGIN {5}\r\n {11}\r\n "another"', literals: ['12345', '12345678901'] },
|
|
26
|
-
{ command: 'A LOGOUT', literals: [] }
|
|
27
|
-
];
|
|
28
|
-
|
|
29
|
-
let stream = new ImapStream();
|
|
30
|
-
|
|
31
|
-
let reading = false;
|
|
32
|
-
let reader = async () => {
|
|
33
|
-
let cmd;
|
|
34
|
-
while ((cmd = stream.read()) !== null) {
|
|
35
|
-
test.deepEqual({ command: cmd.payload.toString(), literals: cmd.literals.map(literal => literal.toString()) }, expecting.shift());
|
|
36
|
-
|
|
37
|
-
let parsed = await parser(cmd.payload, { literals: cmd.literals });
|
|
38
|
-
console.log(parsed);
|
|
39
|
-
cmd.next();
|
|
40
|
-
}
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
stream.on('readable', () => {
|
|
44
|
-
if (!reading) {
|
|
45
|
-
reading = true;
|
|
46
|
-
reader()
|
|
47
|
-
.catch(err => console.error(err))
|
|
48
|
-
.finally(() => {
|
|
49
|
-
reading = false;
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
stream.on('error', err => {
|
|
55
|
-
test.ifError(err);
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
stream.on('end', () => {
|
|
59
|
-
test.done();
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
let writer = async () => {
|
|
63
|
-
stream.end(input);
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
writer().catch(err => test.ifError(err));
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
module.exports['Literal8 marker activates literal extraction'] = test => {
|
|
70
|
-
// RFC 9051 folds the FETCH side of BINARY into base IMAP4rev2 - servers answer
|
|
71
|
-
// BINARY fetches with literal8 syntax (~{n}) whose content may contain NULs.
|
|
72
|
-
// The stream must treat the trailing {n} as a literal marker regardless of the
|
|
73
|
-
// '~' prefix and hand the raw bytes over unmodified.
|
|
74
|
-
let input = Buffer.from('* 1 FETCH (BINARY[1] ~{5}\r\nhel\x00o)\r\n', 'binary');
|
|
75
|
-
|
|
76
|
-
let stream = new ImapStream();
|
|
77
|
-
|
|
78
|
-
let reading = false;
|
|
79
|
-
let reader = async () => {
|
|
80
|
-
let cmd;
|
|
81
|
-
while ((cmd = stream.read()) !== null) {
|
|
82
|
-
test.equal(cmd.payload.toString('binary'), '* 1 FETCH (BINARY[1] ~{5}\r\n)');
|
|
83
|
-
test.equal(cmd.literals.length, 1);
|
|
84
|
-
test.deepEqual(cmd.literals[0], Buffer.from('hel\x00o', 'binary'));
|
|
85
|
-
|
|
86
|
-
// and the parser consumes the literal8 into a LITERAL node with the NUL intact
|
|
87
|
-
let parsed = await parser(cmd.payload, { literals: cmd.literals });
|
|
88
|
-
test.deepEqual(parsed.attributes[1][1], { type: 'LITERAL', value: Buffer.from('hel\x00o', 'binary') });
|
|
89
|
-
cmd.next();
|
|
90
|
-
}
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
stream.on('readable', () => {
|
|
94
|
-
if (!reading) {
|
|
95
|
-
reading = true;
|
|
96
|
-
reader()
|
|
97
|
-
.catch(err => test.ifError(err))
|
|
98
|
-
.finally(() => {
|
|
99
|
-
reading = false;
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
stream.on('error', err => {
|
|
105
|
-
test.ifError(err);
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
stream.on('end', () => {
|
|
109
|
-
test.done();
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
stream.end(input);
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
module.exports['Single byte'] = test => {
|
|
116
|
-
let input = Buffer.from(
|
|
117
|
-
`A CAPABILITY
|
|
118
|
-
A LOGIN "aaa" "bbb"
|
|
119
|
-
A APPEND INBOX {5}
|
|
120
|
-
12345
|
|
121
|
-
A LOGIN {5}
|
|
122
|
-
12345 {11}
|
|
123
|
-
12345678901 "another"
|
|
124
|
-
A LOGOUT
|
|
125
|
-
`.replace(/\r?\n/g, '\r\n')
|
|
126
|
-
);
|
|
127
|
-
|
|
128
|
-
let expecting = [
|
|
129
|
-
{ command: 'A CAPABILITY', literals: [] },
|
|
130
|
-
{ command: 'A LOGIN "aaa" "bbb"', literals: [] },
|
|
131
|
-
{ command: 'A APPEND INBOX {5}\r\n', literals: ['12345'] },
|
|
132
|
-
{ command: 'A LOGIN {5}\r\n {11}\r\n "another"', literals: ['12345', '12345678901'] },
|
|
133
|
-
{ command: 'A LOGOUT', literals: [] }
|
|
134
|
-
];
|
|
135
|
-
|
|
136
|
-
let stream = new ImapStream();
|
|
137
|
-
|
|
138
|
-
let reading = false;
|
|
139
|
-
let reader = async () => {
|
|
140
|
-
let cmd;
|
|
141
|
-
while ((cmd = stream.read()) !== null) {
|
|
142
|
-
test.deepEqual({ command: cmd.payload.toString(), literals: cmd.literals.map(literal => literal.toString()) }, expecting.shift());
|
|
143
|
-
cmd.next();
|
|
144
|
-
}
|
|
145
|
-
};
|
|
146
|
-
|
|
147
|
-
stream.on('readable', () => {
|
|
148
|
-
if (!reading) {
|
|
149
|
-
reading = true;
|
|
150
|
-
reader()
|
|
151
|
-
.catch(err => console.error(err))
|
|
152
|
-
.finally(() => {
|
|
153
|
-
reading = false;
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
stream.on('error', err => {
|
|
159
|
-
test.ifError(err);
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
stream.on('end', () => {
|
|
163
|
-
test.done();
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
let writer = async () => {
|
|
167
|
-
for (let i = 0; i < input.length; i++) {
|
|
168
|
-
if (stream.write(Buffer.from([input[i]])) === false) {
|
|
169
|
-
await new Promise(resolve => stream.once('drain', resolve));
|
|
170
|
-
}
|
|
171
|
-
await new Promise(resolve => setTimeout(resolve, 10));
|
|
172
|
-
}
|
|
173
|
-
stream.end();
|
|
174
|
-
};
|
|
175
|
-
|
|
176
|
-
writer().catch(err => test.ifError(err));
|
|
177
|
-
};
|
package/test/imapflow-test.js
DELETED
|
@@ -1,258 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const { ImapFlow } = require('../lib/imap-flow');
|
|
4
|
-
|
|
5
|
-
module.exports['Create imapflow instance'] = test => {
|
|
6
|
-
let imapFlow = new ImapFlow();
|
|
7
|
-
test.ok(imapFlow);
|
|
8
|
-
test.done();
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
module.exports['Create imapflow instance with custom logger'] = async test => {
|
|
12
|
-
class CustomLogger {
|
|
13
|
-
constructor() {}
|
|
14
|
-
|
|
15
|
-
debug(obj) {
|
|
16
|
-
console.log(JSON.stringify(obj));
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
info(obj) {
|
|
20
|
-
console.log(JSON.stringify(obj));
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
warn(obj) {
|
|
24
|
-
console.log(JSON.stringify(obj));
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// eslint-disable-next-line no-unused-vars
|
|
28
|
-
error(obj) {
|
|
29
|
-
// we don't actually want to log anything here.
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
let imapFlow = new ImapFlow({
|
|
34
|
-
logger: new CustomLogger()
|
|
35
|
-
});
|
|
36
|
-
test.ok(imapFlow);
|
|
37
|
-
try {
|
|
38
|
-
await imapFlow.connect();
|
|
39
|
-
} catch (ex) {
|
|
40
|
-
// it is PERFECTLY okay to have an exception here. We expect an ECONNREFUSED if an exception occurs.
|
|
41
|
-
test.equal(ex.code, 'ECONNREFUSED');
|
|
42
|
-
}
|
|
43
|
-
test.done();
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
// ---------------------------------------------------------------------------
|
|
47
|
-
// Helpers for testing prototype methods on a mock context
|
|
48
|
-
// ---------------------------------------------------------------------------
|
|
49
|
-
const createFetchContext = runOverride => ({
|
|
50
|
-
id: 'test-cid',
|
|
51
|
-
mailbox: { path: 'INBOX', exists: 10 },
|
|
52
|
-
isClosed: false,
|
|
53
|
-
socket: { destroyed: false },
|
|
54
|
-
resolveRange: async range => range,
|
|
55
|
-
createConnectionError: ImapFlow.prototype.createConnectionError,
|
|
56
|
-
run: runOverride
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
function callFetch(ctx, range, query, options) {
|
|
60
|
-
return ImapFlow.prototype.fetch.call(ctx, range, query, options);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// ---------------------------------------------------------------------------
|
|
64
|
-
// setFlagColor tests
|
|
65
|
-
// ---------------------------------------------------------------------------
|
|
66
|
-
|
|
67
|
-
module.exports['setFlagColor with valid color calls STORE for add and remove'] = async test => {
|
|
68
|
-
let storeCalls = [];
|
|
69
|
-
const ctx = {
|
|
70
|
-
resolveRange: async range => range,
|
|
71
|
-
run: async (command, range, flags, opts) => {
|
|
72
|
-
storeCalls.push({ command, range, flags, operation: opts.operation });
|
|
73
|
-
return true;
|
|
74
|
-
}
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
let result = await ImapFlow.prototype.setFlagColor.call(ctx, '1:*', 'orange');
|
|
78
|
-
|
|
79
|
-
test.equal(storeCalls.length, 2, 'run should be called twice');
|
|
80
|
-
|
|
81
|
-
test.equal(storeCalls[0].command, 'STORE');
|
|
82
|
-
test.equal(storeCalls[0].operation, 'add');
|
|
83
|
-
test.ok(storeCalls[0].flags.indexOf('\\Flagged') >= 0, 'add flags should include \\Flagged');
|
|
84
|
-
test.ok(storeCalls[0].flags.indexOf('$MailFlagBit0') >= 0, 'add flags should include $MailFlagBit0');
|
|
85
|
-
|
|
86
|
-
test.equal(storeCalls[1].command, 'STORE');
|
|
87
|
-
test.equal(storeCalls[1].operation, 'remove');
|
|
88
|
-
test.ok(storeCalls[1].flags.indexOf('$MailFlagBit1') >= 0, 'remove flags should include $MailFlagBit1');
|
|
89
|
-
test.ok(storeCalls[1].flags.indexOf('$MailFlagBit2') >= 0, 'remove flags should include $MailFlagBit2');
|
|
90
|
-
|
|
91
|
-
test.ok(result, 'should return truthy result');
|
|
92
|
-
test.done();
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
module.exports['setFlagColor with invalid color returns false'] = async test => {
|
|
96
|
-
let runCalled = false;
|
|
97
|
-
const ctx = {
|
|
98
|
-
resolveRange: async range => range,
|
|
99
|
-
run: async () => {
|
|
100
|
-
runCalled = true;
|
|
101
|
-
}
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
let result = await ImapFlow.prototype.setFlagColor.call(ctx, '1:*', 'invalid');
|
|
105
|
-
|
|
106
|
-
test.strictEqual(result, false, 'should return false for invalid color');
|
|
107
|
-
test.ok(!runCalled, 'run should not be called');
|
|
108
|
-
test.done();
|
|
109
|
-
};
|
|
110
|
-
|
|
111
|
-
module.exports['setFlagColor with empty range returns false'] = async test => {
|
|
112
|
-
let runCalled = false;
|
|
113
|
-
const ctx = {
|
|
114
|
-
resolveRange: async () => null,
|
|
115
|
-
run: async () => {
|
|
116
|
-
runCalled = true;
|
|
117
|
-
}
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
let result = await ImapFlow.prototype.setFlagColor.call(ctx, '1:*', 'orange');
|
|
121
|
-
|
|
122
|
-
test.strictEqual(result, false, 'should return false for empty range');
|
|
123
|
-
test.ok(!runCalled, 'run should not be called');
|
|
124
|
-
test.done();
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
module.exports['setFlagColor with red calls STORE with Flagged in add'] = async test => {
|
|
128
|
-
let storeCalls = [];
|
|
129
|
-
const ctx = {
|
|
130
|
-
resolveRange: async range => range,
|
|
131
|
-
run: async (command, range, flags, opts) => {
|
|
132
|
-
storeCalls.push({ command, range, flags, operation: opts.operation });
|
|
133
|
-
return true;
|
|
134
|
-
}
|
|
135
|
-
};
|
|
136
|
-
|
|
137
|
-
let result = await ImapFlow.prototype.setFlagColor.call(ctx, '1:*', 'red');
|
|
138
|
-
|
|
139
|
-
// red is index 0 (all bits zero), so add should contain only \Flagged,
|
|
140
|
-
// and remove should contain all three MailFlagBit flags
|
|
141
|
-
test.ok(storeCalls.length >= 1, 'run should be called at least once');
|
|
142
|
-
|
|
143
|
-
let addCall = storeCalls.find(c => c.operation === 'add');
|
|
144
|
-
test.ok(addCall, 'should have an add operation');
|
|
145
|
-
test.ok(addCall.flags.indexOf('\\Flagged') >= 0, 'add flags should include \\Flagged for red');
|
|
146
|
-
|
|
147
|
-
let removeCall = storeCalls.find(c => c.operation === 'remove');
|
|
148
|
-
test.ok(removeCall, 'should have a remove operation');
|
|
149
|
-
test.ok(removeCall.flags.indexOf('$MailFlagBit0') >= 0, 'remove flags should include $MailFlagBit0');
|
|
150
|
-
test.ok(removeCall.flags.indexOf('$MailFlagBit1') >= 0, 'remove flags should include $MailFlagBit1');
|
|
151
|
-
test.ok(removeCall.flags.indexOf('$MailFlagBit2') >= 0, 'remove flags should include $MailFlagBit2');
|
|
152
|
-
|
|
153
|
-
test.ok(result, 'should return truthy result');
|
|
154
|
-
test.done();
|
|
155
|
-
};
|
|
156
|
-
|
|
157
|
-
// ---------------------------------------------------------------------------
|
|
158
|
-
// status test
|
|
159
|
-
// ---------------------------------------------------------------------------
|
|
160
|
-
|
|
161
|
-
module.exports['status delegates to run with STATUS command'] = async test => {
|
|
162
|
-
let runArgs = null;
|
|
163
|
-
const ctx = {
|
|
164
|
-
run: async (...args) => {
|
|
165
|
-
runArgs = args;
|
|
166
|
-
return { messages: 10, unseen: 3 };
|
|
167
|
-
}
|
|
168
|
-
};
|
|
169
|
-
|
|
170
|
-
let result = await ImapFlow.prototype.status.call(ctx, 'INBOX', { unseen: true });
|
|
171
|
-
|
|
172
|
-
test.deepEqual(runArgs, ['STATUS', 'INBOX', { unseen: true }], 'run should be called with correct arguments');
|
|
173
|
-
test.deepEqual(result, { messages: 10, unseen: 3 }, 'should return the result from run');
|
|
174
|
-
test.done();
|
|
175
|
-
};
|
|
176
|
-
|
|
177
|
-
// ---------------------------------------------------------------------------
|
|
178
|
-
// getQuota tests
|
|
179
|
-
// ---------------------------------------------------------------------------
|
|
180
|
-
|
|
181
|
-
module.exports['getQuota delegates to run with QUOTA command'] = async test => {
|
|
182
|
-
let runArgs = null;
|
|
183
|
-
const ctx = {
|
|
184
|
-
run: async (...args) => {
|
|
185
|
-
runArgs = args;
|
|
186
|
-
return { storage: { usage: 1024, limit: 10240 } };
|
|
187
|
-
}
|
|
188
|
-
};
|
|
189
|
-
|
|
190
|
-
let result = await ImapFlow.prototype.getQuota.call(ctx, 'Sent');
|
|
191
|
-
|
|
192
|
-
test.deepEqual(runArgs, ['QUOTA', 'Sent'], 'run should be called with correct arguments');
|
|
193
|
-
test.ok(result.storage, 'should return quota result');
|
|
194
|
-
test.done();
|
|
195
|
-
};
|
|
196
|
-
|
|
197
|
-
module.exports['getQuota defaults path to INBOX'] = async test => {
|
|
198
|
-
let runArgs = null;
|
|
199
|
-
const ctx = {
|
|
200
|
-
run: async (...args) => {
|
|
201
|
-
runArgs = args;
|
|
202
|
-
return { storage: { usage: 512, limit: 10240 } };
|
|
203
|
-
}
|
|
204
|
-
};
|
|
205
|
-
|
|
206
|
-
let result = await ImapFlow.prototype.getQuota.call(ctx);
|
|
207
|
-
|
|
208
|
-
test.deepEqual(runArgs, ['QUOTA', 'INBOX'], 'run should be called with INBOX as default path');
|
|
209
|
-
test.ok(result.storage, 'should return quota result');
|
|
210
|
-
test.done();
|
|
211
|
-
};
|
|
212
|
-
|
|
213
|
-
// ---------------------------------------------------------------------------
|
|
214
|
-
// fetch generator: error propagation tests
|
|
215
|
-
// ---------------------------------------------------------------------------
|
|
216
|
-
|
|
217
|
-
module.exports['fetch generator: run error propagates to consumer'] = async test => {
|
|
218
|
-
const run = async () => {
|
|
219
|
-
throw new Error('FETCH failed');
|
|
220
|
-
};
|
|
221
|
-
const ctx = createFetchContext(run);
|
|
222
|
-
let caught = false;
|
|
223
|
-
try {
|
|
224
|
-
// eslint-disable-next-line no-unused-vars
|
|
225
|
-
for await (let msg of callFetch(ctx, '1:5', { uid: true })) {
|
|
226
|
-
// should not reach here
|
|
227
|
-
}
|
|
228
|
-
} catch (err) {
|
|
229
|
-
caught = true;
|
|
230
|
-
test.equal(err.message, 'FETCH failed');
|
|
231
|
-
}
|
|
232
|
-
test.ok(caught, 'error was caught');
|
|
233
|
-
test.done();
|
|
234
|
-
};
|
|
235
|
-
|
|
236
|
-
module.exports['fetch generator: socket destroyed mid-iteration throws EConnectionClosed'] = async test => {
|
|
237
|
-
const run = async (cmd, range, query, opts) => {
|
|
238
|
-
for (let i = 0; i < 3; i++) {
|
|
239
|
-
await new Promise(resolve => {
|
|
240
|
-
opts.onUntaggedFetch({ seq: i + 1, uid: 400 + i }, () => resolve());
|
|
241
|
-
});
|
|
242
|
-
}
|
|
243
|
-
};
|
|
244
|
-
const ctx = createFetchContext(run);
|
|
245
|
-
let caught = false;
|
|
246
|
-
try {
|
|
247
|
-
for await (let msg of callFetch(ctx, '1:3', { uid: true })) {
|
|
248
|
-
if (msg.seq === 1) {
|
|
249
|
-
ctx.socket.destroyed = true;
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
} catch (err) {
|
|
253
|
-
caught = true;
|
|
254
|
-
test.equal(err.code, 'EConnectionClosed');
|
|
255
|
-
}
|
|
256
|
-
test.ok(caught, 'error was caught');
|
|
257
|
-
test.done();
|
|
258
|
-
};
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
# Live IMAP4rev2 integration tests
|
|
2
|
-
|
|
3
|
-
Runs the ImapFlow client against a real IMAP4rev2 server - Dovecot 2.4+ in
|
|
4
|
-
Docker - instead of protocol mocks. Covers the ENABLE IMAP4rev2 negotiation,
|
|
5
|
-
LIST RETURN (SUBSCRIBED) without LSUB, subscription round-trips, UTF-8 mailbox
|
|
6
|
-
names, inline LIST-STATUS (including the rev2 SIZE and DELETED status items),
|
|
7
|
-
ESEARCH responses to plain SEARCH, STATUS SIZE/DELETED, the rev2-shaped SELECT
|
|
8
|
-
response (untagged LIST, CLOSED on re-select), the folded-in FETCH BINARY,
|
|
9
|
-
COPYUID from the untagged OK on MOVE, and a message lifecycle smoke test with
|
|
10
|
-
UID EXPUNGE.
|
|
11
|
-
|
|
12
|
-
## Running
|
|
13
|
-
|
|
14
|
-
```
|
|
15
|
-
npm run test:rev2
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
Requires Docker. The script pulls `dovecot/dovecot:2.4.4`, starts a container
|
|
19
|
-
with the drop-in config from `dovecot-test.conf`, waits for the IMAP greeting
|
|
20
|
-
on `127.0.0.1:31143`, runs `rev2-live-test.js` with nodeunit, and always
|
|
21
|
-
removes the container afterwards.
|
|
22
|
-
|
|
23
|
-
These tests are intentionally not part of `npm test` - the Gruntfile nodeunit
|
|
24
|
-
config excludes `test/integration/**`, so plain test runs stay Docker-free.
|
|
25
|
-
CI runs this suite in a dedicated `test-rev2` job on `ubuntu-latest` (amd64
|
|
26
|
-
with Docker preinstalled), forcing `IMAPFLOW_DOVECOT_PLATFORM=linux/amd64` -
|
|
27
|
-
that job is the authoritative linux/amd64 run, since Apple Silicon machines
|
|
28
|
-
cannot execute the amd64 image (see below).
|
|
29
|
-
|
|
30
|
-
If a local image for the configured tag exists but was pulled for a different
|
|
31
|
-
architecture than the Docker host (e.g. an amd64 image left behind on an arm64
|
|
32
|
-
host), the runner script detects the mismatch and re-pulls the host-native
|
|
33
|
-
variant before starting the container.
|
|
34
|
-
|
|
35
|
-
## Environment overrides
|
|
36
|
-
|
|
37
|
-
- `IMAPFLOW_DOVECOT_IMAGE` - image to run (default `dovecot/dovecot:2.4.4`;
|
|
38
|
-
any 2.4.2+ tag supports IMAP4rev2)
|
|
39
|
-
- `IMAPFLOW_DOVECOT_PLATFORM` - e.g. `linux/amd64`; defaults to the host
|
|
40
|
-
platform. Forcing `linux/amd64` on Apple Silicon does not work - Rosetta
|
|
41
|
-
cannot start Dovecot's privilege-separated login processes
|
|
42
|
-
(`rosetta error: mmap_anonymous_rw mmap failed`; reconfirmed with
|
|
43
|
-
dovecot/dovecot:2.4.4 in July 2026)
|
|
44
|
-
- `IMAPFLOW_TEST_PORT` - host port to publish (default 31143)
|
|
45
|
-
|
|
46
|
-
## Test account model
|
|
47
|
-
|
|
48
|
-
The container uses Dovecot's static passdb (`USER_PASSWORD=pass`): any
|
|
49
|
-
username authenticates with the password `pass` and gets its own empty mail
|
|
50
|
-
home, so every test connects as a brand-new user and needs no cleanup between
|
|
51
|
-
runs. Special-use mailboxes (Sent/Drafts/Junk/Trash) are auto-created and
|
|
52
|
-
subscribed via `dovecot-test.conf`.
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
# Drop-in configuration for the ImapFlow IMAP4rev2 integration test container.
|
|
2
|
-
# Mounted into /etc/dovecot/conf.d/ of the dovecot/dovecot:2.4.x image, which
|
|
3
|
-
# already ships with imap4rev2_enable = yes and mail_utf8_extensions = yes.
|
|
4
|
-
|
|
5
|
-
# The test client connects over plain TCP on the container-internal IMAP port
|
|
6
|
-
# (31143), so cleartext logins must be allowed
|
|
7
|
-
auth_allow_cleartext = yes
|
|
8
|
-
|
|
9
|
-
# Special-use mailboxes, auto-created and subscribed for every test user
|
|
10
|
-
namespace inbox {
|
|
11
|
-
mailbox Sent {
|
|
12
|
-
special_use = \Sent
|
|
13
|
-
auto = subscribe
|
|
14
|
-
}
|
|
15
|
-
mailbox Drafts {
|
|
16
|
-
special_use = \Drafts
|
|
17
|
-
auto = subscribe
|
|
18
|
-
}
|
|
19
|
-
mailbox Junk {
|
|
20
|
-
special_use = \Junk
|
|
21
|
-
auto = subscribe
|
|
22
|
-
}
|
|
23
|
-
mailbox Trash {
|
|
24
|
-
special_use = \Trash
|
|
25
|
-
auto = subscribe
|
|
26
|
-
}
|
|
27
|
-
}
|