imapflow 1.7.8 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (296) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +8 -2
  3. package/dist/cjs/charsets.d.ts +1 -0
  4. package/dist/cjs/charsets.js +294 -0
  5. package/dist/cjs/commands/append.d.ts +22 -0
  6. package/dist/cjs/commands/append.js +151 -0
  7. package/dist/cjs/commands/authenticate.d.ts +24 -0
  8. package/dist/cjs/commands/authenticate.js +223 -0
  9. package/dist/cjs/commands/capability.d.ts +8 -0
  10. package/dist/cjs/commands/capability.js +32 -0
  11. package/dist/cjs/commands/close.d.ts +8 -0
  12. package/dist/cjs/commands/close.js +39 -0
  13. package/dist/cjs/commands/compress.d.ts +8 -0
  14. package/dist/cjs/commands/compress.js +56 -0
  15. package/dist/cjs/commands/copy.d.ts +13 -0
  16. package/dist/cjs/commands/copy.js +44 -0
  17. package/dist/cjs/commands/copyuid-parser.d.ts +11 -0
  18. package/dist/cjs/commands/copyuid-parser.js +32 -0
  19. package/dist/cjs/commands/create.d.ts +11 -0
  20. package/dist/cjs/commands/create.js +80 -0
  21. package/dist/cjs/commands/delete.d.ts +11 -0
  22. package/dist/cjs/commands/delete.js +40 -0
  23. package/dist/cjs/commands/enable.d.ts +9 -0
  24. package/dist/cjs/commands/enable.js +61 -0
  25. package/dist/cjs/commands/esearch-parser.d.ts +17 -0
  26. package/dist/cjs/commands/esearch-parser.js +91 -0
  27. package/dist/cjs/commands/expunge.d.ts +12 -0
  28. package/dist/cjs/commands/expunge.js +60 -0
  29. package/dist/cjs/commands/fetch.d.ts +30 -0
  30. package/dist/cjs/commands/fetch.js +241 -0
  31. package/dist/cjs/commands/id.d.ts +10 -0
  32. package/dist/cjs/commands/id.js +80 -0
  33. package/dist/cjs/commands/idle.d.ts +9 -0
  34. package/dist/cjs/commands/idle.js +347 -0
  35. package/dist/cjs/commands/list.d.ts +16 -0
  36. package/dist/cjs/commands/list.js +518 -0
  37. package/dist/cjs/commands/login.d.ts +11 -0
  38. package/dist/cjs/commands/login.js +42 -0
  39. package/dist/cjs/commands/logout.d.ts +8 -0
  40. package/dist/cjs/commands/logout.js +47 -0
  41. package/dist/cjs/commands/move.d.ts +13 -0
  42. package/dist/cjs/commands/move.js +57 -0
  43. package/dist/cjs/commands/namespace.d.ts +25 -0
  44. package/dist/cjs/commands/namespace.js +139 -0
  45. package/dist/cjs/commands/noop.d.ts +8 -0
  46. package/dist/cjs/commands/noop.js +22 -0
  47. package/dist/cjs/commands/quota.d.ts +10 -0
  48. package/dist/cjs/commands/quota.js +119 -0
  49. package/dist/cjs/commands/rename.d.ts +12 -0
  50. package/dist/cjs/commands/rename.js +48 -0
  51. package/dist/cjs/commands/search.d.ts +15 -0
  52. package/dist/cjs/commands/search.js +228 -0
  53. package/dist/cjs/commands/select.d.ts +25 -0
  54. package/dist/cjs/commands/select.js +250 -0
  55. package/dist/cjs/commands/starttls.d.ts +8 -0
  56. package/dist/cjs/commands/starttls.js +30 -0
  57. package/dist/cjs/commands/status-fields.d.ts +14 -0
  58. package/dist/cjs/commands/status-fields.js +61 -0
  59. package/dist/cjs/commands/status.d.ts +12 -0
  60. package/dist/cjs/commands/status.js +108 -0
  61. package/dist/cjs/commands/store.d.ts +19 -0
  62. package/dist/cjs/commands/store.js +93 -0
  63. package/dist/cjs/commands/subscribe.d.ts +9 -0
  64. package/dist/cjs/commands/subscribe.js +31 -0
  65. package/dist/cjs/commands/unsubscribe.d.ts +9 -0
  66. package/dist/cjs/commands/unsubscribe.js +31 -0
  67. package/dist/cjs/connection-deadline.d.ts +49 -0
  68. package/dist/cjs/connection-deadline.js +91 -0
  69. package/dist/cjs/errors.d.ts +83 -0
  70. package/dist/cjs/errors.js +13 -0
  71. package/dist/cjs/handler/imap-compiler.d.ts +24 -0
  72. package/dist/cjs/handler/imap-compiler.js +285 -0
  73. package/dist/cjs/handler/imap-formal-syntax.d.ts +28 -0
  74. package/dist/cjs/handler/imap-formal-syntax.js +121 -0
  75. package/dist/cjs/handler/imap-handler.d.ts +9 -0
  76. package/dist/cjs/handler/imap-handler.js +10 -0
  77. package/dist/cjs/handler/imap-parser.d.ts +16 -0
  78. package/dist/cjs/handler/imap-parser.js +90 -0
  79. package/dist/cjs/handler/imap-stream.d.ts +181 -0
  80. package/dist/cjs/handler/imap-stream.js +446 -0
  81. package/dist/cjs/handler/limits.d.ts +25 -0
  82. package/dist/cjs/handler/limits.js +51 -0
  83. package/dist/cjs/handler/parser-instance.d.ts +68 -0
  84. package/dist/cjs/handler/parser-instance.js +223 -0
  85. package/dist/cjs/handler/token-parser.d.ts +91 -0
  86. package/dist/cjs/handler/token-parser.js +673 -0
  87. package/dist/cjs/handler/types.d.ts +91 -0
  88. package/dist/cjs/handler/types.js +4 -0
  89. package/dist/cjs/imap-commands.d.ts +16 -0
  90. package/dist/cjs/imap-commands.js +74 -0
  91. package/dist/cjs/imap-flow.d.ts +676 -0
  92. package/dist/cjs/imap-flow.js +3949 -0
  93. package/dist/cjs/jp-decoder.d.ts +12 -0
  94. package/dist/cjs/jp-decoder.js +79 -0
  95. package/dist/cjs/limited-passthrough.d.ts +25 -0
  96. package/dist/cjs/limited-passthrough.js +54 -0
  97. package/dist/cjs/logger.d.ts +3 -0
  98. package/dist/cjs/logger.js +11 -0
  99. package/dist/cjs/package-info.d.ts +3 -0
  100. package/dist/cjs/package-info.js +7 -0
  101. package/dist/cjs/package.json +3 -0
  102. package/dist/cjs/proxy-connection.d.ts +33 -0
  103. package/dist/cjs/proxy-connection.js +392 -0
  104. package/dist/cjs/search-compiler.d.ts +34 -0
  105. package/dist/cjs/search-compiler.js +476 -0
  106. package/dist/cjs/special-use.d.ts +22 -0
  107. package/dist/cjs/special-use.js +911 -0
  108. package/dist/cjs/tools.d.ts +427 -0
  109. package/dist/cjs/tools.js +1496 -0
  110. package/{lib/imap-flow.d.ts → dist/cjs/types.d.ts} +386 -516
  111. package/dist/cjs/types.js +5 -0
  112. package/dist/esm/charsets.d.ts +1 -0
  113. package/{lib → dist/esm}/charsets.js +1 -6
  114. package/dist/esm/commands/append.d.ts +22 -0
  115. package/{lib → dist/esm}/commands/append.js +22 -52
  116. package/dist/esm/commands/authenticate.d.ts +24 -0
  117. package/{lib → dist/esm}/commands/authenticate.js +62 -87
  118. package/dist/esm/commands/capability.d.ts +8 -0
  119. package/{lib → dist/esm}/commands/capability.js +6 -9
  120. package/dist/esm/commands/close.d.ts +8 -0
  121. package/{lib → dist/esm}/commands/close.js +6 -10
  122. package/dist/esm/commands/compress.d.ts +8 -0
  123. package/{lib → dist/esm}/commands/compress.js +7 -11
  124. package/dist/esm/commands/copy.d.ts +13 -0
  125. package/{lib → dist/esm}/commands/copy.js +12 -20
  126. package/dist/esm/commands/copyuid-parser.d.ts +11 -0
  127. package/{lib → dist/esm}/commands/copyuid-parser.js +9 -15
  128. package/dist/esm/commands/create.d.ts +11 -0
  129. package/{lib → dist/esm}/commands/create.js +13 -27
  130. package/dist/esm/commands/delete.d.ts +11 -0
  131. package/{lib → dist/esm}/commands/delete.js +9 -14
  132. package/dist/esm/commands/enable.d.ts +9 -0
  133. package/{lib → dist/esm}/commands/enable.js +23 -30
  134. package/dist/esm/commands/esearch-parser.d.ts +17 -0
  135. package/dist/esm/commands/esearch-parser.js +88 -0
  136. package/dist/esm/commands/expunge.d.ts +12 -0
  137. package/{lib → dist/esm}/commands/expunge.js +17 -22
  138. package/dist/esm/commands/fetch.d.ts +30 -0
  139. package/{lib → dist/esm}/commands/fetch.js +32 -64
  140. package/dist/esm/commands/id.d.ts +10 -0
  141. package/{lib → dist/esm}/commands/id.js +17 -23
  142. package/dist/esm/commands/idle.d.ts +9 -0
  143. package/{lib → dist/esm}/commands/idle.js +47 -81
  144. package/dist/esm/commands/list.d.ts +16 -0
  145. package/{lib → dist/esm}/commands/list.js +56 -121
  146. package/dist/esm/commands/login.d.ts +11 -0
  147. package/{lib → dist/esm}/commands/login.js +10 -15
  148. package/dist/esm/commands/logout.d.ts +8 -0
  149. package/{lib → dist/esm}/commands/logout.js +9 -11
  150. package/dist/esm/commands/move.d.ts +13 -0
  151. package/{lib → dist/esm}/commands/move.js +13 -21
  152. package/dist/esm/commands/namespace.d.ts +25 -0
  153. package/{lib → dist/esm}/commands/namespace.js +34 -44
  154. package/dist/esm/commands/noop.d.ts +8 -0
  155. package/{lib → dist/esm}/commands/noop.js +6 -7
  156. package/dist/esm/commands/quota.d.ts +10 -0
  157. package/{lib → dist/esm}/commands/quota.js +18 -36
  158. package/dist/esm/commands/rename.d.ts +12 -0
  159. package/{lib → dist/esm}/commands/rename.js +10 -15
  160. package/dist/esm/commands/search.d.ts +15 -0
  161. package/{lib → dist/esm}/commands/search.js +36 -135
  162. package/dist/esm/commands/select.d.ts +25 -0
  163. package/{lib → dist/esm}/commands/select.js +33 -64
  164. package/dist/esm/commands/starttls.d.ts +8 -0
  165. package/{lib → dist/esm}/commands/starttls.js +6 -8
  166. package/dist/esm/commands/status-fields.d.ts +14 -0
  167. package/{lib → dist/esm}/commands/status-fields.js +5 -16
  168. package/dist/esm/commands/status.d.ts +12 -0
  169. package/{lib → dist/esm}/commands/status.js +18 -29
  170. package/dist/esm/commands/store.d.ts +19 -0
  171. package/{lib → dist/esm}/commands/store.js +24 -37
  172. package/dist/esm/commands/subscribe.d.ts +9 -0
  173. package/{lib → dist/esm}/commands/subscribe.js +8 -12
  174. package/dist/esm/commands/unsubscribe.d.ts +9 -0
  175. package/{lib → dist/esm}/commands/unsubscribe.js +8 -12
  176. package/dist/esm/connection-deadline.d.ts +49 -0
  177. package/{lib → dist/esm}/connection-deadline.js +14 -25
  178. package/dist/esm/errors.d.ts +83 -0
  179. package/dist/esm/errors.js +9 -0
  180. package/dist/esm/handler/imap-compiler.d.ts +24 -0
  181. package/{lib → dist/esm}/handler/imap-compiler.js +22 -80
  182. package/dist/esm/handler/imap-formal-syntax.d.ts +28 -0
  183. package/dist/esm/handler/imap-formal-syntax.js +117 -0
  184. package/dist/esm/handler/imap-handler.d.ts +9 -0
  185. package/dist/esm/handler/imap-handler.js +9 -0
  186. package/dist/esm/handler/imap-parser.d.ts +16 -0
  187. package/{lib → dist/esm}/handler/imap-parser.js +31 -44
  188. package/dist/esm/handler/imap-stream.d.ts +181 -0
  189. package/{lib → dist/esm}/handler/imap-stream.js +29 -121
  190. package/dist/esm/handler/limits.d.ts +25 -0
  191. package/{lib → dist/esm}/handler/limits.js +13 -22
  192. package/dist/esm/handler/parser-instance.d.ts +68 -0
  193. package/{lib → dist/esm}/handler/parser-instance.js +19 -47
  194. package/dist/esm/handler/token-parser.d.ts +91 -0
  195. package/{lib → dist/esm}/handler/token-parser.js +71 -155
  196. package/dist/esm/handler/types.d.ts +91 -0
  197. package/dist/esm/handler/types.js +3 -0
  198. package/dist/esm/imap-commands.d.ts +16 -0
  199. package/dist/esm/imap-commands.js +67 -0
  200. package/dist/esm/imap-flow.d.ts +676 -0
  201. package/{lib → dist/esm}/imap-flow.js +761 -1789
  202. package/dist/esm/jp-decoder.d.ts +12 -0
  203. package/{lib → dist/esm}/jp-decoder.js +6 -21
  204. package/dist/esm/limited-passthrough.d.ts +25 -0
  205. package/{lib → dist/esm}/limited-passthrough.js +7 -20
  206. package/dist/esm/logger.d.ts +3 -0
  207. package/dist/esm/logger.js +4 -0
  208. package/dist/esm/package-info.d.ts +3 -0
  209. package/dist/esm/package-info.js +4 -0
  210. package/dist/esm/package.json +3 -0
  211. package/dist/esm/proxy-connection.d.ts +33 -0
  212. package/{lib → dist/esm}/proxy-connection.js +56 -127
  213. package/dist/esm/search-compiler.d.ts +34 -0
  214. package/{lib → dist/esm}/search-compiler.js +54 -110
  215. package/dist/esm/special-use.d.ts +22 -0
  216. package/dist/esm/special-use.js +907 -0
  217. package/dist/esm/tools.d.ts +427 -0
  218. package/dist/esm/tools.js +1446 -0
  219. package/dist/esm/types.d.ts +828 -0
  220. package/dist/esm/types.js +4 -0
  221. package/package.json +60 -20
  222. package/.gitattributes +0 -1
  223. package/.github/CODE_OF_CONDUCT.md +0 -76
  224. package/.github/FUNDING.yml +0 -4
  225. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -40
  226. package/.github/ISSUE_TEMPLATE/feature_request.md +0 -19
  227. package/.github/contributing.md +0 -17
  228. package/.github/workflows/release.yaml +0 -36
  229. package/.github/workflows/stale.yml +0 -29
  230. package/.github/workflows/test.yml +0 -51
  231. package/.ncurc.js +0 -4
  232. package/.prettierignore +0 -4
  233. package/.prettierrc.js +0 -8
  234. package/.release-please-manifest.json +0 -3
  235. package/CLAUDE.md +0 -104
  236. package/Gruntfile.js +0 -23
  237. package/eslint.config.js +0 -45
  238. package/lib/handler/imap-formal-syntax.js +0 -189
  239. package/lib/handler/imap-handler.js +0 -17
  240. package/lib/imap-commands.js +0 -45
  241. package/lib/logger.js +0 -5
  242. package/lib/special-use.js +0 -923
  243. package/lib/tools.js +0 -1612
  244. package/release-please-config.json +0 -10
  245. package/test/authentication-test.js +0 -101
  246. package/test/auto-idle-test.js +0 -470
  247. package/test/bodystructure-test.js +0 -899
  248. package/test/charsets-test.js +0 -161
  249. package/test/commands-branches-test.js +0 -1095
  250. package/test/commands-integration-test.js +0 -11124
  251. package/test/commands-test.js +0 -73
  252. package/test/connection-edge-cases-test.js +0 -1828
  253. package/test/connection-test.js +0 -162
  254. package/test/copyuid-parser-test.js +0 -173
  255. package/test/fetch-generator-test.js +0 -218
  256. package/test/fixtures/fake-timers.js +0 -115
  257. package/test/fixtures/serialized-mimetorture.js +0 -2738
  258. package/test/fixtures/test-client.js +0 -101
  259. package/test/fixtures/test-tls.js +0 -8
  260. package/test/handler-branches-test.js +0 -310
  261. package/test/idle-polling-test.js +0 -518
  262. package/test/imap-compiler-test.js +0 -809
  263. package/test/imap-flow-compress-test.js +0 -166
  264. package/test/imap-flow-coverage-test.js +0 -612
  265. package/test/imap-flow-fetch-download-test.js +0 -909
  266. package/test/imap-flow-internals-test.js +0 -725
  267. package/test/imap-flow-methods-test.js +0 -889
  268. package/test/imap-flow-proxy-paths-test.js +0 -366
  269. package/test/imap-flow-secure-test.js +0 -573
  270. package/test/imap-flow-server-test.js +0 -1474
  271. package/test/imap-formal-syntax-test.js +0 -293
  272. package/test/imap-parser-test.js +0 -1474
  273. package/test/imap-stream-edge-cases-test.js +0 -666
  274. package/test/imap-stream-test.js +0 -177
  275. package/test/imapflow-test.js +0 -258
  276. package/test/integration/README.md +0 -52
  277. package/test/integration/dovecot-test.conf +0 -27
  278. package/test/integration/rev2-live-test.js +0 -431
  279. package/test/integration/run-rev2-tests.sh +0 -75
  280. package/test/integration-test.js +0 -83
  281. package/test/jp-decoder-test.js +0 -304
  282. package/test/limited-passthrough-test.js +0 -299
  283. package/test/memory-cleanup-test.js +0 -144
  284. package/test/memory-leak-test.js +0 -667
  285. package/test/parser-limits-test.js +0 -292
  286. package/test/proxy-connection-test.js +0 -738
  287. package/test/reliability-improvements-test.js +0 -548
  288. package/test/search-compiler-test.js +0 -1300
  289. package/test/search-test.js +0 -329
  290. package/test/special-use-test.js +0 -418
  291. package/test/starttls-injection-test.js +0 -181
  292. package/test/tag-correlation-test.js +0 -333
  293. package/test/timer-policy-test.js +0 -227
  294. package/test/token-parser-test.js +0 -456
  295. package/test/tools-test.js +0 -2013
  296. package/test/unhandled-rejection-test.js +0 -661
