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
package/test/memory-leak-test.js
DELETED
|
@@ -1,667 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Tests for memory leak detection in socket, TLS, and event handling
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
const net = require('net');
|
|
8
|
-
const zlib = require('zlib');
|
|
9
|
-
const { PassThrough } = require('stream');
|
|
10
|
-
const { ImapFlow } = require('../lib/imap-flow');
|
|
11
|
-
|
|
12
|
-
// Helper to get listener counts for key objects
|
|
13
|
-
function getListenerReport(client) {
|
|
14
|
-
return {
|
|
15
|
-
streamer: client.streamer
|
|
16
|
-
? {
|
|
17
|
-
error: client.streamer.listenerCount('error'),
|
|
18
|
-
readable: client.streamer.listenerCount('readable')
|
|
19
|
-
}
|
|
20
|
-
: null,
|
|
21
|
-
socket: client.socket
|
|
22
|
-
? {
|
|
23
|
-
error: client.socket.listenerCount('error'),
|
|
24
|
-
close: client.socket.listenerCount('close'),
|
|
25
|
-
end: client.socket.listenerCount('end'),
|
|
26
|
-
timeout: client.socket.listenerCount('timeout'),
|
|
27
|
-
tlsClientError: client.socket.listenerCount('tlsClientError')
|
|
28
|
-
}
|
|
29
|
-
: null,
|
|
30
|
-
client: {
|
|
31
|
-
error: client.listenerCount('error'),
|
|
32
|
-
close: client.listenerCount('close')
|
|
33
|
-
},
|
|
34
|
-
inflate: client._inflate ? client._inflate.listenerCount('error') : 0,
|
|
35
|
-
deflate: client._deflate ? client._deflate.listenerCount('error') : 0
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// Helper to measure memory (requires --expose-gc flag)
|
|
40
|
-
// Usage: node --expose-gc -e "require('./test/memory-leak-test.js')"
|
|
41
|
-
// eslint-disable-next-line no-unused-vars
|
|
42
|
-
async function measureMemory(label) {
|
|
43
|
-
if (global.gc) {
|
|
44
|
-
global.gc();
|
|
45
|
-
await new Promise(r => setTimeout(r, 50));
|
|
46
|
-
global.gc();
|
|
47
|
-
}
|
|
48
|
-
const mem = process.memoryUsage();
|
|
49
|
-
return {
|
|
50
|
-
label,
|
|
51
|
-
heapUsedMB: Math.round((mem.heapUsed / 1024 / 1024) * 100) / 100,
|
|
52
|
-
externalMB: Math.round((mem.external / 1024 / 1024) * 100) / 100
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// Create a simple mock IMAP server for testing
|
|
57
|
-
function createMockServer() {
|
|
58
|
-
const server = net.createServer(socket => {
|
|
59
|
-
// Send greeting
|
|
60
|
-
socket.write('* OK Mock IMAP Server ready\r\n');
|
|
61
|
-
|
|
62
|
-
socket.on('data', data => {
|
|
63
|
-
const line = data.toString().trim();
|
|
64
|
-
const parts = line.split(' ');
|
|
65
|
-
const tag = parts[0];
|
|
66
|
-
const command = parts[1] ? parts[1].toUpperCase() : '';
|
|
67
|
-
|
|
68
|
-
if (command === 'CAPABILITY') {
|
|
69
|
-
socket.write('* CAPABILITY IMAP4rev1 AUTH=PLAIN\r\n');
|
|
70
|
-
socket.write(`${tag} OK CAPABILITY completed\r\n`);
|
|
71
|
-
} else if (command === 'LOGIN') {
|
|
72
|
-
socket.write(`${tag} OK LOGIN completed\r\n`);
|
|
73
|
-
} else if (command === 'LOGOUT') {
|
|
74
|
-
socket.write('* BYE Server logging out\r\n');
|
|
75
|
-
socket.write(`${tag} OK LOGOUT completed\r\n`);
|
|
76
|
-
socket.end();
|
|
77
|
-
} else if (command === 'NOOP') {
|
|
78
|
-
socket.write(`${tag} OK NOOP completed\r\n`);
|
|
79
|
-
} else if (command === 'NAMESPACE') {
|
|
80
|
-
socket.write('* NAMESPACE (("" "/")) NIL NIL\r\n');
|
|
81
|
-
socket.write(`${tag} OK NAMESPACE completed\r\n`);
|
|
82
|
-
} else if (command === 'COMPRESS') {
|
|
83
|
-
socket.write(`${tag} NO COMPRESS not supported\r\n`);
|
|
84
|
-
} else if (command === 'ENABLE') {
|
|
85
|
-
socket.write(`${tag} OK ENABLE completed\r\n`);
|
|
86
|
-
} else if (command === 'ID') {
|
|
87
|
-
socket.write('* ID NIL\r\n');
|
|
88
|
-
socket.write(`${tag} OK ID completed\r\n`);
|
|
89
|
-
} else if (tag && command) {
|
|
90
|
-
socket.write(`${tag} OK Command completed\r\n`);
|
|
91
|
-
}
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
socket.on('error', () => {
|
|
95
|
-
// Ignore errors
|
|
96
|
-
});
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
return server;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
exports['Memory Leak Tests'] = {
|
|
103
|
-
'should not leak listeners after multiple client creations without connect'(test) {
|
|
104
|
-
const clients = [];
|
|
105
|
-
|
|
106
|
-
// Create multiple clients without connecting
|
|
107
|
-
for (let i = 0; i < 50; i++) {
|
|
108
|
-
const client = new ImapFlow({
|
|
109
|
-
host: '127.0.0.1',
|
|
110
|
-
port: 1,
|
|
111
|
-
secure: false,
|
|
112
|
-
logger: false
|
|
113
|
-
});
|
|
114
|
-
clients.push(client);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// Close all clients
|
|
118
|
-
for (const client of clients) {
|
|
119
|
-
client.close();
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
// Verify all clients are cleaned up
|
|
123
|
-
for (const client of clients) {
|
|
124
|
-
const report = getListenerReport(client);
|
|
125
|
-
test.equal(report.streamer.error, 0, 'streamer error listeners should be 0');
|
|
126
|
-
test.equal(report.streamer.readable, 0, 'streamer readable listeners should be 0');
|
|
127
|
-
test.ok(client.isClosed, 'client should be closed');
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
test.done();
|
|
131
|
-
},
|
|
132
|
-
|
|
133
|
-
'should clean up listeners after connect error'(test) {
|
|
134
|
-
test.expect(4);
|
|
135
|
-
|
|
136
|
-
const client = new ImapFlow({
|
|
137
|
-
host: '127.0.0.1',
|
|
138
|
-
port: 1, // Invalid port, will fail to connect
|
|
139
|
-
secure: false,
|
|
140
|
-
logger: false,
|
|
141
|
-
connectionTimeout: 500
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
client.connect().catch(() => {
|
|
145
|
-
// Wait a bit for cleanup
|
|
146
|
-
setTimeout(() => {
|
|
147
|
-
const report = getListenerReport(client);
|
|
148
|
-
test.equal(report.streamer.error, 0, 'streamer error listeners should be 0 after error');
|
|
149
|
-
test.equal(report.socket, null, 'socket should be null after error');
|
|
150
|
-
test.ok(client.isClosed, 'client should be closed after error');
|
|
151
|
-
test.equal(client.state, client.states.LOGOUT, 'state should be LOGOUT');
|
|
152
|
-
test.done();
|
|
153
|
-
}, 100);
|
|
154
|
-
});
|
|
155
|
-
},
|
|
156
|
-
|
|
157
|
-
async 'should clean up listeners after successful connect and close'(test) {
|
|
158
|
-
test.expect(6);
|
|
159
|
-
|
|
160
|
-
const server = createMockServer();
|
|
161
|
-
|
|
162
|
-
server.listen(0, '127.0.0.1', async () => {
|
|
163
|
-
const port = server.address().port;
|
|
164
|
-
|
|
165
|
-
const client = new ImapFlow({
|
|
166
|
-
host: '127.0.0.1',
|
|
167
|
-
port,
|
|
168
|
-
secure: false,
|
|
169
|
-
logger: false,
|
|
170
|
-
auth: {
|
|
171
|
-
user: 'test',
|
|
172
|
-
pass: 'test'
|
|
173
|
-
}
|
|
174
|
-
});
|
|
175
|
-
|
|
176
|
-
try {
|
|
177
|
-
await client.connect();
|
|
178
|
-
|
|
179
|
-
// Verify we're connected
|
|
180
|
-
test.ok(client.usable, 'client should be usable after connect');
|
|
181
|
-
|
|
182
|
-
// Get listener report while connected
|
|
183
|
-
const connectedReport = getListenerReport(client);
|
|
184
|
-
test.ok(connectedReport.socket !== null, 'socket should exist while connected');
|
|
185
|
-
|
|
186
|
-
await client.logout();
|
|
187
|
-
|
|
188
|
-
// Wait for cleanup
|
|
189
|
-
await new Promise(r => setTimeout(r, 100));
|
|
190
|
-
|
|
191
|
-
const closedReport = getListenerReport(client);
|
|
192
|
-
test.equal(closedReport.streamer.error, 0, 'streamer error listeners should be 0');
|
|
193
|
-
test.equal(closedReport.streamer.readable, 0, 'streamer readable listeners should be 0');
|
|
194
|
-
test.ok(client.isClosed, 'client should be closed');
|
|
195
|
-
test.equal(client.state, client.states.LOGOUT, 'state should be LOGOUT');
|
|
196
|
-
} catch (err) {
|
|
197
|
-
test.ok(false, 'should not throw: ' + err.message);
|
|
198
|
-
} finally {
|
|
199
|
-
server.close();
|
|
200
|
-
test.done();
|
|
201
|
-
}
|
|
202
|
-
});
|
|
203
|
-
},
|
|
204
|
-
|
|
205
|
-
async 'should not accumulate listeners over multiple connect/disconnect cycles'(test) {
|
|
206
|
-
const server = createMockServer();
|
|
207
|
-
|
|
208
|
-
server.listen(0, '127.0.0.1', async () => {
|
|
209
|
-
const port = server.address().port;
|
|
210
|
-
const cycles = 10;
|
|
211
|
-
|
|
212
|
-
for (let i = 0; i < cycles; i++) {
|
|
213
|
-
const client = new ImapFlow({
|
|
214
|
-
host: '127.0.0.1',
|
|
215
|
-
port,
|
|
216
|
-
secure: false,
|
|
217
|
-
logger: false,
|
|
218
|
-
auth: {
|
|
219
|
-
user: 'test',
|
|
220
|
-
pass: 'test'
|
|
221
|
-
}
|
|
222
|
-
});
|
|
223
|
-
|
|
224
|
-
try {
|
|
225
|
-
await client.connect();
|
|
226
|
-
await client.logout();
|
|
227
|
-
} catch {
|
|
228
|
-
// Ignore connection errors
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
// Wait for cleanup
|
|
232
|
-
await new Promise(r => setTimeout(r, 50));
|
|
233
|
-
|
|
234
|
-
const report = getListenerReport(client);
|
|
235
|
-
test.equal(report.streamer.error, 0, `cycle ${i + 1}: streamer error listeners should be 0`);
|
|
236
|
-
test.equal(report.streamer.readable, 0, `cycle ${i + 1}: streamer readable listeners should be 0`);
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
server.close();
|
|
240
|
-
test.done();
|
|
241
|
-
});
|
|
242
|
-
},
|
|
243
|
-
|
|
244
|
-
'should clean up socket handlers after close'(test) {
|
|
245
|
-
const client = new ImapFlow({
|
|
246
|
-
host: '127.0.0.1',
|
|
247
|
-
port: 1,
|
|
248
|
-
secure: false,
|
|
249
|
-
logger: false
|
|
250
|
-
});
|
|
251
|
-
|
|
252
|
-
// Simulate having socket handlers set
|
|
253
|
-
const mockSocket = new net.Socket();
|
|
254
|
-
client.socket = mockSocket;
|
|
255
|
-
client.writeSocket = mockSocket;
|
|
256
|
-
|
|
257
|
-
// Set up handlers like connect() would
|
|
258
|
-
client._socketError = () => {};
|
|
259
|
-
client._socketClose = () => {};
|
|
260
|
-
client._socketEnd = () => {};
|
|
261
|
-
client._socketTimeout = () => {};
|
|
262
|
-
|
|
263
|
-
mockSocket.on('error', client._socketError);
|
|
264
|
-
mockSocket.on('close', client._socketClose);
|
|
265
|
-
mockSocket.on('end', client._socketEnd);
|
|
266
|
-
mockSocket.on('timeout', client._socketTimeout);
|
|
267
|
-
|
|
268
|
-
// Verify listeners are set
|
|
269
|
-
test.equal(mockSocket.listenerCount('error'), 1, 'error listener should be set');
|
|
270
|
-
test.equal(mockSocket.listenerCount('close'), 1, 'close listener should be set');
|
|
271
|
-
|
|
272
|
-
// Close the client
|
|
273
|
-
client.close();
|
|
274
|
-
|
|
275
|
-
// Verify cleanup (socket is destroyed, so listeners are removed)
|
|
276
|
-
test.ok(client.isClosed, 'client should be closed');
|
|
277
|
-
|
|
278
|
-
test.done();
|
|
279
|
-
},
|
|
280
|
-
|
|
281
|
-
'should not leak memory on repeated streamer error handler registrations'(test) {
|
|
282
|
-
// This test verifies that the streamer error handler pattern doesn't leak
|
|
283
|
-
const clients = [];
|
|
284
|
-
|
|
285
|
-
for (let i = 0; i < 20; i++) {
|
|
286
|
-
const client = new ImapFlow({
|
|
287
|
-
host: '127.0.0.1',
|
|
288
|
-
port: 1,
|
|
289
|
-
secure: false,
|
|
290
|
-
logger: false
|
|
291
|
-
});
|
|
292
|
-
|
|
293
|
-
// Initially, streamer should have error listener from constructor
|
|
294
|
-
test.ok(client.streamer.listenerCount('error') >= 1, 'streamer should have error listener');
|
|
295
|
-
|
|
296
|
-
clients.push(client);
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
// Close all and verify
|
|
300
|
-
for (const client of clients) {
|
|
301
|
-
client.close();
|
|
302
|
-
test.equal(client.streamer.listenerCount('error'), 0, 'streamer error listeners should be removed');
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
test.done();
|
|
306
|
-
},
|
|
307
|
-
|
|
308
|
-
'should handle rapid create/destroy cycles'(test) {
|
|
309
|
-
const iterations = 100;
|
|
310
|
-
let completed = 0;
|
|
311
|
-
|
|
312
|
-
for (let i = 0; i < iterations; i++) {
|
|
313
|
-
const client = new ImapFlow({
|
|
314
|
-
host: '127.0.0.1',
|
|
315
|
-
port: 1,
|
|
316
|
-
secure: false,
|
|
317
|
-
logger: false
|
|
318
|
-
});
|
|
319
|
-
|
|
320
|
-
client.close();
|
|
321
|
-
completed++;
|
|
322
|
-
|
|
323
|
-
// Spot check listener cleanup
|
|
324
|
-
if (i % 10 === 0) {
|
|
325
|
-
test.equal(client.streamer.listenerCount('error'), 0, `iteration ${i}: error listeners should be 0`);
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
test.equal(completed, iterations, 'all iterations should complete');
|
|
330
|
-
test.done();
|
|
331
|
-
},
|
|
332
|
-
|
|
333
|
-
'should export helper functions for external memory testing'(test) {
|
|
334
|
-
// Verify helpers are working correctly
|
|
335
|
-
const client = new ImapFlow({
|
|
336
|
-
host: '127.0.0.1',
|
|
337
|
-
port: 1,
|
|
338
|
-
secure: false,
|
|
339
|
-
logger: false
|
|
340
|
-
});
|
|
341
|
-
|
|
342
|
-
const report = getListenerReport(client);
|
|
343
|
-
|
|
344
|
-
test.ok(report.streamer !== null, 'report should include streamer');
|
|
345
|
-
test.ok(typeof report.streamer.error === 'number', 'error count should be a number');
|
|
346
|
-
test.ok(typeof report.streamer.readable === 'number', 'readable count should be a number');
|
|
347
|
-
test.ok(report.client !== null, 'report should include client');
|
|
348
|
-
|
|
349
|
-
client.close();
|
|
350
|
-
test.done();
|
|
351
|
-
}
|
|
352
|
-
};
|
|
353
|
-
|
|
354
|
-
exports['Compression Stream Tests'] = {
|
|
355
|
-
'should clean up compression streams on close'(test) {
|
|
356
|
-
const client = new ImapFlow({
|
|
357
|
-
host: '127.0.0.1',
|
|
358
|
-
port: 1,
|
|
359
|
-
secure: false,
|
|
360
|
-
logger: false
|
|
361
|
-
});
|
|
362
|
-
|
|
363
|
-
// Simulate compression being enabled by manually setting up streams
|
|
364
|
-
client._deflate = zlib.createDeflateRaw();
|
|
365
|
-
client._inflate = zlib.createInflateRaw();
|
|
366
|
-
|
|
367
|
-
// Add error handlers like compress() does
|
|
368
|
-
client._deflate.on('error', () => {});
|
|
369
|
-
client._inflate.on('error', () => {});
|
|
370
|
-
|
|
371
|
-
// Create writeSocket like compress() does
|
|
372
|
-
const writeSocket = new PassThrough();
|
|
373
|
-
writeSocket.on('readable', () => {});
|
|
374
|
-
writeSocket.on('error', () => {});
|
|
375
|
-
client.writeSocket = writeSocket;
|
|
376
|
-
|
|
377
|
-
// Verify streams exist
|
|
378
|
-
test.ok(client._deflate, 'deflate should exist');
|
|
379
|
-
test.ok(client._inflate, 'inflate should exist');
|
|
380
|
-
test.ok(client.writeSocket, 'writeSocket should exist');
|
|
381
|
-
|
|
382
|
-
// Close the client
|
|
383
|
-
client.close();
|
|
384
|
-
|
|
385
|
-
// Verify compression streams are cleaned up
|
|
386
|
-
test.equal(client._deflate, null, 'deflate should be null after close');
|
|
387
|
-
test.equal(client._inflate, null, 'inflate should be null after close');
|
|
388
|
-
test.equal(client.writeSocket, null, 'writeSocket should be null after close');
|
|
389
|
-
test.ok(client.isClosed, 'client should be closed');
|
|
390
|
-
|
|
391
|
-
test.done();
|
|
392
|
-
},
|
|
393
|
-
|
|
394
|
-
'should not leak compression stream listeners over multiple cycles'(test) {
|
|
395
|
-
// Create multiple deflate/inflate streams to verify they don't accumulate
|
|
396
|
-
const streams = [];
|
|
397
|
-
|
|
398
|
-
for (let i = 0; i < 10; i++) {
|
|
399
|
-
const client = new ImapFlow({
|
|
400
|
-
host: '127.0.0.1',
|
|
401
|
-
port: 1,
|
|
402
|
-
secure: false,
|
|
403
|
-
logger: false
|
|
404
|
-
});
|
|
405
|
-
|
|
406
|
-
// Simulate compression setup
|
|
407
|
-
client._deflate = zlib.createDeflateRaw();
|
|
408
|
-
client._inflate = zlib.createInflateRaw();
|
|
409
|
-
client._deflate.on('error', () => {});
|
|
410
|
-
client._inflate.on('error', () => {});
|
|
411
|
-
|
|
412
|
-
streams.push({
|
|
413
|
-
deflate: client._deflate,
|
|
414
|
-
inflate: client._inflate
|
|
415
|
-
});
|
|
416
|
-
|
|
417
|
-
// Close immediately
|
|
418
|
-
client.close();
|
|
419
|
-
|
|
420
|
-
// Verify cleanup
|
|
421
|
-
test.equal(client._deflate, null, `cycle ${i + 1}: deflate should be null`);
|
|
422
|
-
test.equal(client._inflate, null, `cycle ${i + 1}: inflate should be null`);
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
// Verify all streams are destroyed
|
|
426
|
-
for (let i = 0; i < streams.length; i++) {
|
|
427
|
-
test.ok(streams[i].deflate.destroyed, `cycle ${i + 1}: deflate should be destroyed`);
|
|
428
|
-
test.ok(streams[i].inflate.destroyed, `cycle ${i + 1}: inflate should be destroyed`);
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
test.done();
|
|
432
|
-
},
|
|
433
|
-
|
|
434
|
-
'should handle compression stream errors without leaking'(test) {
|
|
435
|
-
const client = new ImapFlow({
|
|
436
|
-
host: '127.0.0.1',
|
|
437
|
-
port: 1,
|
|
438
|
-
secure: false,
|
|
439
|
-
logger: false
|
|
440
|
-
});
|
|
441
|
-
|
|
442
|
-
// Setup compression streams with error handlers
|
|
443
|
-
client._deflate = zlib.createDeflateRaw();
|
|
444
|
-
client._inflate = zlib.createInflateRaw();
|
|
445
|
-
|
|
446
|
-
// Add error handlers (like compress() does)
|
|
447
|
-
client._deflate.on('error', () => {});
|
|
448
|
-
client._inflate.on('error', () => {});
|
|
449
|
-
|
|
450
|
-
// Verify initial listener counts
|
|
451
|
-
test.equal(client._deflate.listenerCount('error'), 1, 'deflate should have 1 error listener');
|
|
452
|
-
test.equal(client._inflate.listenerCount('error'), 1, 'inflate should have 1 error listener');
|
|
453
|
-
|
|
454
|
-
// Close and verify cleanup
|
|
455
|
-
client.close();
|
|
456
|
-
|
|
457
|
-
test.equal(client._deflate, null, 'deflate should be null');
|
|
458
|
-
test.equal(client._inflate, null, 'inflate should be null');
|
|
459
|
-
|
|
460
|
-
test.done();
|
|
461
|
-
}
|
|
462
|
-
};
|
|
463
|
-
|
|
464
|
-
exports['Fetch Stream Tests'] = {
|
|
465
|
-
'should clean up internal fetch state on close'(test) {
|
|
466
|
-
const client = new ImapFlow({
|
|
467
|
-
host: '127.0.0.1',
|
|
468
|
-
port: 1,
|
|
469
|
-
secure: false,
|
|
470
|
-
logger: false
|
|
471
|
-
});
|
|
472
|
-
|
|
473
|
-
// Simulate some internal state that would exist during fetch
|
|
474
|
-
client.requestTagMap.set('A001', { tag: 'A001', command: 'FETCH' });
|
|
475
|
-
client.requestTagMap.set('A002', { tag: 'A002', command: 'FETCH' });
|
|
476
|
-
|
|
477
|
-
test.equal(client.requestTagMap.size, 2, 'should have 2 pending requests');
|
|
478
|
-
|
|
479
|
-
// Close the client
|
|
480
|
-
client.close();
|
|
481
|
-
|
|
482
|
-
// Verify request map is cleared
|
|
483
|
-
test.equal(client.requestTagMap.size, 0, 'requestTagMap should be cleared after close');
|
|
484
|
-
test.ok(client.isClosed, 'client should be closed');
|
|
485
|
-
|
|
486
|
-
test.done();
|
|
487
|
-
},
|
|
488
|
-
|
|
489
|
-
'should clean up mailbox state on close'(test) {
|
|
490
|
-
const client = new ImapFlow({
|
|
491
|
-
host: '127.0.0.1',
|
|
492
|
-
port: 1,
|
|
493
|
-
secure: false,
|
|
494
|
-
logger: false
|
|
495
|
-
});
|
|
496
|
-
|
|
497
|
-
// Simulate mailbox state
|
|
498
|
-
client.folders.set('INBOX', { path: 'INBOX', exists: 100 });
|
|
499
|
-
client.folders.set('Sent', { path: 'Sent', exists: 50 });
|
|
500
|
-
client.folders.set('Drafts', { path: 'Drafts', exists: 10 });
|
|
501
|
-
|
|
502
|
-
test.equal(client.folders.size, 3, 'should have 3 folders cached');
|
|
503
|
-
|
|
504
|
-
// Close the client
|
|
505
|
-
client.close();
|
|
506
|
-
|
|
507
|
-
// Verify folders are cleared
|
|
508
|
-
test.equal(client.folders.size, 0, 'folders should be cleared after close');
|
|
509
|
-
|
|
510
|
-
test.done();
|
|
511
|
-
},
|
|
512
|
-
|
|
513
|
-
async 'should clean up after fetch with mock server'(test) {
|
|
514
|
-
const server = createMockServerWithFetch();
|
|
515
|
-
|
|
516
|
-
server.listen(0, '127.0.0.1', async () => {
|
|
517
|
-
const port = server.address().port;
|
|
518
|
-
|
|
519
|
-
const client = new ImapFlow({
|
|
520
|
-
host: '127.0.0.1',
|
|
521
|
-
port,
|
|
522
|
-
secure: false,
|
|
523
|
-
logger: false,
|
|
524
|
-
auth: {
|
|
525
|
-
user: 'test',
|
|
526
|
-
pass: 'test'
|
|
527
|
-
}
|
|
528
|
-
});
|
|
529
|
-
|
|
530
|
-
try {
|
|
531
|
-
await client.connect();
|
|
532
|
-
|
|
533
|
-
// Select INBOX
|
|
534
|
-
await client.mailboxOpen('INBOX');
|
|
535
|
-
|
|
536
|
-
// Verify we're in selected state
|
|
537
|
-
test.ok(client.mailbox, 'mailbox should be selected');
|
|
538
|
-
|
|
539
|
-
await client.logout();
|
|
540
|
-
|
|
541
|
-
// Wait for cleanup
|
|
542
|
-
await new Promise(r => setTimeout(r, 100));
|
|
543
|
-
|
|
544
|
-
// Verify cleanup
|
|
545
|
-
const report = getListenerReport(client);
|
|
546
|
-
test.equal(report.streamer.error, 0, 'streamer error listeners should be 0');
|
|
547
|
-
test.equal(report.streamer.readable, 0, 'streamer readable listeners should be 0');
|
|
548
|
-
test.ok(client.isClosed, 'client should be closed');
|
|
549
|
-
} catch (err) {
|
|
550
|
-
test.ok(false, 'should not throw: ' + err.message);
|
|
551
|
-
} finally {
|
|
552
|
-
server.close();
|
|
553
|
-
test.done();
|
|
554
|
-
}
|
|
555
|
-
});
|
|
556
|
-
},
|
|
557
|
-
|
|
558
|
-
async 'should not leak listeners during multiple mailbox operations'(test) {
|
|
559
|
-
const server = createMockServerWithFetch();
|
|
560
|
-
|
|
561
|
-
server.listen(0, '127.0.0.1', async () => {
|
|
562
|
-
const port = server.address().port;
|
|
563
|
-
|
|
564
|
-
const client = new ImapFlow({
|
|
565
|
-
host: '127.0.0.1',
|
|
566
|
-
port,
|
|
567
|
-
secure: false,
|
|
568
|
-
logger: false,
|
|
569
|
-
auth: {
|
|
570
|
-
user: 'test',
|
|
571
|
-
pass: 'test'
|
|
572
|
-
}
|
|
573
|
-
});
|
|
574
|
-
|
|
575
|
-
try {
|
|
576
|
-
await client.connect();
|
|
577
|
-
|
|
578
|
-
// Perform multiple mailbox operations
|
|
579
|
-
for (let i = 0; i < 5; i++) {
|
|
580
|
-
await client.mailboxOpen('INBOX');
|
|
581
|
-
await client.mailboxClose();
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
await client.logout();
|
|
585
|
-
|
|
586
|
-
// Wait for cleanup
|
|
587
|
-
await new Promise(r => setTimeout(r, 100));
|
|
588
|
-
|
|
589
|
-
// Verify no listener accumulation
|
|
590
|
-
const report = getListenerReport(client);
|
|
591
|
-
test.equal(report.streamer.error, 0, 'streamer error listeners should be 0');
|
|
592
|
-
test.equal(report.streamer.readable, 0, 'streamer readable listeners should be 0');
|
|
593
|
-
} catch (err) {
|
|
594
|
-
test.ok(false, 'should not throw: ' + err.message);
|
|
595
|
-
} finally {
|
|
596
|
-
server.close();
|
|
597
|
-
test.done();
|
|
598
|
-
}
|
|
599
|
-
});
|
|
600
|
-
}
|
|
601
|
-
};
|
|
602
|
-
|
|
603
|
-
// Create a mock server that supports SELECT/FETCH operations
|
|
604
|
-
function createMockServerWithFetch() {
|
|
605
|
-
const server = net.createServer(socket => {
|
|
606
|
-
socket.write('* OK Mock IMAP Server ready\r\n');
|
|
607
|
-
|
|
608
|
-
socket.on('data', data => {
|
|
609
|
-
const lines = data
|
|
610
|
-
.toString()
|
|
611
|
-
.split('\r\n')
|
|
612
|
-
.filter(l => l.trim());
|
|
613
|
-
|
|
614
|
-
for (const line of lines) {
|
|
615
|
-
const parts = line.split(' ');
|
|
616
|
-
const tag = parts[0];
|
|
617
|
-
const command = parts[1] ? parts[1].toUpperCase() : '';
|
|
618
|
-
|
|
619
|
-
if (command === 'CAPABILITY') {
|
|
620
|
-
socket.write('* CAPABILITY IMAP4rev1 AUTH=PLAIN\r\n');
|
|
621
|
-
socket.write(`${tag} OK CAPABILITY completed\r\n`);
|
|
622
|
-
} else if (command === 'LOGIN') {
|
|
623
|
-
socket.write(`${tag} OK LOGIN completed\r\n`);
|
|
624
|
-
} else if (command === 'LOGOUT') {
|
|
625
|
-
socket.write('* BYE Server logging out\r\n');
|
|
626
|
-
socket.write(`${tag} OK LOGOUT completed\r\n`);
|
|
627
|
-
socket.end();
|
|
628
|
-
} else if (command === 'NAMESPACE') {
|
|
629
|
-
socket.write('* NAMESPACE (("" "/")) NIL NIL\r\n');
|
|
630
|
-
socket.write(`${tag} OK NAMESPACE completed\r\n`);
|
|
631
|
-
} else if (command === 'COMPRESS') {
|
|
632
|
-
socket.write(`${tag} NO COMPRESS not supported\r\n`);
|
|
633
|
-
} else if (command === 'ENABLE') {
|
|
634
|
-
socket.write(`${tag} OK ENABLE completed\r\n`);
|
|
635
|
-
} else if (command === 'ID') {
|
|
636
|
-
socket.write('* ID NIL\r\n');
|
|
637
|
-
socket.write(`${tag} OK ID completed\r\n`);
|
|
638
|
-
} else if (command === 'SELECT' || command === 'EXAMINE') {
|
|
639
|
-
socket.write('* FLAGS (\\Answered \\Flagged \\Deleted \\Seen \\Draft)\r\n');
|
|
640
|
-
socket.write('* OK [PERMANENTFLAGS (\\Answered \\Flagged \\Deleted \\Seen \\Draft \\*)] Flags permitted\r\n');
|
|
641
|
-
socket.write('* 10 EXISTS\r\n');
|
|
642
|
-
socket.write('* 0 RECENT\r\n');
|
|
643
|
-
socket.write('* OK [UIDVALIDITY 1234567890] UIDs valid\r\n');
|
|
644
|
-
socket.write('* OK [UIDNEXT 11] Predicted next UID\r\n');
|
|
645
|
-
socket.write(`${tag} OK [READ-WRITE] SELECT completed\r\n`);
|
|
646
|
-
} else if (command === 'CLOSE') {
|
|
647
|
-
socket.write(`${tag} OK CLOSE completed\r\n`);
|
|
648
|
-
} else if (command === 'FETCH') {
|
|
649
|
-
// Simple fetch response
|
|
650
|
-
socket.write('* 1 FETCH (UID 1 FLAGS (\\Seen))\r\n');
|
|
651
|
-
socket.write(`${tag} OK FETCH completed\r\n`);
|
|
652
|
-
} else if (command === 'NOOP') {
|
|
653
|
-
socket.write(`${tag} OK NOOP completed\r\n`);
|
|
654
|
-
} else if (tag && command) {
|
|
655
|
-
socket.write(`${tag} OK Command completed\r\n`);
|
|
656
|
-
}
|
|
657
|
-
}
|
|
658
|
-
});
|
|
659
|
-
|
|
660
|
-
socket.on('error', () => {});
|
|
661
|
-
});
|
|
662
|
-
|
|
663
|
-
return server;
|
|
664
|
-
}
|
|
665
|
-
|
|
666
|
-
// Note: helpers (getListenerReport, measureMemory, createMockServer) are available
|
|
667
|
-
// within this module for testing purposes
|