imapflow 2.2.0 → 2.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## [2.2.1](https://github.com/postalsys/imapflow/compare/v2.2.0...v2.2.1) (2026-10-01)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * do not enable IMAP4rev2 on Strato RZimapd ([dce30f8](https://github.com/postalsys/imapflow/commit/dce30f86ab5caa8c1d31320d21c16cb73a611a0c)), closes [#411](https://github.com/postalsys/imapflow/issues/411)
9
+
3
10
  ## [2.2.0](https://github.com/postalsys/imapflow/compare/v2.1.2...v2.2.0) (2026-10-01)
4
11
 
5
12
 
@@ -83,6 +83,12 @@ const RAW_SENSITIVE_COMMANDS = new Set(['LOGIN', 'AUTHENTICATE']);
83
83
  // Stand-in payload for a withheld raw client frame. Fixed width, so the entry says nothing
84
84
  // about the length of what it replaced.
85
85
  const RAW_HIDDEN_PLACEHOLDER = Buffer.from('(* value hidden *)\r\n').toString('base64');
86
+ // Servers, matched by the name in their ID response, that advertise IMAP4rev2 next to
87
+ // IMAP4rev1 and accept ENABLE IMAP4rev2, but misbehave once it is enabled. ENABLE cannot
88
+ // be undone (RFC 5161), so these are kept in IMAP4rev1 mode from the start. Strato's
89
+ // RZimapd (7.1.12, 2026-09) answers every SEARCH in a rev2 session with an ESEARCH
90
+ // response that omits ALL, which reads as "no matches" for any query. Names are lowercase
91
+ const BROKEN_REV2_SERVERS = new Set(['rzimapd']);
86
92
  // Whether any attribute of a command is marked as a secret. Recurses into nested lists because
87
93
  // the command compiler honors `sensitive` at any depth, and the two must agree on what counts.
