imapflow 2.2.2 → 2.2.4
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 +15 -0
- package/dist/cjs/package-info.d.ts +1 -1
- package/dist/cjs/package-info.js +1 -1
- package/dist/cjs/search-compiler.js +21 -31
- package/dist/esm/package-info.d.ts +1 -1
- package/dist/esm/package-info.js +1 -1
- package/dist/esm/search-compiler.js +21 -31
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [2.2.4](https://github.com/postalsys/imapflow/compare/v2.2.3...v2.2.4) (2026-10-03)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* **deps:** hold pino at 10.3.x to match EmailEngine ([65062e0](https://github.com/postalsys/imapflow/commit/65062e0a3fb66527e7296ee288e658ef215851d4))
|
|
9
|
+
|
|
10
|
+
## [2.2.3](https://github.com/postalsys/imapflow/compare/v2.2.2...v2.2.3) (2026-10-03)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* reject NUL in a non-ASCII search value instead of sending it as a literal ([8c35084](https://github.com/postalsys/imapflow/commit/8c35084f12376a1195989bf99621c2587f548aca))
|
|
16
|
+
* send non-ASCII search values as literals ([b58d93e](https://github.com/postalsys/imapflow/commit/b58d93e49dd37ef008418c2670afaf53b3d78034)), closes [#417](https://github.com/postalsys/imapflow/issues/417)
|
|
17
|
+
|
|
3
18
|
## [2.2.2](https://github.com/postalsys/imapflow/compare/v2.2.1...v2.2.2) (2026-10-03)
|
|
4
19
|
|
|
5
20
|
|
package/dist/cjs/package-info.js
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
4
|
exports.searchCompiler = void 0;
|
|
5
5
|
const tools_js_1 = require("./tools.js");
|
|
6
|
+
// Matches any character outside the ASCII range
|
|
7
|
+
const UNICODE_PATTERN = /[^\x00-\x7F]/;
|
|
6
8
|
/**
|
|
7
9
|
* Sets a boolean flag in the IMAP search attributes.
|
|
8
10
|
* Automatically handles UN- prefixing for falsy values.
|
|
@@ -38,6 +40,17 @@ let setBoolOpt = (attributes, term, value) => {
|
|
|
38
40
|
* @returns The joined sequence set string
|
|
39
41
|
*/
|
|
40
42
|
let toSequenceValue = (value) => [].concat(value).join(',');
|
|
43
|
+
/**
|
|
44
|
+
* Builds the token for a search value. A quoted string may only carry 7-bit
|
|
45
|
+
* characters (RFC 3501 section 9), so a non-ASCII value is sent as a literal;
|
|
46
|
+
* strict servers reply BAD to UTF-8 inside a quoted string. A literal can not
|
|
47
|
+
* carry NUL either (CHAR8 is %x01-ff), so such a value stays an ATOM and the
|
|
48
|
+
* compiler rejects it.
|
|
49
|
+
*
|
|
50
|
+
* @param value - The search value
|
|
51
|
+
* @returns An ATOM token (quoted by the compiler when needed), or a LITERAL token
|
|
52
|
+
*/
|
|
53
|
+
let toSearchValue = (value) => UNICODE_PATTERN.test(value) && !value.includes('\0') ? { type: 'LITERAL', value: Buffer.from(value) } : { type: 'ATOM', value };
|
|
41
54
|
/**
|
|
42
55
|
* Adds a search option with its value(s) to the attributes array.
|
|
43
56
|
* Handles NOT operations and array values.
|
|
@@ -54,10 +67,10 @@ let setOpt = (attributes, term, value) => {
|
|
|
54
67
|
attributes.push({ type: 'ATOM', value: term.toUpperCase() });
|
|
55
68
|
// Handle array values (e.g. HEADER name/value pairs)
|
|
56
69
|
if (Array.isArray(value)) {
|
|
57
|
-
value.forEach(entry => attributes.push(
|
|
70
|
+
value.forEach(entry => attributes.push(toSearchValue((entry || '').toString())));
|
|
58
71
|
}
|
|
59
72
|
else {
|
|
60
|
-
attributes.push(
|
|
73
|
+
attributes.push(toSearchValue(value.toString()));
|
|
61
74
|
}
|
|
62
75
|
};
|
|
63
76
|
/**
|
|
@@ -89,8 +102,6 @@ let processDateField = (attributes, term, value) => {
|
|
|
89
102
|
}
|
|
90
103
|
setOpt(attributes, term, formatted);
|
|
91
104
|
};
|
|
92
|
-
// Pre-compiled regex for better performance
|
|
93
|
-
const UNICODE_PATTERN = /[^\x00-\x7F]/;
|
|
94
105
|
/**
|
|
95
106
|
* Throws a coded search compilation error.
|
|
96
107
|
*
|
|
@@ -103,20 +114,13 @@ let fail = (code, message) => {
|
|
|
103
114
|
throw error;
|
|
104
115
|
};
|
|
105
116
|
/**
|
|
106
|
-
* Checks
|
|
107
|
-
*
|
|
117
|
+
* Checks whether any search value was compiled into a literal, which only
|
|
118
|
+
* happens for non-ASCII values and means CHARSET UTF-8 needs to be specified.
|
|
108
119
|
*
|
|
109
|
-
* @param
|
|
110
|
-
* @returns True if
|
|
120
|
+
* @param attributes - Compiled search attributes
|
|
121
|
+
* @returns True if a LITERAL token is present
|
|
111
122
|
*/
|
|
112
|
-
let
|
|
113
|
-
if (!str || typeof str !== 'string') {
|
|
114
|
-
return false;
|
|
115
|
-
}
|
|
116
|
-
// Regex test is ~3-5x faster than Buffer.byteLength
|
|
117
|
-
// Matches any character outside ASCII range (0x00-0x7F)
|
|
118
|
-
return UNICODE_PATTERN.test(str);
|
|
119
|
-
};
|
|
123
|
+
let hasLiteral = (attributes) => attributes.some(attr => (Array.isArray(attr) ? hasLiteral(attr) : attr.type === 'LITERAL'));
|
|
120
124
|
/**
|
|
121
125
|
* Compiles a JavaScript object query into IMAP search command attributes.
|
|
122
126
|
* Supports standard IMAP search criteria and extensions like OBJECTID and Gmail extensions.
|
|
@@ -145,8 +149,6 @@ let isUnicodeString = (str) => {
|
|
|
145
149
|
*/
|
|
146
150
|
const searchCompiler = (connection, query) => {
|
|
147
151
|
const attributes = [];
|
|
148
|
-
// Track if we need to specify UTF-8 charset
|
|
149
|
-
let hasUnicode = false;
|
|
150
152
|
/**
|
|
151
153
|
* Recursively walks through the query object and builds IMAP attributes.
|
|
152
154
|
* @param params - Query parameters to process
|
|
@@ -236,9 +238,6 @@ const searchCompiler = (connection, query) => {
|
|
|
236
238
|
case 'SUBJECT':
|
|
237
239
|
case 'TEXT':
|
|
238
240
|
case 'TO':
|
|
239
|
-
if (isUnicodeString(params[term])) {
|
|
240
|
-
hasUnicode = true;
|
|
241
|
-
}
|
|
242
241
|
if (params[term]) {
|
|
243
242
|
setOpt(attributes, term, params[term]);
|
|
244
243
|
}
|
|
@@ -286,9 +285,6 @@ const searchCompiler = (connection, query) => {
|
|
|
286
285
|
case 'GMRAW':
|
|
287
286
|
case 'GMAILRAW': // alias for GMRAW
|
|
288
287
|
if (connection.capabilities.has('X-GM-EXT-1')) {
|
|
289
|
-
if (isUnicodeString(params[term])) {
|
|
290
|
-
hasUnicode = true;
|
|
291
|
-
}
|
|
292
288
|
setOpt(attributes, 'X-GM-RAW', params[term]);
|
|
293
289
|
}
|
|
294
290
|
else {
|
|
@@ -329,9 +325,6 @@ const searchCompiler = (connection, query) => {
|
|
|
329
325
|
fail('MissingServerExtension', 'Server does not support X-GM-EXT-1 extension required for label search');
|
|
330
326
|
}
|
|
331
327
|
let rawQuery = rawParts.join(' ');
|
|
332
|
-
if (isUnicodeString(rawQuery)) {
|
|
333
|
-
hasUnicode = true;
|
|
334
|
-
}
|
|
335
328
|
setOpt(attributes, 'X-GM-RAW', rawQuery);
|
|
336
329
|
break;
|
|
337
330
|
}
|
|
@@ -391,9 +384,6 @@ const searchCompiler = (connection, query) => {
|
|
|
391
384
|
if (typeof value !== 'string') {
|
|
392
385
|
return;
|
|
393
386
|
}
|
|
394
|
-
if (isUnicodeString(value)) {
|
|
395
|
-
hasUnicode = true;
|
|
396
|
-
}
|
|
397
387
|
setOpt(attributes, term, [header.toUpperCase().trim(), value]);
|
|
398
388
|
});
|
|
399
389
|
}
|
|
@@ -473,7 +463,7 @@ const searchCompiler = (connection, query) => {
|
|
|
473
463
|
walk(query);
|
|
474
464
|
// If we encountered Unicode strings and UTF-8 is not already accepted,
|
|
475
465
|
// prepend CHARSET UTF-8 to the search command
|
|
476
|
-
if (
|
|
466
|
+
if (!connection.enabled.has('UTF8=ACCEPT') && hasLiteral(attributes)) {
|
|
477
467
|
attributes.unshift({ type: 'ATOM', value: 'UTF-8' });
|
|
478
468
|
attributes.unshift({ type: 'ATOM', value: 'CHARSET' });
|
|
479
469
|
}
|
package/dist/esm/package-info.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
/* eslint no-control-regex:0 */
|
|
2
2
|
import { formatDate, formatFlag, toValidDate, isRev2Active } from './tools.js';
|
|
3
|
+
// Matches any character outside the ASCII range
|
|
4
|
+
const UNICODE_PATTERN = /[^\x00-\x7F]/;
|
|
3
5
|
/**
|
|
4
6
|
* Sets a boolean flag in the IMAP search attributes.
|
|
5
7
|
* Automatically handles UN- prefixing for falsy values.
|
|
@@ -35,6 +37,17 @@ let setBoolOpt = (attributes, term, value) => {
|
|
|
35
37
|
* @returns The joined sequence set string
|
|
36
38
|
*/
|
|
37
39
|
let toSequenceValue = (value) => [].concat(value).join(',');
|
|
40
|
+
/**
|
|
41
|
+
* Builds the token for a search value. A quoted string may only carry 7-bit
|
|
42
|
+
* characters (RFC 3501 section 9), so a non-ASCII value is sent as a literal;
|
|
43
|
+
* strict servers reply BAD to UTF-8 inside a quoted string. A literal can not
|
|
44
|
+
* carry NUL either (CHAR8 is %x01-ff), so such a value stays an ATOM and the
|
|
45
|
+
* compiler rejects it.
|
|
46
|
+
*
|
|
47
|
+
* @param value - The search value
|
|
48
|
+
* @returns An ATOM token (quoted by the compiler when needed), or a LITERAL token
|
|
49
|
+
*/
|
|
50
|
+
let toSearchValue = (value) => UNICODE_PATTERN.test(value) && !value.includes('\0') ? { type: 'LITERAL', value: Buffer.from(value) } : { type: 'ATOM', value };
|
|
38
51
|
/**
|
|
39
52
|
* Adds a search option with its value(s) to the attributes array.
|
|
40
53
|
* Handles NOT operations and array values.
|
|
@@ -51,10 +64,10 @@ let setOpt = (attributes, term, value) => {
|
|
|
51
64
|
attributes.push({ type: 'ATOM', value: term.toUpperCase() });
|
|
52
65
|
// Handle array values (e.g. HEADER name/value pairs)
|
|
53
66
|
if (Array.isArray(value)) {
|
|
54
|
-
value.forEach(entry => attributes.push(
|
|
67
|
+
value.forEach(entry => attributes.push(toSearchValue((entry || '').toString())));
|
|
55
68
|
}
|
|
56
69
|
else {
|
|
57
|
-
attributes.push(
|
|
70
|
+
attributes.push(toSearchValue(value.toString()));
|
|
58
71
|
}
|
|
59
72
|
};
|
|
60
73
|
/**
|
|
@@ -86,8 +99,6 @@ let processDateField = (attributes, term, value) => {
|
|
|
86
99
|
}
|
|
87
100
|
setOpt(attributes, term, formatted);
|
|
88
101
|
};
|
|
89
|
-
// Pre-compiled regex for better performance
|
|
90
|
-
const UNICODE_PATTERN = /[^\x00-\x7F]/;
|
|
91
102
|
/**
|
|
92
103
|
* Throws a coded search compilation error.
|
|
93
104
|
*
|
|
@@ -100,20 +111,13 @@ let fail = (code, message) => {
|
|
|
100
111
|
throw error;
|
|
101
112
|
};
|
|
102
113
|
/**
|
|
103
|
-
* Checks
|
|
104
|
-
*
|
|
114
|
+
* Checks whether any search value was compiled into a literal, which only
|
|
115
|
+
* happens for non-ASCII values and means CHARSET UTF-8 needs to be specified.
|
|
105
116
|
*
|
|
106
|
-
* @param
|
|
107
|
-
* @returns True if
|
|
117
|
+
* @param attributes - Compiled search attributes
|
|
118
|
+
* @returns True if a LITERAL token is present
|
|
108
119
|
*/
|
|
109
|
-
let
|
|
110
|
-
if (!str || typeof str !== 'string') {
|
|
111
|
-
return false;
|
|
112
|
-
}
|
|
113
|
-
// Regex test is ~3-5x faster than Buffer.byteLength
|
|
114
|
-
// Matches any character outside ASCII range (0x00-0x7F)
|
|
115
|
-
return UNICODE_PATTERN.test(str);
|
|
116
|
-
};
|
|
120
|
+
let hasLiteral = (attributes) => attributes.some(attr => (Array.isArray(attr) ? hasLiteral(attr) : attr.type === 'LITERAL'));
|
|
117
121
|
/**
|
|
118
122
|
* Compiles a JavaScript object query into IMAP search command attributes.
|
|
119
123
|
* Supports standard IMAP search criteria and extensions like OBJECTID and Gmail extensions.
|
|
@@ -142,8 +146,6 @@ let isUnicodeString = (str) => {
|
|
|
142
146
|
*/
|
|
143
147
|
export const searchCompiler = (connection, query) => {
|
|
144
148
|
const attributes = [];
|
|
145
|
-
// Track if we need to specify UTF-8 charset
|
|
146
|
-
let hasUnicode = false;
|
|
147
149
|
/**
|
|
148
150
|
* Recursively walks through the query object and builds IMAP attributes.
|
|
149
151
|
* @param params - Query parameters to process
|
|
@@ -233,9 +235,6 @@ export const searchCompiler = (connection, query) => {
|
|
|
233
235
|
case 'SUBJECT':
|
|
234
236
|
case 'TEXT':
|
|
235
237
|
case 'TO':
|
|
236
|
-
if (isUnicodeString(params[term])) {
|
|
237
|
-
hasUnicode = true;
|
|
238
|
-
}
|
|
239
238
|
if (params[term]) {
|
|
240
239
|
setOpt(attributes, term, params[term]);
|
|
241
240
|
}
|
|
@@ -283,9 +282,6 @@ export const searchCompiler = (connection, query) => {
|
|
|
283
282
|
case 'GMRAW':
|
|
284
283
|
case 'GMAILRAW': // alias for GMRAW
|
|
285
284
|
if (connection.capabilities.has('X-GM-EXT-1')) {
|
|
286
|
-
if (isUnicodeString(params[term])) {
|
|
287
|
-
hasUnicode = true;
|
|
288
|
-
}
|
|
289
285
|
setOpt(attributes, 'X-GM-RAW', params[term]);
|
|
290
286
|
}
|
|
291
287
|
else {
|
|
@@ -326,9 +322,6 @@ export const searchCompiler = (connection, query) => {
|
|
|
326
322
|
fail('MissingServerExtension', 'Server does not support X-GM-EXT-1 extension required for label search');
|
|
327
323
|
}
|
|
328
324
|
let rawQuery = rawParts.join(' ');
|
|
329
|
-
if (isUnicodeString(rawQuery)) {
|
|
330
|
-
hasUnicode = true;
|
|
331
|
-
}
|
|
332
325
|
setOpt(attributes, 'X-GM-RAW', rawQuery);
|
|
333
326
|
break;
|
|
334
327
|
}
|
|
@@ -388,9 +381,6 @@ export const searchCompiler = (connection, query) => {
|
|
|
388
381
|
if (typeof value !== 'string') {
|
|
389
382
|
return;
|
|
390
383
|
}
|
|
391
|
-
if (isUnicodeString(value)) {
|
|
392
|
-
hasUnicode = true;
|
|
393
|
-
}
|
|
394
384
|
setOpt(attributes, term, [header.toUpperCase().trim(), value]);
|
|
395
385
|
});
|
|
396
386
|
}
|
|
@@ -470,7 +460,7 @@ export const searchCompiler = (connection, query) => {
|
|
|
470
460
|
walk(query);
|
|
471
461
|
// If we encountered Unicode strings and UTF-8 is not already accepted,
|
|
472
462
|
// prepend CHARSET UTF-8 to the search command
|
|
473
|
-
if (
|
|
463
|
+
if (!connection.enabled.has('UTF8=ACCEPT') && hasLiteral(attributes)) {
|
|
474
464
|
attributes.unshift({ type: 'ATOM', value: 'UTF-8' });
|
|
475
465
|
attributes.unshift({ type: 'ATOM', value: 'CHARSET' });
|
|
476
466
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "imapflow",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.4",
|
|
4
4
|
"description": "IMAP Client for Node",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/cjs/imap-flow.js",
|
|
@@ -87,7 +87,7 @@
|
|
|
87
87
|
"libbase64": "1.3.1",
|
|
88
88
|
"libmime": "5.4.6",
|
|
89
89
|
"libqp": "2.1.2",
|
|
90
|
-
"pino": "10.
|
|
90
|
+
"pino": "10.3.1",
|
|
91
91
|
"socks": "2.8.10"
|
|
92
92
|
},
|
|
93
93
|
"engines": {
|