imapflow 1.6.5 → 1.6.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 +7 -0
- package/lib/commands/append.js +26 -4
- package/lib/commands/copyuid-parser.js +4 -2
- package/lib/commands/expunge.js +5 -2
- package/lib/commands/fetch.js +9 -3
- package/lib/commands/list.js +18 -38
- package/lib/commands/namespace.js +2 -2
- package/lib/commands/quota.js +10 -2
- package/lib/commands/search.js +54 -14
- package/lib/commands/select.js +81 -70
- package/lib/commands/status-fields.js +68 -0
- package/lib/commands/status.js +23 -61
- package/lib/handler/imap-stream.js +56 -2
- package/lib/handler/limits.js +16 -4
- package/lib/imap-flow.d.ts +22 -2
- package/lib/imap-flow.js +131 -61
- package/lib/jp-decoder.js +30 -5
- package/lib/limited-passthrough.js +19 -1
- package/lib/tools.js +190 -39
- package/package.json +4 -4
- package/test/commands-branches-test.js +4 -0
- package/test/commands-integration-test.js +683 -0
- package/test/copyuid-parser-test.js +20 -0
- package/test/imap-flow-coverage-test.js +4 -2
- package/test/imap-flow-fetch-download-test.js +26 -0
- package/test/imap-flow-methods-test.js +92 -0
- package/test/imap-stream-edge-cases-test.js +136 -0
- package/test/jp-decoder-test.js +57 -0
- package/test/limited-passthrough-test.js +24 -0
- package/test/parser-limits-test.js +18 -0
- package/test/reliability-improvements-test.js +3 -3
- package/test/timer-policy-test.js +27 -1
- package/test/tools-test.js +151 -2
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { parseBigIntValue, parseUintValue, MAX_UINT32_DIGITS } = require('../tools.js');
|
|
4
|
+
|
|
5
|
+
// STATUS data items (RFC 3501 section 6.3.10, RFC 7162 for HIGHESTMODSEQ, RFC 9051 for SIZE
|
|
6
|
+
// and DELETED) mapped to the property name each one is exposed under, together with the
|
|
7
|
+
// parser that turns the raw response token into a usable value. Shared by the STATUS command
|
|
8
|
+
// and by the inline STATUS responses of LIST-STATUS (RFC 5819) so the two cannot drift apart.
|
|
9
|
+
//
|
|
10
|
+
// Every parser rejects anything that is not a bounded decimal digit run, returning false.
|
|
11
|
+
// These values are server-controlled and several of them are written straight into the live
|
|
12
|
+
// mailbox state, where a NaN or a value coerced to Infinity corrupts every later range
|
|
13
|
+
// computation. A plain isNaN() test is not enough: it passes '1e5', ' 12 ' and 'Infinity',
|
|
14
|
+
// and BigInt() throws on all three, aborting the walk over the remaining fields.
|
|
15
|
+
const uint32 = value => parseUintValue(value, MAX_UINT32_DIGITS);
|
|
16
|
+
|
|
17
|
+
const STATUS_FIELDS = {
|
|
18
|
+
MESSAGES: { key: 'messages', parser: uint32 },
|
|
19
|
+
RECENT: { key: 'recent', parser: uint32 },
|
|
20
|
+
UIDNEXT: { key: 'uidNext', parser: uint32 },
|
|
21
|
+
// Nominally 32-bit, but stored as a BigInt precisely so a server that exceeds that still
|
|
22
|
+
// round-trips, so the wider bound applies
|
|
23
|
+
UIDVALIDITY: { key: 'uidValidity', parser: value => parseBigIntValue(value) },
|
|
24
|
+
UNSEEN: { key: 'unseen', parser: uint32 },
|
|
25
|
+
HIGHESTMODSEQ: { key: 'highestModseq', parser: value => parseBigIntValue(value) },
|
|
26
|
+
// IMAP4rev2 additions (RFC 9051): total mailbox size in octets (number64, exact as a JS
|
|
27
|
+
// number up to 2^53-1) and count of messages carrying the \Deleted flag
|
|
28
|
+
SIZE: { key: 'size', parser: value => parseUintValue(value) },
|
|
29
|
+
DELETED: { key: 'deleted', parser: uint32 }
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Walks a STATUS data-item list - alternating item-name and item-value tokens - and reports
|
|
34
|
+
* every recognized field that parsed successfully. Unknown item names and unusable values are
|
|
35
|
+
* skipped, so one bad field never costs the rest of the response.
|
|
36
|
+
*
|
|
37
|
+
* @param {Array} list - Parsed attribute list from the untagged STATUS response.
|
|
38
|
+
* @param {Function} onField - Called as (key, value) for each usable field.
|
|
39
|
+
*/
|
|
40
|
+
const parseStatusList = (list, onField) => {
|
|
41
|
+
let name;
|
|
42
|
+
list.forEach((entry, i) => {
|
|
43
|
+
if (i % 2 === 0) {
|
|
44
|
+
name = entry && typeof entry.value === 'string' ? entry.value : false;
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (!name || !entry) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// The item name is server-controlled, but uppercasing it before the lookup means no
|
|
53
|
+
// Object.prototype member can be reached: every builtin name has a lowercase letter.
|
|
54
|
+
const field = STATUS_FIELDS[name.toUpperCase()];
|
|
55
|
+
if (!field) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const value = field.parser(entry.value);
|
|
60
|
+
if (value === false) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
onField(field.key, value);
|
|
65
|
+
});
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
module.exports = { parseStatusList };
|
package/lib/commands/status.js
CHANGED
|
@@ -1,6 +1,25 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const { encodePath, normalizePath, buildStatusQueryAttributes, isRev2Active } = require('../tools.js');
|
|
4
|
+
const { parseStatusList } = require('./status-fields.js');
|
|
5
|
+
|
|
6
|
+
// STATUS fields that also refresh the live mailbox state when the queried mailbox is the
|
|
7
|
+
// currently selected one. Keyed by the output property name parseStatusList() reports.
|
|
8
|
+
const MAILBOX_UPDATERS = {
|
|
9
|
+
messages: (value, connection, path) => {
|
|
10
|
+
let prevCount = connection.mailbox.exists;
|
|
11
|
+
if (prevCount !== value) {
|
|
12
|
+
connection.mailbox.exists = value;
|
|
13
|
+
connection.emit('exists', { path, count: value, prevCount });
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
uidNext: (value, connection) => {
|
|
17
|
+
connection.mailbox.uidNext = value;
|
|
18
|
+
},
|
|
19
|
+
highestModseq: (value, connection) => {
|
|
20
|
+
connection.mailbox.highestModseq = value;
|
|
21
|
+
}
|
|
22
|
+
};
|
|
4
23
|
|
|
5
24
|
/**
|
|
6
25
|
* Requests status information about a mailbox.
|
|
@@ -56,68 +75,11 @@ module.exports = async (connection, path, query) => {
|
|
|
56
75
|
if (!list) {
|
|
57
76
|
return;
|
|
58
77
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
const STATUS_FIELD_MAP = {
|
|
62
|
-
MESSAGES: {
|
|
63
|
-
key: 'messages',
|
|
64
|
-
parser: Number,
|
|
65
|
-
updateMailbox: (val, conn) => {
|
|
66
|
-
let prevCount = conn.mailbox.exists;
|
|
67
|
-
if (prevCount !== val) {
|
|
68
|
-
conn.mailbox.exists = val;
|
|
69
|
-
conn.emit('exists', { path, count: val, prevCount });
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
},
|
|
73
|
-
RECENT: { key: 'recent', parser: Number },
|
|
74
|
-
UIDNEXT: {
|
|
75
|
-
key: 'uidNext',
|
|
76
|
-
parser: Number,
|
|
77
|
-
updateMailbox: (val, conn) => {
|
|
78
|
-
conn.mailbox.uidNext = val;
|
|
79
|
-
}
|
|
80
|
-
},
|
|
81
|
-
UIDVALIDITY: { key: 'uidValidity', parser: BigInt },
|
|
82
|
-
UNSEEN: { key: 'unseen', parser: Number },
|
|
83
|
-
HIGHESTMODSEQ: {
|
|
84
|
-
key: 'highestModseq',
|
|
85
|
-
parser: BigInt,
|
|
86
|
-
updateMailbox: (val, conn) => {
|
|
87
|
-
conn.mailbox.highestModseq = val;
|
|
88
|
-
}
|
|
89
|
-
},
|
|
90
|
-
// IMAP4rev2 additions (RFC 9051): total mailbox size in octets
|
|
91
|
-
// (number64, exact as a JS number up to 2^53-1) and count of
|
|
92
|
-
// messages with the \Deleted flag
|
|
93
|
-
SIZE: { key: 'size', parser: Number },
|
|
94
|
-
DELETED: { key: 'deleted', parser: Number }
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
let key;
|
|
98
|
-
list.forEach((entry, i) => {
|
|
99
|
-
if (i % 2 === 0) {
|
|
100
|
-
key = entry && typeof entry.value === 'string' ? entry.value : false;
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
103
|
-
if (!key || !entry || typeof entry.value !== 'string') {
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
const fieldConfig = STATUS_FIELD_MAP[key.toUpperCase()];
|
|
108
|
-
if (!fieldConfig) {
|
|
109
|
-
return;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
const value = !isNaN(entry.value) ? fieldConfig.parser(entry.value) : false;
|
|
113
|
-
if (value === false) {
|
|
114
|
-
return;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
map[fieldConfig.key] = value;
|
|
78
|
+
parseStatusList(list, (key, value) => {
|
|
79
|
+
map[key] = value;
|
|
118
80
|
|
|
119
|
-
if (updateCurrent &&
|
|
120
|
-
|
|
81
|
+
if (updateCurrent && MAILBOX_UPDATERS[key]) {
|
|
82
|
+
MAILBOX_UPDATERS[key](value, connection, path);
|
|
121
83
|
}
|
|
122
84
|
});
|
|
123
85
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const Transform = require('stream').Transform;
|
|
4
4
|
const logger = require('../logger');
|
|
5
|
-
const { MAX_LITERAL_SIZE, MAX_LINE_SIZE, normalizeLimit, createLiteralTooLargeError } = require('./limits');
|
|
5
|
+
const { MAX_LITERAL_SIZE, MAX_LINE_SIZE, MAX_RESPONSE_SIZE, normalizeLimit, createLiteralTooLargeError } = require('./limits');
|
|
6
6
|
|
|
7
7
|
const LINE = 0x01;
|
|
8
8
|
const LITERAL = 0x02;
|
|
@@ -44,6 +44,16 @@ class ImapStream extends Transform {
|
|
|
44
44
|
* exactly at the limit is accepted. Exceeding it is terminal: the stream is destroyed with a
|
|
45
45
|
* `LiteralTooLarge` error, the marker line is not emitted, and no byte of the rejected
|
|
46
46
|
* literal body is parsed as protocol.
|
|
47
|
+
* @param {number} [options.maxResponseSize] - Maximum allowed total size (in bytes) of a
|
|
48
|
+
* single assembled response: every line segment and literal of one response combined.
|
|
49
|
+
* Defaults to MAX_RESPONSE_SIZE (2GB), which leaves room above the literal cap for a
|
|
50
|
+
* maximum-size literal plus its marker line. The per-line and per-literal caps alone
|
|
51
|
+
* cannot stop a server that spreads attacker-controlled bytes across an unbounded
|
|
52
|
+
* number of tokens of a single response. Declared literal sizes count when their
|
|
53
|
+
* marker is parsed, so an oversized total is rejected before the literal bytes arrive,
|
|
54
|
+
* and a line still being assembled counts against whatever budget is left.
|
|
55
|
+
* Exceeding the limit is terminal: the stream is destroyed with a `ResponseTooLarge`
|
|
56
|
+
* error and no further input is parsed.
|
|
47
57
|
*/
|
|
48
58
|
constructor(options) {
|
|
49
59
|
super({
|
|
@@ -73,6 +83,8 @@ class ImapStream extends Transform {
|
|
|
73
83
|
// announcing an oversized literal cannot exhaust memory.
|
|
74
84
|
this.maxLiteralSize = normalizeLimit(this.options.maxLiteralSize, MAX_LITERAL_SIZE);
|
|
75
85
|
|
|
86
|
+
this.maxResponseSize = normalizeLimit(this.options.maxResponseSize, MAX_RESPONSE_SIZE);
|
|
87
|
+
|
|
76
88
|
this.state = LINE;
|
|
77
89
|
this.literalWaiting = 0;
|
|
78
90
|
this.inputBuffer = []; // lines
|
|
@@ -80,6 +92,7 @@ class ImapStream extends Transform {
|
|
|
80
92
|
this.lineBytes = 0; // bytes currently buffered for the in-progress line
|
|
81
93
|
this.literalBuffer = [];
|
|
82
94
|
this.literals = [];
|
|
95
|
+
this.responseBytes = 0; // bytes accumulated for the in-progress response (lines + declared literals)
|
|
83
96
|
|
|
84
97
|
this.compress = false;
|
|
85
98
|
this.secureConnection = this.options.secureConnection;
|
|
@@ -228,6 +241,33 @@ class ImapStream extends Transform {
|
|
|
228
241
|
return this.failStream(err);
|
|
229
242
|
}
|
|
230
243
|
|
|
244
|
+
/**
|
|
245
|
+
* Enforces the configured per-response size cap: the cumulative bytes of every line
|
|
246
|
+
* segment and declared literal of the response currently being assembled. Counting
|
|
247
|
+
* declared literal sizes at marker time means an oversized total is rejected before
|
|
248
|
+
* the literal bytes even arrive. The counter is reset when a response is emitted.
|
|
249
|
+
*
|
|
250
|
+
* @param {number} additionalBytes - Bytes the next token would add to the response.
|
|
251
|
+
* @param {boolean} [peek] - Measure only, without committing the bytes to the counter.
|
|
252
|
+
* Used for a line that is still being assembled: its bytes are committed once, when the
|
|
253
|
+
* line completes.
|
|
254
|
+
* @returns {boolean} True if within the limit, false if the stream was failed.
|
|
255
|
+
*/
|
|
256
|
+
checkResponseSize(additionalBytes, peek) {
|
|
257
|
+
let total = this.responseBytes + additionalBytes;
|
|
258
|
+
if (total <= this.maxResponseSize) {
|
|
259
|
+
if (!peek) {
|
|
260
|
+
this.responseBytes = total;
|
|
261
|
+
}
|
|
262
|
+
return true;
|
|
263
|
+
}
|
|
264
|
+
const err = new Error(`Response size ${total} exceeds maximum allowed size of ${this.maxResponseSize} bytes`);
|
|
265
|
+
err.code = 'ResponseTooLarge';
|
|
266
|
+
err.responseSize = total;
|
|
267
|
+
err.maxSize = this.maxResponseSize;
|
|
268
|
+
return this.failStream(err);
|
|
269
|
+
}
|
|
270
|
+
|
|
231
271
|
/**
|
|
232
272
|
* Processes a single input chunk of raw data. In LINE state, scans for LF-terminated
|
|
233
273
|
* lines and checks for literal markers. In LITERAL state, collects the expected number
|
|
@@ -273,6 +313,13 @@ class ImapStream extends Transform {
|
|
|
273
313
|
return;
|
|
274
314
|
}
|
|
275
315
|
|
|
316
|
+
// Count the line itself and, for a literal marker, the declared
|
|
317
|
+
// literal bytes against the cumulative per-response budget, so a
|
|
318
|
+
// response assembled from many tokens stays bounded as a whole
|
|
319
|
+
if (!this.checkResponseSize(line.length + (isLiteralMarker ? this.literalWaiting : 0))) {
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
|
|
276
323
|
this.inputBuffer.push(line);
|
|
277
324
|
|
|
278
325
|
if (isLiteralMarker) {
|
|
@@ -285,6 +332,7 @@ class ImapStream extends Transform {
|
|
|
285
332
|
let literals = this.literals;
|
|
286
333
|
this.inputBuffer = [];
|
|
287
334
|
this.literals = [];
|
|
335
|
+
this.responseBytes = 0;
|
|
288
336
|
|
|
289
337
|
if (payload.length) {
|
|
290
338
|
// remove final line terminator (\n or \r\n)
|
|
@@ -323,7 +371,12 @@ class ImapStream extends Transform {
|
|
|
323
371
|
// No line terminator was found in the remaining bytes; carry the tail over to
|
|
324
372
|
// the next chunk after measuring the line it belongs to.
|
|
325
373
|
let tail = chunk.slice(lineStart);
|
|
326
|
-
|
|
374
|
+
// The response counter is only committed when a line completes, so an
|
|
375
|
+
// in-progress line is measured against the remaining budget separately.
|
|
376
|
+
// Without this a response cap lowered to bound parser memory buys nothing
|
|
377
|
+
// while a server streams a line that never terminates - only the much
|
|
378
|
+
// larger line cap would hold it back.
|
|
379
|
+
if (!this.checkLineLength(this.lineBytes + tail.length) || !this.checkResponseSize(this.lineBytes + tail.length, true)) {
|
|
327
380
|
return;
|
|
328
381
|
}
|
|
329
382
|
this.lineBytes += tail.length;
|
|
@@ -453,6 +506,7 @@ class ImapStream extends Transform {
|
|
|
453
506
|
this.lineBytes = 0;
|
|
454
507
|
this.literalBuffer = [];
|
|
455
508
|
this.literals = [];
|
|
509
|
+
this.responseBytes = 0;
|
|
456
510
|
|
|
457
511
|
// Settle an in-flight push() wait so processInput() can unwind
|
|
458
512
|
if (typeof this.pendingPush === 'function') {
|
package/lib/handler/limits.js
CHANGED
|
@@ -12,16 +12,28 @@ const MAX_LITERAL_SIZE = 1024 * 1024 * 1024;
|
|
|
12
12
|
// only to stop a server that never sends a line terminator, not to constrain normal traffic.
|
|
13
13
|
const MAX_LINE_SIZE = MAX_LITERAL_SIZE;
|
|
14
14
|
|
|
15
|
+
// Default maximum total size of a single assembled response: every line segment and literal of
|
|
16
|
+
// one response combined. The per-line and per-literal caps alone cannot stop a server that
|
|
17
|
+
// spreads attacker-controlled bytes across an unbounded number of tokens of a single response
|
|
18
|
+
// (e.g. one FETCH answer carrying many maximum-size literals).
|
|
19
|
+
//
|
|
20
|
+
// Deliberately above the literal cap: the response total also carries the literal's marker line
|
|
21
|
+
// and the rest of the response framing, so a cap equal to MAX_LITERAL_SIZE would make a literal
|
|
22
|
+
// of exactly the maximum permitted size impossible to receive. Configuring both limits calls for
|
|
23
|
+
// the same headroom - set maxResponseSize above maxLiteralSize, not equal to it.
|
|
24
|
+
const MAX_RESPONSE_SIZE = 2 * MAX_LITERAL_SIZE;
|
|
25
|
+
|
|
15
26
|
/**
|
|
16
27
|
* Normalizes a configured size limit. A non-negative integer is honored as-is (including 0, which
|
|
17
|
-
* means "reject anything non-empty")
|
|
18
|
-
* not silently swallowed the way `value || DEFAULT` would
|
|
28
|
+
* means "reject anything non-empty"), and `Infinity` disables the limit; anything else falls back
|
|
29
|
+
* to the default, so an explicit 0 is not silently swallowed the way `value || DEFAULT` would
|
|
30
|
+
* swallow it.
|
|
19
31
|
*
|
|
20
32
|
* @param {*} value - The configured value.
|
|
21
33
|
* @param {number} defaultValue - Fallback when the value is not a usable limit.
|
|
22
34
|
* @returns {number} The normalized limit.
|
|
23
35
|
*/
|
|
24
|
-
const normalizeLimit = (value, defaultValue) => (Number.isInteger(value) && value >= 0 ? value : defaultValue);
|
|
36
|
+
const normalizeLimit = (value, defaultValue) => ((Number.isInteger(value) || value === Infinity) && value >= 0 ? value : defaultValue);
|
|
25
37
|
|
|
26
38
|
/**
|
|
27
39
|
* Builds the `LiteralTooLarge` error. One shape for every place a literal is refused, so callers
|
|
@@ -40,4 +52,4 @@ const createLiteralTooLargeError = (literalSize, maxSize, reason) => {
|
|
|
40
52
|
return err;
|
|
41
53
|
};
|
|
42
54
|
|
|
43
|
-
module.exports = { MAX_LITERAL_SIZE, MAX_LINE_SIZE, normalizeLimit, createLiteralTooLargeError };
|
|
55
|
+
module.exports = { MAX_LITERAL_SIZE, MAX_LINE_SIZE, MAX_RESPONSE_SIZE, normalizeLimit, createLiteralTooLargeError };
|
package/lib/imap-flow.d.ts
CHANGED
|
@@ -85,18 +85,34 @@ export interface ImapFlowOptions {
|
|
|
85
85
|
* Maximum allowed length in bytes of a single response line (a response without a literal).
|
|
86
86
|
* Guards against a malicious or broken server that never sends a line terminator. Defaults to
|
|
87
87
|
* 1GB. The line terminator counts towards the limit and a line exactly at the limit is
|
|
88
|
-
* accepted.
|
|
88
|
+
* accepted. `Infinity` disables the limit. An in-progress line is additionally bounded by
|
|
89
|
+
* whatever is left of `maxResponseSize`, so lowering that also bounds line buffering.
|
|
90
|
+
* Exceeding it is terminal: the connection fails with error code `LineTooLarge` and
|
|
89
91
|
* no further input is parsed.
|
|
90
92
|
*/
|
|
91
93
|
maxLineLength?: number;
|
|
92
94
|
/**
|
|
93
95
|
* Maximum allowed size in bytes of a single IMAP literal block. Bounds peak memory allocation
|
|
94
96
|
* against a malicious or broken server announcing an oversized literal. Defaults to 1GB. A
|
|
95
|
-
* literal exactly at the limit is accepted
|
|
97
|
+
* literal exactly at the limit is accepted, provided `maxResponseSize` leaves room for the
|
|
98
|
+
* marker line as the defaults do. `Infinity` disables the limit. Exceeding it is terminal: the connection fails
|
|
96
99
|
* with error code `LiteralTooLarge`, and neither the marker line nor any byte of the rejected
|
|
97
100
|
* literal is interpreted as protocol.
|
|
98
101
|
*/
|
|
99
102
|
maxLiteralSize?: number;
|
|
103
|
+
/**
|
|
104
|
+
* Maximum allowed total size in bytes of a single assembled IMAP response (every line
|
|
105
|
+
* segment and literal of one response combined). Bounds peak memory allocation against
|
|
106
|
+
* a malicious or broken server that spreads response data across an unbounded number of
|
|
107
|
+
* tokens, which the per-line and per-literal caps alone cannot stop. Defaults to 2GB,
|
|
108
|
+
* which is above the default literal cap on purpose: the total also carries the literal
|
|
109
|
+
* marker line and the rest of the response framing, so a value equal to `maxLiteralSize`
|
|
110
|
+
* would make a literal of exactly the maximum permitted size impossible to receive. Set
|
|
111
|
+
* this above `maxLiteralSize` when configuring both. `Infinity` disables the limit.
|
|
112
|
+
* Exceeding it is terminal: the connection fails with error code `ResponseTooLarge` and
|
|
113
|
+
* no further input is parsed.
|
|
114
|
+
*/
|
|
115
|
+
maxResponseSize?: number;
|
|
100
116
|
/**
|
|
101
117
|
* Threshold in milliseconds for warning that a mailbox lock has been held
|
|
102
118
|
* for a long time (diagnostic for forgotten release() calls). Defaults to
|
|
@@ -145,6 +161,10 @@ export interface MailboxObject {
|
|
|
145
161
|
uidNext: number;
|
|
146
162
|
/** Messages in this folder */
|
|
147
163
|
exists: number;
|
|
164
|
+
/** Sequence number of the first unseen message, if the server reported [UNSEEN] on SELECT. Not a count of unseen messages - use mailboxStatus() with {unseen: true} for that */
|
|
165
|
+
unseen?: number;
|
|
166
|
+
/** Largest message size in octets the server accepts for APPEND into this mailbox, if it reported [APPENDLIMIT] (RFC 7889) */
|
|
167
|
+
appendlimit?: number;
|
|
148
168
|
/** Read-only state */
|
|
149
169
|
readOnly?: boolean;
|
|
150
170
|
}
|