@@ -1,366 +0,0 @@
1
- 'use strict';
2
-
3
- // Exercises connect()'s proxy code path by injecting a mocked proxyConnection
4
- // (via proxyquire) that returns a cleartext socket connected to an in-process
5
- // mock IMAP server. Also covers the proxy-setup failure path.
6
-
7
- const net = require('net');
8
- const tls = require('tls');
9
- const proxyquire = require('proxyquire');
10
- const { cert, key } = require('./fixtures/test-tls');
11
- const { ImapFlow } = require('../lib/imap-flow');
12
-
13
- const CAPS = 'IMAP4rev1 ID ENABLE NAMESPACE';
14
-
15
- // Minimal mock IMAP server (plaintext) sufficient for a full session.
16
- const createServer = () =>
17
- net.createServer(socket => {
18
- socket.on('error', () => {});
19
- let buf = '';
20
- socket.on('data', data => {
21
- buf += data.toString('binary');
22
- let idx;
23
- while ((idx = buf.indexOf('\r\n')) >= 0) {
24
- let line = buf.slice(0, idx);
25
- buf = buf.slice(idx + 2);
26
- let parts = line.split(' ');
27
- let tag = parts[0];
28
- let cmd = (parts[1] || '').toUpperCase();
29
- switch (cmd) {
30
- case 'CAPABILITY':
31
- socket.write(`* CAPABILITY ${CAPS}\r\n${tag} OK done\r\n`);
32
- break;
33
- case 'ID':
34
- socket.write(`* ID ("name" "mock" "version" "1")\r\n${tag} OK done\r\n`);
35
- break;
36
- case 'LOGIN':
37
- socket.write(`${tag} OK LOGIN done\r\n`);
38
- break;
39
- case 'NAMESPACE':
40
- socket.write(`* NAMESPACE (("" "/")) NIL NIL\r\n${tag} OK done\r\n`);
41
- break;
42
- case 'ENABLE':
43
- socket.write(`${tag} OK done\r\n`);
44
- break;
45
- case 'LOGOUT':
46
- socket.write(`* BYE bye\r\n${tag} OK done\r\n`);
47
- break;
48
- default:
49
- socket.write(`${tag} OK ok\r\n`);
50
- }
51
- }
52
- });
53
- socket.write(`* OK [CAPABILITY ${CAPS}] ready\r\n`);
54
- });
55
-
56
- const listen = server => new Promise(resolve => server.listen(0, '127.0.0.1', () => resolve(server.address().port)));
57
-
58
- module.exports['Proxy: cleartext connection established through a proxy socket'] = async test => {
59
- let server = createServer();
60
- let port = await listen(server);
61
-
62
- // Mock proxyConnection to hand back a real cleartext socket to our server.
63
- const { ImapFlow } = proxyquire('../lib/imap-flow', {
64
- './proxy-connection': {
65
- proxyConnection: async () => net.connect(port, '127.0.0.1'),
66
- detachEarlyErrorHandler: () => {}
67
- }
68
- });
69
-
70
- let client = new ImapFlow({
71
- host: '127.0.0.1',
72
- port,
73
- secure: false,
74
- proxy: 'socks://127.0.0.1:1080',
75
- disableAutoIdle: true,
76
- disableCompression: true,
77
- logger: false,
78
- auth: { user: 'test', pass: 'secret' }
79
- });
80
- client.on('error', () => {});
81
-
82
- await client.connect();
83
- test.ok(client.usable, 'session established over the proxied socket');
84
- test.ok(client.authenticated);
85
-
86
- await client.logout();
87
- client.close();
88
- server.close();
89
- test.done();
90
- };
91
-
92
- module.exports['Proxy: TLS connection established through a proxy socket'] = async test => {
93
- let server = tls.createServer({ cert, key }, socket => {
94
- socket.on('error', () => {});
95
- let buf = '';
96
- socket.on('data', data => {
97
- buf += data.toString('binary');
98
- let idx;
99
- while ((idx = buf.indexOf('\r\n')) >= 0) {
100
- let line = buf.slice(0, idx);
101
- buf = buf.slice(idx + 2);
102
- let parts = line.split(' ');
103
- let tag = parts[0];
104
- let cmd = (parts[1] || '').toUpperCase();
105
- if (cmd === 'CAPABILITY') socket.write(`* CAPABILITY ${CAPS}\r\n${tag} OK done\r\n`);
106
- else if (cmd === 'ID') socket.write(`* ID ("name" "m")\r\n${tag} OK done\r\n`);
107
- else if (cmd === 'NAMESPACE') socket.write(`* NAMESPACE (("" "/")) NIL NIL\r\n${tag} OK done\r\n`);
108
- else if (cmd === 'LOGOUT') socket.write(`* BYE bye\r\n${tag} OK done\r\n`);
109
- else socket.write(`${tag} OK ok\r\n`);
110
- }
111
- });
112
- socket.write(`* OK [CAPABILITY ${CAPS}] ready\r\n`);
113
- });
114
- let port = await listen(server);
115
-
116
- // proxyConnection returns a plaintext TCP socket; the client then wraps it in TLS.
117
- const { ImapFlow } = proxyquire('../lib/imap-flow', {
118
- './proxy-connection': {
119
- proxyConnection: async () => net.connect(port, '127.0.0.1'),
120
- detachEarlyErrorHandler: () => {}
121
- }
122
- });
123
-
124
- let client = new ImapFlow({
125
- host: '127.0.0.1',
126
- port,
127
- secure: true,
128
- servername: 'localhost',
129
- tls: { rejectUnauthorized: false },
130
- proxy: 'socks://127.0.0.1:1080',
131
- disableAutoIdle: true,
132
- disableCompression: true,
133
- logger: false,
134
- auth: { user: 'test', pass: 'secret' }
135
- });
136
- client.on('error', () => {});
137
-
138
- await client.connect();
139
- test.ok(client.secureConnection, 'TLS handshake over the proxy socket');
140
- test.ok(client.usable);
141
-
142
- await client.logout();
143
- client.close();
144
- server.close();
145
- test.done();
146
- };
147
-
148
- module.exports['Proxy: a null proxy socket rejects connect'] = async test => {
149
- const { ImapFlow } = proxyquire('../lib/imap-flow', {
150
- './proxy-connection': {
151
- proxyConnection: async () => null, // proxy setup yields no socket
152
- detachEarlyErrorHandler: () => {}
153
- }
154
- });
155
-
156
- let client = new ImapFlow({
157
- host: '127.0.0.1',
158
- port: 1,
159
- secure: false,
160
- proxy: 'socks://127.0.0.1:1080',
161
- disableAutoIdle: true,
162
- disableCompression: true,
163
- logger: false,
164
- auth: { user: 'test', pass: 'secret' }
165
- });
166
- client.on('error', () => {});
167
-
168
- let err = null;
169
- try {
170
- await client.connect();
171
- } catch (e) {
172
- err = e;
173
- }
174
- test.ok(err, 'connect rejected when proxy returns no socket');
175
- test.equal(err.code, 'ProxyError');
176
-
177
- client.close();
178
- test.done();
179
- };
180
-
181
- module.exports['Proxy: proxyConnection throwing rejects connect'] = async test => {
182
- const { ImapFlow } = proxyquire('../lib/imap-flow', {
183
- './proxy-connection': {
184
- proxyConnection: async () => {
185
- let e = new Error('SOCKS handshake failed');
186
- e.code = 'ESOCKS';
187
- throw e;
188
- },
189
- detachEarlyErrorHandler: () => {}
190
- }
191
- });
192
-
193
- let client = new ImapFlow({
194
- host: '127.0.0.1',
195
- port: 1,
196
- secure: false,
197
- proxy: 'socks://127.0.0.1:1080',
198
- disableAutoIdle: true,
199
- disableCompression: true,
200
- logger: false,
201
- auth: { user: 'test', pass: 'secret' }
202
- });
203
- client.on('error', () => {});
204
-
205
- let err = null;
206
- try {
207
- await client.connect();
208
- } catch (e) {
209
- err = e;
210
- }
211
- test.ok(err);
212
- test.equal(err.code, 'ESOCKS');
213
-
214
- client.close();
215
- test.done();
216
- };
217
-
218
- // ---------------------------------------------------------------------------
219
- // End to end through a real HTTP CONNECT proxy (ImapFlow's own CONNECT helper)
220
- // ---------------------------------------------------------------------------
221
-
222
- // Minimal HTTP CONNECT proxy. `onConnect(socket, headers)` may take over the exchange (used to
223
- // simulate a proxy that never answers); otherwise the tunnel is established and piped. With
224
- // `coalesce`, the 200 response is held back until the destination speaks and then written in the
225
- // same segment as the destination's greeting - which is what a real proxy often does.
226
- const createHttpProxy = (onConnect, { coalesce = false } = {}) =>
227
- net.createServer(socket => {
228
- socket.on('error', () => {});
229
- let buf = '';
230
- const onData = data => {
231
- buf += data.toString('binary');
232
- let idx = buf.indexOf('\r\n\r\n');
233
- if (idx < 0) {
234
- return;
235
- }
236
- socket.removeListener('data', onData);
237
- let headers = buf.slice(0, idx);
238
- let rest = buf.slice(idx + 4);
239
- let authority = (headers.split('\r\n')[0].match(/^CONNECT (\S+)/) || [])[1] || '';
240
-
241
- if (onConnect && onConnect(socket, headers)) {
242
- return;
243
- }
244
-
245
- let [, targetPort] = authority.split(':');
246
- let upstream = net.connect(Number(targetPort), '127.0.0.1', () => {
247
- if (rest) {
248
- upstream.write(Buffer.from(rest, 'binary'));
249
- }
250
-
251
- const startTunnel = () => {
252
- socket.pipe(upstream);
253
- upstream.pipe(socket);
254
- };
255
-
256
- if (!coalesce) {
257
- socket.write('HTTP/1.1 200 Connection established\r\n\r\n');
258
- return startTunnel();
259
- }
260
-
261
- // Hold the CONNECT response until the destination sends something, then emit both
262
- // in a single write so the client has to preserve the trailing bytes itself.
263
- upstream.once('data', firstChunk => {
264
- socket.write(Buffer.concat([Buffer.from('HTTP/1.1 200 Connection established\r\n\r\n'), firstChunk]));
265
- startTunnel();
266
- });
267
- });
268
- upstream.on('error', () => socket.destroy());
269
- };
270
- socket.on('data', onData);
271
- });
272
-
273
- module.exports['Proxy: session established through a real HTTP CONNECT proxy'] = async test => {
274
- let imapServer = createServer();
275
- let imapPort = await listen(imapServer);
276
- let proxy = createHttpProxy();
277
- let proxyPort = await listen(proxy);
278
-
279
- let client = new ImapFlow({
280
- host: '127.0.0.1',
281
- port: imapPort,
282
- secure: false,
283
- proxy: `http://127.0.0.1:${proxyPort}`,
284
- disableAutoIdle: true,
285
- disableCompression: true,
286
- logger: false,
287
- auth: { user: 'test', pass: 'secret' }
288
- });
289
- client.on('error', () => {});
290
-
291
- await client.connect();
292
- test.ok(client.usable, 'session established through the CONNECT tunnel');
293
- test.ok(client.authenticated);
294
-
295
- await client.logout();
296
- client.close();
297
- proxy.close();
298
- imapServer.close();
299
- test.done();
300
- };
301
-
302
- module.exports['Proxy: a greeting coalesced with the CONNECT response is not lost'] = async test => {
303
- // The proxy answers CONNECT and the destination greeting in one segment. The bytes after the
304
- // header terminator belong to the tunnel, so they have to reach the parser - otherwise the
305
- // greeting is swallowed and connect() only fails at the greeting timeout.
306
- let imapServer = createServer();
307
- let imapPort = await listen(imapServer);
308
- let proxy = createHttpProxy(null, { coalesce: true });
309
- let proxyPort = await listen(proxy);
310
-
311
- let client = new ImapFlow({
312
- host: '127.0.0.1',
313
- port: imapPort,
314
- secure: false,
315
- proxy: `http://127.0.0.1:${proxyPort}`,
316
- greetingTimeout: 1500,
317
- disableAutoIdle: true,
318
- disableCompression: true,
319
- logger: false,
320
- auth: { user: 'test', pass: 'secret' }
321
- });
322
- client.on('error', () => {});
323
-
324
- await client.connect();
325
- test.ok(client.usable, 'the coalesced greeting was parsed, so the session came up');
326
- test.ok(client.capabilities.has('IMAP4rev1'), 'capabilities from the coalesced greeting are present');
327
-
328
- await client.logout();
329
- client.close();
330
- proxy.close();
331
- imapServer.close();
332
- test.done();
333
- };
334
-
335
- module.exports['Proxy: a stalled proxy negotiation is bounded by connectionTimeout'] = async test => {
336
- // The proxy accepts the CONNECT request and then goes silent
337
- let proxy = createHttpProxy(() => true);
338
- let proxyPort = await listen(proxy);
339
-
340
- let client = new ImapFlow({
341
- host: '127.0.0.1',
342
- port: 143,
343
- secure: false,
344
- proxy: `http://127.0.0.1:${proxyPort}`,
345
- connectionTimeout: 150,
346
- disableAutoIdle: true,
347
- disableCompression: true,
348
- logger: false,
349
- auth: { user: 'test', pass: 'secret' }
350
- });
351
- client.on('error', () => {});
352
-
353
- let started = Date.now();
354
- let err = await client.connect().then(
355
- () => null,
356
- e => e
357
- );
358
-
359
- test.ok(err, 'connect rejected instead of hanging in proxy negotiation');
360
- test.equal(err.code, 'CONNECT_TIMEOUT', 'the documented connection timeout covers proxy negotiation');
361
- test.ok(Date.now() - started < 3000, 'the deadline applied to the proxy phase, not only to the transport');
362
-
363
- client.close();
364
- proxy.close();
365
- test.done();
366
- };