imapflow 1.0.187 → 1.0.189
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/lib/handler/imap-parser.js +8 -3
- package/lib/handler/token-parser.js +2 -21
- package/lib/imap-flow.d.ts +786 -0
- package/lib/imap-flow.js +224 -92
- package/package.json +12 -12
- package/test/imap-parser-test.js +14 -4
- package/lib/types.d.ts +0 -1123
- package/types.js +0 -56
package/lib/imap-flow.js
CHANGED
|
@@ -242,6 +242,13 @@ class ImapFlow extends EventEmitter {
|
|
|
242
242
|
this.options.clientInfo || {}
|
|
243
243
|
);
|
|
244
244
|
|
|
245
|
+
// remove diacritics
|
|
246
|
+
for (let key of Object.keys(this.clientInfo)) {
|
|
247
|
+
if (typeof this.clientInfo[key] === 'string') {
|
|
248
|
+
this.clientInfo[key] = this.clientInfo[key].normalize('NFD').replace(/\p{Diacritic}/gu, '');
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
245
252
|
/**
|
|
246
253
|
* Server identification info. Available after successful `connect()`.
|
|
247
254
|
* If server does not provide identification info then this value is `null`.
|
|
@@ -408,7 +415,8 @@ class ImapFlow extends EventEmitter {
|
|
|
408
415
|
}
|
|
409
416
|
|
|
410
417
|
if (this.writeSocket.destroyed) {
|
|
411
|
-
this.
|
|
418
|
+
this.log.error({ msg: 'Write socket destroyed', cid: this.id });
|
|
419
|
+
this.close();
|
|
412
420
|
return;
|
|
413
421
|
}
|
|
414
422
|
|
|
@@ -509,6 +517,12 @@ class ImapFlow extends EventEmitter {
|
|
|
509
517
|
}
|
|
510
518
|
|
|
511
519
|
async exec(command, attributes, options) {
|
|
520
|
+
if (this.state === this.states.LOGOUT || this.isClosed) {
|
|
521
|
+
const error = new Error('Connection not available');
|
|
522
|
+
error.code = 'NoConnection';
|
|
523
|
+
throw error;
|
|
524
|
+
}
|
|
525
|
+
|
|
512
526
|
if (!this.socket || this.socket.destroyed) {
|
|
513
527
|
let error = new Error('Connection closed');
|
|
514
528
|
error.code = 'EConnectionClosed';
|
|
@@ -758,22 +772,37 @@ class ImapFlow extends EventEmitter {
|
|
|
758
772
|
this.close();
|
|
759
773
|
});
|
|
760
774
|
|
|
775
|
+
/**
|
|
776
|
+
* Socket timeout event handler.
|
|
777
|
+
*
|
|
778
|
+
* When a socket timeout occurs during IDLE, the handler attempts to recover the connection
|
|
779
|
+
* by sending a NOOP command and then returning to IDLE state.
|
|
780
|
+
*
|
|
781
|
+
* @fires ImapFlow#error Emits error event unless the current command is IDLE
|
|
782
|
+
*/
|
|
761
783
|
this._socketTimeout =
|
|
762
784
|
this._socketTimeout ||
|
|
763
785
|
(() => {
|
|
786
|
+
const err = new Error('Socket timeout');
|
|
787
|
+
err.code = 'ETIMEOUT';
|
|
788
|
+
|
|
764
789
|
if (this.idling) {
|
|
790
|
+
// Attempt to recover IDLE connections
|
|
765
791
|
this.run('NOOP')
|
|
766
792
|
.then(() => this.idle())
|
|
767
|
-
.catch(this._socketError);
|
|
793
|
+
.catch(this._socketError); // Natural circuit breaker
|
|
768
794
|
} else {
|
|
795
|
+
// Close immediately for non-IDLE operations
|
|
769
796
|
this.log.debug({ msg: 'Socket timeout', cid: this.id });
|
|
797
|
+
this.emit('error', err);
|
|
770
798
|
this.close();
|
|
771
799
|
}
|
|
772
800
|
});
|
|
773
801
|
|
|
774
|
-
this.socket.
|
|
775
|
-
this.socket.
|
|
776
|
-
this.socket.
|
|
802
|
+
this.socket.once('error', this._socketError);
|
|
803
|
+
this.socket.once('close', this._socketClose);
|
|
804
|
+
this.socket.once('end', this._socketEnd);
|
|
805
|
+
|
|
777
806
|
this.socket.on('tlsClientError', this._socketError);
|
|
778
807
|
this.socket.on('timeout', this._socketTimeout);
|
|
779
808
|
|
|
@@ -784,6 +813,10 @@ class ImapFlow extends EventEmitter {
|
|
|
784
813
|
if (this._socketError) {
|
|
785
814
|
this.socket.removeListener('error', this._socketError);
|
|
786
815
|
this.socket.removeListener('tlsClientError', this._socketError);
|
|
816
|
+
this.writeSocket.removeListener('error', this._socketError);
|
|
817
|
+
}
|
|
818
|
+
if (this._socketTimeout) {
|
|
819
|
+
this.socket.removeListener('timeout', this._socketTimeout);
|
|
787
820
|
}
|
|
788
821
|
if (this._socketClose) {
|
|
789
822
|
this.socket.removeListener('close', this._socketClose);
|
|
@@ -1130,7 +1163,11 @@ class ImapFlow extends EventEmitter {
|
|
|
1130
1163
|
return reject(err);
|
|
1131
1164
|
}
|
|
1132
1165
|
|
|
1133
|
-
|
|
1166
|
+
// ALWAYS emit the error so users can handle it
|
|
1167
|
+
setImmediate(() => {
|
|
1168
|
+
this.emit('error', err);
|
|
1169
|
+
this.close();
|
|
1170
|
+
});
|
|
1134
1171
|
});
|
|
1135
1172
|
}
|
|
1136
1173
|
|
|
@@ -2786,6 +2823,7 @@ class ImapFlow extends EventEmitter {
|
|
|
2786
2823
|
|
|
2787
2824
|
let stream;
|
|
2788
2825
|
let output;
|
|
2826
|
+
let fetchAborted = false;
|
|
2789
2827
|
|
|
2790
2828
|
switch (meta.encoding) {
|
|
2791
2829
|
case 'base64':
|
|
@@ -2833,31 +2871,105 @@ class ImapFlow extends EventEmitter {
|
|
|
2833
2871
|
});
|
|
2834
2872
|
output = output.pipe(limiter);
|
|
2835
2873
|
|
|
2874
|
+
// Cleanup function
|
|
2875
|
+
const cleanup = () => {
|
|
2876
|
+
fetchAborted = true;
|
|
2877
|
+
if (stream && !stream.destroyed) {
|
|
2878
|
+
stream.destroy();
|
|
2879
|
+
}
|
|
2880
|
+
};
|
|
2881
|
+
|
|
2882
|
+
// Listen for stream destruction
|
|
2883
|
+
output.once('error', cleanup);
|
|
2884
|
+
output.once('close', cleanup);
|
|
2885
|
+
|
|
2836
2886
|
let writeChunk = chunk => {
|
|
2837
|
-
if (limiter.limited) {
|
|
2887
|
+
if (limiter.limited || fetchAborted || stream.destroyed) {
|
|
2838
2888
|
return true;
|
|
2839
2889
|
}
|
|
2840
2890
|
return stream.write(chunk);
|
|
2841
2891
|
};
|
|
2842
2892
|
|
|
2843
2893
|
let fetchAllParts = async () => {
|
|
2844
|
-
while (hasMore && !limiter.limited) {
|
|
2894
|
+
while (hasMore && !limiter.limited && !fetchAborted) {
|
|
2845
2895
|
let { chunk } = await getNextPart();
|
|
2846
|
-
if (!chunk) {
|
|
2896
|
+
if (!chunk || fetchAborted) {
|
|
2847
2897
|
break;
|
|
2848
2898
|
}
|
|
2849
2899
|
|
|
2900
|
+
// Handle backpressure
|
|
2850
2901
|
if (writeChunk(chunk) === false) {
|
|
2851
|
-
|
|
2902
|
+
// Wait for drain event before continuing
|
|
2903
|
+
try {
|
|
2904
|
+
await new Promise((resolve, reject) => {
|
|
2905
|
+
let resolved = false;
|
|
2906
|
+
|
|
2907
|
+
const finish = err => {
|
|
2908
|
+
if (resolved) return;
|
|
2909
|
+
resolved = true;
|
|
2910
|
+
|
|
2911
|
+
// Remove all listeners
|
|
2912
|
+
stream.removeAllListeners('drain');
|
|
2913
|
+
stream.removeAllListeners('error');
|
|
2914
|
+
stream.removeAllListeners('close');
|
|
2915
|
+
|
|
2916
|
+
if (err) {
|
|
2917
|
+
reject(err);
|
|
2918
|
+
} else {
|
|
2919
|
+
resolve();
|
|
2920
|
+
}
|
|
2921
|
+
};
|
|
2922
|
+
|
|
2923
|
+
stream.once('drain', () => finish());
|
|
2924
|
+
stream.once('error', err => finish(err));
|
|
2925
|
+
stream.once('close', () => finish());
|
|
2926
|
+
});
|
|
2927
|
+
} catch (err) {
|
|
2928
|
+
// Re-throw only if not aborted
|
|
2929
|
+
if (!fetchAborted) {
|
|
2930
|
+
throw err;
|
|
2931
|
+
}
|
|
2932
|
+
}
|
|
2933
|
+
|
|
2934
|
+
// Check if we should abort after waiting
|
|
2935
|
+
if (fetchAborted) {
|
|
2936
|
+
break;
|
|
2937
|
+
}
|
|
2852
2938
|
}
|
|
2853
2939
|
}
|
|
2854
2940
|
};
|
|
2855
2941
|
|
|
2856
2942
|
setImmediate(() => {
|
|
2857
|
-
writeChunk(chunk)
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2943
|
+
if (!writeChunk(chunk)) {
|
|
2944
|
+
// Initial chunk filled the buffer, wait for drain
|
|
2945
|
+
stream.once('drain', () => {
|
|
2946
|
+
if (!fetchAborted) {
|
|
2947
|
+
fetchAllParts()
|
|
2948
|
+
.catch(err => {
|
|
2949
|
+
if (!fetchAborted && stream && !stream.destroyed) {
|
|
2950
|
+
stream.emit('error', err);
|
|
2951
|
+
}
|
|
2952
|
+
})
|
|
2953
|
+
.finally(() => {
|
|
2954
|
+
if (!fetchAborted && stream && !stream.destroyed) {
|
|
2955
|
+
stream.end();
|
|
2956
|
+
}
|
|
2957
|
+
});
|
|
2958
|
+
}
|
|
2959
|
+
});
|
|
2960
|
+
} else {
|
|
2961
|
+
fetchAllParts()
|
|
2962
|
+
.catch(err => {
|
|
2963
|
+
if (!fetchAborted && stream && !stream.destroyed) {
|
|
2964
|
+
stream.emit('error', err);
|
|
2965
|
+
}
|
|
2966
|
+
})
|
|
2967
|
+
.finally(() => {
|
|
2968
|
+
if (!fetchAborted && stream && !stream.destroyed) {
|
|
2969
|
+
stream.end();
|
|
2970
|
+
}
|
|
2971
|
+
});
|
|
2972
|
+
}
|
|
2861
2973
|
});
|
|
2862
2974
|
|
|
2863
2975
|
return {
|
|
@@ -3028,76 +3140,73 @@ class ImapFlow extends EventEmitter {
|
|
|
3028
3140
|
}
|
|
3029
3141
|
|
|
3030
3142
|
async processLocks(force) {
|
|
3031
|
-
if (!force
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
lockId: this.currentLock.lockId,
|
|
3040
|
-
...(this.currentLock.options?.description && { description: this.currentLock.options?.description })
|
|
3041
|
-
}
|
|
3042
|
-
: null
|
|
3043
|
-
});
|
|
3044
|
-
return;
|
|
3045
|
-
}
|
|
3046
|
-
|
|
3047
|
-
if (!this.locks.length) {
|
|
3048
|
-
this.processingLock = false;
|
|
3049
|
-
this.log.trace({
|
|
3050
|
-
msg: 'Mailbox locking queue processed',
|
|
3051
|
-
idling: this.idling
|
|
3052
|
-
});
|
|
3053
|
-
return;
|
|
3054
|
-
}
|
|
3055
|
-
|
|
3056
|
-
this.processingLock = true;
|
|
3057
|
-
|
|
3058
|
-
const release = () => {
|
|
3059
|
-
if (this.currentLock) {
|
|
3143
|
+
if (!force) {
|
|
3144
|
+
// Atomic test-and-set to prevent race condition
|
|
3145
|
+
const wasProcessing = this.processingLock;
|
|
3146
|
+
this.processingLock = true;
|
|
3147
|
+
|
|
3148
|
+
if (wasProcessing) {
|
|
3149
|
+
// Another processor is already running, reset and exit
|
|
3150
|
+
this.processingLock = wasProcessing;
|
|
3060
3151
|
this.log.trace({
|
|
3061
|
-
msg: 'Mailbox
|
|
3062
|
-
lockId: this.currentLock.lockId,
|
|
3152
|
+
msg: 'Mailbox locking queued',
|
|
3063
3153
|
path: this.mailbox && this.mailbox.path,
|
|
3064
3154
|
pending: this.locks.length,
|
|
3155
|
+
idling: this.idling,
|
|
3156
|
+
activeLock: this.currentLock
|
|
3157
|
+
? {
|
|
3158
|
+
lockId: this.currentLock.lockId,
|
|
3159
|
+
...(this.currentLock.options?.description && { description: this.currentLock.options?.description })
|
|
3160
|
+
}
|
|
3161
|
+
: null
|
|
3162
|
+
});
|
|
3163
|
+
return;
|
|
3164
|
+
}
|
|
3165
|
+
} else {
|
|
3166
|
+
this.processingLock = true;
|
|
3167
|
+
}
|
|
3168
|
+
|
|
3169
|
+
try {
|
|
3170
|
+
if (!this.locks.length) {
|
|
3171
|
+
this.log.trace({
|
|
3172
|
+
msg: 'Mailbox locking queue processed',
|
|
3065
3173
|
idling: this.idling
|
|
3066
3174
|
});
|
|
3067
|
-
|
|
3175
|
+
return;
|
|
3068
3176
|
}
|
|
3069
|
-
this.processLocks(true).catch(err => this.log.error({ err, cid: this.id }));
|
|
3070
|
-
};
|
|
3071
3177
|
|
|
3072
|
-
|
|
3073
|
-
|
|
3178
|
+
const release = () => {
|
|
3179
|
+
if (this.currentLock) {
|
|
3180
|
+
this.log.trace({
|
|
3181
|
+
msg: 'Mailbox lock released',
|
|
3182
|
+
lockId: this.currentLock.lockId,
|
|
3183
|
+
path: this.mailbox && this.mailbox.path,
|
|
3184
|
+
pending: this.locks.length,
|
|
3185
|
+
idling: this.idling
|
|
3186
|
+
});
|
|
3187
|
+
this.currentLock = false;
|
|
3188
|
+
}
|
|
3189
|
+
this.processLocks(true).catch(err => this.log.error({ err, cid: this.id }));
|
|
3190
|
+
};
|
|
3074
3191
|
|
|
3075
|
-
|
|
3076
|
-
|
|
3077
|
-
// reject all
|
|
3078
|
-
let error = new Error('Connection not available');
|
|
3079
|
-
error.code = 'NoConnection';
|
|
3080
|
-
reject(error);
|
|
3081
|
-
return await this.processLocks(true);
|
|
3082
|
-
}
|
|
3192
|
+
const lock = this.locks.shift();
|
|
3193
|
+
const { resolve, reject, path, options, lockId } = lock;
|
|
3083
3194
|
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
// Try to open. Throws if mailbox does not exists or can't open
|
|
3098
|
-
await this.mailboxOpen(path, options);
|
|
3195
|
+
if (!this.usable || !this.socket || this.socket.destroyed) {
|
|
3196
|
+
this.log.trace({ msg: 'Failed to acquire mailbox lock', path, lockId, idling: this.idling });
|
|
3197
|
+
// reject all
|
|
3198
|
+
let error = new Error('Connection not available');
|
|
3199
|
+
error.code = 'NoConnection';
|
|
3200
|
+
reject(error);
|
|
3201
|
+
// Process remaining locks
|
|
3202
|
+
await this.processLocks(true);
|
|
3203
|
+
return;
|
|
3204
|
+
}
|
|
3205
|
+
|
|
3206
|
+
if (this.mailbox && this.mailbox.path === path && !!this.mailbox.readOnly === !!options.readOnly) {
|
|
3207
|
+
// nothing to do here, already selected
|
|
3099
3208
|
this.log.trace({
|
|
3100
|
-
msg: 'Mailbox lock acquired [
|
|
3209
|
+
msg: 'Mailbox lock acquired [existing]',
|
|
3101
3210
|
path,
|
|
3102
3211
|
lockId,
|
|
3103
3212
|
idling: this.idling,
|
|
@@ -3105,28 +3214,51 @@ class ImapFlow extends EventEmitter {
|
|
|
3105
3214
|
});
|
|
3106
3215
|
this.currentLock = lock;
|
|
3107
3216
|
return resolve({ path, release });
|
|
3108
|
-
}
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3217
|
+
} else {
|
|
3218
|
+
try {
|
|
3219
|
+
// Try to open. Throws if mailbox does not exists or can't open
|
|
3220
|
+
await this.mailboxOpen(path, options);
|
|
3221
|
+
this.log.trace({
|
|
3222
|
+
msg: 'Mailbox lock acquired [selected]',
|
|
3223
|
+
path,
|
|
3224
|
+
lockId,
|
|
3225
|
+
idling: this.idling,
|
|
3226
|
+
...(options.description && { description: options.description })
|
|
3227
|
+
});
|
|
3228
|
+
this.currentLock = lock;
|
|
3229
|
+
return resolve({ path, release });
|
|
3230
|
+
} catch (err) {
|
|
3231
|
+
if (err.responseStatus === 'NO') {
|
|
3232
|
+
try {
|
|
3233
|
+
let folders = await this.run('LIST', '', path, { listOnly: true });
|
|
3234
|
+
if (!folders || !folders.length) {
|
|
3235
|
+
err.mailboxMissing = true;
|
|
3236
|
+
}
|
|
3237
|
+
} catch (E) {
|
|
3238
|
+
this.log.trace({ msg: 'Failed to verify failed mailbox', path, err: E });
|
|
3114
3239
|
}
|
|
3115
|
-
} catch (E) {
|
|
3116
|
-
this.log.trace({ msg: 'Failed to verify failed mailbox', path, err: E });
|
|
3117
3240
|
}
|
|
3241
|
+
|
|
3242
|
+
this.log.trace({
|
|
3243
|
+
msg: 'Failed to acquire mailbox lock',
|
|
3244
|
+
path,
|
|
3245
|
+
lockId,
|
|
3246
|
+
idling: this.idling,
|
|
3247
|
+
...(options.description && { description: options.description }),
|
|
3248
|
+
err
|
|
3249
|
+
});
|
|
3250
|
+
reject(err);
|
|
3251
|
+
// Process remaining locks
|
|
3252
|
+
await this.processLocks(true);
|
|
3118
3253
|
}
|
|
3254
|
+
}
|
|
3255
|
+
} finally {
|
|
3256
|
+
this.processingLock = false;
|
|
3119
3257
|
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
idling: this.idling,
|
|
3125
|
-
...(options.description && { description: options.description }),
|
|
3126
|
-
err
|
|
3127
|
-
});
|
|
3128
|
-
reject(err);
|
|
3129
|
-
await this.processLocks(true);
|
|
3258
|
+
// Check if new locks were added while we were processing
|
|
3259
|
+
if (this.locks.length && !this.processingLock) {
|
|
3260
|
+
// Recursively process any new locks that were added
|
|
3261
|
+
this.processLocks(true).catch(err => this.log.error({ err, cid: this.id }));
|
|
3130
3262
|
}
|
|
3131
3263
|
}
|
|
3132
3264
|
}
|
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "imapflow",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.189",
|
|
4
4
|
"description": "IMAP Client for Node",
|
|
5
|
-
"main": "
|
|
5
|
+
"main": "lib/imap-flow.js",
|
|
6
|
+
"types": "lib/imap-flow.d.ts",
|
|
6
7
|
"scripts": {
|
|
7
8
|
"test": "grunt",
|
|
8
9
|
"prepare": "npm run build",
|
|
9
10
|
"docs": "rm -rf docs && mkdir -p docs && jsdoc lib/imap-flow.js -c jsdoc.json -R README.md --destination docs/ && cp assets/favicon.ico docs",
|
|
10
|
-
"
|
|
11
|
-
"build": "npm run docs && npm run dst",
|
|
11
|
+
"build": "npm run docs",
|
|
12
12
|
"st": "npm run docs && st -d docs -i index.html",
|
|
13
13
|
"update": "rm -rf node_modules package-lock.json && ncu -u && npm install"
|
|
14
14
|
},
|
|
@@ -28,11 +28,11 @@
|
|
|
28
28
|
},
|
|
29
29
|
"homepage": "https://imapflow.com/",
|
|
30
30
|
"devDependencies": {
|
|
31
|
-
"@babel/eslint-parser": "7.27.
|
|
31
|
+
"@babel/eslint-parser": "7.27.5",
|
|
32
32
|
"@babel/eslint-plugin": "7.27.1",
|
|
33
33
|
"@babel/plugin-syntax-class-properties": "7.12.13",
|
|
34
34
|
"@babel/preset-env": "7.27.2",
|
|
35
|
-
"@types/node": "
|
|
35
|
+
"@types/node": "24.0.7",
|
|
36
36
|
"eslint": "8.57.0",
|
|
37
37
|
"eslint-config-nodemailer": "1.2.0",
|
|
38
38
|
"eslint-config-prettier": "9.1.0",
|
|
@@ -42,18 +42,18 @@
|
|
|
42
42
|
"grunt-eslint": "24.3.0",
|
|
43
43
|
"imapflow-jsdoc-template": "3.4.0-imapflow.2",
|
|
44
44
|
"jsdoc": "3.6.11",
|
|
45
|
-
"st": "3.0.
|
|
46
|
-
"
|
|
45
|
+
"st": "3.0.2",
|
|
46
|
+
"typescript": "5.8.3"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
49
|
"encoding-japanese": "2.2.0",
|
|
50
50
|
"iconv-lite": "0.6.3",
|
|
51
51
|
"libbase64": "1.3.0",
|
|
52
|
-
"libmime": "5.3.
|
|
52
|
+
"libmime": "5.3.7",
|
|
53
53
|
"libqp": "2.1.1",
|
|
54
|
-
"mailsplit": "5.4.
|
|
55
|
-
"nodemailer": "7.0.
|
|
54
|
+
"mailsplit": "5.4.5",
|
|
55
|
+
"nodemailer": "7.0.4",
|
|
56
56
|
"pino": "9.7.0",
|
|
57
|
-
"socks": "2.8.
|
|
57
|
+
"socks": "2.8.5"
|
|
58
58
|
}
|
|
59
59
|
}
|
package/test/imap-parser-test.js
CHANGED
|
@@ -439,8 +439,6 @@ module.exports['IMAP Parser: Section: allow trailing ws'] = test =>
|
|
|
439
439
|
])
|
|
440
440
|
);
|
|
441
441
|
|
|
442
|
-
module.exports['IMAP Parser: Tags: fail unknown section'] = test => asyncWrapperFail(test, async test => test.ok(await parser('TAG1 CMD KODY[]')));
|
|
443
|
-
|
|
444
442
|
module.exports['IMAP Parser: Readable: simple'] = test =>
|
|
445
443
|
asyncWrapper(test, async test =>
|
|
446
444
|
test.deepEqual(await parser('* OK Hello world!'), {
|
|
@@ -817,8 +815,6 @@ module.exports['IMAP Parser: Partial: Section'] = test =>
|
|
|
817
815
|
])
|
|
818
816
|
);
|
|
819
817
|
|
|
820
|
-
module.exports['IMAP Parser: Partial: fail unknown section'] = test => asyncWrapperFail(test, async test => test.ok(await parser('TAG1 CMD KODY<0.123>')));
|
|
821
|
-
|
|
822
818
|
module.exports['IMAP Parser: Partial: fail zero prefix for start'] = test => asyncWrapperFail(test, async test => test.ok(await parser('TAG1 CMD BODY[]<01>')));
|
|
823
819
|
|
|
824
820
|
module.exports['IMAP Parser: Partial: fail zero prefix for end'] = test => asyncWrapperFail(test, async test => test.ok(await parser('TAG1 CMD BODY[]<0.01>')));
|
|
@@ -1249,3 +1245,17 @@ module.exports['IMAP Parser, FETCH with BODYSTRUCTURE'] = test =>
|
|
|
1249
1245
|
}
|
|
1250
1246
|
}
|
|
1251
1247
|
});
|
|
1248
|
+
|
|
1249
|
+
module.exports['IMAP Parser, ATOM with <, [, ]'] = test =>
|
|
1250
|
+
asyncWrapper(test, async test => {
|
|
1251
|
+
let password = `<[+=</$]`;
|
|
1252
|
+
let parsed = await parser(`3 LOGIN user@domain ${password}`);
|
|
1253
|
+
test.deepEqual(parsed, {
|
|
1254
|
+
tag: '3',
|
|
1255
|
+
command: 'LOGIN',
|
|
1256
|
+
attributes: [
|
|
1257
|
+
{ type: 'ATOM', value: 'user@domain' },
|
|
1258
|
+
{ type: 'ATOM', value: password }
|
|
1259
|
+
]
|
|
1260
|
+
});
|
|
1261
|
+
});
|