imapflow 1.0.90 → 1.0.93
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 +13 -3
- package/lib/commands/authenticate.js +1 -1
- package/lib/commands/copy.js +2 -2
- package/lib/commands/create.js +2 -2
- package/lib/commands/delete.js +2 -2
- package/lib/commands/fetch.js +4 -2
- package/lib/commands/list.js +44 -60
- package/lib/commands/move.js +2 -2
- 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 +46 -12
- package/lib/tools.js +29 -11
- package/lib/types.d.ts +11 -3
- package/package.json +12 -10
- package/test/imap-compiler-test.js +41 -2
- package/test/imap-parser-test.js +30 -0
package/.gitattributes
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
*.js text eol=lf
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
name: Run tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
pull_request:
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
test:
|
|
9
|
+
strategy:
|
|
10
|
+
matrix:
|
|
11
|
+
node: [16.x, 18.x]
|
|
12
|
+
os: [ubuntu-latest]
|
|
13
|
+
runs-on: ${{ matrix.os }}
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v2
|
|
16
|
+
- name: Use Node.js ${{ matrix.node }}
|
|
17
|
+
uses: actions/setup-node@v1
|
|
18
|
+
with:
|
|
19
|
+
node-version: ${{ matrix.node }}
|
|
20
|
+
- run: npm install
|
|
21
|
+
- run: npm test
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# CHANGELOG
|
|
2
2
|
|
|
3
|
+
- 1.0.93 - 2022-05-03
|
|
4
|
+
|
|
5
|
+
- Fixed missing unicode encoding for APPEND, COPY, MOVE
|
|
6
|
+
|
|
7
|
+
- 1.0.92 - 2022-05-03
|
|
8
|
+
|
|
9
|
+
- Added support for IMAP BINARY extension (rfc3516)
|
|
10
|
+
- Logging improvements
|
|
11
|
+
|
|
12
|
+
- 1.0.91 - 2022-05-02
|
|
13
|
+
|
|
14
|
+
- Do not throw if literal includes a null byte
|
|
15
|
+
- Use UTF8 path names by default
|
|
16
|
+
- Overrides '\*' range query with the EXISTS value
|
|
17
|
+
|
|
3
18
|
- 1.0.90 - 2022-04-04
|
|
4
19
|
|
|
5
20
|
- Added new configuration option `maxIdleTime`
|
package/lib/commands/append.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { getStatusCode, formatFlag, canUseFlag, formatDateTime, normalizePath, comparePaths, getErrorText } = require('../tools.js');
|
|
3
|
+
const { getStatusCode, formatFlag, canUseFlag, formatDateTime, normalizePath, encodePath, comparePaths, getErrorText } = require('../tools.js');
|
|
4
4
|
|
|
5
5
|
// Appends a message to a mailbox
|
|
6
6
|
module.exports = async (connection, destination, content, flags, idate) => {
|
|
@@ -26,7 +26,7 @@ module.exports = async (connection, destination, content, flags, idate) => {
|
|
|
26
26
|
.map(flag => flag && formatFlag(flag.toString()))
|
|
27
27
|
.filter(flag => flag && canUseFlag(connection.mailbox, flag));
|
|
28
28
|
|
|
29
|
-
let attributes = [{ type: 'ATOM', value: destination }];
|
|
29
|
+
let attributes = [{ type: 'ATOM', value: encodePath(connection, destination) }];
|
|
30
30
|
|
|
31
31
|
idate = idate ? formatDateTime(idate) : false;
|
|
32
32
|
|
|
@@ -38,7 +38,17 @@ module.exports = async (connection, destination, content, flags, idate) => {
|
|
|
38
38
|
attributes.push({ type: 'STRING', value: idate }); // force quotes as required by date-time
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
let isLiteral8 = false;
|
|
42
|
+
if (connection.capabilities.has('BINARY') && !connection.disableBinary) {
|
|
43
|
+
if (typeof content === 'string') {
|
|
44
|
+
content = Buffer.from(content);
|
|
45
|
+
}
|
|
46
|
+
// Value is literal8 if it contains NULL bytes. The server must support the BINARY extension
|
|
47
|
+
// and if it does not then send the value as a regular literal and hope for the best
|
|
48
|
+
isLiteral8 = content.indexOf(Buffer.from([0])) >= 0;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
attributes.push({ type: 'LITERAL', value: content, isLiteral8 });
|
|
42
52
|
|
|
43
53
|
let map = { destination };
|
|
44
54
|
if (connection.mailbox && connection.mailbox.path) {
|
|
@@ -39,7 +39,7 @@ module.exports = async (connection, username, accessToken) => {
|
|
|
39
39
|
try {
|
|
40
40
|
errorResponse = JSON.parse(Buffer.from(resp.attributes[0].value, 'base64').toString());
|
|
41
41
|
} catch (err) {
|
|
42
|
-
connection.log.debug({
|
|
42
|
+
connection.log.debug({ errorResponse: resp.attributes[0].value, err });
|
|
43
43
|
}
|
|
44
44
|
}
|
|
45
45
|
|
package/lib/commands/copy.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { getStatusCode, normalizePath, expandRange, getErrorText } = require('../tools.js');
|
|
3
|
+
const { getStatusCode, normalizePath, encodePath, expandRange, getErrorText } = require('../tools.js');
|
|
4
4
|
|
|
5
5
|
// Copies messages from current mailbox to some other mailbox
|
|
6
6
|
module.exports = async (connection, range, destination, options) => {
|
|
@@ -14,7 +14,7 @@ module.exports = async (connection, range, destination, options) => {
|
|
|
14
14
|
|
|
15
15
|
let attributes = [
|
|
16
16
|
{ type: 'SEQUENCE', value: range },
|
|
17
|
-
{ type: 'ATOM', value: destination }
|
|
17
|
+
{ type: 'ATOM', value: encodePath(connection, destination) }
|
|
18
18
|
];
|
|
19
19
|
|
|
20
20
|
let response;
|
package/lib/commands/create.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
|
// Creates a new mailbox
|
|
6
6
|
module.exports = async (connection, path) => {
|
|
@@ -16,7 +16,7 @@ module.exports = async (connection, path) => {
|
|
|
16
16
|
let map = {
|
|
17
17
|
path
|
|
18
18
|
};
|
|
19
|
-
response = await connection.exec('CREATE', [{ type: 'ATOM', value: path }]);
|
|
19
|
+
response = await connection.exec('CREATE', [{ type: 'ATOM', value: encodePath(connection, path) }]);
|
|
20
20
|
|
|
21
21
|
let section =
|
|
22
22
|
response.response.attributes &&
|
package/lib/commands/delete.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
|
// Deletes an existing mailbox
|
|
6
6
|
module.exports = async (connection, path) => {
|
|
@@ -20,7 +20,7 @@ module.exports = async (connection, path) => {
|
|
|
20
20
|
let map = {
|
|
21
21
|
path
|
|
22
22
|
};
|
|
23
|
-
response = await connection.exec('DELETE', [{ type: 'ATOM', value: path }]);
|
|
23
|
+
response = await connection.exec('DELETE', [{ type: 'ATOM', value: encodePath(connection, path) }]);
|
|
24
24
|
response.next();
|
|
25
25
|
return map;
|
|
26
26
|
} catch (err) {
|
package/lib/commands/fetch.js
CHANGED
|
@@ -18,6 +18,8 @@ module.exports = async (connection, range, query, options) => {
|
|
|
18
18
|
list: []
|
|
19
19
|
};
|
|
20
20
|
|
|
21
|
+
const commandKey = connection.capabilities.has('BINARY') && !connection.disableBinary ? 'BINARY' : 'BODY';
|
|
22
|
+
|
|
21
23
|
let response;
|
|
22
24
|
try {
|
|
23
25
|
let attributes = [{ type: 'SEQUENCE', value: (range || '*').toString() }];
|
|
@@ -27,7 +29,7 @@ module.exports = async (connection, range, query, options) => {
|
|
|
27
29
|
let setBodyPeek = (attributes, partial) => {
|
|
28
30
|
let bodyPeek = {
|
|
29
31
|
type: 'ATOM',
|
|
30
|
-
value:
|
|
32
|
+
value: `${commandKey}.PEEK`,
|
|
31
33
|
section: [],
|
|
32
34
|
partial
|
|
33
35
|
};
|
|
@@ -61,7 +63,7 @@ module.exports = async (connection, range, query, options) => {
|
|
|
61
63
|
partial.push(Number(query.source.maxLength));
|
|
62
64
|
}
|
|
63
65
|
}
|
|
64
|
-
queryStructure.push({ type: 'ATOM', value:
|
|
66
|
+
queryStructure.push({ type: 'ATOM', value: `${commandKey}.PEEK`, section: [], partial });
|
|
65
67
|
}
|
|
66
68
|
|
|
67
69
|
// if possible, always request for unique email id
|
package/lib/commands/list.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { normalizePath } = require('../tools.js');
|
|
3
|
+
const { decodePath, encodePath, normalizePath } = require('../tools.js');
|
|
4
4
|
const { specialUse } = require('../special-use');
|
|
5
|
-
const iconv = require('iconv-lite');
|
|
6
5
|
|
|
7
6
|
// Lists mailboxes from server
|
|
8
7
|
module.exports = async (connection, reference, mailbox) => {
|
|
@@ -14,7 +13,7 @@ module.exports = async (connection, reference, mailbox) => {
|
|
|
14
13
|
let entries = [];
|
|
15
14
|
|
|
16
15
|
let runList = async (reference, mailbox) => {
|
|
17
|
-
response = await connection.exec(listCommand, [reference, mailbox], {
|
|
16
|
+
response = await connection.exec(listCommand, [encodePath(connection, reference), encodePath(connection, mailbox)], {
|
|
18
17
|
untagged: {
|
|
19
18
|
[listCommand]: async untagged => {
|
|
20
19
|
if (!untagged.attributes || !untagged.attributes.length) {
|
|
@@ -22,7 +21,8 @@ module.exports = async (connection, reference, mailbox) => {
|
|
|
22
21
|
}
|
|
23
22
|
|
|
24
23
|
let entry = {
|
|
25
|
-
path: (untagged.attributes[2] && untagged.attributes[2].value) || '',
|
|
24
|
+
path: normalizePath(connection, decodePath(connection, (untagged.attributes[2] && untagged.attributes[2].value) || '')),
|
|
25
|
+
pathAsListed: (untagged.attributes[2] && untagged.attributes[2].value) || '',
|
|
26
26
|
flags: new Set(untagged.attributes[0].map(entry => entry.value)),
|
|
27
27
|
delimiter: untagged.attributes[1] && untagged.attributes[1].value,
|
|
28
28
|
listed: true
|
|
@@ -45,19 +45,8 @@ module.exports = async (connection, reference, mailbox) => {
|
|
|
45
45
|
entry.path = entry.path.slice(1);
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
entry.
|
|
49
|
-
|
|
50
|
-
try {
|
|
51
|
-
return iconv.decode(Buffer.from(folder), 'utf-7-imap').toString();
|
|
52
|
-
} catch (err) {
|
|
53
|
-
return folder; // keep as is
|
|
54
|
-
}
|
|
55
|
-
} else {
|
|
56
|
-
return folder; // keep as is
|
|
57
|
-
}
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
entry.path = normalizePath(connection, entry.path);
|
|
48
|
+
entry.parentPath = entry.delimiter && entry.path ? entry.path.substr(0, entry.path.lastIndexOf(entry.delimiter)) : '';
|
|
49
|
+
entry.parent = entry.delimiter ? entry.path.split(entry.delimiter) : [entry.path];
|
|
61
50
|
entry.name = entry.parent.pop();
|
|
62
51
|
|
|
63
52
|
let specialUseFlag = specialUse(connection.capabilities.has('XLIST') || connection.capabilities.has('SPECIAL-USE'), entry);
|
|
@@ -80,59 +69,54 @@ module.exports = async (connection, reference, mailbox) => {
|
|
|
80
69
|
await runList('', 'INBOX');
|
|
81
70
|
}
|
|
82
71
|
|
|
83
|
-
response = await connection.exec(
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
delimiter: untagged.attributes[1] && untagged.attributes[1].value,
|
|
93
|
-
path: (untagged.attributes[2] && untagged.attributes[2].value) || '',
|
|
94
|
-
subscribed: true
|
|
95
|
-
};
|
|
72
|
+
response = await connection.exec(
|
|
73
|
+
'LSUB',
|
|
74
|
+
[encodePath(connection, normalizePath(connection, reference || '')), encodePath(connection, normalizePath(connection, mailbox || '', true))],
|
|
75
|
+
{
|
|
76
|
+
untagged: {
|
|
77
|
+
LSUB: async untagged => {
|
|
78
|
+
if (!untagged.attributes || !untagged.attributes.length) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
96
81
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
82
|
+
let entry = {
|
|
83
|
+
path: normalizePath(connection, decodePath(connection, (untagged.attributes[2] && untagged.attributes[2].value) || '')),
|
|
84
|
+
pathAsListed: (untagged.attributes[2] && untagged.attributes[2].value) || '',
|
|
85
|
+
flags: new Set(untagged.attributes[0].map(entry => entry.value)),
|
|
86
|
+
delimiter: untagged.attributes[1] && untagged.attributes[1].value,
|
|
87
|
+
subscribed: true
|
|
88
|
+
};
|
|
100
89
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
90
|
+
if (entry.path.toUpperCase() === 'INBOX') {
|
|
91
|
+
entry.specialUse = '\\Inbox';
|
|
92
|
+
}
|
|
104
93
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
try {
|
|
108
|
-
return iconv.decode(Buffer.from(folder), 'utf-7-imap').toString();
|
|
109
|
-
} catch (err) {
|
|
110
|
-
return folder; // keep as is
|
|
111
|
-
}
|
|
112
|
-
} else {
|
|
113
|
-
return folder; // keep as is
|
|
94
|
+
if (entry.delimiter && entry.path.charAt(0) === entry.delimiter) {
|
|
95
|
+
entry.path = entry.path.slice(1);
|
|
114
96
|
}
|
|
115
|
-
});
|
|
116
97
|
|
|
117
|
-
|
|
98
|
+
entry.parentPath = entry.delimiter && entry.path ? entry.path.substr(0, entry.path.lastIndexOf(entry.delimiter)) : '';
|
|
99
|
+
entry.parent = entry.delimiter ? entry.path.split(entry.delimiter) : [entry.path];
|
|
100
|
+
entry.name = entry.parent.pop();
|
|
118
101
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
102
|
+
let existing = entries.find(existing => existing.path === entry.path);
|
|
103
|
+
if (existing) {
|
|
104
|
+
existing.subscribed = true;
|
|
105
|
+
entry.flags.forEach(flag => existing.flags.add(flag));
|
|
106
|
+
} else {
|
|
107
|
+
// ignore non-listed folders
|
|
108
|
+
/*
|
|
109
|
+
let specialUseFlag = specialUse(connection.capabilities.has('XLIST') || connection.capabilities.has('SPECIAL-USE'), entry);
|
|
110
|
+
if (specialUseFlag && !flagsSeen.has(specialUseFlag)) {
|
|
111
|
+
entry.specialUse = specialUseFlag;
|
|
112
|
+
}
|
|
113
|
+
entries.push(entry);
|
|
114
|
+
*/
|
|
129
115
|
}
|
|
130
|
-
entries.push(entry);
|
|
131
|
-
*/
|
|
132
116
|
}
|
|
133
117
|
}
|
|
134
118
|
}
|
|
135
|
-
|
|
119
|
+
);
|
|
136
120
|
response.next();
|
|
137
121
|
|
|
138
122
|
let inboxEntry = entries.find(entry => entry.specialUse === '\\Inbox');
|
package/lib/commands/move.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { getStatusCode, normalizePath, expandRange, getErrorText } = require('../tools.js');
|
|
3
|
+
const { getStatusCode, normalizePath, encodePath, expandRange, getErrorText } = require('../tools.js');
|
|
4
4
|
|
|
5
5
|
// Moves messages from current mailbox to some other mailbox
|
|
6
6
|
module.exports = async (connection, range, destination, options) => {
|
|
@@ -14,7 +14,7 @@ module.exports = async (connection, range, destination, options) => {
|
|
|
14
14
|
|
|
15
15
|
let attributes = [
|
|
16
16
|
{ type: 'SEQUENCE', value: range },
|
|
17
|
-
{ type: 'ATOM', value: destination }
|
|
17
|
+
{ type: 'ATOM', value: encodePath(connection, destination) }
|
|
18
18
|
];
|
|
19
19
|
|
|
20
20
|
let map = { path: connection.mailbox.path, destination };
|
package/lib/commands/quota.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { getStatusCode, normalizePath, getErrorText } = require('../tools.js');
|
|
3
|
+
const { encodePath, getStatusCode, normalizePath, getErrorText } = require('../tools.js');
|
|
4
4
|
|
|
5
5
|
// Requests quota information for a mailbox
|
|
6
6
|
module.exports = async (connection, path) => {
|
|
@@ -63,7 +63,7 @@ module.exports = async (connection, path) => {
|
|
|
63
63
|
let quotaFound = false;
|
|
64
64
|
let response;
|
|
65
65
|
try {
|
|
66
|
-
response = await connection.exec('GETQUOTAROOT', [{ type: 'ATOM', value: path }], {
|
|
66
|
+
response = await connection.exec('GETQUOTAROOT', [{ type: 'ATOM', value: encodePath(connection, path) }], {
|
|
67
67
|
untagged: {
|
|
68
68
|
QUOTAROOT: async untagged => {
|
|
69
69
|
let quotaRoot =
|
package/lib/commands/rename.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
|
// Renames existing mailbox
|
|
6
6
|
module.exports = async (connection, path, newPath) => {
|
|
@@ -23,8 +23,8 @@ module.exports = async (connection, path, newPath) => {
|
|
|
23
23
|
newPath
|
|
24
24
|
};
|
|
25
25
|
response = await connection.exec('RENAME', [
|
|
26
|
-
{ type: 'ATOM', value: path },
|
|
27
|
-
{ type: 'ATOM', value: newPath }
|
|
26
|
+
{ type: 'ATOM', value: encodePath(connection, path) },
|
|
27
|
+
{ type: 'ATOM', value: encodePath(connection, newPath) }
|
|
28
28
|
]);
|
|
29
29
|
response.next();
|
|
30
30
|
return map;
|
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');
|