imapflow 1.0.169 → 1.0.170
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 +7 -0
- package/lib/commands/authenticate.js +3 -4
- package/lib/commands/select.js +1 -1
- package/lib/imap-flow.js +48 -12
- package/lib/tools.js +5 -1
- package/lib/types.d.ts +19 -2
- package/package.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.0.170](https://github.com/postalsys/imapflow/compare/v1.0.169...v1.0.170) (2024-12-05)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* **uidvalidity:** Do not expect UIDVALIDITY to always exist ([7761653](https://github.com/postalsys/imapflow/commit/7761653e92965a09be8a872813363eb81f7f3d9a))
|
|
9
|
+
|
|
3
10
|
## [1.0.169](https://github.com/postalsys/imapflow/compare/v1.0.168...v1.0.169) (2024-11-08)
|
|
4
11
|
|
|
5
12
|
|
|
@@ -151,13 +151,12 @@ module.exports = async (connection, username, { accessToken, password }) => {
|
|
|
151
151
|
}
|
|
152
152
|
|
|
153
153
|
if (password) {
|
|
154
|
-
if (connection.capabilities.has('AUTH=LOGIN')) {
|
|
155
|
-
return await authLogin(connection, username, password);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
154
|
if (connection.capabilities.has('AUTH=PLAIN')) {
|
|
159
155
|
return await authPlain(connection, username, password);
|
|
160
156
|
}
|
|
157
|
+
if (connection.capabilities.has('AUTH=LOGIN')) {
|
|
158
|
+
return await authLogin(connection, username, password);
|
|
159
|
+
}
|
|
161
160
|
}
|
|
162
161
|
|
|
163
162
|
throw new Error('Unsupported authentication mechanism');
|
package/lib/commands/select.js
CHANGED
|
@@ -40,7 +40,7 @@ module.exports = async (connection, path, options) => {
|
|
|
40
40
|
extraArgs.push([
|
|
41
41
|
{ type: 'ATOM', value: 'QRESYNC' },
|
|
42
42
|
[
|
|
43
|
-
{ type: 'ATOM', value: options.uidValidity
|
|
43
|
+
{ type: 'ATOM', value: options.uidValidity?.toString() },
|
|
44
44
|
{ type: 'ATOM', value: options.changedSince.toString() }
|
|
45
45
|
]
|
|
46
46
|
]);
|
package/lib/imap-flow.js
CHANGED
|
@@ -119,8 +119,19 @@ class ImapFlow extends EventEmitter {
|
|
|
119
119
|
* @param {Object} options IMAP connection options
|
|
120
120
|
* @param {String} options.host Hostname of the IMAP server
|
|
121
121
|
* @param {Number} options.port Port number for the IMAP server
|
|
122
|
-
* @param {Boolean} [options.secure=false] Should the connection be established over TLS.
|
|
123
|
-
*
|
|
122
|
+
* @param {Boolean} [options.secure=false] Should the connection be established immediately and directly over TLS? Typically on port 993.
|
|
123
|
+
* @param {Boolean} [options.doSTARTTLS=undefined] Should the connection be established using STARTTLS?
|
|
124
|
+
* * If `true`, the connection is first established as unencrypted and then upgraded to TLS using STARTTLS, before authentication.
|
|
125
|
+
* If the server does not advertize the `STARTTLS` `CAPABILITY`, or the upgrade fails for other reasons, then the connection fails.
|
|
126
|
+
* Note: The combination `secure=true` (direct TLS) and `doSTARTTLS=true` is invalid.
|
|
127
|
+
* * If `false`, then STARTTLS will not be used, even if the server advertizes it in IMAP `CAPABILITY`.
|
|
128
|
+
* This helps with servers that have a broken TLS configuration.
|
|
129
|
+
* If `doSTARTTLS=false` and `secure=false`, then a plain unencrypted socket is used.
|
|
130
|
+
* Be sure to clearly warn the user about the consequences.
|
|
131
|
+
* * If `undefined` (default) and `secure=false` (default), the connection is upgraded using STARTTLS before authentication, /only if possible/ .
|
|
132
|
+
* If not possible, the connection will use an unencrypted plain socket.
|
|
133
|
+
* This can mean TLS is used under normal circumstances, but a serious attacker can force an unencrypted connection and steal passwords,
|
|
134
|
+
* called "downgrade attack". This can lead to a false sense of security. Be sure to warn the user.
|
|
124
135
|
* @param {String} [options.servername] Servername for SNI (or when host is set to an IP address)
|
|
125
136
|
* @param {Boolean} [options.disableCompression=false] if `true` then client does not try to use COMPRESS=DEFLATE extension
|
|
126
137
|
* @param {Object} options.auth Authentication options. Authentication is requested automatically during <code>connect()</code>
|
|
@@ -589,7 +600,7 @@ class ImapFlow extends EventEmitter {
|
|
|
589
600
|
|
|
590
601
|
if (err.responseStatus === 'NO' && txt.includes('Some of the requested messages no longer exist')) {
|
|
591
602
|
// Treat as successful response
|
|
592
|
-
this.log.warn({ msg: 'Partial
|
|
603
|
+
this.log.warn({ msg: 'Partial FETCH response', cid: this.id, err });
|
|
593
604
|
await new Promise(resolve => request.resolve({ response: parsed, next: resolve }));
|
|
594
605
|
break;
|
|
595
606
|
}
|
|
@@ -715,10 +726,7 @@ class ImapFlow extends EventEmitter {
|
|
|
715
726
|
this.idRequested = await this.run('ID', this.clientInfo);
|
|
716
727
|
}
|
|
717
728
|
|
|
718
|
-
|
|
719
|
-
if (!this.secureConnection) {
|
|
720
|
-
await this.upgradeConnection();
|
|
721
|
-
}
|
|
729
|
+
await this.upgradeToSTARTTLS();
|
|
722
730
|
|
|
723
731
|
let authenticated = await this.authenticate();
|
|
724
732
|
if (!authenticated) {
|
|
@@ -837,21 +845,47 @@ class ImapFlow extends EventEmitter {
|
|
|
837
845
|
});
|
|
838
846
|
}
|
|
839
847
|
|
|
840
|
-
|
|
848
|
+
_failSTARTTLS() {
|
|
849
|
+
if (this.options.doSTARTTLS === true) {
|
|
850
|
+
// STARTTLS configured as requirement
|
|
851
|
+
let err = new Error('Server does not support STARTTLS');
|
|
852
|
+
err.tlsFailed = true;
|
|
853
|
+
throw err;
|
|
854
|
+
} else {
|
|
855
|
+
// Opportunistic STARTTLS. But it's not possible right now.
|
|
856
|
+
// Attention: Could be a downgrade attack.
|
|
857
|
+
return false;
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
/**
|
|
862
|
+
* Tries to upgrade the connection to TLS using STARTTLS.
|
|
863
|
+
* @throws if STARTTLS is required, but not possible.
|
|
864
|
+
* @returns {boolean} true, if the connection is now protected by TLS, either direct TLS or STARTTLS.
|
|
865
|
+
*/
|
|
866
|
+
async upgradeToSTARTTLS() {
|
|
867
|
+
if (this.options.doSTARTTLS === true && this.options.secure === true) {
|
|
868
|
+
throw new Error('Misconfiguration: Cannot set both secure=true for TLS and doSTARTTLS=true for STARTTLS.');
|
|
869
|
+
}
|
|
870
|
+
|
|
841
871
|
if (this.secureConnection) {
|
|
842
|
-
//
|
|
872
|
+
// Already using direct TLS. No need for STARTTLS.
|
|
843
873
|
return true;
|
|
844
874
|
}
|
|
845
875
|
|
|
846
|
-
if (
|
|
847
|
-
//
|
|
876
|
+
if (this.options.doSTARTTLS === false) {
|
|
877
|
+
// STARTTLS explictly disabled by config
|
|
848
878
|
return false;
|
|
849
879
|
}
|
|
850
880
|
|
|
881
|
+
if (!this.capabilities.has('STARTTLS')) {
|
|
882
|
+
return this._failSTARTTLS();
|
|
883
|
+
}
|
|
884
|
+
|
|
851
885
|
this.expectCapabilityUpdate = true;
|
|
852
886
|
let canUpgrade = await this.run('STARTTLS');
|
|
853
887
|
if (!canUpgrade) {
|
|
854
|
-
return;
|
|
888
|
+
return this._failSTARTTLS();
|
|
855
889
|
}
|
|
856
890
|
|
|
857
891
|
this.socket.unpipe(this.streamer);
|
|
@@ -875,6 +909,7 @@ class ImapFlow extends EventEmitter {
|
|
|
875
909
|
}
|
|
876
910
|
setImmediate(() => this.close());
|
|
877
911
|
this.upgrading = false;
|
|
912
|
+
err.tlsFailed = true;
|
|
878
913
|
reject(err);
|
|
879
914
|
});
|
|
880
915
|
|
|
@@ -884,6 +919,7 @@ class ImapFlow extends EventEmitter {
|
|
|
884
919
|
}
|
|
885
920
|
setImmediate(() => this.close());
|
|
886
921
|
let err = new Error('Failed to upgrade connection in required time');
|
|
922
|
+
err.tlsFailed = true;
|
|
887
923
|
err.code = 'UPGRADE_TIMEOUT';
|
|
888
924
|
reject(err);
|
|
889
925
|
}, UPGRADE_TIMEOUT);
|
package/lib/tools.js
CHANGED
|
@@ -390,7 +390,11 @@ module.exports = {
|
|
|
390
390
|
}
|
|
391
391
|
}
|
|
392
392
|
|
|
393
|
-
map.id =
|
|
393
|
+
map.id =
|
|
394
|
+
map.emailId ||
|
|
395
|
+
createHash('md5')
|
|
396
|
+
.update([path, mailbox.uidValidity?.toString() || '', map.uid.toString()].join(':'))
|
|
397
|
+
.digest('hex');
|
|
394
398
|
}
|
|
395
399
|
|
|
396
400
|
if (map.flags) {
|
package/lib/types.d.ts
CHANGED
|
@@ -8,8 +8,19 @@ declare module "imapflow" {
|
|
|
8
8
|
* @param options - IMAP connection options
|
|
9
9
|
* @param options.host - Hostname of the IMAP server
|
|
10
10
|
* @param options.port - Port number for the IMAP server
|
|
11
|
-
* @param [options.secure = false] - Should the connection be established over TLS.
|
|
12
|
-
*
|
|
11
|
+
* @param [options.secure = false] - Should the connection be established immediately and directly over TLS? Typically on port 993.
|
|
12
|
+
* @param [options.doSTARTTLS] - Should the connection be established using STARTTLS?
|
|
13
|
+
* * If `true`, the connection is first established as unencrypted and then upgraded to TLS using STARTTLS, before authentication.
|
|
14
|
+
* If the server does not advertize the `STARTTLS` `CAPABILITY`, or the upgrade fails for other reasons, then the connection fails.
|
|
15
|
+
* Note: The combination `secure=true` (direct TLS) and `doSTARTTLS=true` is invalid.
|
|
16
|
+
* * If `false`, then STARTTLS will not be used, even if the server advertizes it in IMAP `CAPABILITY`.
|
|
17
|
+
* This helps with servers that have a broken TLS configuration.
|
|
18
|
+
* If `doSTARTTLS=false` and `secure=false`, then a plain unencrypted socket is used.
|
|
19
|
+
* Be sure to clearly warn the user about the consequences.
|
|
20
|
+
* * If `undefined` (default) and `secure=false` (default), the connection is upgraded using STARTTLS before authentication, /only if possible/ .
|
|
21
|
+
* If not possible, the connection will use an unencrypted plain socket.
|
|
22
|
+
* This can mean TLS is used under normal circumstances, but a serious attacker can force an unencrypted connection and steal passwords,
|
|
23
|
+
* called "downgrade attack". This can lead to a false sense of security. Be sure to warn the user.
|
|
13
24
|
* @param [options.servername] - Servername for SNI (or when host is set to an IP address)
|
|
14
25
|
* @param [options.disableCompression = false] - if `true` then client does not try to use COMPRESS=DEFLATE extension
|
|
15
26
|
* @param options.auth - Authentication options. Authentication is requested automatically during <code>connect()</code>
|
|
@@ -41,6 +52,7 @@ declare module "imapflow" {
|
|
|
41
52
|
host: string;
|
|
42
53
|
port: number;
|
|
43
54
|
secure?: boolean;
|
|
55
|
+
doSTARTTLS?: boolean;
|
|
44
56
|
servername?: string;
|
|
45
57
|
disableCompression?: boolean;
|
|
46
58
|
auth: {
|
|
@@ -122,6 +134,11 @@ declare module "imapflow" {
|
|
|
122
134
|
* If `true` then in addition of sending data to logger, ImapFlow emits 'log' events with the same data
|
|
123
135
|
*/
|
|
124
136
|
emitLogs: boolean;
|
|
137
|
+
/**
|
|
138
|
+
* Tries to upgrade the connection to TLS using STARTTLS.
|
|
139
|
+
* @returns true, if the connection is now protected by TLS, either direct TLS or STARTTLS.
|
|
140
|
+
*/
|
|
141
|
+
upgradeToSTARTTLS(): boolean;
|
|
125
142
|
/**
|
|
126
143
|
* Initiates a connection against IMAP server. Throws if anything goes wrong. This is something you have to call before you can run any IMAP commands
|
|
127
144
|
* @example
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "imapflow",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.170",
|
|
4
4
|
"description": "IMAP Client for Node",
|
|
5
5
|
"main": "./lib/imap-flow.js",
|
|
6
6
|
"scripts": {
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"@babel/eslint-plugin": "7.25.9",
|
|
33
33
|
"@babel/plugin-syntax-class-properties": "7.12.13",
|
|
34
34
|
"@babel/preset-env": "7.26.0",
|
|
35
|
-
"@types/node": "22.
|
|
35
|
+
"@types/node": "22.10.1",
|
|
36
36
|
"eslint": "8.57.0",
|
|
37
37
|
"eslint-config-nodemailer": "1.2.0",
|
|
38
38
|
"eslint-config-prettier": "9.1.0",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"grunt-cli": "1.5.0",
|
|
41
41
|
"grunt-contrib-nodeunit": "5.0.0",
|
|
42
42
|
"grunt-eslint": "24.3.0",
|
|
43
|
-
"imapflow-jsdoc-template": "3.4.0-imapflow.
|
|
43
|
+
"imapflow-jsdoc-template": "3.4.0-imapflow.2",
|
|
44
44
|
"jsdoc": "3.6.11",
|
|
45
45
|
"st": "3.0.1",
|
|
46
46
|
"tsd-jsdoc": "2.5.0"
|
|
@@ -49,9 +49,9 @@
|
|
|
49
49
|
"encoding-japanese": "2.2.0",
|
|
50
50
|
"iconv-lite": "0.6.3",
|
|
51
51
|
"libbase64": "1.3.0",
|
|
52
|
-
"libmime": "5.3.
|
|
53
|
-
"libqp": "2.1.
|
|
54
|
-
"mailsplit": "5.4.
|
|
52
|
+
"libmime": "5.3.6",
|
|
53
|
+
"libqp": "2.1.1",
|
|
54
|
+
"mailsplit": "5.4.2",
|
|
55
55
|
"nodemailer": "6.9.16",
|
|
56
56
|
"pino": "9.5.0",
|
|
57
57
|
"socks": "2.8.3"
|