imapflow 1.2.5 → 1.2.6
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/.release-please-manifest.json +1 -1
- package/CHANGELOG.md +8 -0
- package/lib/commands/append.js +2 -7
- package/lib/commands/copy.js +2 -7
- package/lib/commands/create.js +8 -17
- package/lib/commands/delete.js +2 -7
- package/lib/commands/expunge.js +2 -7
- package/lib/commands/move.js +2 -7
- package/lib/commands/quota.js +2 -7
- package/lib/commands/rename.js +2 -7
- package/lib/commands/search.js +2 -7
- package/lib/commands/select.js +2 -6
- package/lib/commands/store.js +2 -7
- package/lib/commands/subscribe.js +2 -7
- package/lib/commands/unsubscribe.js +2 -8
- package/lib/imap-flow.js +68 -63
- package/lib/tools.js +26 -15
- package/package.json +1 -1
- package/test/connection-edge-cases-test.js +518 -0
- package/test/memory-leak-test.js +667 -0
- package/test/tools-test.js +204 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.2.6](https://github.com/postalsys/imapflow/compare/v1.2.5...v1.2.6) (2026-01-12)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* prevent memory leaks in socket and event handling ([2dfa6f3](https://github.com/postalsys/imapflow/commit/2dfa6f3d77ccdf31244752d669ab694f1c157b4c))
|
|
9
|
+
* restore isClosed check and error logging in initialPREAUTH ([ae2ebcf](https://github.com/postalsys/imapflow/commit/ae2ebcff5a65aa870b1af70726f7c5c6ac54395e))
|
|
10
|
+
|
|
3
11
|
## [1.2.5](https://github.com/postalsys/imapflow/compare/v1.2.4...v1.2.5) (2026-01-11)
|
|
4
12
|
|
|
5
13
|
|
package/lib/commands/append.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const {
|
|
3
|
+
const { formatFlag, canUseFlag, formatDateTime, normalizePath, encodePath, comparePaths, enhanceCommandError } = require('../tools.js');
|
|
4
4
|
|
|
5
5
|
// Appends a message to a mailbox
|
|
6
6
|
module.exports = async (connection, destination, content, flags, idate) => {
|
|
@@ -139,12 +139,7 @@ module.exports = async (connection, destination, content, flags, idate) => {
|
|
|
139
139
|
|
|
140
140
|
return map;
|
|
141
141
|
} catch (err) {
|
|
142
|
-
|
|
143
|
-
if (errorCode) {
|
|
144
|
-
err.serverResponseCode = errorCode;
|
|
145
|
-
}
|
|
146
|
-
err.response = await getErrorText(err.response);
|
|
147
|
-
|
|
142
|
+
await enhanceCommandError(err);
|
|
148
143
|
connection.log.warn({ err, cid: connection.id });
|
|
149
144
|
throw err;
|
|
150
145
|
}
|
package/lib/commands/copy.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const {
|
|
3
|
+
const { normalizePath, encodePath, expandRange, enhanceCommandError } = require('../tools.js');
|
|
4
4
|
|
|
5
5
|
// Copies messages from current mailbox to some other mailbox
|
|
6
6
|
module.exports = async (connection, range, destination, options) => {
|
|
@@ -44,12 +44,7 @@ module.exports = async (connection, range, destination, options) => {
|
|
|
44
44
|
|
|
45
45
|
return map;
|
|
46
46
|
} catch (err) {
|
|
47
|
-
|
|
48
|
-
if (errorCode) {
|
|
49
|
-
err.serverResponseCode = errorCode;
|
|
50
|
-
}
|
|
51
|
-
err.response = await getErrorText(err.response);
|
|
52
|
-
|
|
47
|
+
await enhanceCommandError(err);
|
|
53
48
|
connection.log.warn({ err, cid: connection.id });
|
|
54
49
|
return false;
|
|
55
50
|
}
|
package/lib/commands/create.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { encodePath, normalizePath, getStatusCode,
|
|
3
|
+
const { encodePath, normalizePath, getStatusCode, enhanceCommandError } = require('../tools.js');
|
|
4
4
|
|
|
5
5
|
// Creates a new mailbox
|
|
6
6
|
module.exports = async (connection, path) => {
|
|
@@ -61,24 +61,15 @@ module.exports = async (connection, path) => {
|
|
|
61
61
|
return map;
|
|
62
62
|
} catch (err) {
|
|
63
63
|
let errorCode = getStatusCode(err.response);
|
|
64
|
-
if (errorCode) {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
case 'ALREADYEXISTS':
|
|
71
|
-
// no need to do anything, mailbox already exists
|
|
72
|
-
return {
|
|
73
|
-
path,
|
|
74
|
-
created: false
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
if (errorCode) {
|
|
79
|
-
err.serverResponseCode = errorCode;
|
|
64
|
+
if (errorCode === 'ALREADYEXISTS') {
|
|
65
|
+
// no need to do anything, mailbox already exists
|
|
66
|
+
return {
|
|
67
|
+
path,
|
|
68
|
+
created: false
|
|
69
|
+
};
|
|
80
70
|
}
|
|
81
71
|
|
|
72
|
+
await enhanceCommandError(err);
|
|
82
73
|
connection.log.warn({ err, cid: connection.id });
|
|
83
74
|
throw err;
|
|
84
75
|
}
|
package/lib/commands/delete.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { encodePath, normalizePath,
|
|
3
|
+
const { encodePath, normalizePath, enhanceCommandError } = require('../tools.js');
|
|
4
4
|
|
|
5
5
|
// Deletes an existing mailbox
|
|
6
6
|
module.exports = async (connection, path) => {
|
|
@@ -24,12 +24,7 @@ module.exports = async (connection, path) => {
|
|
|
24
24
|
response.next();
|
|
25
25
|
return map;
|
|
26
26
|
} catch (err) {
|
|
27
|
-
|
|
28
|
-
if (errorCode) {
|
|
29
|
-
err.serverResponseCode = errorCode;
|
|
30
|
-
}
|
|
31
|
-
err.response = await getErrorText(err.response);
|
|
32
|
-
|
|
27
|
+
await enhanceCommandError(err);
|
|
33
28
|
connection.log.warn({ err, cid: connection.id });
|
|
34
29
|
throw err;
|
|
35
30
|
}
|
package/lib/commands/expunge.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const {
|
|
3
|
+
const { enhanceCommandError } = require('../tools.js');
|
|
4
4
|
|
|
5
5
|
// Deletes specified messages
|
|
6
6
|
module.exports = async (connection, range, options) => {
|
|
@@ -34,12 +34,7 @@ module.exports = async (connection, range, options) => {
|
|
|
34
34
|
response.next();
|
|
35
35
|
return true;
|
|
36
36
|
} catch (err) {
|
|
37
|
-
|
|
38
|
-
if (errorCode) {
|
|
39
|
-
err.serverResponseCode = errorCode;
|
|
40
|
-
}
|
|
41
|
-
err.response = await getErrorText(err.response);
|
|
42
|
-
|
|
37
|
+
await enhanceCommandError(err);
|
|
43
38
|
connection.log.warn({ err, cid: connection.id });
|
|
44
39
|
return false;
|
|
45
40
|
}
|
package/lib/commands/move.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const {
|
|
3
|
+
const { normalizePath, encodePath, expandRange, enhanceCommandError } = require('../tools.js');
|
|
4
4
|
|
|
5
5
|
// Moves messages from current mailbox to some other mailbox
|
|
6
6
|
module.exports = async (connection, range, destination, options) => {
|
|
@@ -60,12 +60,7 @@ module.exports = async (connection, range, destination, options) => {
|
|
|
60
60
|
checkMoveInfo(response.response);
|
|
61
61
|
return map;
|
|
62
62
|
} catch (err) {
|
|
63
|
-
|
|
64
|
-
if (errorCode) {
|
|
65
|
-
err.serverResponseCode = errorCode;
|
|
66
|
-
}
|
|
67
|
-
err.response = await getErrorText(err.response);
|
|
68
|
-
|
|
63
|
+
await enhanceCommandError(err);
|
|
69
64
|
connection.log.warn({ err, cid: connection.id });
|
|
70
65
|
return false;
|
|
71
66
|
}
|
package/lib/commands/quota.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { encodePath,
|
|
3
|
+
const { encodePath, normalizePath, enhanceCommandError } = require('../tools.js');
|
|
4
4
|
|
|
5
5
|
// Requests quota information for a mailbox
|
|
6
6
|
module.exports = async (connection, path) => {
|
|
@@ -95,12 +95,7 @@ module.exports = async (connection, path) => {
|
|
|
95
95
|
|
|
96
96
|
return map;
|
|
97
97
|
} catch (err) {
|
|
98
|
-
|
|
99
|
-
if (errorCode) {
|
|
100
|
-
err.serverResponseCode = errorCode;
|
|
101
|
-
}
|
|
102
|
-
err.response = await getErrorText(err.response);
|
|
103
|
-
|
|
98
|
+
await enhanceCommandError(err);
|
|
104
99
|
connection.log.warn({ err, cid: connection.id });
|
|
105
100
|
return false;
|
|
106
101
|
}
|
package/lib/commands/rename.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { encodePath, normalizePath,
|
|
3
|
+
const { encodePath, normalizePath, enhanceCommandError } = require('../tools.js');
|
|
4
4
|
|
|
5
5
|
// Renames existing mailbox
|
|
6
6
|
module.exports = async (connection, path, newPath) => {
|
|
@@ -29,12 +29,7 @@ module.exports = async (connection, path, newPath) => {
|
|
|
29
29
|
response.next();
|
|
30
30
|
return map;
|
|
31
31
|
} catch (err) {
|
|
32
|
-
|
|
33
|
-
if (errorCode) {
|
|
34
|
-
err.serverResponseCode = errorCode;
|
|
35
|
-
}
|
|
36
|
-
err.response = await getErrorText(err.response);
|
|
37
|
-
|
|
32
|
+
await enhanceCommandError(err);
|
|
38
33
|
connection.log.warn({ err, cid: connection.id });
|
|
39
34
|
throw err;
|
|
40
35
|
}
|
package/lib/commands/search.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const {
|
|
3
|
+
const { enhanceCommandError } = require('../tools.js');
|
|
4
4
|
const { searchCompiler } = require('../search-compiler.js');
|
|
5
5
|
|
|
6
6
|
// Updates flags for a message
|
|
@@ -43,12 +43,7 @@ module.exports = async (connection, query, options) => {
|
|
|
43
43
|
response.next();
|
|
44
44
|
return Array.from(results).sort((a, b) => a - b);
|
|
45
45
|
} catch (err) {
|
|
46
|
-
|
|
47
|
-
if (errorCode) {
|
|
48
|
-
err.serverResponseCode = errorCode;
|
|
49
|
-
}
|
|
50
|
-
err.response = await getErrorText(err.response);
|
|
51
|
-
|
|
46
|
+
await enhanceCommandError(err);
|
|
52
47
|
connection.log.warn({ err, cid: connection.id });
|
|
53
48
|
return false;
|
|
54
49
|
}
|
package/lib/commands/select.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { encodePath, normalizePath,
|
|
3
|
+
const { encodePath, normalizePath, enhanceCommandError } = require('../tools.js');
|
|
4
4
|
|
|
5
5
|
// Selects a mailbox
|
|
6
6
|
module.exports = async (connection, path, options) => {
|
|
@@ -194,11 +194,7 @@ module.exports = async (connection, path, options) => {
|
|
|
194
194
|
response.next();
|
|
195
195
|
return map;
|
|
196
196
|
} catch (err) {
|
|
197
|
-
|
|
198
|
-
if (errorCode) {
|
|
199
|
-
err.serverResponseCode = errorCode;
|
|
200
|
-
}
|
|
201
|
-
err.response = await getErrorText(err.response);
|
|
197
|
+
await enhanceCommandError(err);
|
|
202
198
|
|
|
203
199
|
if (connection.state === connection.states.SELECTED) {
|
|
204
200
|
// reset selected state
|
package/lib/commands/store.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const {
|
|
3
|
+
const { formatFlag, canUseFlag, enhanceCommandError } = require('../tools.js');
|
|
4
4
|
|
|
5
5
|
// Updates flags for a message
|
|
6
6
|
module.exports = async (connection, range, flags, options) => {
|
|
@@ -72,12 +72,7 @@ module.exports = async (connection, range, flags, options) => {
|
|
|
72
72
|
response.next();
|
|
73
73
|
return true;
|
|
74
74
|
} catch (err) {
|
|
75
|
-
|
|
76
|
-
if (errorCode) {
|
|
77
|
-
err.serverResponseCode = errorCode;
|
|
78
|
-
}
|
|
79
|
-
err.response = await getErrorText(err.response);
|
|
80
|
-
|
|
75
|
+
await enhanceCommandError(err);
|
|
81
76
|
connection.log.warn({ err, cid: connection.id });
|
|
82
77
|
return false;
|
|
83
78
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { encodePath, normalizePath,
|
|
3
|
+
const { encodePath, normalizePath, enhanceCommandError } = require('../tools.js');
|
|
4
4
|
|
|
5
5
|
// Subscribes to a mailbox
|
|
6
6
|
module.exports = async (connection, path) => {
|
|
@@ -17,12 +17,7 @@ module.exports = async (connection, path) => {
|
|
|
17
17
|
response.next();
|
|
18
18
|
return true;
|
|
19
19
|
} catch (err) {
|
|
20
|
-
|
|
21
|
-
if (errorCode) {
|
|
22
|
-
err.serverResponseCode = errorCode;
|
|
23
|
-
}
|
|
24
|
-
err.response = await getErrorText(err.response);
|
|
25
|
-
|
|
20
|
+
await enhanceCommandError(err);
|
|
26
21
|
connection.log.warn({ err, cid: connection.id });
|
|
27
22
|
return false;
|
|
28
23
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { encodePath, normalizePath,
|
|
3
|
+
const { encodePath, normalizePath, enhanceCommandError } = require('../tools.js');
|
|
4
4
|
|
|
5
5
|
// Unsubscribes from a mailbox
|
|
6
6
|
module.exports = async (connection, path) => {
|
|
@@ -17,13 +17,7 @@ module.exports = async (connection, path) => {
|
|
|
17
17
|
response.next();
|
|
18
18
|
return true;
|
|
19
19
|
} catch (err) {
|
|
20
|
-
|
|
21
|
-
if (errorCode) {
|
|
22
|
-
err.serverResponseCode = errorCode;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
err.response = await getErrorText(err.response);
|
|
26
|
-
|
|
20
|
+
await enhanceCommandError(err);
|
|
27
21
|
connection.log.warn({ err, cid: connection.id });
|
|
28
22
|
return false;
|
|
29
23
|
}
|
package/lib/imap-flow.js
CHANGED
|
@@ -372,7 +372,8 @@ class ImapFlow extends EventEmitter {
|
|
|
372
372
|
|
|
373
373
|
this.disableBinary = !!this.options.disableBinary;
|
|
374
374
|
|
|
375
|
-
|
|
375
|
+
// Named error handler for proper cleanup
|
|
376
|
+
this._streamerErrorHandler = err => {
|
|
376
377
|
if (['Z_BUF_ERROR', 'ECONNRESET', 'EPIPE', 'ETIMEDOUT', 'EHOSTUNREACH'].includes(err.code)) {
|
|
377
378
|
// just close the connection, usually nothing but noise
|
|
378
379
|
this.closeAfter();
|
|
@@ -381,7 +382,8 @@ class ImapFlow extends EventEmitter {
|
|
|
381
382
|
|
|
382
383
|
this.log.error({ err, cid: this.id });
|
|
383
384
|
this.emitError(err);
|
|
384
|
-
}
|
|
385
|
+
};
|
|
386
|
+
this.streamer.on('error', this._streamerErrorHandler);
|
|
385
387
|
|
|
386
388
|
// Has the `connect` method already been called
|
|
387
389
|
this._connectCalled = false;
|
|
@@ -772,6 +774,12 @@ class ImapFlow extends EventEmitter {
|
|
|
772
774
|
// Clear any existing handlers first to prevent duplicates
|
|
773
775
|
this.clearSocketHandlers();
|
|
774
776
|
|
|
777
|
+
// Remove temporary connection error handler if present
|
|
778
|
+
if (this._connectErrorHandler && this.socket) {
|
|
779
|
+
this.socket.removeListener('error', this._connectErrorHandler);
|
|
780
|
+
this._connectErrorHandler = null;
|
|
781
|
+
}
|
|
782
|
+
|
|
775
783
|
this._socketError =
|
|
776
784
|
this._socketError ||
|
|
777
785
|
(err => {
|
|
@@ -828,6 +836,12 @@ class ImapFlow extends EventEmitter {
|
|
|
828
836
|
return;
|
|
829
837
|
}
|
|
830
838
|
|
|
839
|
+
// Remove temporary connection error handler if still present
|
|
840
|
+
if (this._connectErrorHandler) {
|
|
841
|
+
this.socket.removeListener('error', this._connectErrorHandler);
|
|
842
|
+
this._connectErrorHandler = null;
|
|
843
|
+
}
|
|
844
|
+
|
|
831
845
|
if (this._socketError) {
|
|
832
846
|
this.socket.removeListener('error', this._socketError);
|
|
833
847
|
this.socket.removeListener('tlsClientError', this._socketError);
|
|
@@ -1043,7 +1057,8 @@ class ImapFlow extends EventEmitter {
|
|
|
1043
1057
|
);
|
|
1044
1058
|
this.clearSocketHandlers();
|
|
1045
1059
|
|
|
1046
|
-
|
|
1060
|
+
// Store error handler for cleanup after successful upgrade
|
|
1061
|
+
const socketPlainErrorHandler = err => {
|
|
1047
1062
|
clearTimeout(this.connectTimeout);
|
|
1048
1063
|
clearTimeout(this.upgradeTimeout);
|
|
1049
1064
|
if (!this.upgrading) {
|
|
@@ -1054,7 +1069,8 @@ class ImapFlow extends EventEmitter {
|
|
|
1054
1069
|
this.upgrading = false;
|
|
1055
1070
|
err.tlsFailed = true;
|
|
1056
1071
|
reject(err);
|
|
1057
|
-
}
|
|
1072
|
+
};
|
|
1073
|
+
socketPlain.once('error', socketPlainErrorHandler);
|
|
1058
1074
|
|
|
1059
1075
|
this.upgradeTimeout = setTimeout(() => {
|
|
1060
1076
|
if (!this.upgrading) {
|
|
@@ -1093,6 +1109,9 @@ class ImapFlow extends EventEmitter {
|
|
|
1093
1109
|
});
|
|
1094
1110
|
}
|
|
1095
1111
|
|
|
1112
|
+
// Clean up the plain socket error handler after successful upgrade
|
|
1113
|
+
socketPlain.removeListener('error', socketPlainErrorHandler);
|
|
1114
|
+
|
|
1096
1115
|
return resolve(true);
|
|
1097
1116
|
} catch (ex) {
|
|
1098
1117
|
this.emitError(ex);
|
|
@@ -1175,13 +1194,7 @@ class ImapFlow extends EventEmitter {
|
|
|
1175
1194
|
throw new AuthenticationFailure('No matching authentication method');
|
|
1176
1195
|
}
|
|
1177
1196
|
|
|
1178
|
-
|
|
1179
|
-
this.greeting = (message.attributes || [])
|
|
1180
|
-
.filter(entry => entry.type === 'TEXT')
|
|
1181
|
-
.map(entry => entry.value)
|
|
1182
|
-
.filter(entry => entry)
|
|
1183
|
-
.join('');
|
|
1184
|
-
|
|
1197
|
+
beginSession(onUnhandledError) {
|
|
1185
1198
|
clearTimeout(this.greetingTimeout);
|
|
1186
1199
|
this.untaggedHandlers.OK = null;
|
|
1187
1200
|
this.untaggedHandlers.PREAUTH = null;
|
|
@@ -1211,45 +1224,30 @@ class ImapFlow extends EventEmitter {
|
|
|
1211
1224
|
return reject(err);
|
|
1212
1225
|
}
|
|
1213
1226
|
|
|
1214
|
-
|
|
1215
|
-
this.emitError(err);
|
|
1227
|
+
onUnhandledError(err);
|
|
1216
1228
|
});
|
|
1217
1229
|
}
|
|
1218
1230
|
|
|
1219
|
-
async
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1231
|
+
async initialOK(message) {
|
|
1232
|
+
this.greeting = (message.attributes || [])
|
|
1233
|
+
.filter(entry => entry.type === 'TEXT')
|
|
1234
|
+
.map(entry => entry.value)
|
|
1235
|
+
.filter(entry => entry)
|
|
1236
|
+
.join('');
|
|
1223
1237
|
|
|
1238
|
+
// ALWAYS emit the error so users can handle it
|
|
1239
|
+
this.beginSession(err => this.emitError(err));
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
async initialPREAUTH() {
|
|
1224
1243
|
if (this.isClosed) {
|
|
1225
1244
|
return;
|
|
1226
1245
|
}
|
|
1227
|
-
|
|
1228
1246
|
this.state = this.states.AUTHENTICATED;
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
if (typeof this.initialResolve === 'function') {
|
|
1234
|
-
let resolve = this.initialResolve;
|
|
1235
|
-
this.initialResolve = false;
|
|
1236
|
-
this.initialReject = false;
|
|
1237
|
-
return resolve();
|
|
1238
|
-
}
|
|
1239
|
-
})
|
|
1240
|
-
.catch(err => {
|
|
1241
|
-
this.log.error({ err, cid: this.id });
|
|
1242
|
-
|
|
1243
|
-
if (typeof this.initialReject === 'function') {
|
|
1244
|
-
clearTimeout(this.greetingTimeout);
|
|
1245
|
-
let reject = this.initialReject;
|
|
1246
|
-
this.initialResolve = false;
|
|
1247
|
-
this.initialReject = false;
|
|
1248
|
-
return reject(err);
|
|
1249
|
-
}
|
|
1250
|
-
|
|
1251
|
-
this.closeAfter();
|
|
1252
|
-
});
|
|
1247
|
+
this.beginSession(err => {
|
|
1248
|
+
this.log.error({ err, cid: this.id });
|
|
1249
|
+
this.closeAfter();
|
|
1250
|
+
});
|
|
1253
1251
|
}
|
|
1254
1252
|
|
|
1255
1253
|
async serverBye(parsed) {
|
|
@@ -1267,9 +1265,9 @@ class ImapFlow extends EventEmitter {
|
|
|
1267
1265
|
this.state = this.states.LOGOUT;
|
|
1268
1266
|
}
|
|
1269
1267
|
|
|
1270
|
-
|
|
1271
|
-
this.rawCapabilities =
|
|
1272
|
-
this.capabilities = updateCapabilities(
|
|
1268
|
+
updateCapabilitiesFromRaw(rawCapabilities) {
|
|
1269
|
+
this.rawCapabilities = rawCapabilities;
|
|
1270
|
+
this.capabilities = updateCapabilities(rawCapabilities);
|
|
1273
1271
|
|
|
1274
1272
|
if (this.capabilities) {
|
|
1275
1273
|
for (let [capa] of this.capabilities) {
|
|
@@ -1284,21 +1282,12 @@ class ImapFlow extends EventEmitter {
|
|
|
1284
1282
|
}
|
|
1285
1283
|
}
|
|
1286
1284
|
|
|
1287
|
-
async
|
|
1288
|
-
this.
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
if (this.capabilities) {
|
|
1292
|
-
for (let [capa] of this.capabilities) {
|
|
1293
|
-
if (/^AUTH=/i.test(capa) && !this.authCapabilities.has(capa.toUpperCase())) {
|
|
1294
|
-
this.authCapabilities.set(capa.toUpperCase(), false);
|
|
1295
|
-
}
|
|
1296
|
-
}
|
|
1297
|
-
}
|
|
1285
|
+
async sectionCapability(section) {
|
|
1286
|
+
this.updateCapabilitiesFromRaw(section);
|
|
1287
|
+
}
|
|
1298
1288
|
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
}
|
|
1289
|
+
async untaggedCapability(untagged) {
|
|
1290
|
+
this.updateCapabilitiesFromRaw(untagged.attributes);
|
|
1302
1291
|
}
|
|
1303
1292
|
|
|
1304
1293
|
async untaggedExists(untagged) {
|
|
@@ -1637,13 +1626,15 @@ class ImapFlow extends EventEmitter {
|
|
|
1637
1626
|
|
|
1638
1627
|
this.writeSocket = this.socket;
|
|
1639
1628
|
|
|
1640
|
-
|
|
1629
|
+
// Store connection error handler for cleanup
|
|
1630
|
+
this._connectErrorHandler = err => {
|
|
1641
1631
|
clearTimeout(this.connectTimeout);
|
|
1642
1632
|
clearTimeout(this.greetingTimeout);
|
|
1643
1633
|
this.closeAfter();
|
|
1644
1634
|
this.log.error({ err, cid: this.id });
|
|
1645
1635
|
reject(err);
|
|
1646
|
-
}
|
|
1636
|
+
};
|
|
1637
|
+
this.socket.on('error', this._connectErrorHandler);
|
|
1647
1638
|
});
|
|
1648
1639
|
}
|
|
1649
1640
|
|
|
@@ -1791,11 +1782,13 @@ class ImapFlow extends EventEmitter {
|
|
|
1791
1782
|
// cleanup streamer
|
|
1792
1783
|
if (this.streamer) {
|
|
1793
1784
|
try {
|
|
1794
|
-
// remove our listeners
|
|
1785
|
+
// remove our listeners explicitly by reference
|
|
1795
1786
|
if (this.socketReadable) {
|
|
1796
1787
|
this.streamer.removeListener('readable', this.socketReadable);
|
|
1797
1788
|
}
|
|
1798
|
-
this.
|
|
1789
|
+
if (this._streamerErrorHandler) {
|
|
1790
|
+
this.streamer.removeListener('error', this._streamerErrorHandler);
|
|
1791
|
+
}
|
|
1799
1792
|
if (!this.streamer.destroyed) {
|
|
1800
1793
|
this.streamer.destroy();
|
|
1801
1794
|
}
|
|
@@ -1841,6 +1834,18 @@ class ImapFlow extends EventEmitter {
|
|
|
1841
1834
|
}
|
|
1842
1835
|
}
|
|
1843
1836
|
|
|
1837
|
+
// Explicit nullification to help garbage collection
|
|
1838
|
+
this.socket = null;
|
|
1839
|
+
this.writeSocket = null;
|
|
1840
|
+
this._inflate = null;
|
|
1841
|
+
this._deflate = null;
|
|
1842
|
+
this._streamerErrorHandler = null;
|
|
1843
|
+
this._connectErrorHandler = null;
|
|
1844
|
+
this._socketError = null;
|
|
1845
|
+
this._socketClose = null;
|
|
1846
|
+
this._socketEnd = null;
|
|
1847
|
+
this._socketTimeout = null;
|
|
1848
|
+
|
|
1844
1849
|
this.log.trace({ msg: 'Connection closed', cid: this.id });
|
|
1845
1850
|
this.emit('close');
|
|
1846
1851
|
} catch (ex) {
|
package/lib/tools.js
CHANGED
|
@@ -115,6 +115,15 @@ const tools = {
|
|
|
115
115
|
return (await compiler(response)).toString();
|
|
116
116
|
},
|
|
117
117
|
|
|
118
|
+
async enhanceCommandError(err) {
|
|
119
|
+
let errorCode = tools.getStatusCode(err.response);
|
|
120
|
+
if (errorCode) {
|
|
121
|
+
err.serverResponseCode = errorCode;
|
|
122
|
+
}
|
|
123
|
+
err.response = await tools.getErrorText(err.response);
|
|
124
|
+
return err;
|
|
125
|
+
},
|
|
126
|
+
|
|
118
127
|
getFolderTree(folders) {
|
|
119
128
|
let tree = {
|
|
120
129
|
root: true,
|
|
@@ -758,38 +767,40 @@ const tools = {
|
|
|
758
767
|
return Object.prototype.toString.call(obj) === '[object Date]';
|
|
759
768
|
},
|
|
760
769
|
|
|
761
|
-
|
|
770
|
+
toValidDate(value) {
|
|
771
|
+
if (!value) {
|
|
772
|
+
return null;
|
|
773
|
+
}
|
|
762
774
|
if (typeof value === 'string') {
|
|
763
775
|
value = new Date(value);
|
|
764
776
|
}
|
|
777
|
+
if (!tools.isDate(value) || value.toString() === 'Invalid Date') {
|
|
778
|
+
return null;
|
|
779
|
+
}
|
|
780
|
+
return value;
|
|
781
|
+
},
|
|
765
782
|
|
|
766
|
-
|
|
783
|
+
formatDate(value) {
|
|
784
|
+
value = tools.toValidDate(value);
|
|
785
|
+
if (!value) {
|
|
767
786
|
return;
|
|
768
787
|
}
|
|
769
788
|
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
value.reverse();
|
|
789
|
+
let dateParts = value.toISOString().substr(0, 10).split('-');
|
|
790
|
+
dateParts.reverse();
|
|
773
791
|
|
|
774
792
|
let months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
|
|
775
|
-
|
|
793
|
+
dateParts[1] = months[Number(dateParts[1]) - 1];
|
|
776
794
|
|
|
777
|
-
return
|
|
795
|
+
return dateParts.join('-');
|
|
778
796
|
},
|
|
779
797
|
|
|
780
798
|
formatDateTime(value) {
|
|
799
|
+
value = tools.toValidDate(value);
|
|
781
800
|
if (!value) {
|
|
782
801
|
return;
|
|
783
802
|
}
|
|
784
803
|
|
|
785
|
-
if (typeof value === 'string') {
|
|
786
|
-
value = new Date(value);
|
|
787
|
-
}
|
|
788
|
-
|
|
789
|
-
if (Object.prototype.toString(value) !== '[object Object]' || value.toString() === 'Invalid Date') {
|
|
790
|
-
return;
|
|
791
|
-
}
|
|
792
|
-
|
|
793
804
|
let dateStr = tools.formatDate(value).replace(/^0/, ' '); //starts with date-day-fixed with leading 0 replaced by SP
|
|
794
805
|
let timeStr = value.toISOString().substr(11, 8);
|
|
795
806
|
|