88
94
  function hasSensitiveAttribute(attributes) {
@@ -1165,6 +1171,12 @@ class ImapFlow extends node_events_1.EventEmitter {
1165
1171
  // re-request ID after LOGIN
1166
1172
  this.idRequested = await this.run('ID', this.clientInfo);
1167
1173
  }
1174
+ let serverName = this.serverInfo?.name;
1175
+ if (!this.skipRev2 && typeof serverName === 'string' && BROKEN_REV2_SERVERS.has(serverName.trim().toLowerCase())) {
1176
+ // Same effect as disableIMAP4rev2, decided before autoEnable() can send the ENABLE
1177
+ this.skipRev2 = true;
1178
+ this.log.info({ msg: 'Not enabling IMAP4rev2, the server is known to answer SEARCH incorrectly in that mode', server: serverName, cid: this.id });
1179
+ }
1168
1180
  // Make sure we have namespace set. This should also throw if Exchange actually failed authentication
1169
1181
  let nsResponse = await this.run('NAMESPACE');
1170
1182
  if (nsResponse && nsResponse.error && nsResponse.status === 'BAD' && /User is authenticated but not connected/i.test(nsResponse.text)) {
@@ -1,3 +1,3 @@
1
1
  export declare const name = "imapflow";
2
- export declare const version = "2.2.0";
2
+ export declare const version = "2.2.1";
3
3
  export declare const homepage = "https://imapflow.com/";
@@ -3,5 +3,5 @@
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.homepage = exports.version = exports.name = void 0;
5
5
  exports.name = "imapflow";
6
- exports.version = "2.2.0";
6
+ exports.version = "2.2.1";
7
7
  exports.homepage = "https://imapflow.com/";
@@ -105,7 +105,7 @@ export interface ImapFlowOptions {
105
105
  disableBinary?: boolean | undefined;
106
106
  /** If true, do not enable supported extensions */
107
107
  disableAutoEnable?: boolean | undefined;
108
- /** If true, do not enable IMAP4rev2 mode even if the server advertises it, and do not treat the advertisement as support for anything IMAP4rev2 implies, such as the extended LIST syntax */
108
+ /** If true, do not enable IMAP4rev2 mode even if the server advertises it, and do not treat the advertisement as support for anything IMAP4rev2 implies, such as the extended LIST syntax. Servers known to misbehave in IMAP4rev2 mode (Strato RZimapd, detected from its ID response) are kept out of it without this option */
109
109
  disableIMAP4rev2?: boolean | undefined;
110
110
  /**
111
111
  * How long to wait for a usable transport, covering DNS resolution, proxy negotiation and the
@@ -42,6 +42,12 @@ const RAW_SENSITIVE_COMMANDS = new Set(['LOGIN', 'AUTHENTICATE']);
42
42
  // Stand-in payload for a withheld raw client frame. Fixed width, so the entry says nothing
43
43
  // about the length of what it replaced.
44
44
  const RAW_HIDDEN_PLACEHOLDER = Buffer.from('(* value hidden *)\r\n').toString('base64');
45
+ // Servers, matched by the name in their ID response, that advertise IMAP4rev2 next to
46
+ // IMAP4rev1 and accept ENABLE IMAP4rev2, but misbehave once it is enabled. ENABLE cannot
47
+ // be undone (RFC 5161), so these are kept in IMAP4rev1 mode from the start. Strato's
48
+ // RZimapd (7.1.12, 2026-09) answers every SEARCH in a rev2 session with an ESEARCH
49
+ // response that omits ALL, which reads as "no matches" for any query. Names are lowercase
50
+ const BROKEN_REV2_SERVERS = new Set(['rzimapd']);
45
51
  // Whether any attribute of a command is marked as a secret. Recurses into nested lists because
46
52
  // the command compiler honors `sensitive` at any depth, and the two must agree on what counts.
47
53
  function hasSensitiveAttribute(attributes) {
@@ -1124,6 +1130,12 @@ export class ImapFlow extends EventEmitter {
1124
1130
  // re-request ID after LOGIN
1125
1131
  this.idRequested = await this.run('ID', this.clientInfo);
1126
1132
  }
1133
+ let serverName = this.serverInfo?.name;
1134
+ if (!this.skipRev2 && typeof serverName === 'string' && BROKEN_REV2_SERVERS.has(serverName.trim().toLowerCase())) {
1135
+ // Same effect as disableIMAP4rev2, decided before autoEnable() can send the ENABLE
1136
+ this.skipRev2 = true;
1137
+ this.log.info({ msg: 'Not enabling IMAP4rev2, the server is known to answer SEARCH incorrectly in that mode', server: serverName, cid: this.id });
1138
+ }
1127
1139
  // Make sure we have namespace set. This should also throw if Exchange actually failed authentication
1128
1140
  let nsResponse = await this.run('NAMESPACE');
1129
1141
  if (nsResponse && nsResponse.error && nsResponse.status === 'BAD' && /User is authenticated but not connected/i.test(nsResponse.text)) {
@@ -1,3 +1,3 @@
1
1
  export declare const name = "imapflow";
2
- export declare const version = "2.2.0";
2
+ export declare const version = "2.2.1";
3
3
  export declare const homepage = "https://imapflow.com/";
@@ -1,4 +1,4 @@
1
1
  // Generated by scripts/build.js from package.json. Do not edit by hand.
2
2
  export const name = "imapflow";
3
- export const version = "2.2.0";
3
+ export const version = "2.2.1";
4
4
  export const homepage = "https://imapflow.com/";
@@ -105,7 +105,7 @@ export interface ImapFlowOptions {
105
105
  disableBinary?: boolean | undefined;
106
106
  /** If true, do not enable supported extensions */
107
107
  disableAutoEnable?: boolean | undefined;
108
- /** If true, do not enable IMAP4rev2 mode even if the server advertises it, and do not treat the advertisement as support for anything IMAP4rev2 implies, such as the extended LIST syntax */
108
+ /** If true, do not enable IMAP4rev2 mode even if the server advertises it, and do not treat the advertisement as support for anything IMAP4rev2 implies, such as the extended LIST syntax. Servers known to misbehave in IMAP4rev2 mode (Strato RZimapd, detected from its ID response) are kept out of it without this option */
109
109
  disableIMAP4rev2?: boolean | undefined;
110
110
  /**
111
111
  * How long to wait for a usable transport, covering DNS resolution, proxy negotiation and the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "imapflow",
3
- "version": "2.2.0",
3
+ "version": "2.2.1",
4
4
  "description": "IMAP Client for Node",
5
5
  "type": "module",
6
6
  "main": "./dist/cjs/imap-flow.js",
@@ -72,13 +72,13 @@
72
72
  "c8": "12.0.0",
73
73
  "eslint": "10.11.0",
74
74
  "eslint-config-prettier": "10.1.8",
75
- "globals": "17.12.0",
75
+ "globals": "17.13.0",
76
76
  "prettier": "3.9.9",
77
77
  "tsx": "4.23.15",
78
78
  "types-node-legacy": "npm:@types/node@20.0.0",
79
79
  "typescript": "6.0.3",
80
- "typescript-eslint": "8.70.1",
81
- "wrangler": "4.142.0"
80
+ "typescript-eslint": "8.71.0",
81
+ "wrangler": "4.145.0"
82
82
  },
83
83
  "dependencies": {
84
84
  "@zone-eu/mailsplit": "5.4.19",