imapflow 1.0.89 → 1.0.92
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/.gitattributes +1 -0
- package/.github/workflows/test.yml +21 -0
- package/CHANGELOG.md +15 -0
- package/lib/commands/append.js +11 -1
- package/lib/commands/authenticate.js +1 -1
- package/lib/commands/create.js +2 -2
- package/lib/commands/delete.js +2 -2
- package/lib/commands/fetch.js +4 -2
- package/lib/commands/idle.js +120 -91
- package/lib/commands/list.js +44 -60
- package/lib/commands/quota.js +2 -2
- package/lib/commands/rename.js +3 -3
- package/lib/commands/select.js +93 -89
- package/lib/commands/status.js +2 -2
- package/lib/commands/subscribe.js +2 -2
- package/lib/commands/unsubscribe.js +2 -2
- package/lib/handler/imap-compiler.js +1 -1
- package/lib/handler/imap-formal-syntax.js +13 -13
- package/lib/handler/imap-stream.js +9 -1
- package/lib/handler/token-parser.js +41 -26
- package/lib/imap-flow.js +49 -12
- package/lib/tools.js +29 -11
- package/lib/types.d.ts +13 -3
- package/package.json +12 -10
- package/test/imap-compiler-test.js +41 -2
- package/test/imap-parser-test.js +30 -0
package/lib/commands/select.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { normalizePath, getStatusCode, getErrorText } = require('../tools.js');
|
|
3
|
+
const { encodePath, normalizePath, getStatusCode, getErrorText } = require('../tools.js');
|
|
4
4
|
|
|
5
5
|
// Selects a mailbox
|
|
6
6
|
module.exports = async (connection, path, options) => {
|
|
@@ -47,101 +47,105 @@ module.exports = async (connection, path, options) => {
|
|
|
47
47
|
map.qresync = true;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
response = await connection.exec(
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
let key = section[0].value.toLowerCase();
|
|
59
|
-
let value;
|
|
60
|
-
|
|
61
|
-
if (typeof section[1].value === 'string') {
|
|
62
|
-
value = section[1].value;
|
|
63
|
-
} else if (Array.isArray(section[1])) {
|
|
64
|
-
value = section[1].map(entry => (typeof entry.value === 'string' ? entry.value : false)).filter(entry => entry);
|
|
50
|
+
response = await connection.exec(
|
|
51
|
+
!options.readOnly ? 'SELECT' : 'EXAMINE',
|
|
52
|
+
[{ type: 'ATOM', value: encodePath(connection, path) }].concat(extraArgs || []),
|
|
53
|
+
{
|
|
54
|
+
untagged: {
|
|
55
|
+
OK: async untagged => {
|
|
56
|
+
if (!untagged.attributes || !untagged.attributes.length) {
|
|
57
|
+
return;
|
|
65
58
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
59
|
+
let section = !untagged.attributes[0].value && untagged.attributes[0].section;
|
|
60
|
+
if (section && section.length > 1 && section[0].type === 'ATOM' && typeof section[0].value === 'string') {
|
|
61
|
+
let key = section[0].value.toLowerCase();
|
|
62
|
+
let value;
|
|
63
|
+
|
|
64
|
+
if (typeof section[1].value === 'string') {
|
|
65
|
+
value = section[1].value;
|
|
66
|
+
} else if (Array.isArray(section[1])) {
|
|
67
|
+
value = section[1].map(entry => (typeof entry.value === 'string' ? entry.value : false)).filter(entry => entry);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
switch (key) {
|
|
71
|
+
case 'highestmodseq':
|
|
72
|
+
key = 'highestModseq';
|
|
73
|
+
if (/^[0-9]+$/.test(value)) {
|
|
74
|
+
value = BigInt(value);
|
|
75
|
+
}
|
|
76
|
+
break;
|
|
77
|
+
|
|
78
|
+
case 'mailboxid':
|
|
79
|
+
key = 'mailboxId';
|
|
80
|
+
if (Array.isArray(value) && value.length) {
|
|
81
|
+
value = value[0];
|
|
82
|
+
}
|
|
83
|
+
break;
|
|
84
|
+
|
|
85
|
+
case 'permanentflags':
|
|
86
|
+
key = 'permanentFlags';
|
|
87
|
+
value = new Set(value);
|
|
88
|
+
break;
|
|
89
|
+
|
|
90
|
+
case 'uidnext':
|
|
91
|
+
key = 'uidNext';
|
|
92
|
+
value = Number(value);
|
|
93
|
+
break;
|
|
94
|
+
|
|
95
|
+
case 'uidvalidity':
|
|
96
|
+
key = 'uidValidity';
|
|
97
|
+
if (/^[0-9]+$/.test(value)) {
|
|
98
|
+
value = BigInt(value);
|
|
99
|
+
}
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
map[key] = value;
|
|
98
104
|
}
|
|
99
105
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
106
|
+
if (section && section.length === 1 && section[0].type === 'ATOM' && typeof section[0].value === 'string') {
|
|
107
|
+
let key = section[0].value.toLowerCase();
|
|
108
|
+
switch (key) {
|
|
109
|
+
case 'nomodseq':
|
|
110
|
+
key = 'noModseq';
|
|
111
|
+
map[key] = true;
|
|
112
|
+
break;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
FLAGS: async untagged => {
|
|
117
|
+
if (!untagged.attributes || (!untagged.attributes.length && Array.isArray(untagged.attributes[0]))) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
let flags = untagged.attributes[0].map(flag => (typeof flag.value === 'string' ? flag.value : false)).filter(flag => flag);
|
|
121
|
+
map.flags = new Set(flags);
|
|
122
|
+
},
|
|
123
|
+
EXISTS: async untagged => {
|
|
124
|
+
let num = Number(untagged.command);
|
|
125
|
+
if (isNaN(num)) {
|
|
126
|
+
return false;
|
|
110
127
|
}
|
|
111
|
-
}
|
|
112
|
-
},
|
|
113
|
-
FLAGS: async untagged => {
|
|
114
|
-
if (!untagged.attributes || (!untagged.attributes.length && Array.isArray(untagged.attributes[0]))) {
|
|
115
|
-
return;
|
|
116
|
-
}
|
|
117
|
-
let flags = untagged.attributes[0].map(flag => (typeof flag.value === 'string' ? flag.value : false)).filter(flag => flag);
|
|
118
|
-
map.flags = new Set(flags);
|
|
119
|
-
},
|
|
120
|
-
EXISTS: async untagged => {
|
|
121
|
-
let num = Number(untagged.command);
|
|
122
|
-
if (isNaN(num)) {
|
|
123
|
-
return false;
|
|
124
|
-
}
|
|
125
128
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
129
|
+
map.exists = num;
|
|
130
|
+
},
|
|
131
|
+
VANISHED: async untagged => {
|
|
132
|
+
await connection.untaggedVanished(
|
|
133
|
+
untagged,
|
|
134
|
+
// mailbox is not yet open, so use a dummy mailbox object
|
|
135
|
+
{ path, uidNext: false, uidValidity: false }
|
|
136
|
+
);
|
|
137
|
+
},
|
|
138
|
+
// we should only get an untagged FETCH for a SELECT/EXAMINE if QRESYNC was asked for
|
|
139
|
+
FETCH: async untagged => {
|
|
140
|
+
await connection.untaggedFetch(
|
|
141
|
+
untagged,
|
|
142
|
+
// mailbox is not yet open, so use a dummy mailbox object
|
|
143
|
+
{ path, uidNext: false, uidValidity: false }
|
|
144
|
+
);
|
|
145
|
+
}
|
|
142
146
|
}
|
|
143
147
|
}
|
|
144
|
-
|
|
148
|
+
);
|
|
145
149
|
|
|
146
150
|
let section = !response.response.attributes[0].value && response.response.attributes[0].section;
|
|
147
151
|
if (section && section.length && section[0].type === 'ATOM' && typeof section[0].value === 'string') {
|
package/lib/commands/status.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { normalizePath } = require('../tools.js');
|
|
3
|
+
const { encodePath, normalizePath } = require('../tools.js');
|
|
4
4
|
|
|
5
5
|
// Requests info about a mailbox
|
|
6
6
|
module.exports = async (connection, path, query) => {
|
|
@@ -11,7 +11,7 @@ module.exports = async (connection, path, query) => {
|
|
|
11
11
|
|
|
12
12
|
path = normalizePath(connection, path);
|
|
13
13
|
|
|
14
|
-
let attributes = [{ type: 'ATOM', value: path }];
|
|
14
|
+
let attributes = [{ type: 'ATOM', value: encodePath(connection, path) }];
|
|
15
15
|
|
|
16
16
|
let queryAttributes = [];
|
|
17
17
|
Object.keys(query || {}).forEach(key => {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { normalizePath, getStatusCode, getErrorText } = require('../tools.js');
|
|
3
|
+
const { encodePath, normalizePath, getStatusCode, getErrorText } = require('../tools.js');
|
|
4
4
|
|
|
5
5
|
// Subscribes to a mailbox
|
|
6
6
|
module.exports = async (connection, path) => {
|
|
@@ -13,7 +13,7 @@ module.exports = async (connection, path) => {
|
|
|
13
13
|
|
|
14
14
|
let response;
|
|
15
15
|
try {
|
|
16
|
-
response = await connection.exec('SUBSCRIBE', [{ type: 'ATOM', value: path }]);
|
|
16
|
+
response = await connection.exec('SUBSCRIBE', [{ type: 'ATOM', value: encodePath(connection, path) }]);
|
|
17
17
|
response.next();
|
|
18
18
|
return true;
|
|
19
19
|
} catch (err) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { normalizePath, getStatusCode, getErrorText } = require('../tools.js');
|
|
3
|
+
const { encodePath, normalizePath, getStatusCode, getErrorText } = require('../tools.js');
|
|
4
4
|
|
|
5
5
|
// Unsubscribes from a mailbox
|
|
6
6
|
module.exports = async (connection, path) => {
|
|
@@ -13,7 +13,7 @@ module.exports = async (connection, path) => {
|
|
|
13
13
|
|
|
14
14
|
let response;
|
|
15
15
|
try {
|
|
16
|
-
response = await connection.exec('UNSUBSCRIBE', [{ type: 'ATOM', value: path }]);
|
|
16
|
+
response = await connection.exec('UNSUBSCRIBE', [{ type: 'ATOM', value: encodePath(connection, path) }]);
|
|
17
17
|
response.next();
|
|
18
18
|
return true;
|
|
19
19
|
} catch (err) {
|
|
@@ -85,7 +85,7 @@ module.exports = async (response, options) => {
|
|
|
85
85
|
let canAppend = !asArray || literalPlus || (literalMinus && literalLength <= 4096);
|
|
86
86
|
let usePlus = canAppend && (literalMinus || literalPlus);
|
|
87
87
|
|
|
88
|
-
resp +=
|
|
88
|
+
resp += `${node.isLiteral8 ? '~' : ''}{${literalLength}${usePlus ? '+' : ''}}\r\n`;
|
|
89
89
|
|
|
90
90
|
if (canAppend) {
|
|
91
91
|
resp += (node.value || '').toString('binary');
|
|
@@ -26,7 +26,7 @@ function excludeChars(source, exclude) {
|
|
|
26
26
|
module.exports = {
|
|
27
27
|
CHAR() {
|
|
28
28
|
let value = expandRange(0x01, 0x7f);
|
|
29
|
-
this.CHAR = function() {
|
|
29
|
+
this.CHAR = function () {
|
|
30
30
|
return value;
|
|
31
31
|
};
|
|
32
32
|
return value;
|
|
@@ -34,7 +34,7 @@ module.exports = {
|
|
|
34
34
|
|
|
35
35
|
CHAR8() {
|
|
36
36
|
let value = expandRange(0x01, 0xff);
|
|
37
|
-
this.CHAR8 = function() {
|
|
37
|
+
this.CHAR8 = function () {
|
|
38
38
|
return value;
|
|
39
39
|
};
|
|
40
40
|
return value;
|
|
@@ -46,7 +46,7 @@ module.exports = {
|
|
|
46
46
|
|
|
47
47
|
CTL() {
|
|
48
48
|
let value = expandRange(0x00, 0x1f) + '\x7F';
|
|
49
|
-
this.CTL = function() {
|
|
49
|
+
this.CTL = function () {
|
|
50
50
|
return value;
|
|
51
51
|
};
|
|
52
52
|
return value;
|
|
@@ -58,7 +58,7 @@ module.exports = {
|
|
|
58
58
|
|
|
59
59
|
ALPHA() {
|
|
60
60
|
let value = expandRange(0x41, 0x5a) + expandRange(0x61, 0x7a);
|
|
61
|
-
this.ALPHA = function() {
|
|
61
|
+
this.ALPHA = function () {
|
|
62
62
|
return value;
|
|
63
63
|
};
|
|
64
64
|
return value;
|
|
@@ -66,7 +66,7 @@ module.exports = {
|
|
|
66
66
|
|
|
67
67
|
DIGIT() {
|
|
68
68
|
let value = expandRange(0x30, 0x39) + expandRange(0x61, 0x7a);
|
|
69
|
-
this.DIGIT = function() {
|
|
69
|
+
this.DIGIT = function () {
|
|
70
70
|
return value;
|
|
71
71
|
};
|
|
72
72
|
return value;
|
|
@@ -74,7 +74,7 @@ module.exports = {
|
|
|
74
74
|
|
|
75
75
|
'ATOM-CHAR'() {
|
|
76
76
|
let value = excludeChars(this.CHAR(), this['atom-specials']());
|
|
77
|
-
this['ATOM-CHAR'] = function() {
|
|
77
|
+
this['ATOM-CHAR'] = function () {
|
|
78
78
|
return value;
|
|
79
79
|
};
|
|
80
80
|
return value;
|
|
@@ -82,7 +82,7 @@ module.exports = {
|
|
|
82
82
|
|
|
83
83
|
'ASTRING-CHAR'() {
|
|
84
84
|
let value = this['ATOM-CHAR']() + this['resp-specials']();
|
|
85
|
-
this['ASTRING-CHAR'] = function() {
|
|
85
|
+
this['ASTRING-CHAR'] = function () {
|
|
86
86
|
return value;
|
|
87
87
|
};
|
|
88
88
|
return value;
|
|
@@ -90,7 +90,7 @@ module.exports = {
|
|
|
90
90
|
|
|
91
91
|
'TEXT-CHAR'() {
|
|
92
92
|
let value = excludeChars(this.CHAR(), '\r\n');
|
|
93
|
-
this['TEXT-CHAR'] = function() {
|
|
93
|
+
this['TEXT-CHAR'] = function () {
|
|
94
94
|
return value;
|
|
95
95
|
};
|
|
96
96
|
return value;
|
|
@@ -98,7 +98,7 @@ module.exports = {
|
|
|
98
98
|
|
|
99
99
|
'atom-specials'() {
|
|
100
100
|
let value = '(' + ')' + '{' + this.SP() + this.CTL() + this['list-wildcards']() + this['quoted-specials']() + this['resp-specials']();
|
|
101
|
-
this['atom-specials'] = function() {
|
|
101
|
+
this['atom-specials'] = function () {
|
|
102
102
|
return value;
|
|
103
103
|
};
|
|
104
104
|
return value;
|
|
@@ -109,8 +109,8 @@ module.exports = {
|
|
|
109
109
|
},
|
|
110
110
|
|
|
111
111
|
'quoted-specials'() {
|
|
112
|
-
let value = this.DQUOTE() + '\\
|
|
113
|
-
this['quoted-specials'] = function() {
|
|
112
|
+
let value = this.DQUOTE() + "'\\";
|
|
113
|
+
this['quoted-specials'] = function () {
|
|
114
114
|
return value;
|
|
115
115
|
};
|
|
116
116
|
return value;
|
|
@@ -122,7 +122,7 @@ module.exports = {
|
|
|
122
122
|
|
|
123
123
|
tag() {
|
|
124
124
|
let value = excludeChars(this['ASTRING-CHAR'](), '+');
|
|
125
|
-
this.tag = function() {
|
|
125
|
+
this.tag = function () {
|
|
126
126
|
return value;
|
|
127
127
|
};
|
|
128
128
|
return value;
|
|
@@ -130,7 +130,7 @@ module.exports = {
|
|
|
130
130
|
|
|
131
131
|
command() {
|
|
132
132
|
let value = this.ALPHA() + this.DIGIT() + '-';
|
|
133
|
-
this.command = function() {
|
|
133
|
+
this.command = function () {
|
|
134
134
|
return value;
|
|
135
135
|
};
|
|
136
136
|
return value;
|
|
@@ -40,6 +40,7 @@ class ImapStream extends Transform {
|
|
|
40
40
|
this.literals = [];
|
|
41
41
|
|
|
42
42
|
this.compress = false;
|
|
43
|
+
this.secureConnection = this.options.secureConnection;
|
|
43
44
|
|
|
44
45
|
this.processingInput = false;
|
|
45
46
|
this.inputQueue = []; // unprocessed input chunks
|
|
@@ -197,7 +198,14 @@ class ImapStream extends Transform {
|
|
|
197
198
|
}
|
|
198
199
|
|
|
199
200
|
if (this.options.logRaw) {
|
|
200
|
-
this.log.trace({
|
|
201
|
+
this.log.trace({
|
|
202
|
+
src: 's',
|
|
203
|
+
msg: 'read from socket',
|
|
204
|
+
data: chunk.toString('base64'),
|
|
205
|
+
compress: !!this.compress,
|
|
206
|
+
secure: !!this.secureConnection,
|
|
207
|
+
cid: this.cid
|
|
208
|
+
});
|
|
201
209
|
}
|
|
202
210
|
|
|
203
211
|
if (chunk && chunk.length) {
|
|
@@ -29,13 +29,13 @@ class TokenParser {
|
|
|
29
29
|
let elm;
|
|
30
30
|
let partial;
|
|
31
31
|
|
|
32
|
-
if (!node.
|
|
33
|
-
node.
|
|
32
|
+
if (!node.isClosed && node.type === 'SEQUENCE' && node.value === '*') {
|
|
33
|
+
node.isClosed = true;
|
|
34
34
|
node.type = 'ATOM';
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
// If the node was never closed, throw it
|
|
38
|
-
if (!node.
|
|
38
|
+
if (!node.isClosed) {
|
|
39
39
|
throw new Error('Unexpected end of input at position ' + (this.pos + this.str.length - 1));
|
|
40
40
|
}
|
|
41
41
|
|
|
@@ -97,7 +97,7 @@ class TokenParser {
|
|
|
97
97
|
childNodes: [],
|
|
98
98
|
type: false,
|
|
99
99
|
value: '',
|
|
100
|
-
|
|
100
|
+
isClosed: true
|
|
101
101
|
};
|
|
102
102
|
|
|
103
103
|
if (parentNode) {
|
|
@@ -136,14 +136,14 @@ class TokenParser {
|
|
|
136
136
|
this.currentNode = this.createNode(this.currentNode, this.pos + i);
|
|
137
137
|
this.currentNode.type = 'string';
|
|
138
138
|
this.state = 'STRING';
|
|
139
|
-
this.currentNode.
|
|
139
|
+
this.currentNode.isClosed = false;
|
|
140
140
|
break;
|
|
141
141
|
|
|
142
142
|
// ( starts a new list
|
|
143
143
|
case '(':
|
|
144
144
|
this.currentNode = this.createNode(this.currentNode, this.pos + i);
|
|
145
145
|
this.currentNode.type = 'LIST';
|
|
146
|
-
this.currentNode.
|
|
146
|
+
this.currentNode.isClosed = false;
|
|
147
147
|
break;
|
|
148
148
|
|
|
149
149
|
// ) closes a list
|
|
@@ -152,7 +152,7 @@ class TokenParser {
|
|
|
152
152
|
throw new Error('Unexpected list terminator ) at position ' + (this.pos + i));
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
-
this.currentNode.
|
|
155
|
+
this.currentNode.isClosed = true;
|
|
156
156
|
this.currentNode.endPos = this.pos + i;
|
|
157
157
|
this.currentNode = this.currentNode.parentNode;
|
|
158
158
|
|
|
@@ -164,7 +164,7 @@ class TokenParser {
|
|
|
164
164
|
if (this.currentNode.type !== 'SECTION') {
|
|
165
165
|
throw new Error('Unexpected section terminator ] at position ' + (this.pos + i));
|
|
166
166
|
}
|
|
167
|
-
this.currentNode.
|
|
167
|
+
this.currentNode.isClosed = true;
|
|
168
168
|
this.currentNode.endPos = this.pos + i;
|
|
169
169
|
this.currentNode = this.currentNode.parentNode;
|
|
170
170
|
checkSP();
|
|
@@ -181,24 +181,36 @@ class TokenParser {
|
|
|
181
181
|
this.currentNode = this.createNode(this.currentNode, this.pos + i);
|
|
182
182
|
this.currentNode.type = 'PARTIAL';
|
|
183
183
|
this.state = 'PARTIAL';
|
|
184
|
-
this.currentNode.
|
|
184
|
+
this.currentNode.isClosed = false;
|
|
185
185
|
}
|
|
186
186
|
break;
|
|
187
187
|
|
|
188
|
+
// binary literal8
|
|
189
|
+
case '~': {
|
|
190
|
+
let nextChr = this.str.charAt(i + 1);
|
|
191
|
+
if (nextChr !== '{') {
|
|
192
|
+
throw new Error('Unexpected literal8 marker at position ' + (this.pos + i));
|
|
193
|
+
}
|
|
194
|
+
this.expectedLiteralType = 'literal8';
|
|
195
|
+
break;
|
|
196
|
+
}
|
|
197
|
+
|
|
188
198
|
// { starts a new literal
|
|
189
199
|
case '{':
|
|
190
200
|
this.currentNode = this.createNode(this.currentNode, this.pos + i);
|
|
191
201
|
this.currentNode.type = 'LITERAL';
|
|
202
|
+
this.currentNode.literalType = this.expectedLiteralType || 'literal';
|
|
203
|
+
this.expectedLiteralType = false;
|
|
192
204
|
this.state = 'LITERAL';
|
|
193
|
-
this.currentNode.
|
|
205
|
+
this.currentNode.isClosed = false;
|
|
194
206
|
break;
|
|
195
207
|
|
|
196
|
-
//
|
|
208
|
+
// * starts a new sequence
|
|
197
209
|
case '*':
|
|
198
210
|
this.currentNode = this.createNode(this.currentNode, this.pos + i);
|
|
199
211
|
this.currentNode.type = 'SEQUENCE';
|
|
200
212
|
this.currentNode.value = chr;
|
|
201
|
-
this.currentNode.
|
|
213
|
+
this.currentNode.isClosed = false;
|
|
202
214
|
this.state = 'SEQUENCE';
|
|
203
215
|
break;
|
|
204
216
|
|
|
@@ -218,7 +230,7 @@ class TokenParser {
|
|
|
218
230
|
|
|
219
231
|
this.currentNode = this.createNode(this.currentNode, this.pos + i);
|
|
220
232
|
this.currentNode.type = 'SECTION';
|
|
221
|
-
this.currentNode.
|
|
233
|
+
this.currentNode.isClosed = false;
|
|
222
234
|
this.state = 'NORMAL';
|
|
223
235
|
|
|
224
236
|
// RFC2221 defines a response code REFERRAL whose payload is an
|
|
@@ -244,7 +256,7 @@ class TokenParser {
|
|
|
244
256
|
this.currentNode = this.currentNode.parentNode;
|
|
245
257
|
|
|
246
258
|
// close out the SECTION
|
|
247
|
-
this.currentNode.
|
|
259
|
+
this.currentNode.isClosed = true;
|
|
248
260
|
this.currentNode = this.currentNode.parentNode;
|
|
249
261
|
checkSP();
|
|
250
262
|
}
|
|
@@ -286,7 +298,7 @@ class TokenParser {
|
|
|
286
298
|
this.currentNode.endPos = this.pos + i - 1;
|
|
287
299
|
this.currentNode = this.currentNode.parentNode;
|
|
288
300
|
|
|
289
|
-
this.currentNode.
|
|
301
|
+
this.currentNode.isClosed = true;
|
|
290
302
|
this.currentNode.endPos = this.pos + i;
|
|
291
303
|
this.currentNode = this.currentNode.parentNode;
|
|
292
304
|
this.state = 'NORMAL';
|
|
@@ -297,20 +309,20 @@ class TokenParser {
|
|
|
297
309
|
|
|
298
310
|
if ((chr === ',' || chr === ':') && this.currentNode.value.match(/^\d+$/)) {
|
|
299
311
|
this.currentNode.type = 'SEQUENCE';
|
|
300
|
-
this.currentNode.
|
|
312
|
+
this.currentNode.isClosed = true;
|
|
301
313
|
this.state = 'SEQUENCE';
|
|
302
314
|
}
|
|
303
315
|
|
|
304
316
|
// [ starts a section group for this element
|
|
305
317
|
if (chr === '[') {
|
|
306
318
|
// allowed only for selected elements
|
|
307
|
-
if (['BODY', 'BODY.PEEK'].indexOf(this.currentNode.value.toUpperCase()) < 0) {
|
|
319
|
+
if (['BODY', 'BODY.PEEK', 'BINARY', 'BINARY.PEEK'].indexOf(this.currentNode.value.toUpperCase()) < 0) {
|
|
308
320
|
throw new Error('Unexpected section start char [ at position ' + this.pos);
|
|
309
321
|
}
|
|
310
322
|
this.currentNode.endPos = this.pos + i;
|
|
311
323
|
this.currentNode = this.createNode(this.currentNode.parentNode, this.pos + i);
|
|
312
324
|
this.currentNode.type = 'SECTION';
|
|
313
|
-
this.currentNode.
|
|
325
|
+
this.currentNode.isClosed = false;
|
|
314
326
|
this.state = 'NORMAL';
|
|
315
327
|
break;
|
|
316
328
|
}
|
|
@@ -333,7 +345,7 @@ class TokenParser {
|
|
|
333
345
|
// DQUOTE ends the string sequence
|
|
334
346
|
if (chr === '"') {
|
|
335
347
|
this.currentNode.endPos = this.pos + i;
|
|
336
|
-
this.currentNode.
|
|
348
|
+
this.currentNode.isClosed = true;
|
|
337
349
|
this.currentNode = this.currentNode.parentNode;
|
|
338
350
|
this.state = 'NORMAL';
|
|
339
351
|
|
|
@@ -359,7 +371,7 @@ class TokenParser {
|
|
|
359
371
|
throw new Error('Unexpected end of partial at position ' + this.pos);
|
|
360
372
|
}
|
|
361
373
|
this.currentNode.endPos = this.pos + i;
|
|
362
|
-
this.currentNode.
|
|
374
|
+
this.currentNode.isClosed = true;
|
|
363
375
|
this.currentNode = this.currentNode.parentNode;
|
|
364
376
|
this.state = 'NORMAL';
|
|
365
377
|
checkSP();
|
|
@@ -385,16 +397,19 @@ class TokenParser {
|
|
|
385
397
|
if (this.currentNode.started) {
|
|
386
398
|
// only relevant if literals are not already parsed out from input
|
|
387
399
|
|
|
388
|
-
//
|
|
400
|
+
// Disabled NULL byte check
|
|
401
|
+
// See https://github.com/emailjs/emailjs-imap-handler/commit/f11b2822bedabe492236e8263afc630134a3c41c
|
|
402
|
+
/*
|
|
389
403
|
if (chr === '\u0000') {
|
|
390
404
|
throw new Error('Unexpected \\x00 at position ' + (this.pos + i));
|
|
391
405
|
}
|
|
406
|
+
*/
|
|
392
407
|
|
|
393
408
|
this.currentNode.chBuffer[this.currentNode.chPos++] = chr.charCodeAt(0);
|
|
394
409
|
|
|
395
410
|
if (this.currentNode.chPos >= this.currentNode.literalLength) {
|
|
396
411
|
this.currentNode.endPos = this.pos + i;
|
|
397
|
-
this.currentNode.
|
|
412
|
+
this.currentNode.isClosed = true;
|
|
398
413
|
this.currentNode.value = this.currentNode.chBuffer.toString('binary');
|
|
399
414
|
this.currentNode.chBuffer = Buffer.alloc(0);
|
|
400
415
|
this.currentNode = this.currentNode.parentNode;
|
|
@@ -427,7 +442,7 @@ class TokenParser {
|
|
|
427
442
|
// special case where literal content length is 0
|
|
428
443
|
// close the node right away, do not wait for additional input
|
|
429
444
|
this.currentNode.endPos = this.pos + i;
|
|
430
|
-
this.currentNode.
|
|
445
|
+
this.currentNode.isClosed = true;
|
|
431
446
|
this.currentNode = this.currentNode.parentNode;
|
|
432
447
|
this.state = 'NORMAL';
|
|
433
448
|
checkSP();
|
|
@@ -445,7 +460,7 @@ class TokenParser {
|
|
|
445
460
|
this.currentNode.endPos = this.pos + i + this.currentNode.value.length;
|
|
446
461
|
|
|
447
462
|
this.currentNode.started = false;
|
|
448
|
-
this.currentNode.
|
|
463
|
+
this.currentNode.isClosed = true;
|
|
449
464
|
this.currentNode = this.currentNode.parentNode;
|
|
450
465
|
this.state = 'NORMAL';
|
|
451
466
|
checkSP();
|
|
@@ -478,7 +493,7 @@ class TokenParser {
|
|
|
478
493
|
throw new Error('Unexpected whitespace at position ' + (this.pos + i));
|
|
479
494
|
}
|
|
480
495
|
|
|
481
|
-
this.currentNode.
|
|
496
|
+
this.currentNode.isClosed = true;
|
|
482
497
|
this.currentNode.endPos = this.pos + i - 1;
|
|
483
498
|
this.currentNode = this.currentNode.parentNode;
|
|
484
499
|
this.state = 'NORMAL';
|
|
@@ -487,7 +502,7 @@ class TokenParser {
|
|
|
487
502
|
this.currentNode.endPos = this.pos + i - 1;
|
|
488
503
|
this.currentNode = this.currentNode.parentNode;
|
|
489
504
|
|
|
490
|
-
this.currentNode.
|
|
505
|
+
this.currentNode.isClosed = true;
|
|
491
506
|
this.currentNode.endPos = this.pos + i;
|
|
492
507
|
this.currentNode = this.currentNode.parentNode;
|
|
493
508
|
this.state = 'NORMAL';
|