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/.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.92 - 2022-05-03
|
|
4
|
+
|
|
5
|
+
- Added support for IMAP BINARY extension (rfc3516)
|
|
6
|
+
- Logging improvements
|
|
7
|
+
|
|
8
|
+
- 1.0.91 - 2022-05-02
|
|
9
|
+
|
|
10
|
+
- Do not throw if literal includes a null byte
|
|
11
|
+
- Use UTF8 path names by default
|
|
12
|
+
- Overrides '\*' range query with the EXISTS value
|
|
13
|
+
|
|
14
|
+
- 1.0.90 - 2022-04-04
|
|
15
|
+
|
|
16
|
+
- Added new configuration option `maxIdleTime`
|
|
17
|
+
|
|
3
18
|
- 1.0.89 - 2022-04-04
|
|
4
19
|
|
|
5
20
|
- Added new event type `response` that emits all command responses
|
package/lib/commands/append.js
CHANGED
|
@@ -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/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/idle.js
CHANGED
|
@@ -2,112 +2,139 @@
|
|
|
2
2
|
|
|
3
3
|
const NOOP_INTERVAL = 2 * 60 * 1000;
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
if (connection.state !== connection.states.SELECTED) {
|
|
8
|
-
// nothing to do here
|
|
9
|
-
return;
|
|
10
|
-
}
|
|
5
|
+
async function runIdle(connection) {
|
|
6
|
+
let response;
|
|
11
7
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
let preCheckWaitQueue = [];
|
|
16
|
-
try {
|
|
17
|
-
connection.idling = true;
|
|
18
|
-
|
|
19
|
-
//let idleSent = false;
|
|
20
|
-
let doneRequested = false;
|
|
21
|
-
let doneSent = false;
|
|
22
|
-
let canEnd = false;
|
|
23
|
-
|
|
24
|
-
let preCheck = () => {
|
|
25
|
-
doneRequested = true;
|
|
26
|
-
if (canEnd && !doneSent) {
|
|
27
|
-
connection.log.debug({
|
|
28
|
-
src: 'c',
|
|
29
|
-
msg: `DONE`,
|
|
30
|
-
comment: `breaking IDLE`,
|
|
31
|
-
lockId: connection.currentLockId,
|
|
32
|
-
path: connection.mailbox && connection.mailbox.path
|
|
33
|
-
});
|
|
34
|
-
connection.write('DONE');
|
|
35
|
-
doneSent = true;
|
|
36
|
-
|
|
37
|
-
connection.idling = false;
|
|
38
|
-
connection.preCheck = false; // unset itself
|
|
39
|
-
|
|
40
|
-
while (preCheckWaitQueue.length) {
|
|
41
|
-
let { resolve } = preCheckWaitQueue.shift();
|
|
42
|
-
resolve();
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
connection.preCheck = () => {
|
|
48
|
-
let handler = new Promise((resolve, reject) => {
|
|
49
|
-
preCheckWaitQueue.push({ resolve, reject });
|
|
50
|
-
});
|
|
8
|
+
let preCheckWaitQueue = [];
|
|
9
|
+
try {
|
|
10
|
+
connection.idling = true;
|
|
51
11
|
|
|
52
|
-
|
|
53
|
-
|
|
12
|
+
//let idleSent = false;
|
|
13
|
+
let doneRequested = false;
|
|
14
|
+
let doneSent = false;
|
|
15
|
+
let canEnd = false;
|
|
16
|
+
|
|
17
|
+
let preCheck = () => {
|
|
18
|
+
doneRequested = true;
|
|
19
|
+
if (canEnd && !doneSent) {
|
|
20
|
+
connection.log.debug({
|
|
21
|
+
src: 'c',
|
|
22
|
+
msg: `DONE`,
|
|
23
|
+
comment: `breaking IDLE`,
|
|
54
24
|
lockId: connection.currentLockId,
|
|
55
|
-
path: connection.mailbox && connection.mailbox.path
|
|
56
|
-
queued: preCheckWaitQueue.length,
|
|
57
|
-
doneRequested,
|
|
58
|
-
canEnd,
|
|
59
|
-
doneSent
|
|
25
|
+
path: connection.mailbox && connection.mailbox.path
|
|
60
26
|
});
|
|
27
|
+
connection.write('DONE');
|
|
28
|
+
doneSent = true;
|
|
61
29
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
return handler;
|
|
65
|
-
};
|
|
30
|
+
connection.idling = false;
|
|
31
|
+
connection.preCheck = false; // unset itself
|
|
66
32
|
|
|
67
|
-
response = await connection.exec('IDLE', false, {
|
|
68
|
-
onPlusTag: async () => {
|
|
69
|
-
connection.log.debug({ msg: `Initiated IDLE, waiting for server input`, doneRequested });
|
|
70
|
-
canEnd = true;
|
|
71
|
-
if (doneRequested) {
|
|
72
|
-
preCheck();
|
|
73
|
-
}
|
|
74
|
-
},
|
|
75
|
-
onSend: () => {
|
|
76
|
-
//idleSent = true;
|
|
77
|
-
}
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
// unset before response.next()
|
|
81
|
-
if (typeof connection.preCheck === 'function') {
|
|
82
|
-
connection.log.trace({
|
|
83
|
-
msg: 'Clearing pre-check function',
|
|
84
|
-
lockId: connection.currentLockId,
|
|
85
|
-
path: connection.mailbox && connection.mailbox.path,
|
|
86
|
-
queued: preCheckWaitQueue.length,
|
|
87
|
-
doneRequested,
|
|
88
|
-
canEnd,
|
|
89
|
-
doneSent
|
|
90
|
-
});
|
|
91
|
-
connection.preCheck = false;
|
|
92
33
|
while (preCheckWaitQueue.length) {
|
|
93
34
|
let { resolve } = preCheckWaitQueue.shift();
|
|
94
35
|
resolve();
|
|
95
36
|
}
|
|
96
37
|
}
|
|
38
|
+
};
|
|
97
39
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
40
|
+
connection.preCheck = () => {
|
|
41
|
+
let handler = new Promise((resolve, reject) => {
|
|
42
|
+
preCheckWaitQueue.push({ resolve, reject });
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
connection.log.trace({
|
|
46
|
+
msg: 'Requesting IDLE break',
|
|
47
|
+
lockId: connection.currentLockId,
|
|
48
|
+
path: connection.mailbox && connection.mailbox.path,
|
|
49
|
+
queued: preCheckWaitQueue.length,
|
|
50
|
+
doneRequested,
|
|
51
|
+
canEnd,
|
|
52
|
+
doneSent
|
|
53
|
+
});
|
|
103
54
|
|
|
104
|
-
|
|
55
|
+
preCheck();
|
|
56
|
+
|
|
57
|
+
return handler;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
response = await connection.exec('IDLE', false, {
|
|
61
|
+
onPlusTag: async () => {
|
|
62
|
+
connection.log.debug({ msg: `Initiated IDLE, waiting for server input`, doneRequested });
|
|
63
|
+
canEnd = true;
|
|
64
|
+
if (doneRequested) {
|
|
65
|
+
preCheck();
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
onSend: () => {
|
|
69
|
+
//idleSent = true;
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// unset before response.next()
|
|
74
|
+
if (typeof connection.preCheck === 'function') {
|
|
75
|
+
connection.log.trace({
|
|
76
|
+
msg: 'Clearing pre-check function',
|
|
77
|
+
lockId: connection.currentLockId,
|
|
78
|
+
path: connection.mailbox && connection.mailbox.path,
|
|
79
|
+
queued: preCheckWaitQueue.length,
|
|
80
|
+
doneRequested,
|
|
81
|
+
canEnd,
|
|
82
|
+
doneSent
|
|
83
|
+
});
|
|
84
|
+
connection.preCheck = false;
|
|
105
85
|
while (preCheckWaitQueue.length) {
|
|
106
|
-
let {
|
|
107
|
-
|
|
86
|
+
let { resolve } = preCheckWaitQueue.shift();
|
|
87
|
+
resolve();
|
|
108
88
|
}
|
|
109
|
-
return false;
|
|
110
89
|
}
|
|
90
|
+
|
|
91
|
+
response.next();
|
|
92
|
+
return;
|
|
93
|
+
} catch (err) {
|
|
94
|
+
connection.preCheck = false;
|
|
95
|
+
connection.idling = false;
|
|
96
|
+
|
|
97
|
+
connection.log.warn({ err, cid: connection.id });
|
|
98
|
+
while (preCheckWaitQueue.length) {
|
|
99
|
+
let { reject } = preCheckWaitQueue.shift();
|
|
100
|
+
reject(err);
|
|
101
|
+
}
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Listes for changes in mailbox
|
|
107
|
+
module.exports = async (connection, maxIdleTime) => {
|
|
108
|
+
if (connection.state !== connection.states.SELECTED) {
|
|
109
|
+
// nothing to do here
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (connection.capabilities.has('IDLE')) {
|
|
114
|
+
let idleTimer;
|
|
115
|
+
let stillIdling = false;
|
|
116
|
+
let runIdleLoop = async () => {
|
|
117
|
+
if (maxIdleTime) {
|
|
118
|
+
idleTimer = setTimeout(() => {
|
|
119
|
+
if (connection.idling) {
|
|
120
|
+
if (typeof connection.preCheck === 'function') {
|
|
121
|
+
stillIdling = true;
|
|
122
|
+
// request IDLE break if IDLE has been running for allowed time
|
|
123
|
+
connection.log.trace({ msg: 'Max allowed IDLE time reached', cid: connection.id });
|
|
124
|
+
connection.preCheck().catch(err => connection.log.warn({ err, cid: connection.id }));
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}, maxIdleTime);
|
|
128
|
+
}
|
|
129
|
+
let resp = await runIdle(connection);
|
|
130
|
+
clearTimeout(idleTimer);
|
|
131
|
+
if (stillIdling) {
|
|
132
|
+
stillIdling = false;
|
|
133
|
+
return runIdleLoop();
|
|
134
|
+
}
|
|
135
|
+
return resp;
|
|
136
|
+
};
|
|
137
|
+
return runIdleLoop();
|
|
111
138
|
}
|
|
112
139
|
|
|
113
140
|
let idleTimer;
|
|
@@ -127,11 +154,13 @@ module.exports = async connection => {
|
|
|
127
154
|
response.next();
|
|
128
155
|
};
|
|
129
156
|
|
|
157
|
+
let noopInterval = maxIdleTime ? Math.min(NOOP_INTERVAL, maxIdleTime) : NOOP_INTERVAL;
|
|
158
|
+
|
|
130
159
|
let runLoop = () => {
|
|
131
160
|
idleCheck()
|
|
132
161
|
.then(() => {
|
|
133
162
|
clearTimeout(idleTimer);
|
|
134
|
-
idleTimer = setTimeout(runLoop,
|
|
163
|
+
idleTimer = setTimeout(runLoop, noopInterval);
|
|
135
164
|
})
|
|
136
165
|
.catch(err => {
|
|
137
166
|
clearTimeout(idleTimer);
|
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/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;
|