imapflow 2.0.2 → 2.0.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/imap-flow.js +37 -3
- package/dist/cjs/package-info.d.ts +1 -1
- package/dist/cjs/package-info.js +1 -1
- package/dist/esm/imap-flow.js +37 -3
- package/dist/esm/package-info.d.ts +1 -1
- package/dist/esm/package-info.js +1 -1
- package/package.json +5 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [2.0.4](https://github.com/postalsys/imapflow/compare/v2.0.3...v2.0.4) (2026-09-15)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Bug Fixes
|
|
7
|
+
|
|
8
|
+
* **deps:** update libmime to 5.4.4 and encoding-japanese to 2.4.0 ([ffa0f93](https://github.com/postalsys/imapflow/commit/ffa0f93a60e7b17f8bd6f6920c158ee308b95cee))
|
|
9
|
+
|
|
10
|
+
## [2.0.3](https://github.com/postalsys/imapflow/compare/v2.0.2...v2.0.3) (2026-09-14)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* bound the chunked download loop by the reported message size ([fc9b4eb](https://github.com/postalsys/imapflow/commit/fc9b4ebdcc15fbc952fb2a18d98a723511e384ab))
|
|
16
|
+
* stop chunked download when the server ignores the partial spec ([#396](https://github.com/postalsys/imapflow/issues/396)) ([e216fde](https://github.com/postalsys/imapflow/commit/e216fdeaad4724bdeead6f346e207cd3d75a20fd))
|
|
17
|
+
|
|
3
18
|
## [2.0.2](https://github.com/postalsys/imapflow/compare/v2.0.1...v2.0.2) (2026-09-10)
|
|
4
19
|
|
|
5
20
|
|
package/dist/cjs/imap-flow.js
CHANGED
|
@@ -3029,7 +3029,22 @@ class ImapFlow extends node_events_1.EventEmitter {
|
|
|
3029
3029
|
return {};
|
|
3030
3030
|
}
|
|
3031
3031
|
processed += chunk.length;
|
|
3032
|
-
|
|
3032
|
+
// A compliant server returns at most `chunkSize` bytes for a partial
|
|
3033
|
+
// request. Some servers (Tencent Exmail among them) ignore the partial
|
|
3034
|
+
// spec and answer every request with the complete part. That chunk is
|
|
3035
|
+
// then larger than requested, so treating it as "full, keep going"
|
|
3036
|
+
// would advance the offset past the end forever and never see a short
|
|
3037
|
+
// chunk. An oversized answer already contains the whole part - stop.
|
|
3038
|
+
hasMore = chunk.length === chunkSize;
|
|
3039
|
+
if (chunk.length > chunkSize) {
|
|
3040
|
+
this.log.warn({
|
|
3041
|
+
msg: 'Server returned more than the requested window, treating the part as complete',
|
|
3042
|
+
chunkSize,
|
|
3043
|
+
received: chunk.length,
|
|
3044
|
+
processed,
|
|
3045
|
+
cid: this.id
|
|
3046
|
+
});
|
|
3047
|
+
}
|
|
3033
3048
|
let result = { chunk };
|
|
3034
3049
|
if (query.size) {
|
|
3035
3050
|
result.response = response;
|
|
@@ -3198,11 +3213,30 @@ class ImapFlow extends node_events_1.EventEmitter {
|
|
|
3198
3213
|
}
|
|
3199
3214
|
return stream.write(chunk);
|
|
3200
3215
|
};
|
|
3216
|
+
// Ceiling on how many bytes one download may pull off the wire, as the backstop for the
|
|
3217
|
+
// partial-ignoring servers above: a part whose size happens to equal chunkSize exactly
|
|
3218
|
+
// comes back looking like a full window every time, so no test over chunk lengths can end
|
|
3219
|
+
// that loop. RFC822.SIZE bounds any part of the message; doubled for servers that count
|
|
3220
|
+
// line endings differently than they deliver, plus one window so a download sitting right
|
|
3221
|
+
// at the bound still gets its terminating chunk. Infinity when the server reported no
|
|
3222
|
+
// size, which leaves the loop bounded by maxBytes alone.
|
|
3223
|
+
let maxTotalBytes = (0, limited_passthrough_js_1.normalizeByteLimit)(meta.expectedSize ? meta.expectedSize * 2 + chunkSize : 0);
|
|
3201
3224
|
// Fetch remaining chunks in a loop, writing each to the decoder stream.
|
|
3202
|
-
// Stops when the server returns a short chunk (< chunkSize), the
|
|
3203
|
-
// limiter is satisfied, or the consumer destroys the output
|
|
3225
|
+
// Stops when the server returns a short chunk (< chunkSize), answers with more than the
|
|
3226
|
+
// requested window, the byte limiter is satisfied, or the consumer destroys the output
|
|
3227
|
+
// stream. Throws when the ceiling above is crossed.
|
|
3204
3228
|
let fetchAllParts = async () => {
|
|
3205
3229
|
while (hasMore && !isLimited() && !fetchAborted) {
|
|
3230
|
+
if (processed >= maxTotalBytes) {
|
|
3231
|
+
// Loud on purpose. Everything written downstream by this point holds
|
|
3232
|
+
// duplicated content, and a quiet stop is indistinguishable from a clean EOF,
|
|
3233
|
+
// so the consumer would store a corrupt body believing it intact.
|
|
3234
|
+
let err = new Error('Download exceeded the expected message size');
|
|
3235
|
+
err.code = 'DownloadOverflow';
|
|
3236
|
+
err.maxSize = maxTotalBytes;
|
|
3237
|
+
err.cid = this.id;
|
|
3238
|
+
throw err;
|
|
3239
|
+
}
|
|
3206
3240
|
let { chunk } = await getNextPart();
|
|
3207
3241
|
if (!chunk || fetchAborted) {
|
|
3208
3242
|
break;
|
package/dist/cjs/package-info.js
CHANGED
package/dist/esm/imap-flow.js
CHANGED
|
@@ -2989,7 +2989,22 @@ export class ImapFlow extends EventEmitter {
|
|
|
2989
2989
|
return {};
|
|
2990
2990
|
}
|
|
2991
2991
|
processed += chunk.length;
|
|
2992
|
-
|
|
2992
|
+
// A compliant server returns at most `chunkSize` bytes for a partial
|
|
2993
|
+
// request. Some servers (Tencent Exmail among them) ignore the partial
|
|
2994
|
+
// spec and answer every request with the complete part. That chunk is
|
|
2995
|
+
// then larger than requested, so treating it as "full, keep going"
|
|
2996
|
+
// would advance the offset past the end forever and never see a short
|
|
2997
|
+
// chunk. An oversized answer already contains the whole part - stop.
|
|
2998
|
+
hasMore = chunk.length === chunkSize;
|
|
2999
|
+
if (chunk.length > chunkSize) {
|
|
3000
|
+
this.log.warn({
|
|
3001
|
+
msg: 'Server returned more than the requested window, treating the part as complete',
|
|
3002
|
+
chunkSize,
|
|
3003
|
+
received: chunk.length,
|
|
3004
|
+
processed,
|
|
3005
|
+
cid: this.id
|
|
3006
|
+
});
|
|
3007
|
+
}
|
|
2993
3008
|
let result = { chunk };
|
|
2994
3009
|
if (query.size) {
|
|
2995
3010
|
result.response = response;
|
|
@@ -3158,11 +3173,30 @@ export class ImapFlow extends EventEmitter {
|
|
|
3158
3173
|
}
|
|
3159
3174
|
return stream.write(chunk);
|
|
3160
3175
|
};
|
|
3176
|
+
// Ceiling on how many bytes one download may pull off the wire, as the backstop for the
|
|
3177
|
+
// partial-ignoring servers above: a part whose size happens to equal chunkSize exactly
|
|
3178
|
+
// comes back looking like a full window every time, so no test over chunk lengths can end
|
|
3179
|
+
// that loop. RFC822.SIZE bounds any part of the message; doubled for servers that count
|
|
3180
|
+
// line endings differently than they deliver, plus one window so a download sitting right
|
|
3181
|
+
// at the bound still gets its terminating chunk. Infinity when the server reported no
|
|
3182
|
+
// size, which leaves the loop bounded by maxBytes alone.
|
|
3183
|
+
let maxTotalBytes = normalizeByteLimit(meta.expectedSize ? meta.expectedSize * 2 + chunkSize : 0);
|
|
3161
3184
|
// Fetch remaining chunks in a loop, writing each to the decoder stream.
|
|
3162
|
-
// Stops when the server returns a short chunk (< chunkSize), the
|
|
3163
|
-
// limiter is satisfied, or the consumer destroys the output
|
|
3185
|
+
// Stops when the server returns a short chunk (< chunkSize), answers with more than the
|
|
3186
|
+
// requested window, the byte limiter is satisfied, or the consumer destroys the output
|
|
3187
|
+
// stream. Throws when the ceiling above is crossed.
|
|
3164
3188
|
let fetchAllParts = async () => {
|
|
3165
3189
|
while (hasMore && !isLimited() && !fetchAborted) {
|
|
3190
|
+
if (processed >= maxTotalBytes) {
|
|
3191
|
+
// Loud on purpose. Everything written downstream by this point holds
|
|
3192
|
+
// duplicated content, and a quiet stop is indistinguishable from a clean EOF,
|
|
3193
|
+
// so the consumer would store a corrupt body believing it intact.
|
|
3194
|
+
let err = new Error('Download exceeded the expected message size');
|
|
3195
|
+
err.code = 'DownloadOverflow';
|
|
3196
|
+
err.maxSize = maxTotalBytes;
|
|
3197
|
+
err.cid = this.id;
|
|
3198
|
+
throw err;
|
|
3199
|
+
}
|
|
3166
3200
|
let { chunk } = await getNextPart();
|
|
3167
3201
|
if (!chunk || fetchAborted) {
|
|
3168
3202
|
break;
|
package/dist/esm/package-info.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "imapflow",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.4",
|
|
4
4
|
"description": "IMAP Client for Node",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/cjs/imap-flow.js",
|
|
@@ -77,15 +77,15 @@
|
|
|
77
77
|
"tsx": "4.23.13",
|
|
78
78
|
"types-node-legacy": "npm:@types/node@20.0.0",
|
|
79
79
|
"typescript": "6.0.3",
|
|
80
|
-
"typescript-eslint": "8.
|
|
81
|
-
"wrangler": "4.
|
|
80
|
+
"typescript-eslint": "8.70.0",
|
|
81
|
+
"wrangler": "4.131.2"
|
|
82
82
|
},
|
|
83
83
|
"dependencies": {
|
|
84
84
|
"@zone-eu/mailsplit": "5.4.16",
|
|
85
|
-
"encoding-japanese": "2.
|
|
85
|
+
"encoding-japanese": "2.4.0",
|
|
86
86
|
"iconv-lite": "0.7.3",
|
|
87
87
|
"libbase64": "1.3.0",
|
|
88
|
-
"libmime": "5.4.
|
|
88
|
+
"libmime": "5.4.4",
|
|
89
89
|
"libqp": "2.1.1",
|
|
90
90
|
"pino": "10.3.1",
|
|
91
91
|
"socks": "2.8.10"
|