imapflow 1.2.13 → 1.2.14
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 +8 -0
- package/lib/charsets.js +1 -4
- package/lib/commands/append.js +1 -1
- package/lib/commands/copy.js +3 -24
- package/lib/commands/copyuid-parser.js +33 -0
- package/lib/commands/idle.js +5 -7
- package/lib/commands/list.js +1 -1
- package/lib/commands/move.js +4 -27
- package/lib/commands/store.js +1 -3
- package/lib/handler/imap-compiler.js +1 -3
- package/lib/handler/imap-formal-syntax.js +1 -7
- package/lib/handler/imap-parser.js +1 -1
- package/lib/handler/imap-stream.js +7 -13
- package/lib/handler/parser-instance.js +2 -2
- package/lib/handler/token-parser.js +9 -9
- package/lib/imap-flow.js +80 -117
- package/lib/search-compiler.js +4 -10
- package/lib/tools.js +29 -36
- package/package.json +2 -2
- package/test/charsets-test.js +161 -0
- package/test/commands-integration-test.js +61 -0
- package/test/connection-edge-cases-test.js +94 -0
- package/test/copyuid-parser-test.js +153 -0
- package/test/imap-compiler-test.js +66 -0
- package/test/imap-formal-syntax-test.js +275 -0
- package/test/imap-stream-edge-cases-test.js +268 -0
- package/test/imapflow-test.js +212 -0
- package/test/search-compiler-test.js +74 -0
- package/test/token-parser-test.js +374 -0
- package/test/tools-test.js +149 -2
package/lib/imap-flow.js
CHANGED
|
@@ -298,7 +298,6 @@ class ImapFlow extends EventEmitter {
|
|
|
298
298
|
this.state = this.states.NOT_AUTHENTICATED;
|
|
299
299
|
|
|
300
300
|
this.lockCounter = 0;
|
|
301
|
-
this.currentLock = false;
|
|
302
301
|
|
|
303
302
|
this.tagCounter = 0;
|
|
304
303
|
this.requestTagMap = new Map();
|
|
@@ -407,7 +406,8 @@ class ImapFlow extends EventEmitter {
|
|
|
407
406
|
let rid = BigInt('0x' + crypto.randomBytes(13).toString('hex')).toString(36);
|
|
408
407
|
if (rid.length < 20) {
|
|
409
408
|
rid = '0'.repeat(20 - rid.length) + rid;
|
|
410
|
-
}
|
|
409
|
+
}
|
|
410
|
+
if (rid.length > 20) {
|
|
411
411
|
rid = rid.substr(0, 20);
|
|
412
412
|
}
|
|
413
413
|
return rid;
|
|
@@ -1087,11 +1087,11 @@ class ImapFlow extends EventEmitter {
|
|
|
1087
1087
|
let err = new Error('Server does not support STARTTLS');
|
|
1088
1088
|
err.tlsFailed = true;
|
|
1089
1089
|
throw err;
|
|
1090
|
-
} else {
|
|
1091
|
-
// Opportunistic STARTTLS. But it's not possible right now.
|
|
1092
|
-
// Attention: Could be a downgrade attack.
|
|
1093
|
-
return false;
|
|
1094
1090
|
}
|
|
1091
|
+
|
|
1092
|
+
// Opportunistic STARTTLS. But it's not possible right now.
|
|
1093
|
+
// Attention: Could be a downgrade attack.
|
|
1094
|
+
return false;
|
|
1095
1095
|
}
|
|
1096
1096
|
|
|
1097
1097
|
/**
|
|
@@ -1510,7 +1510,7 @@ class ImapFlow extends EventEmitter {
|
|
|
1510
1510
|
return false;
|
|
1511
1511
|
}
|
|
1512
1512
|
|
|
1513
|
-
if (
|
|
1513
|
+
if (!this.mailbox || !comparePaths(this, this.mailbox.path, path)) {
|
|
1514
1514
|
return await this.mailboxOpen(path);
|
|
1515
1515
|
}
|
|
1516
1516
|
|
|
@@ -2515,13 +2515,7 @@ class ImapFlow extends EventEmitter {
|
|
|
2515
2515
|
* await client.append('INBOX', rawMessageBuffer, ['\\Seen'], new Date(2000, 1, 1));
|
|
2516
2516
|
*/
|
|
2517
2517
|
async append(path, content, flags, idate) {
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
if (!response) {
|
|
2521
|
-
return false;
|
|
2522
|
-
}
|
|
2523
|
-
|
|
2524
|
-
return response;
|
|
2518
|
+
return (await this.run('APPEND', path, content, flags, idate)) || false;
|
|
2525
2519
|
}
|
|
2526
2520
|
|
|
2527
2521
|
/**
|
|
@@ -2608,13 +2602,7 @@ class ImapFlow extends EventEmitter {
|
|
|
2608
2602
|
return;
|
|
2609
2603
|
}
|
|
2610
2604
|
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
if (!response) {
|
|
2614
|
-
return false;
|
|
2615
|
-
}
|
|
2616
|
-
|
|
2617
|
-
return response;
|
|
2605
|
+
return (await this.run('SEARCH', query, options)) || false;
|
|
2618
2606
|
}
|
|
2619
2607
|
|
|
2620
2608
|
/**
|
|
@@ -2753,9 +2741,8 @@ class ImapFlow extends EventEmitter {
|
|
|
2753
2741
|
let entry = rowQueue.shift();
|
|
2754
2742
|
if (entry.err) {
|
|
2755
2743
|
return reject(entry.err);
|
|
2756
|
-
} else {
|
|
2757
|
-
return resolve(entry.value);
|
|
2758
2744
|
}
|
|
2745
|
+
return resolve(entry.value);
|
|
2759
2746
|
}
|
|
2760
2747
|
|
|
2761
2748
|
if (finished) {
|
|
@@ -3257,6 +3244,29 @@ class ImapFlow extends EventEmitter {
|
|
|
3257
3244
|
// stream and then fetch remaining chunks via fetchAllParts().
|
|
3258
3245
|
// setImmediate ensures the caller gets the {meta, content} return
|
|
3259
3246
|
// value before streaming begins.
|
|
3247
|
+
let runFetchAllParts = () => {
|
|
3248
|
+
fetchAllParts()
|
|
3249
|
+
.catch(err => {
|
|
3250
|
+
if (!fetchAborted && stream && !stream.destroyed) {
|
|
3251
|
+
stream.emit('error', err);
|
|
3252
|
+
} else {
|
|
3253
|
+
// Log when error cannot be emitted to stream
|
|
3254
|
+
this.log.warn({
|
|
3255
|
+
msg: 'Download error after stream closed',
|
|
3256
|
+
err,
|
|
3257
|
+
fetchAborted,
|
|
3258
|
+
streamDestroyed: stream?.destroyed,
|
|
3259
|
+
cid: this.id
|
|
3260
|
+
});
|
|
3261
|
+
}
|
|
3262
|
+
})
|
|
3263
|
+
.finally(() => {
|
|
3264
|
+
if (!fetchAborted && stream && !stream.destroyed) {
|
|
3265
|
+
stream.end();
|
|
3266
|
+
}
|
|
3267
|
+
});
|
|
3268
|
+
};
|
|
3269
|
+
|
|
3260
3270
|
setImmediate(() => {
|
|
3261
3271
|
let writeResult;
|
|
3262
3272
|
try {
|
|
@@ -3273,49 +3283,11 @@ class ImapFlow extends EventEmitter {
|
|
|
3273
3283
|
// Initial chunk filled the buffer, wait for drain
|
|
3274
3284
|
stream.once('drain', () => {
|
|
3275
3285
|
if (!fetchAborted) {
|
|
3276
|
-
|
|
3277
|
-
.catch(err => {
|
|
3278
|
-
if (!fetchAborted && stream && !stream.destroyed) {
|
|
3279
|
-
stream.emit('error', err);
|
|
3280
|
-
} else {
|
|
3281
|
-
// Log when error cannot be emitted to stream
|
|
3282
|
-
this.log.warn({
|
|
3283
|
-
msg: 'Download error after stream closed',
|
|
3284
|
-
err,
|
|
3285
|
-
fetchAborted,
|
|
3286
|
-
streamDestroyed: stream?.destroyed,
|
|
3287
|
-
cid: this.id
|
|
3288
|
-
});
|
|
3289
|
-
}
|
|
3290
|
-
})
|
|
3291
|
-
.finally(() => {
|
|
3292
|
-
if (!fetchAborted && stream && !stream.destroyed) {
|
|
3293
|
-
stream.end();
|
|
3294
|
-
}
|
|
3295
|
-
});
|
|
3286
|
+
runFetchAllParts();
|
|
3296
3287
|
}
|
|
3297
3288
|
});
|
|
3298
3289
|
} else {
|
|
3299
|
-
|
|
3300
|
-
.catch(err => {
|
|
3301
|
-
if (!fetchAborted && stream && !stream.destroyed) {
|
|
3302
|
-
stream.emit('error', err);
|
|
3303
|
-
} else {
|
|
3304
|
-
// Log when error cannot be emitted to stream
|
|
3305
|
-
this.log.warn({
|
|
3306
|
-
msg: 'Download error after stream closed',
|
|
3307
|
-
err,
|
|
3308
|
-
fetchAborted,
|
|
3309
|
-
streamDestroyed: stream?.destroyed,
|
|
3310
|
-
cid: this.id
|
|
3311
|
-
});
|
|
3312
|
-
}
|
|
3313
|
-
})
|
|
3314
|
-
.finally(() => {
|
|
3315
|
-
if (!fetchAborted && stream && !stream.destroyed) {
|
|
3316
|
-
stream.end();
|
|
3317
|
-
}
|
|
3318
|
-
});
|
|
3290
|
+
runFetchAllParts();
|
|
3319
3291
|
}
|
|
3320
3292
|
});
|
|
3321
3293
|
|
|
@@ -3514,14 +3486,6 @@ class ImapFlow extends EventEmitter {
|
|
|
3514
3486
|
// Process all locks in queue until empty
|
|
3515
3487
|
let processedCount = 0;
|
|
3516
3488
|
while (this.locks.length > 0) {
|
|
3517
|
-
if (!this.locks.length) {
|
|
3518
|
-
this.log.trace({
|
|
3519
|
-
msg: 'Mailbox locking queue processed',
|
|
3520
|
-
idling: this.idling
|
|
3521
|
-
});
|
|
3522
|
-
return;
|
|
3523
|
-
}
|
|
3524
|
-
|
|
3525
3489
|
// Yield to event loop periodically to prevent CPU blocking
|
|
3526
3490
|
processedCount++;
|
|
3527
3491
|
if (processedCount % 5 === 0) {
|
|
@@ -3568,46 +3532,46 @@ class ImapFlow extends EventEmitter {
|
|
|
3568
3532
|
this.currentLock = lock;
|
|
3569
3533
|
resolve({ path, release });
|
|
3570
3534
|
break; // Stop processing; next lock waits for release()
|
|
3571
|
-
}
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
})
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
|
|
3585
|
-
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
|
|
3595
|
-
} catch (E) {
|
|
3596
|
-
this.log.trace({ msg: 'Failed to verify failed mailbox', path, err: E });
|
|
3535
|
+
}
|
|
3536
|
+
|
|
3537
|
+
try {
|
|
3538
|
+
// Need to SELECT/EXAMINE a different mailbox
|
|
3539
|
+
await this.mailboxOpen(path, options);
|
|
3540
|
+
this.log.trace({
|
|
3541
|
+
msg: 'Mailbox lock acquired [selected]',
|
|
3542
|
+
path,
|
|
3543
|
+
lockId,
|
|
3544
|
+
idling: this.idling,
|
|
3545
|
+
...(options.description && { description: options.description })
|
|
3546
|
+
});
|
|
3547
|
+
this.currentLock = lock;
|
|
3548
|
+
resolve({ path, release });
|
|
3549
|
+
break; // Wait for this lock to be released
|
|
3550
|
+
} catch (err) {
|
|
3551
|
+
if (err.responseStatus === 'NO') {
|
|
3552
|
+
// SELECT failed with NO -- verify whether the mailbox exists
|
|
3553
|
+
// at all by running LIST. This sets mailboxMissing on the error
|
|
3554
|
+
// so the caller can distinguish "doesn't exist" from other failures.
|
|
3555
|
+
try {
|
|
3556
|
+
let folders = await this.run('LIST', '', path, { listOnly: true });
|
|
3557
|
+
if (!folders || !folders.length) {
|
|
3558
|
+
err.mailboxMissing = true;
|
|
3597
3559
|
}
|
|
3560
|
+
} catch (E) {
|
|
3561
|
+
this.log.trace({ msg: 'Failed to verify failed mailbox', path, err: E });
|
|
3598
3562
|
}
|
|
3599
|
-
|
|
3600
|
-
this.log.trace({
|
|
3601
|
-
msg: 'Failed to acquire mailbox lock',
|
|
3602
|
-
path,
|
|
3603
|
-
lockId,
|
|
3604
|
-
idling: this.idling,
|
|
3605
|
-
...(options.description && { description: options.description }),
|
|
3606
|
-
err
|
|
3607
|
-
});
|
|
3608
|
-
reject(err);
|
|
3609
|
-
// Continue to next lock in queue
|
|
3610
3563
|
}
|
|
3564
|
+
|
|
3565
|
+
this.log.trace({
|
|
3566
|
+
msg: 'Failed to acquire mailbox lock',
|
|
3567
|
+
path,
|
|
3568
|
+
lockId,
|
|
3569
|
+
idling: this.idling,
|
|
3570
|
+
...(options.description && { description: options.description }),
|
|
3571
|
+
err
|
|
3572
|
+
});
|
|
3573
|
+
reject(err);
|
|
3574
|
+
// Continue to next lock in queue
|
|
3611
3575
|
}
|
|
3612
3576
|
}
|
|
3613
3577
|
} finally {
|
|
@@ -3691,16 +3655,15 @@ class ImapFlow extends EventEmitter {
|
|
|
3691
3655
|
synteticLogger[level] = (...args) => {
|
|
3692
3656
|
// using {logger:false} disables logging
|
|
3693
3657
|
if (this.options.logger !== false) {
|
|
3694
|
-
if (
|
|
3695
|
-
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
console.log(JSON.stringify(...args));
|
|
3700
|
-
}
|
|
3701
|
-
} else {
|
|
3702
|
-
mainLogger[level](...args);
|
|
3658
|
+
if (typeof mainLogger[level] !== 'function') {
|
|
3659
|
+
// we are checking to make sure the level is supported.
|
|
3660
|
+
// if it isn't supported but the level is error or fatal, log to console anyway.
|
|
3661
|
+
if (level === 'fatal' || level === 'error') {
|
|
3662
|
+
console.log(JSON.stringify(...args));
|
|
3703
3663
|
}
|
|
3664
|
+
} else {
|
|
3665
|
+
mainLogger[level](...args);
|
|
3666
|
+
}
|
|
3704
3667
|
}
|
|
3705
3668
|
|
|
3706
3669
|
if (this.emitLogs && args && args[0] && typeof args[0] === 'object') {
|
package/lib/search-compiler.js
CHANGED
|
@@ -316,16 +316,10 @@ module.exports.searchCompiler = (connection, query) => {
|
|
|
316
316
|
|
|
317
317
|
// NOT operator
|
|
318
318
|
case 'NOT':
|
|
319
|
-
{
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
if (typeof params[term] === 'object') {
|
|
325
|
-
attributes.push({ type: 'ATOM', value: 'NOT' });
|
|
326
|
-
// Recursively process NOT conditions
|
|
327
|
-
walk(params[term]);
|
|
328
|
-
}
|
|
319
|
+
if (params[term] && typeof params[term] === 'object') {
|
|
320
|
+
attributes.push({ type: 'ATOM', value: 'NOT' });
|
|
321
|
+
// Recursively process NOT conditions
|
|
322
|
+
walk(params[term]);
|
|
329
323
|
}
|
|
330
324
|
break;
|
|
331
325
|
|
package/lib/tools.js
CHANGED
|
@@ -77,7 +77,7 @@ const tools = {
|
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
// ensure namespace prefix if needed
|
|
80
|
-
if (!skipNamespace && connection.namespace && connection.namespace.prefix && path.
|
|
80
|
+
if (!skipNamespace && connection.namespace && connection.namespace.prefix && !path.startsWith(connection.namespace.prefix)) {
|
|
81
81
|
path = connection.namespace.prefix + path;
|
|
82
82
|
}
|
|
83
83
|
|
|
@@ -111,7 +111,7 @@ const tools = {
|
|
|
111
111
|
if (list && Array.isArray(list)) {
|
|
112
112
|
list.forEach(val => {
|
|
113
113
|
if (typeof val.value !== 'string') {
|
|
114
|
-
return
|
|
114
|
+
return;
|
|
115
115
|
}
|
|
116
116
|
let capability = val.value.toUpperCase().trim();
|
|
117
117
|
|
|
@@ -120,7 +120,7 @@ const tools = {
|
|
|
120
120
|
return;
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
-
if (capability.
|
|
123
|
+
if (capability.startsWith('APPENDLIMIT=')) {
|
|
124
124
|
let splitPos = capability.indexOf('=');
|
|
125
125
|
let appendLimit = Number(capability.substr(splitPos + 1)) || 0;
|
|
126
126
|
map.set('APPENDLIMIT', appendLimit);
|
|
@@ -205,8 +205,6 @@ const tools = {
|
|
|
205
205
|
let cur = node.folders && node.folders.find(folder => folder.name === parent);
|
|
206
206
|
if (cur) {
|
|
207
207
|
node = cur;
|
|
208
|
-
} else {
|
|
209
|
-
// not yet set, will be created by the caller
|
|
210
208
|
}
|
|
211
209
|
}
|
|
212
210
|
|
|
@@ -294,7 +292,7 @@ const tools = {
|
|
|
294
292
|
|
|
295
293
|
const color = bit0 | bit1 | bit2; // eslint-disable-line no-bitwise
|
|
296
294
|
|
|
297
|
-
return FLAG_COLORS[color]
|
|
295
|
+
return FLAG_COLORS[color] ?? 'red'; // default to red for the unused \b111
|
|
298
296
|
},
|
|
299
297
|
|
|
300
298
|
/**
|
|
@@ -306,23 +304,20 @@ const tools = {
|
|
|
306
304
|
getColorFlags(color) {
|
|
307
305
|
// Reverse mapping from a color name to the Apple Mail $MailFlagBit0/1/2 flags.
|
|
308
306
|
// Returns an object with 'add' and 'remove' arrays so the caller can STORE +FLAGS/-FLAGS.
|
|
309
|
-
const colorCode = color ? FLAG_COLORS.indexOf(
|
|
310
|
-
if (colorCode
|
|
307
|
+
const colorCode = color ? FLAG_COLORS.indexOf(color.toString().toLowerCase().trim()) : null;
|
|
308
|
+
if (colorCode === null || colorCode < 0) {
|
|
309
|
+
if (colorCode === null) {
|
|
310
|
+
// Remove color: remove \Flagged and all MailFlagBit flags
|
|
311
|
+
return { add: [], remove: ['\\Flagged', '$MailFlagBit0', '$MailFlagBit1', '$MailFlagBit2'] };
|
|
312
|
+
}
|
|
311
313
|
return null;
|
|
312
314
|
}
|
|
313
315
|
|
|
314
316
|
// Decompose color index back into its 3-bit representation
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
// If colorCode is truthy (non-zero), add \Flagged; if zero/null, remove \Flagged
|
|
321
|
-
let result = { add: colorCode ? ['\\Flagged'] : [], remove: colorCode ? [] : ['\\Flagged'] };
|
|
322
|
-
|
|
323
|
-
// For each bit, add the corresponding $MailFlagBitN if set, remove it if unset
|
|
324
|
-
for (let i = 0; i < bits.length; i++) {
|
|
325
|
-
if (bits[i]) {
|
|
317
|
+
let result = { add: ['\\Flagged'], remove: [] };
|
|
318
|
+
for (let i = 0; i < 3; i++) {
|
|
319
|
+
// eslint-disable-next-line no-bitwise
|
|
320
|
+
if (colorCode & (1 << i)) {
|
|
326
321
|
result.add.push(`$MailFlagBit${i}`);
|
|
327
322
|
} else {
|
|
328
323
|
result.remove.push(`$MailFlagBit${i}`);
|
|
@@ -529,7 +524,7 @@ const tools = {
|
|
|
529
524
|
processName(name) {
|
|
530
525
|
name = (name || '').toString();
|
|
531
526
|
if (name.length > 2 && name.at(0) === '"' && name.at(-1) === '"') {
|
|
532
|
-
name = name.
|
|
527
|
+
name = name.slice(1, -1);
|
|
533
528
|
}
|
|
534
529
|
return name;
|
|
535
530
|
},
|
|
@@ -679,7 +674,7 @@ const tools = {
|
|
|
679
674
|
value = params[key];
|
|
680
675
|
|
|
681
676
|
// The first segment (*0*) may contain charset and language: charset'language'value
|
|
682
|
-
if (nr === 0 && match[0].
|
|
677
|
+
if (nr === 0 && match[0].at(-1) === '*' && (match = value.match(/^([^']*)'[^']*'(.*)$/))) {
|
|
683
678
|
params[actualKey].charset = match[1] || 'utf-8';
|
|
684
679
|
value = match[2];
|
|
685
680
|
}
|
|
@@ -713,12 +708,11 @@ const tools = {
|
|
|
713
708
|
value
|
|
714
709
|
// fix invalidly encoded chars
|
|
715
710
|
.replace(/[=?_\s]/g, s => {
|
|
716
|
-
let c = s.charCodeAt(0).toString(16);
|
|
717
711
|
if (s === ' ') {
|
|
718
712
|
return '_';
|
|
719
|
-
} else {
|
|
720
|
-
return '%' + (c.length < 2 ? '0' : '') + c;
|
|
721
713
|
}
|
|
714
|
+
let c = s.charCodeAt(0).toString(16);
|
|
715
|
+
return '%' + (c.length < 2 ? '0' : '') + c;
|
|
722
716
|
})
|
|
723
717
|
// change from urlencoding to percent encoding
|
|
724
718
|
.replace(/%/g, '=') +
|
|
@@ -788,25 +782,25 @@ const tools = {
|
|
|
788
782
|
|
|
789
783
|
// id
|
|
790
784
|
if (node[i]) {
|
|
791
|
-
curNode.id = (
|
|
785
|
+
curNode.id = (node[i].value || '').toString();
|
|
792
786
|
}
|
|
793
787
|
i++;
|
|
794
788
|
|
|
795
789
|
// description
|
|
796
790
|
if (node[i]) {
|
|
797
|
-
curNode.description = (
|
|
791
|
+
curNode.description = (node[i].value || '').toString();
|
|
798
792
|
}
|
|
799
793
|
i++;
|
|
800
794
|
|
|
801
795
|
// encoding
|
|
802
796
|
if (node[i]) {
|
|
803
|
-
curNode.encoding = (
|
|
797
|
+
curNode.encoding = (node[i].value || '').toString().toLowerCase();
|
|
804
798
|
}
|
|
805
799
|
i++;
|
|
806
800
|
|
|
807
801
|
// size
|
|
808
802
|
if (node[i]) {
|
|
809
|
-
curNode.size = Number(
|
|
803
|
+
curNode.size = Number(node[i].value || 0) || 0;
|
|
810
804
|
}
|
|
811
805
|
i++;
|
|
812
806
|
|
|
@@ -834,7 +828,7 @@ const tools = {
|
|
|
834
828
|
|
|
835
829
|
// line count
|
|
836
830
|
if (node[i]) {
|
|
837
|
-
curNode.lineCount = Number(
|
|
831
|
+
curNode.lineCount = Number(node[i].value || 0) || 0;
|
|
838
832
|
}
|
|
839
833
|
i++;
|
|
840
834
|
}
|
|
@@ -850,8 +844,7 @@ const tools = {
|
|
|
850
844
|
} else {
|
|
851
845
|
// correct structure, line count number is provided
|
|
852
846
|
if (node[i]) {
|
|
853
|
-
|
|
854
|
-
curNode.lineCount = Number((node[i] || {}).value || 0) || 0;
|
|
847
|
+
curNode.lineCount = Number(node[i].value || 0) || 0;
|
|
855
848
|
}
|
|
856
849
|
i++;
|
|
857
850
|
}
|
|
@@ -862,7 +855,7 @@ const tools = {
|
|
|
862
855
|
// md5
|
|
863
856
|
if (i < node.length - 1) {
|
|
864
857
|
if (node[i]) {
|
|
865
|
-
curNode.md5 = (
|
|
858
|
+
curNode.md5 = (node[i].value || '').toString().toLowerCase();
|
|
866
859
|
}
|
|
867
860
|
i++;
|
|
868
861
|
}
|
|
@@ -874,7 +867,7 @@ const tools = {
|
|
|
874
867
|
// body disposition
|
|
875
868
|
if (i < node.length - 1) {
|
|
876
869
|
if (Array.isArray(node[i]) && node[i].length) {
|
|
877
|
-
curNode.disposition = ((node[i][0]
|
|
870
|
+
curNode.disposition = ((node[i][0] && node[i][0].value) || '').toString().toLowerCase();
|
|
878
871
|
if (Array.isArray(node[i][1])) {
|
|
879
872
|
curNode.dispositionParameters = tools.getStructuredParams(node[i][1]);
|
|
880
873
|
}
|
|
@@ -895,7 +888,7 @@ const tools = {
|
|
|
895
888
|
// Errata: http://www.rfc-editor.org/errata_search.php?rfc=3501
|
|
896
889
|
if (i < node.length - 1) {
|
|
897
890
|
if (node[i]) {
|
|
898
|
-
curNode.location = (
|
|
891
|
+
curNode.location = (node[i].value || '').toString();
|
|
899
892
|
}
|
|
900
893
|
}
|
|
901
894
|
|
|
@@ -990,7 +983,7 @@ const tools = {
|
|
|
990
983
|
case '\\flagged':
|
|
991
984
|
case '\\deleted':
|
|
992
985
|
case '\\draft':
|
|
993
|
-
//
|
|
986
|
+
// normalize capitalization (e.g., "\\seen" -> "\\Seen")
|
|
994
987
|
return flag.toLowerCase().replace(/^\\./, c => c.toUpperCase());
|
|
995
988
|
}
|
|
996
989
|
return flag;
|
|
@@ -1048,7 +1041,7 @@ const tools = {
|
|
|
1048
1041
|
*/
|
|
1049
1042
|
getDecoder(charset) {
|
|
1050
1043
|
charset = (charset || 'ascii').toString().trim().toLowerCase();
|
|
1051
|
-
if (/^jis|^iso-?2022-?jp|^
|
|
1044
|
+
if (/^jis|^iso-?2022-?jp|^euc-?jp/.test(charset)) {
|
|
1052
1045
|
// special case not supported by iconv-lite
|
|
1053
1046
|
return new JPDecoder(charset);
|
|
1054
1047
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "imapflow",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.14",
|
|
4
4
|
"description": "IMAP Client for Node",
|
|
5
5
|
"main": "lib/imap-flow.js",
|
|
6
6
|
"types": "lib/imap-flow.d.ts",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"homepage": "https://imapflow.com/",
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"@eslint/js": "10.0.1",
|
|
31
|
-
"@types/node": "25.
|
|
31
|
+
"@types/node": "25.5.0",
|
|
32
32
|
"c8": "11.0.0",
|
|
33
33
|
"eslint": "10.0.3",
|
|
34
34
|
"eslint-config-nodemailer": "1.2.0",
|