imapflow 1.7.8 → 2.0.0
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 +20 -0
- package/README.md +8 -2
- package/dist/cjs/charsets.d.ts +1 -0
- package/dist/cjs/charsets.js +294 -0
- package/dist/cjs/commands/append.d.ts +22 -0
- package/dist/cjs/commands/append.js +151 -0
- package/dist/cjs/commands/authenticate.d.ts +24 -0
- package/dist/cjs/commands/authenticate.js +223 -0
- package/dist/cjs/commands/capability.d.ts +8 -0
- package/dist/cjs/commands/capability.js +32 -0
- package/dist/cjs/commands/close.d.ts +8 -0
- package/dist/cjs/commands/close.js +39 -0
- package/dist/cjs/commands/compress.d.ts +8 -0
- package/dist/cjs/commands/compress.js +56 -0
- package/dist/cjs/commands/copy.d.ts +13 -0
- package/dist/cjs/commands/copy.js +44 -0
- package/dist/cjs/commands/copyuid-parser.d.ts +11 -0
- package/dist/cjs/commands/copyuid-parser.js +32 -0
- package/dist/cjs/commands/create.d.ts +11 -0
- package/dist/cjs/commands/create.js +80 -0
- package/dist/cjs/commands/delete.d.ts +11 -0
- package/dist/cjs/commands/delete.js +40 -0
- package/dist/cjs/commands/enable.d.ts +9 -0
- package/dist/cjs/commands/enable.js +61 -0
- package/dist/cjs/commands/esearch-parser.d.ts +17 -0
- package/dist/cjs/commands/esearch-parser.js +91 -0
- package/dist/cjs/commands/expunge.d.ts +12 -0
- package/dist/cjs/commands/expunge.js +60 -0
- package/dist/cjs/commands/fetch.d.ts +30 -0
- package/dist/cjs/commands/fetch.js +241 -0
- package/dist/cjs/commands/id.d.ts +10 -0
- package/dist/cjs/commands/id.js +80 -0
- package/dist/cjs/commands/idle.d.ts +9 -0
- package/dist/cjs/commands/idle.js +347 -0
- package/dist/cjs/commands/list.d.ts +16 -0
- package/dist/cjs/commands/list.js +518 -0
- package/dist/cjs/commands/login.d.ts +11 -0
- package/dist/cjs/commands/login.js +42 -0
- package/dist/cjs/commands/logout.d.ts +8 -0
- package/dist/cjs/commands/logout.js +47 -0
- package/dist/cjs/commands/move.d.ts +13 -0
- package/dist/cjs/commands/move.js +57 -0
- package/dist/cjs/commands/namespace.d.ts +25 -0
- package/dist/cjs/commands/namespace.js +139 -0
- package/dist/cjs/commands/noop.d.ts +8 -0
- package/dist/cjs/commands/noop.js +22 -0
- package/dist/cjs/commands/quota.d.ts +10 -0
- package/dist/cjs/commands/quota.js +119 -0
- package/dist/cjs/commands/rename.d.ts +12 -0
- package/dist/cjs/commands/rename.js +48 -0
- package/dist/cjs/commands/search.d.ts +15 -0
- package/dist/cjs/commands/search.js +228 -0
- package/dist/cjs/commands/select.d.ts +25 -0
- package/dist/cjs/commands/select.js +250 -0
- package/dist/cjs/commands/starttls.d.ts +8 -0
- package/dist/cjs/commands/starttls.js +30 -0
- package/dist/cjs/commands/status-fields.d.ts +14 -0
- package/dist/cjs/commands/status-fields.js +61 -0
- package/dist/cjs/commands/status.d.ts +12 -0
- package/dist/cjs/commands/status.js +108 -0
- package/dist/cjs/commands/store.d.ts +19 -0
- package/dist/cjs/commands/store.js +93 -0
- package/dist/cjs/commands/subscribe.d.ts +9 -0
- package/dist/cjs/commands/subscribe.js +31 -0
- package/dist/cjs/commands/unsubscribe.d.ts +9 -0
- package/dist/cjs/commands/unsubscribe.js +31 -0
- package/dist/cjs/connection-deadline.d.ts +49 -0
- package/dist/cjs/connection-deadline.js +91 -0
- package/dist/cjs/errors.d.ts +83 -0
- package/dist/cjs/errors.js +13 -0
- package/dist/cjs/handler/imap-compiler.d.ts +24 -0
- package/dist/cjs/handler/imap-compiler.js +285 -0
- package/dist/cjs/handler/imap-formal-syntax.d.ts +28 -0
- package/dist/cjs/handler/imap-formal-syntax.js +121 -0
- package/dist/cjs/handler/imap-handler.d.ts +9 -0
- package/dist/cjs/handler/imap-handler.js +10 -0
- package/dist/cjs/handler/imap-parser.d.ts +16 -0
- package/dist/cjs/handler/imap-parser.js +90 -0
- package/dist/cjs/handler/imap-stream.d.ts +181 -0
- package/dist/cjs/handler/imap-stream.js +446 -0
- package/dist/cjs/handler/limits.d.ts +25 -0
- package/dist/cjs/handler/limits.js +51 -0
- package/dist/cjs/handler/parser-instance.d.ts +68 -0
- package/dist/cjs/handler/parser-instance.js +223 -0
- package/dist/cjs/handler/token-parser.d.ts +91 -0
- package/dist/cjs/handler/token-parser.js +673 -0
- package/dist/cjs/handler/types.d.ts +91 -0
- package/dist/cjs/handler/types.js +4 -0
- package/dist/cjs/imap-commands.d.ts +16 -0
- package/dist/cjs/imap-commands.js +74 -0
- package/dist/cjs/imap-flow.d.ts +676 -0
- package/dist/cjs/imap-flow.js +3949 -0
- package/dist/cjs/jp-decoder.d.ts +12 -0
- package/dist/cjs/jp-decoder.js +79 -0
- package/dist/cjs/limited-passthrough.d.ts +25 -0
- package/dist/cjs/limited-passthrough.js +54 -0
- package/dist/cjs/logger.d.ts +3 -0
- package/dist/cjs/logger.js +11 -0
- package/dist/cjs/package-info.d.ts +3 -0
- package/dist/cjs/package-info.js +7 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/proxy-connection.d.ts +33 -0
- package/dist/cjs/proxy-connection.js +392 -0
- package/dist/cjs/search-compiler.d.ts +34 -0
- package/dist/cjs/search-compiler.js +476 -0
- package/dist/cjs/special-use.d.ts +22 -0
- package/dist/cjs/special-use.js +911 -0
- package/dist/cjs/tools.d.ts +427 -0
- package/dist/cjs/tools.js +1496 -0
- package/{lib/imap-flow.d.ts → dist/cjs/types.d.ts} +386 -516
- package/dist/cjs/types.js +5 -0
- package/dist/esm/charsets.d.ts +1 -0
- package/{lib → dist/esm}/charsets.js +1 -6
- package/dist/esm/commands/append.d.ts +22 -0
- package/{lib → dist/esm}/commands/append.js +22 -52
- package/dist/esm/commands/authenticate.d.ts +24 -0
- package/{lib → dist/esm}/commands/authenticate.js +62 -87
- package/dist/esm/commands/capability.d.ts +8 -0
- package/{lib → dist/esm}/commands/capability.js +6 -9
- package/dist/esm/commands/close.d.ts +8 -0
- package/{lib → dist/esm}/commands/close.js +6 -10
- package/dist/esm/commands/compress.d.ts +8 -0
- package/{lib → dist/esm}/commands/compress.js +7 -11
- package/dist/esm/commands/copy.d.ts +13 -0
- package/{lib → dist/esm}/commands/copy.js +12 -20
- package/dist/esm/commands/copyuid-parser.d.ts +11 -0
- package/{lib → dist/esm}/commands/copyuid-parser.js +9 -15
- package/dist/esm/commands/create.d.ts +11 -0
- package/{lib → dist/esm}/commands/create.js +13 -27
- package/dist/esm/commands/delete.d.ts +11 -0
- package/{lib → dist/esm}/commands/delete.js +9 -14
- package/dist/esm/commands/enable.d.ts +9 -0
- package/{lib → dist/esm}/commands/enable.js +23 -30
- package/dist/esm/commands/esearch-parser.d.ts +17 -0
- package/dist/esm/commands/esearch-parser.js +88 -0
- package/dist/esm/commands/expunge.d.ts +12 -0
- package/{lib → dist/esm}/commands/expunge.js +17 -22
- package/dist/esm/commands/fetch.d.ts +30 -0
- package/{lib → dist/esm}/commands/fetch.js +32 -64
- package/dist/esm/commands/id.d.ts +10 -0
- package/{lib → dist/esm}/commands/id.js +17 -23
- package/dist/esm/commands/idle.d.ts +9 -0
- package/{lib → dist/esm}/commands/idle.js +47 -81
- package/dist/esm/commands/list.d.ts +16 -0
- package/{lib → dist/esm}/commands/list.js +56 -121
- package/dist/esm/commands/login.d.ts +11 -0
- package/{lib → dist/esm}/commands/login.js +10 -15
- package/dist/esm/commands/logout.d.ts +8 -0
- package/{lib → dist/esm}/commands/logout.js +9 -11
- package/dist/esm/commands/move.d.ts +13 -0
- package/{lib → dist/esm}/commands/move.js +13 -21
- package/dist/esm/commands/namespace.d.ts +25 -0
- package/{lib → dist/esm}/commands/namespace.js +34 -44
- package/dist/esm/commands/noop.d.ts +8 -0
- package/{lib → dist/esm}/commands/noop.js +6 -7
- package/dist/esm/commands/quota.d.ts +10 -0
- package/{lib → dist/esm}/commands/quota.js +18 -36
- package/dist/esm/commands/rename.d.ts +12 -0
- package/{lib → dist/esm}/commands/rename.js +10 -15
- package/dist/esm/commands/search.d.ts +15 -0
- package/{lib → dist/esm}/commands/search.js +36 -135
- package/dist/esm/commands/select.d.ts +25 -0
- package/{lib → dist/esm}/commands/select.js +33 -64
- package/dist/esm/commands/starttls.d.ts +8 -0
- package/{lib → dist/esm}/commands/starttls.js +6 -8
- package/dist/esm/commands/status-fields.d.ts +14 -0
- package/{lib → dist/esm}/commands/status-fields.js +5 -16
- package/dist/esm/commands/status.d.ts +12 -0
- package/{lib → dist/esm}/commands/status.js +18 -29
- package/dist/esm/commands/store.d.ts +19 -0
- package/{lib → dist/esm}/commands/store.js +24 -37
- package/dist/esm/commands/subscribe.d.ts +9 -0
- package/{lib → dist/esm}/commands/subscribe.js +8 -12
- package/dist/esm/commands/unsubscribe.d.ts +9 -0
- package/{lib → dist/esm}/commands/unsubscribe.js +8 -12
- package/dist/esm/connection-deadline.d.ts +49 -0
- package/{lib → dist/esm}/connection-deadline.js +14 -25
- package/dist/esm/errors.d.ts +83 -0
- package/dist/esm/errors.js +9 -0
- package/dist/esm/handler/imap-compiler.d.ts +24 -0
- package/{lib → dist/esm}/handler/imap-compiler.js +22 -80
- package/dist/esm/handler/imap-formal-syntax.d.ts +28 -0
- package/dist/esm/handler/imap-formal-syntax.js +117 -0
- package/dist/esm/handler/imap-handler.d.ts +9 -0
- package/dist/esm/handler/imap-handler.js +9 -0
- package/dist/esm/handler/imap-parser.d.ts +16 -0
- package/{lib → dist/esm}/handler/imap-parser.js +31 -44
- package/dist/esm/handler/imap-stream.d.ts +181 -0
- package/{lib → dist/esm}/handler/imap-stream.js +29 -121
- package/dist/esm/handler/limits.d.ts +25 -0
- package/{lib → dist/esm}/handler/limits.js +13 -22
- package/dist/esm/handler/parser-instance.d.ts +68 -0
- package/{lib → dist/esm}/handler/parser-instance.js +19 -47
- package/dist/esm/handler/token-parser.d.ts +91 -0
- package/{lib → dist/esm}/handler/token-parser.js +71 -155
- package/dist/esm/handler/types.d.ts +91 -0
- package/dist/esm/handler/types.js +3 -0
- package/dist/esm/imap-commands.d.ts +16 -0
- package/dist/esm/imap-commands.js +67 -0
- package/dist/esm/imap-flow.d.ts +676 -0
- package/{lib → dist/esm}/imap-flow.js +761 -1789
- package/dist/esm/jp-decoder.d.ts +12 -0
- package/{lib → dist/esm}/jp-decoder.js +6 -21
- package/dist/esm/limited-passthrough.d.ts +25 -0
- package/{lib → dist/esm}/limited-passthrough.js +7 -20
- package/dist/esm/logger.d.ts +3 -0
- package/dist/esm/logger.js +4 -0
- package/dist/esm/package-info.d.ts +3 -0
- package/dist/esm/package-info.js +4 -0
- package/dist/esm/package.json +3 -0
- package/dist/esm/proxy-connection.d.ts +33 -0
- package/{lib → dist/esm}/proxy-connection.js +56 -127
- package/dist/esm/search-compiler.d.ts +34 -0
- package/{lib → dist/esm}/search-compiler.js +54 -110
- package/dist/esm/special-use.d.ts +22 -0
- package/dist/esm/special-use.js +907 -0
- package/dist/esm/tools.d.ts +427 -0
- package/dist/esm/tools.js +1446 -0
- package/dist/esm/types.d.ts +828 -0
- package/dist/esm/types.js +4 -0
- package/package.json +60 -20
- package/.gitattributes +0 -1
- package/.github/CODE_OF_CONDUCT.md +0 -76
- package/.github/FUNDING.yml +0 -4
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -40
- package/.github/ISSUE_TEMPLATE/feature_request.md +0 -19
- package/.github/contributing.md +0 -17
- package/.github/workflows/release.yaml +0 -36
- package/.github/workflows/stale.yml +0 -29
- package/.github/workflows/test.yml +0 -51
- package/.ncurc.js +0 -4
- package/.prettierignore +0 -4
- package/.prettierrc.js +0 -8
- package/.release-please-manifest.json +0 -3
- package/CLAUDE.md +0 -104
- package/Gruntfile.js +0 -23
- package/eslint.config.js +0 -45
- package/lib/handler/imap-formal-syntax.js +0 -189
- package/lib/handler/imap-handler.js +0 -17
- package/lib/imap-commands.js +0 -45
- package/lib/logger.js +0 -5
- package/lib/special-use.js +0 -923
- package/lib/tools.js +0 -1612
- package/release-please-config.json +0 -10
- package/test/authentication-test.js +0 -101
- package/test/auto-idle-test.js +0 -470
- package/test/bodystructure-test.js +0 -899
- package/test/charsets-test.js +0 -161
- package/test/commands-branches-test.js +0 -1095
- package/test/commands-integration-test.js +0 -11124
- package/test/commands-test.js +0 -73
- package/test/connection-edge-cases-test.js +0 -1828
- package/test/connection-test.js +0 -162
- package/test/copyuid-parser-test.js +0 -173
- package/test/fetch-generator-test.js +0 -218
- package/test/fixtures/fake-timers.js +0 -115
- package/test/fixtures/serialized-mimetorture.js +0 -2738
- package/test/fixtures/test-client.js +0 -101
- package/test/fixtures/test-tls.js +0 -8
- package/test/handler-branches-test.js +0 -310
- package/test/idle-polling-test.js +0 -518
- package/test/imap-compiler-test.js +0 -809
- package/test/imap-flow-compress-test.js +0 -166
- package/test/imap-flow-coverage-test.js +0 -612
- package/test/imap-flow-fetch-download-test.js +0 -909
- package/test/imap-flow-internals-test.js +0 -725
- package/test/imap-flow-methods-test.js +0 -889
- package/test/imap-flow-proxy-paths-test.js +0 -366
- package/test/imap-flow-secure-test.js +0 -573
- package/test/imap-flow-server-test.js +0 -1474
- package/test/imap-formal-syntax-test.js +0 -293
- package/test/imap-parser-test.js +0 -1474
- package/test/imap-stream-edge-cases-test.js +0 -666
- package/test/imap-stream-test.js +0 -177
- package/test/imapflow-test.js +0 -258
- package/test/integration/README.md +0 -52
- package/test/integration/dovecot-test.conf +0 -27
- package/test/integration/rev2-live-test.js +0 -431
- package/test/integration/run-rev2-tests.sh +0 -75
- package/test/integration-test.js +0 -83
- package/test/jp-decoder-test.js +0 -304
- package/test/limited-passthrough-test.js +0 -299
- package/test/memory-cleanup-test.js +0 -144
- package/test/memory-leak-test.js +0 -667
- package/test/parser-limits-test.js +0 -292
- package/test/proxy-connection-test.js +0 -738
- package/test/reliability-improvements-test.js +0 -548
- package/test/search-compiler-test.js +0 -1300
- package/test/search-test.js +0 -329
- package/test/special-use-test.js +0 -418
- package/test/starttls-injection-test.js +0 -181
- package/test/tag-correlation-test.js +0 -333
- package/test/timer-policy-test.js +0 -227
- package/test/token-parser-test.js +0 -456
- package/test/tools-test.js +0 -2013
- package/test/unhandled-rejection-test.js +0 -661
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = move;
|
|
4
|
+
const tools_js_1 = require("../tools.js");
|
|
5
|
+
const copyuid_parser_js_1 = require("./copyuid-parser.js");
|
|
6
|
+
/**
|
|
7
|
+
* Moves messages from the current mailbox to another mailbox.
|
|
8
|
+
*
|
|
9
|
+
* @param connection - IMAP connection instance
|
|
10
|
+
* @param range - Message sequence number or UID range
|
|
11
|
+
* @param destination - Destination mailbox path
|
|
12
|
+
* @param options - Move options
|
|
13
|
+
* @param options.uid - If true, use UID MOVE instead of MOVE
|
|
14
|
+
* @returns Move result with UID mapping if available, false on failure, or undefined if preconditions not met
|
|
15
|
+
*/
|
|
16
|
+
async function move(connection, range, destination, options) {
|
|
17
|
+
if (connection.state !== connection.states.SELECTED || !range || !destination) {
|
|
18
|
+
// nothing to do here
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
options = options || {};
|
|
22
|
+
destination = (0, tools_js_1.normalizePath)(connection, destination);
|
|
23
|
+
let attributes = [
|
|
24
|
+
{ type: 'SEQUENCE', value: range },
|
|
25
|
+
{ type: 'ATOM', value: (0, tools_js_1.encodePath)(connection, destination) }
|
|
26
|
+
];
|
|
27
|
+
let map = { path: connection.mailbox.path, destination };
|
|
28
|
+
// Fallback for servers without the MOVE extension (RFC 6851):
|
|
29
|
+
// emulate MOVE using COPY + flag as \Deleted + EXPUNGE.
|
|
30
|
+
if (!(0, tools_js_1.hasCapability)(connection, 'MOVE')) {
|
|
31
|
+
let result = await connection.messageCopy(range, destination, options);
|
|
32
|
+
await connection.messageDelete(range, Object.assign({ silent: true }, options));
|
|
33
|
+
return result;
|
|
34
|
+
}
|
|
35
|
+
let response;
|
|
36
|
+
try {
|
|
37
|
+
// Some servers send COPYUID in an untagged OK before the tagged response,
|
|
38
|
+
// others include it in the tagged OK. We check both to be safe.
|
|
39
|
+
response = await connection.exec(options.uid ? 'UID MOVE' : 'MOVE', attributes, {
|
|
40
|
+
untagged: {
|
|
41
|
+
OK: async (untagged) => {
|
|
42
|
+
(0, copyuid_parser_js_1.parseCopyUid)(untagged, map);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
response.next();
|
|
47
|
+
(0, copyuid_parser_js_1.parseCopyUid)(response.response, map);
|
|
48
|
+
return map;
|
|
49
|
+
}
|
|
50
|
+
catch (err) {
|
|
51
|
+
await (0, tools_js_1.enhanceCommandError)(err);
|
|
52
|
+
connection.log.warn({ err, cid: connection.id });
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
module.exports = exports.default;
|
|
57
|
+
Object.defineProperty(module.exports, 'default', { value: exports.default, enumerable: false, writable: true, configurable: true });
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { ImapFlow } from '../imap-flow.js';
|
|
2
|
+
import type { NamespaceObject } from '../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Returned instead of a namespace when the NAMESPACE command failed
|
|
5
|
+
*/
|
|
6
|
+
export interface NamespaceErrorResult {
|
|
7
|
+
error: true;
|
|
8
|
+
status: string | undefined;
|
|
9
|
+
text: string | undefined;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Prefix and delimiter derived from `LIST "" ""`
|
|
13
|
+
*/
|
|
14
|
+
export interface ListPrefixInfo {
|
|
15
|
+
flags?: Set<string> | undefined;
|
|
16
|
+
delimiter?: string | null | undefined;
|
|
17
|
+
prefix?: string | undefined;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Requests NAMESPACE info from the server.
|
|
21
|
+
*
|
|
22
|
+
* @param connection - IMAP connection instance
|
|
23
|
+
* @returns The primary personal namespace, or an error object on failure
|
|
24
|
+
*/
|
|
25
|
+
export default function namespace(connection: ImapFlow): Promise<NamespaceObject | NamespaceErrorResult | undefined>;
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = namespace;
|
|
4
|
+
const tools_js_1 = require("../tools.js");
|
|
5
|
+
/**
|
|
6
|
+
* Requests NAMESPACE info from the server.
|
|
7
|
+
*
|
|
8
|
+
* @param connection - IMAP connection instance
|
|
9
|
+
* @returns The primary personal namespace, or an error object on failure
|
|
10
|
+
*/
|
|
11
|
+
async function namespace(connection) {
|
|
12
|
+
if (![connection.states.AUTHENTICATED, connection.states.SELECTED].includes(connection.state)) {
|
|
13
|
+
// nothing to do here
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
if (!(0, tools_js_1.hasCapability)(connection, 'NAMESPACE')) {
|
|
17
|
+
// Fallback: when the server does not support the NAMESPACE extension (RFC 2342),
|
|
18
|
+
// derive the prefix and delimiter from a LIST "" "" command, which returns
|
|
19
|
+
// the hierarchy delimiter and root name for the default mailbox hierarchy.
|
|
20
|
+
let { prefix, delimiter } = await getListPrefix(connection);
|
|
21
|
+
// Ensure the prefix ends with the delimiter so that appending a mailbox name
|
|
22
|
+
// produces a valid path (e.g., "INBOX." + "Sent" = "INBOX.Sent").
|
|
23
|
+
if (delimiter && prefix && prefix.charAt(prefix.length - 1) !== delimiter) {
|
|
24
|
+
prefix += delimiter;
|
|
25
|
+
}
|
|
26
|
+
let map = {
|
|
27
|
+
personal: [{ prefix: prefix || '', delimiter }],
|
|
28
|
+
other: false,
|
|
29
|
+
shared: false
|
|
30
|
+
};
|
|
31
|
+
connection.namespaces = map;
|
|
32
|
+
connection.namespace = connection.namespaces.personal[0];
|
|
33
|
+
return connection.namespace;
|
|
34
|
+
}
|
|
35
|
+
let response;
|
|
36
|
+
try {
|
|
37
|
+
let map = {};
|
|
38
|
+
response = await connection.exec('NAMESPACE', false, {
|
|
39
|
+
untagged: {
|
|
40
|
+
// The NAMESPACE response (RFC 2342) contains exactly three sections:
|
|
41
|
+
// [0] = personal namespaces (user's own mailboxes)
|
|
42
|
+
// [1] = other users' namespaces (shared by other users)
|
|
43
|
+
// [2] = shared namespaces (public/organizational folders)
|
|
44
|
+
// Each section is either NIL or a list of (prefix, delimiter) pairs.
|
|
45
|
+
NAMESPACE: async (untagged) => {
|
|
46
|
+
if (!untagged.attributes || !untagged.attributes.length) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
map.personal = getNamsepaceInfo(untagged.attributes[0]);
|
|
50
|
+
map.other = getNamsepaceInfo(untagged.attributes[1]);
|
|
51
|
+
map.shared = getNamsepaceInfo(untagged.attributes[2]);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
connection.namespaces = map;
|
|
56
|
+
// make sure that we have the first personal namespace always set
|
|
57
|
+
if (!connection.namespaces.personal[0]) {
|
|
58
|
+
connection.namespaces.personal[0] = { prefix: '', delimiter: '.' };
|
|
59
|
+
}
|
|
60
|
+
connection.namespaces.personal[0].prefix = connection.namespaces.personal[0].prefix || '';
|
|
61
|
+
response.next();
|
|
62
|
+
connection.namespace = connection.namespaces.personal[0];
|
|
63
|
+
return connection.namespace;
|
|
64
|
+
}
|
|
65
|
+
catch (err) {
|
|
66
|
+
connection.log.warn({ err, cid: connection.id });
|
|
67
|
+
return {
|
|
68
|
+
error: true,
|
|
69
|
+
status: err.responseStatus,
|
|
70
|
+
text: err.responseText
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Derives namespace prefix and delimiter from a LIST command when NAMESPACE is not supported.
|
|
76
|
+
*
|
|
77
|
+
* @param connection - IMAP connection instance
|
|
78
|
+
* @returns Object with prefix, delimiter, and flags, or empty object on failure
|
|
79
|
+
*/
|
|
80
|
+
async function getListPrefix(connection) {
|
|
81
|
+
let response;
|
|
82
|
+
try {
|
|
83
|
+
let map = {};
|
|
84
|
+
// LIST "" "" is a special form that returns only the hierarchy delimiter
|
|
85
|
+
// and the root name, without listing any actual mailboxes.
|
|
86
|
+
response = await connection.exec('LIST', ['', ''], {
|
|
87
|
+
untagged: {
|
|
88
|
+
LIST: async (untagged) => {
|
|
89
|
+
if (!untagged.attributes || !untagged.attributes.length) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
map.flags = new Set((0, tools_js_1.getStringList)(untagged.attributes[0]));
|
|
93
|
+
map.delimiter = (untagged.attributes[1] && untagged.attributes[1].value);
|
|
94
|
+
map.prefix = ((untagged.attributes[2] && untagged.attributes[2].value) || '');
|
|
95
|
+
if (map.delimiter && map.prefix.charAt(0) === map.delimiter) {
|
|
96
|
+
map.prefix = map.prefix.slice(1);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
response.next();
|
|
102
|
+
return map;
|
|
103
|
+
}
|
|
104
|
+
catch (err) {
|
|
105
|
+
connection.log.warn({ err, cid: connection.id });
|
|
106
|
+
return {};
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Parses namespace information from an IMAP NAMESPACE response attribute.
|
|
111
|
+
*
|
|
112
|
+
* @param attribute - Namespace attribute array from the server response
|
|
113
|
+
* @returns Array of namespace entries, or false if empty
|
|
114
|
+
*/
|
|
115
|
+
function getNamsepaceInfo(attribute) {
|
|
116
|
+
if (!attribute || !Array.isArray(attribute) || !attribute.length) {
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
return attribute
|
|
120
|
+
.filter(entry => {
|
|
121
|
+
let pair = entry;
|
|
122
|
+
// RFC 2342 section 5 allows the delimiter to be NIL when the namespace has
|
|
123
|
+
// no hierarchy. The token parser emits a literal `null` for NIL.
|
|
124
|
+
return pair.length >= 2 && pair[0] && typeof pair[0].value === 'string' && (pair[1] === null || (pair[1] && typeof pair[1].value === 'string'));
|
|
125
|
+
})
|
|
126
|
+
.map(entry => {
|
|
127
|
+
let pair = entry;
|
|
128
|
+
let prefix = pair[0].value;
|
|
129
|
+
let delimiter = pair[1] === null ? null : pair[1].value;
|
|
130
|
+
// Append the delimiter to the prefix if it doesn't already end with one,
|
|
131
|
+
// so callers can construct full paths by simply concatenating prefix + name.
|
|
132
|
+
if (delimiter && prefix && prefix.charAt(prefix.length - 1) !== delimiter) {
|
|
133
|
+
prefix += delimiter;
|
|
134
|
+
}
|
|
135
|
+
return { prefix, delimiter };
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
module.exports = exports.default;
|
|
139
|
+
Object.defineProperty(module.exports, 'default', { value: exports.default, enumerable: false, writable: true, configurable: true });
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ImapFlow } from '../imap-flow.js';
|
|
2
|
+
/**
|
|
3
|
+
* Sends a NOOP command to the server.
|
|
4
|
+
*
|
|
5
|
+
* @param connection - IMAP connection instance
|
|
6
|
+
* @returns True on success, false on failure
|
|
7
|
+
*/
|
|
8
|
+
export default function noop(connection: ImapFlow): Promise<boolean>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = noop;
|
|
4
|
+
/**
|
|
5
|
+
* Sends a NOOP command to the server.
|
|
6
|
+
*
|
|
7
|
+
* @param connection - IMAP connection instance
|
|
8
|
+
* @returns True on success, false on failure
|
|
9
|
+
*/
|
|
10
|
+
async function noop(connection) {
|
|
11
|
+
try {
|
|
12
|
+
let response = await connection.exec('NOOP', false, { comment: 'Requested by command' });
|
|
13
|
+
response.next();
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
catch (err) {
|
|
17
|
+
connection.log.warn({ err, cid: connection.id });
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
module.exports = exports.default;
|
|
22
|
+
Object.defineProperty(module.exports, 'default', { value: exports.default, enumerable: false, writable: true, configurable: true });
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ImapFlow } from '../imap-flow.js';
|
|
2
|
+
import type { QuotaResponse } from '../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Requests quota information for a mailbox.
|
|
5
|
+
*
|
|
6
|
+
* @param connection - IMAP connection instance
|
|
7
|
+
* @param path - Mailbox path to query quota for
|
|
8
|
+
* @returns Quota information object, false if QUOTA not supported or on failure, or undefined if preconditions not met
|
|
9
|
+
*/
|
|
10
|
+
export default function quota(connection: ImapFlow, path: string | string[]): Promise<QuotaResponse | false | undefined>;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = quota;
|
|
4
|
+
const tools_js_1 = require("../tools.js");
|
|
5
|
+
/**
|
|
6
|
+
* Requests quota information for a mailbox.
|
|
7
|
+
*
|
|
8
|
+
* @param connection - IMAP connection instance
|
|
9
|
+
* @param path - Mailbox path to query quota for
|
|
10
|
+
* @returns Quota information object, false if QUOTA not supported or on failure, or undefined if preconditions not met
|
|
11
|
+
*/
|
|
12
|
+
async function quota(connection, path) {
|
|
13
|
+
if (![connection.states.AUTHENTICATED, connection.states.SELECTED].includes(connection.state) || !path) {
|
|
14
|
+
// nothing to do here
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
if (!connection.capabilities.has('QUOTA')) {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
path = (0, tools_js_1.normalizePath)(connection, path);
|
|
21
|
+
let map = { path };
|
|
22
|
+
// Parse a QUOTA response. The resource list uses a repeating triplet pattern (i % 3):
|
|
23
|
+
// position 0: resource name (e.g., "STORAGE", "MESSAGE")
|
|
24
|
+
// position 1: current usage
|
|
25
|
+
// position 2: limit
|
|
26
|
+
// Storage values are in KB on the wire; multiply by 1024 to report bytes.
|
|
27
|
+
let processQuotaResponse = (untagged) => {
|
|
28
|
+
let attributes = untagged.attributes && untagged.attributes[1];
|
|
29
|
+
if (!attributes || !Array.isArray(attributes) || !attributes.length) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
let key = false;
|
|
33
|
+
attributes.forEach((attribute, i) => {
|
|
34
|
+
const position = i % 3;
|
|
35
|
+
if (position === 0) {
|
|
36
|
+
key = attribute && typeof attribute.value === 'string' ? attribute.value.toLowerCase() : false;
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
if (!key) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
// isNaN() also passes '1e5', ' 12 ' and 'Infinity', none of which is a usable octet count
|
|
43
|
+
let value = (0, tools_js_1.parseUintValue)(attribute && attribute.value);
|
|
44
|
+
if (value === false) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
// Resource names are server-controlled. This object is returned to the caller, so it
|
|
48
|
+
// keeps a normal prototype and unsafe names are dropped instead; the fixed fields
|
|
49
|
+
// must keep their values too.
|
|
50
|
+
if ((0, tools_js_1.isUnsafeKey)(key) || key === 'path' || key === 'quotaroot') {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
if (!map[key]) {
|
|
54
|
+
map[key] = {};
|
|
55
|
+
}
|
|
56
|
+
// Storage quota is reported in KB by IMAP; convert to bytes for consistency
|
|
57
|
+
const multiplier = key === 'storage' ? 1024 : 1;
|
|
58
|
+
if (position === 1) {
|
|
59
|
+
map[key].usage = value * multiplier;
|
|
60
|
+
}
|
|
61
|
+
else if (position === 2) {
|
|
62
|
+
map[key].limit = value * multiplier;
|
|
63
|
+
// Calculate usage percentage for convenient display
|
|
64
|
+
if (map[key].limit) {
|
|
65
|
+
map[key].status = Math.round(((map[key].usage || 0) / map[key].limit) * 100) + '%';
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
};
|
|
70
|
+
let quotaFound = false;
|
|
71
|
+
let response;
|
|
72
|
+
try {
|
|
73
|
+
// Two-step quota lookup: GETQUOTAROOT identifies the quota root for a mailbox,
|
|
74
|
+
// and the server usually sends the QUOTA response inline. Some servers only
|
|
75
|
+
// send the root name and require a separate GETQUOTA command.
|
|
76
|
+
response = await connection.exec('GETQUOTAROOT', [{ type: 'ATOM', value: (0, tools_js_1.encodePath)(connection, path) }], {
|
|
77
|
+
untagged: {
|
|
78
|
+
// QUOTAROOT response tells us which quota root applies to this mailbox.
|
|
79
|
+
// A mailbox may have zero or one quota root.
|
|
80
|
+
QUOTAROOT: async (untagged) => {
|
|
81
|
+
let quotaRoot = untagged.attributes && untagged.attributes[1] && typeof untagged.attributes[1].value === 'string'
|
|
82
|
+
? untagged.attributes[1].value
|
|
83
|
+
: false;
|
|
84
|
+
if (quotaRoot) {
|
|
85
|
+
map.quotaRoot = quotaRoot;
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
// QUOTA response provides the actual resource usage and limits
|
|
89
|
+
QUOTA: async (untagged) => {
|
|
90
|
+
quotaFound = true;
|
|
91
|
+
processQuotaResponse(untagged);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
response.next();
|
|
96
|
+
// Fallback: if we got a quota root but no QUOTA response inline,
|
|
97
|
+
// explicitly request quota for that root.
|
|
98
|
+
if (map.quotaRoot && !quotaFound) {
|
|
99
|
+
response = await connection.exec('GETQUOTA', [{ type: 'ATOM', value: map.quotaRoot }], {
|
|
100
|
+
untagged: {
|
|
101
|
+
QUOTA: async (untagged) => {
|
|
102
|
+
processQuotaResponse(untagged);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
// Release the parser: without this the connection stalls, because the reader loop
|
|
107
|
+
// waits for the response to be handed back before parsing any further input.
|
|
108
|
+
response.next();
|
|
109
|
+
}
|
|
110
|
+
return map;
|
|
111
|
+
}
|
|
112
|
+
catch (err) {
|
|
113
|
+
await (0, tools_js_1.enhanceCommandError)(err);
|
|
114
|
+
connection.log.warn({ err, cid: connection.id });
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
module.exports = exports.default;
|
|
119
|
+
Object.defineProperty(module.exports, 'default', { value: exports.default, enumerable: false, writable: true, configurable: true });
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ImapFlow } from '../imap-flow.js';
|
|
2
|
+
import type { MailboxRenameResponse } from '../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Renames an existing mailbox.
|
|
5
|
+
*
|
|
6
|
+
* @param connection - IMAP connection instance
|
|
7
|
+
* @param path - Current mailbox path
|
|
8
|
+
* @param newPath - New mailbox path
|
|
9
|
+
* @returns Object with old and new paths, or undefined if preconditions not met
|
|
10
|
+
* @throws If the RENAME command fails
|
|
11
|
+
*/
|
|
12
|
+
export default function rename(connection: ImapFlow, path: string | string[], newPath: string | string[]): Promise<MailboxRenameResponse | undefined>;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.default = rename;
|
|
4
|
+
const tools_js_1 = require("../tools.js");
|
|
5
|
+
/**
|
|
6
|
+
* Renames an existing mailbox.
|
|
7
|
+
*
|
|
8
|
+
* @param connection - IMAP connection instance
|
|
9
|
+
* @param path - Current mailbox path
|
|
10
|
+
* @param newPath - New mailbox path
|
|
11
|
+
* @returns Object with old and new paths, or undefined if preconditions not met
|
|
12
|
+
* @throws If the RENAME command fails
|
|
13
|
+
*/
|
|
14
|
+
async function rename(connection, path, newPath) {
|
|
15
|
+
if (![connection.states.AUTHENTICATED, connection.states.SELECTED].includes(connection.state)) {
|
|
16
|
+
// nothing to do here
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
// Normalize both paths (resolve special names, apply namespace prefix) and encode
|
|
20
|
+
// them for the IMAP wire format (modified UTF-7 for non-ASCII characters).
|
|
21
|
+
path = (0, tools_js_1.normalizePath)(connection, path);
|
|
22
|
+
newPath = (0, tools_js_1.normalizePath)(connection, newPath);
|
|
23
|
+
// Must close/deselect the mailbox before renaming if it's currently selected,
|
|
24
|
+
// as IMAP servers will not rename an active mailbox.
|
|
25
|
+
if (connection.state === connection.states.SELECTED && connection.mailbox.path === path) {
|
|
26
|
+
await connection.run('CLOSE');
|
|
27
|
+
}
|
|
28
|
+
let response;
|
|
29
|
+
try {
|
|
30
|
+
let map = {
|
|
31
|
+
path,
|
|
32
|
+
newPath
|
|
33
|
+
};
|
|
34
|
+
response = await connection.exec('RENAME', [
|
|
35
|
+
{ type: 'ATOM', value: (0, tools_js_1.encodePath)(connection, path) },
|
|
36
|
+
{ type: 'ATOM', value: (0, tools_js_1.encodePath)(connection, newPath) }
|
|
37
|
+
]);
|
|
38
|
+
response.next();
|
|
39
|
+
return map;
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
await (0, tools_js_1.enhanceCommandError)(err);
|
|
43
|
+
connection.log.warn({ err, cid: connection.id });
|
|
44
|
+
throw err;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
module.exports = exports.default;
|
|
48
|
+
Object.defineProperty(module.exports, 'default', { value: exports.default, enumerable: false, writable: true, configurable: true });
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ImapFlow } from '../imap-flow.js';
|
|
2
|
+
import type { ESearchResult, SearchObject, SearchOptions } from '../types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Searches for messages matching the specified criteria.
|
|
5
|
+
*
|
|
6
|
+
* @param connection - IMAP connection instance
|
|
7
|
+
* @param query - Search query object, or true/empty object to match all messages
|
|
8
|
+
* @param options - Search options
|
|
9
|
+
* @param options.uid - If true, use UID SEARCH instead of SEARCH
|
|
10
|
+
* @param options.returnOptions - ESEARCH RETURN options. When present AND the
|
|
11
|
+
* server advertises ESEARCH capability, triggers ESEARCH and returns an ESearchResult.
|
|
12
|
+
* Items are strings ('MIN','MAX','COUNT','ALL') or objects ({ partial: '1:100' }).
|
|
13
|
+
* When server lacks ESEARCH, falls back to plain SEARCH and returns number[].
|
|
14
|
+
*/
|
|
15
|
+
export default function search(connection: ImapFlow, query: SearchObject | boolean | null | undefined, options?: SearchOptions | undefined): Promise<number[] | ESearchResult | false>;
|