imapkit 4.3.1 → 5.0.1

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.
Files changed (121) hide show
  1. package/README.md +187 -67
  2. package/bin/help.txt +20 -5
  3. package/bin/imapkit.js +64 -24
  4. package/dist/cjs/commands/append.js +1 -1
  5. package/dist/cjs/commands/create.js +2 -2
  6. package/dist/cjs/commands/delete.js +2 -14
  7. package/dist/cjs/commands/handlers/fetch.js +2 -1
  8. package/dist/cjs/commands/handlers/flags.d.ts +3 -3
  9. package/dist/cjs/commands/handlers/flags.js +3 -3
  10. package/dist/cjs/commands/handlers/store.js +7 -7
  11. package/dist/cjs/commands/list.js +17 -3
  12. package/dist/cjs/commands/rename.js +6 -46
  13. package/dist/cjs/commands/select.js +1 -0
  14. package/dist/cjs/commands/subscribe.js +7 -8
  15. package/dist/cjs/commands/unsubscribe.js +3 -9
  16. package/dist/cjs/control.d.ts +425 -0
  17. package/dist/cjs/control.js +637 -0
  18. package/dist/cjs/dates.d.ts +9 -1
  19. package/dist/cjs/dates.js +20 -0
  20. package/dist/cjs/envelope.d.ts +1 -1
  21. package/dist/cjs/envelope.js +26 -1
  22. package/dist/cjs/index.d.ts +9 -1
  23. package/dist/cjs/index.js +9 -2
  24. package/dist/cjs/load-plugins.d.ts +3 -1
  25. package/dist/cjs/load-plugins.js +28 -4
  26. package/dist/cjs/plugins/acl.js +109 -42
  27. package/dist/cjs/plugins/binary.js +2 -1
  28. package/dist/cjs/plugins/condstore.js +7 -0
  29. package/dist/cjs/plugins/context-search.js +2 -2
  30. package/dist/cjs/plugins/create-special-use.d.ts +0 -5
  31. package/dist/cjs/plugins/create-special-use.js +11 -1
  32. package/dist/cjs/plugins/imap4rev2.js +5 -0
  33. package/dist/cjs/plugins/index.js +1 -3
  34. package/dist/cjs/plugins/metadata-server.d.ts +6 -2
  35. package/dist/cjs/plugins/metadata-server.js +3 -1
  36. package/dist/cjs/plugins/metadata.d.ts +5 -1
  37. package/dist/cjs/plugins/metadata.js +143 -57
  38. package/dist/cjs/plugins/objectid.js +10 -0
  39. package/dist/cjs/plugins/quota.js +50 -2
  40. package/dist/cjs/plugins/savedate.js +1 -1
  41. package/dist/cjs/plugins/special-use.d.ts +0 -8
  42. package/dist/cjs/plugins/special-use.js +23 -0
  43. package/dist/cjs/quirks.d.ts +28 -0
  44. package/dist/cjs/quirks.js +125 -0
  45. package/dist/cjs/random.d.ts +10 -0
  46. package/dist/cjs/random.js +22 -0
  47. package/dist/cjs/rest.d.ts +54 -0
  48. package/dist/cjs/rest.js +567 -0
  49. package/dist/cjs/script.d.ts +33 -4
  50. package/dist/cjs/script.js +64 -4
  51. package/dist/cjs/server.d.ts +199 -9
  52. package/dist/cjs/server.js +603 -227
  53. package/dist/cjs/smtp-listener.d.ts +3 -2
  54. package/dist/cjs/smtp-listener.js +10 -6
  55. package/dist/cjs/storage-schema.d.ts +139 -0
  56. package/dist/cjs/storage-schema.js +222 -0
  57. package/dist/cjs/store-operations.d.ts +171 -0
  58. package/dist/cjs/store-operations.js +425 -0
  59. package/dist/cjs/types.d.ts +26 -1
  60. package/dist/esm/commands/append.js +1 -1
  61. package/dist/esm/commands/create.js +2 -2
  62. package/dist/esm/commands/delete.js +2 -14
  63. package/dist/esm/commands/handlers/fetch.js +2 -1
  64. package/dist/esm/commands/handlers/flags.d.ts +3 -3
  65. package/dist/esm/commands/handlers/flags.js +3 -3
  66. package/dist/esm/commands/handlers/store.js +7 -7
  67. package/dist/esm/commands/list.js +17 -3
  68. package/dist/esm/commands/rename.js +6 -46
  69. package/dist/esm/commands/select.js +1 -0
  70. package/dist/esm/commands/subscribe.js +7 -8
  71. package/dist/esm/commands/unsubscribe.js +3 -9
  72. package/dist/esm/control.d.ts +425 -0
  73. package/dist/esm/control.js +630 -0
  74. package/dist/esm/dates.d.ts +9 -1
  75. package/dist/esm/dates.js +20 -1
  76. package/dist/esm/envelope.d.ts +1 -1
  77. package/dist/esm/envelope.js +26 -1
  78. package/dist/esm/index.d.ts +9 -1
  79. package/dist/esm/index.js +5 -2
  80. package/dist/esm/load-plugins.d.ts +3 -1
  81. package/dist/esm/load-plugins.js +28 -4
  82. package/dist/esm/plugins/acl.js +109 -42
  83. package/dist/esm/plugins/binary.js +2 -1
  84. package/dist/esm/plugins/condstore.js +7 -0
  85. package/dist/esm/plugins/context-search.js +2 -2
  86. package/dist/esm/plugins/create-special-use.d.ts +0 -5
  87. package/dist/esm/plugins/create-special-use.js +8 -1
  88. package/dist/esm/plugins/imap4rev2.js +5 -0
  89. package/dist/esm/plugins/index.js +1 -3
  90. package/dist/esm/plugins/metadata-server.d.ts +6 -2
  91. package/dist/esm/plugins/metadata-server.js +3 -1
  92. package/dist/esm/plugins/metadata.d.ts +5 -1
  93. package/dist/esm/plugins/metadata.js +143 -57
  94. package/dist/esm/plugins/objectid.js +10 -0
  95. package/dist/esm/plugins/quota.js +50 -2
  96. package/dist/esm/plugins/savedate.js +1 -1
  97. package/dist/esm/plugins/special-use.d.ts +0 -8
  98. package/dist/esm/plugins/special-use.js +23 -0
  99. package/dist/esm/quirks.d.ts +28 -0
  100. package/dist/esm/quirks.js +121 -0
  101. package/dist/esm/random.d.ts +10 -0
  102. package/dist/esm/random.js +19 -0
  103. package/dist/esm/rest.d.ts +54 -0
  104. package/dist/esm/rest.js +559 -0
  105. package/dist/esm/script.d.ts +33 -4
  106. package/dist/esm/script.js +63 -4
  107. package/dist/esm/server.d.ts +199 -9
  108. package/dist/esm/server.js +606 -230
  109. package/dist/esm/smtp-listener.d.ts +3 -2
  110. package/dist/esm/smtp-listener.js +10 -6
  111. package/dist/esm/storage-schema.d.ts +139 -0
  112. package/dist/esm/storage-schema.js +218 -0
  113. package/dist/esm/store-operations.d.ts +171 -0
  114. package/dist/esm/store-operations.js +410 -0
  115. package/dist/esm/types.d.ts +26 -1
  116. package/dist/plugin-help.json +3 -16
  117. package/package.json +11 -3
  118. package/dist/cjs/plugins/xtoybird.d.ts +0 -2
  119. package/dist/cjs/plugins/xtoybird.js +0 -236
  120. package/dist/esm/plugins/xtoybird.d.ts +0 -2
  121. package/dist/esm/plugins/xtoybird.js +0 -218
package/bin/imapkit.js CHANGED
@@ -21,25 +21,39 @@ const { values: argv } = parseArgs({
21
21
  storage: { type: 'string' },
22
22
  script: { type: 'string' },
23
23
  plugin: { type: 'string', multiple: true },
24
- smtpPort: { type: 'string' }
24
+ smtpPort: { type: 'string' },
25
+ 'smtp-port': { type: 'string' },
26
+ 'rest-port': { type: 'string' },
27
+ 'rest-host': { type: 'string' },
28
+ 'rest-token': { type: 'string' },
29
+ quirk: { type: 'string', multiple: true },
30
+ 'script-seed': { type: 'string' }
25
31
  }
26
32
  });
27
33
 
28
34
  const isTrue = value => (value || '').toString().trim().toLowerCase() === 'true';
29
35
 
36
+ // a repeated option or a comma separated list, e.g. --plugin=IDLE,MOVE --plugin=ID
37
+ const listOption = value =>
38
+ []
39
+ .concat(value || [])
40
+ .flatMap(item =>
41
+ String(item)
42
+ .trim()
43
+ .split(/\s*,\s*/)
44
+ )
45
+ .filter(Boolean);
46
+
30
47
  const configLocation = argv.config || process.env.IMAPKIT_CONFIG;
31
48
  const storageLocation = argv.storage || process.env.IMAPKIT_STORAGE;
32
49
  const scriptLocation = argv.script || process.env.IMAPKIT_SCRIPT;
33
- const smtpPort = argv.smtpPort || process.env.IMAPKIT_SMTPPORT;
34
- const pluginsList = []
35
- .concat(argv.plugin || process.env.IMAPKIT_PLUGINS || [])
36
- .flatMap(plugin =>
37
- String(plugin)
38
- .toUpperCase()
39
- .trim()
40
- .split(/\s*,\s*/)
41
- )
42
- .filter(Boolean);
50
+ const smtpPort = argv.smtpPort || argv['smtp-port'] || process.env.IMAPKIT_SMTPPORT;
51
+ const restPort = argv['rest-port'] || process.env.IMAPKIT_REST_PORT;
52
+ const restHost = argv['rest-host'] || process.env.IMAPKIT_REST_HOST;
53
+ const restToken = argv['rest-token'] || process.env.IMAPKIT_REST_TOKEN;
54
+ const quirksList = listOption(argv.quirk || process.env.IMAPKIT_QUIRKS);
55
+ const scriptSeed = argv['script-seed'] || process.env.IMAPKIT_SCRIPT_SEED;
56
+ const pluginsList = listOption(argv.plugin || process.env.IMAPKIT_PLUGINS).map(plugin => plugin.toUpperCase());
43
57
  const secure = isTrue(argv.secure || process.env.IMAPKIT_SECURE);
44
58
  const debug = isTrue(argv.debug || process.env.IMAPKIT_DEBUG);
45
59
 
@@ -69,7 +83,9 @@ if (debug) {
69
83
  config.debug = true;
70
84
  }
71
85
 
72
- const port = argv.port || process.env.IMAPKIT_PORT || config.port || (secure ? 993 : 143);
86
+ // secureConnection can also come from the --config file
87
+ const isSecure = secure || config.secureConnection === true;
88
+ const port = argv.port || process.env.IMAPKIT_PORT || config.port || (isSecure ? 993 : 143);
73
89
 
74
90
  if (argv.help) {
75
91
  const help = fs.readFileSync(path.join(__dirname, 'help.txt'), 'utf-8');
@@ -93,18 +109,42 @@ if (argv.help) {
93
109
  .trim()
94
110
  );
95
111
  } else {
96
- const server = imapkit(config);
97
- console.log('Starting ImapKit ...');
98
- server.server.on('error', err => {
99
- console.error('Failed to start ImapKit on port %s: %s', port, err.message);
100
- process.exit(1);
101
- });
102
- server.listen(port, () => {
103
- console.log('ImapKit successfully%s listening on port %s', secure ? ' and securely' : '', port);
104
- });
105
-
112
+ if (quirksList.length) {
113
+ config.quirks = quirksList;
114
+ }
115
+ if (scriptSeed !== undefined) {
116
+ config.scriptSeed = Number(scriptSeed);
117
+ }
118
+ if (restPort) {
119
+ config.rest = { port: Number(restPort), host: restHost, token: restToken };
120
+ }
106
121
  if (smtpPort) {
107
- // loaded on demand, smtp-server is only needed when SMTP is enabled
108
- import('../dist/esm/smtp-listener.js').then(({ startSMTPServer }) => startSMTPServer(smtpPort, server));
122
+ // smtp-server is an optional dependency, start() loads it only when SMTP is enabled
123
+ config.smtp = { port: Number(smtpPort) };
124
+ }
125
+ let server;
126
+ try {
127
+ server = imapkit(config);
128
+ } catch (err) {
129
+ // an unknown plugin or quirk name, invalid storage ...
130
+ console.error('Failed to start ImapKit: %s', err.message);
131
+ process.exit(1);
109
132
  }
133
+ console.log('Starting ImapKit ...');
134
+ server.start(Number(port)).then(
135
+ listening => {
136
+ // the actual port, also for -p 0
137
+ console.log('ImapKit successfully%s listening on port %s', isSecure ? ' and securely' : '', listening);
138
+ if (server.restServer) {
139
+ console.log('REST API listening on %s:%s', (config.rest && config.rest.host) || '127.0.0.1', server.restServer.address().port);
140
+ }
141
+ if (server.smtpServer) {
142
+ console.log('Incoming SMTP server up and running on port %s', server.smtpServer.server.address().port);
143
+ }
144
+ },
145
+ err => {
146
+ console.error('Failed to start ImapKit: %s', err.message);
147
+ process.exit(1);
148
+ }
149
+ );
110
150
  }
@@ -45,7 +45,7 @@ function parseFlags(connection, list) {
45
45
  throw new Error('Invalid flags argument');
46
46
  }
47
47
  const value = (0, flags_js_1.normalizeSystemFlag)(flag.value);
48
- (0, flags_js_1.checkSystemFlags)(connection, value);
48
+ (0, flags_js_1.checkSystemFlags)(connection.server, value);
49
49
  return value;
50
50
  });
51
51
  }
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.default = createCommand;
4
+ const store_operations_js_1 = require("../store-operations.js");
4
5
  function createCommand(connection, parsed, data, callback) {
5
6
  if (!parsed.attributes || parsed.attributes.length !== 1 || !parsed.attributes[0] || ['STRING', 'LITERAL', 'ATOM'].indexOf(parsed.attributes[0].type) < 0) {
6
7
  connection.send({
@@ -18,7 +19,7 @@ function createCommand(connection, parsed, data, callback) {
18
19
  const path = parsed.attributes[0].value;
19
20
  let mailbox;
20
21
  try {
21
- mailbox = connection.server.createMailbox(path);
22
+ mailbox = (0, store_operations_js_1.createMailbox)(connection.server, path);
22
23
  }
23
24
  catch (err) {
24
25
  const E = err;
@@ -26,7 +27,6 @@ function createCommand(connection, parsed, data, callback) {
26
27
  connection.sendStatus(parsed, data, 'NO', E.message, E.code, 'CREATE FAILED');
27
28
  return callback();
28
29
  }
29
- connection.server.mailboxChanged('create', mailbox.path);
30
30
  connection.send({
31
31
  tag: parsed.tag,
32
32
  command: 'OK',
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.default = deleteCommand;
4
+ const store_operations_js_1 = require("../store-operations.js");
4
5
  function deleteCommand(connection, parsed, data, callback) {
5
6
  if (!parsed.attributes || parsed.attributes.length !== 1 || !parsed.attributes[0] || ['STRING', 'LITERAL', 'ATOM'].indexOf(parsed.attributes[0].type) < 0) {
6
7
  connection.send({
@@ -16,27 +17,14 @@ function deleteCommand(connection, parsed, data, callback) {
16
17
  return callback();
17
18
  }
18
19
  const path = parsed.attributes[0].value;
19
- const mailbox = connection.server.getMailbox(path);
20
- if (!mailbox) {
21
- connection.sendStatus(parsed, data, 'NO', 'Mailbox does not exist', 'NONEXISTENT', 'DELETE FAILED');
22
- return callback();
23
- }
24
20
  try {
25
- connection.server.deleteMailbox(path);
21
+ (0, store_operations_js_1.deleteMailbox)(connection.server, path);
26
22
  }
27
23
  catch (err) {
28
24
  const E = err;
29
25
  connection.sendStatus(parsed, data, 'NO', E.message, E.code, 'DELETE FAILED');
30
26
  return callback();
31
27
  }
32
- connection.server.mailboxChanged('delete', mailbox.path, { mailbox });
33
- // RFC 2180 section 3.3: the other sessions that have the mailbox selected are disconnected with an untagged
34
- // BYE (RFC 2683 section 3.1.2), as they can not be told about the deletion in any other way
35
- connection.server.connections.forEach(other => {
36
- if (other !== connection && other.selectedMailbox === mailbox) {
37
- other.bye('Selected mailbox was deleted', 'MAILBOX DELETED');
38
- }
39
- });
40
28
  connection.send({
41
29
  tag: parsed.tag,
42
30
  command: 'OK',
@@ -85,7 +85,8 @@ function getSection(data, query, global) {
85
85
  else if (query.section.length > 1) {
86
86
  throw new Error((key || 'Part number') + ' does not take any arguments');
87
87
  }
88
- // RFC 3501 6.4.5: a part that does not exist is returned as an empty string
88
+ // a part that does not exist is returned as an empty string, like Dovecot does: RFC 3501 and RFC 9051 section
89
+ // 6.4.5 define no error for it, only an empty string for a partial range that starts beyond the end of the text
89
90
  const node = path ? (0, mimeparser_js_1.resolveNode)(data.tree, path, global) : data.tree;
90
91
  if (!node) {
91
92
  return '';
@@ -1,4 +1,4 @@
1
- import type { IMAPConnection } from '../../types.js';
1
+ import type { IMAPServer } from '../../types.js';
2
2
  declare const INVALID_KEYWORD_CHAR: RegExp;
3
3
  /**
4
4
  * System flags are case-insensitive, so "\seen" becomes "\Seen"
@@ -10,8 +10,8 @@ declare function normalizeSystemFlag(flag: string): string;
10
10
  /**
11
11
  * Throws if a flag can not be stored: an unknown system flag (including \Recent) or an invalid keyword
12
12
  *
13
- * @param {Object} connection IMAP connection
13
+ * @param {Object} server IMAP server, its systemFlags are the system flags that can be stored
14
14
  * @param {String} flag Normalized flag value
15
15
  */
16
- declare function checkSystemFlags(connection: IMAPConnection, flag: string): void;
16
+ declare function checkSystemFlags(server: IMAPServer, flag: string): void;
17
17
  export { INVALID_KEYWORD_CHAR, normalizeSystemFlag, checkSystemFlags };
@@ -24,12 +24,12 @@ function normalizeSystemFlag(flag) {
24
24
  /**
25
25
  * Throws if a flag can not be stored: an unknown system flag (including \Recent) or an invalid keyword
26
26
  *
27
- * @param {Object} connection IMAP connection
27
+ * @param {Object} server IMAP server, its systemFlags are the system flags that can be stored
28
28
  * @param {String} flag Normalized flag value
29
29
  */
30
- function checkSystemFlags(connection, flag) {
30
+ function checkSystemFlags(server, flag) {
31
31
  if (flag.charAt(0) === '\\') {
32
- if (connection.server.systemFlags.indexOf(flag) < 0) {
32
+ if (server.systemFlags.indexOf(flag) < 0) {
33
33
  throw new Error('Invalid system flag ' + flag);
34
34
  }
35
35
  }
@@ -9,9 +9,9 @@ function setFlags(connection, message, flags) {
9
9
  const messageFlags = [];
10
10
  [].concat(flags).forEach(flag => {
11
11
  flag = (0, flags_js_1.normalizeSystemFlag)(typeof flag === 'string' ? flag : String(flag.value || ''));
12
- (0, flags_js_1.checkSystemFlags)(connection, flag);
13
- // Ignore if it is not in allowed list and only permament flags are allowed to use
14
- if (mailbox.permanentFlags.indexOf(flag) < 0 && !mailbox.allowPermanentFlags) {
12
+ (0, flags_js_1.checkSystemFlags)(connection.server, flag);
13
+ // a flag that is not in PERMANENTFLAGS is ignored (RFC 3501 section 7.1, RFC 9051 section 7.1)
14
+ if (!connection.server.isPermanentFlag(mailbox, flag)) {
15
15
  return;
16
16
  }
17
17
  if (messageFlags.indexOf(flag) < 0) {
@@ -25,9 +25,9 @@ function addFlags(connection, message, flags) {
25
25
  const mailbox = connection.selectedMailbox;
26
26
  [].concat(flags).forEach(flag => {
27
27
  flag = (0, flags_js_1.normalizeSystemFlag)(typeof flag === 'string' ? flag : String(flag.value || ''));
28
- (0, flags_js_1.checkSystemFlags)(connection, flag);
29
- // Ignore if it is not in allowed list and only permament flags are allowed to use
30
- if (mailbox.permanentFlags.indexOf(flag) < 0 && !mailbox.allowPermanentFlags) {
28
+ (0, flags_js_1.checkSystemFlags)(connection.server, flag);
29
+ // a flag that is not in PERMANENTFLAGS is ignored (RFC 3501 section 7.1, RFC 9051 section 7.1)
30
+ if (!connection.server.isPermanentFlag(mailbox, flag)) {
31
31
  return;
32
32
  }
33
33
  if (message.flags.indexOf(flag) < 0) {
@@ -39,7 +39,7 @@ function addFlags(connection, message, flags) {
39
39
  function removeFlags(connection, message, flags) {
40
40
  [].concat(flags).forEach(flag => {
41
41
  flag = (0, flags_js_1.normalizeSystemFlag)(typeof flag === 'string' ? flag : String(flag.value || ''));
42
- (0, flags_js_1.checkSystemFlags)(connection, flag);
42
+ (0, flags_js_1.checkSystemFlags)(connection.server, flag);
43
43
  if (message.flags.indexOf(flag) >= 0) {
44
44
  for (let i = 0; i < message.flags.length; i++) {
45
45
  if (message.flags[i] === flag) {
@@ -22,8 +22,22 @@ function listCommand(connection, parsed, data, callback) {
22
22
  return callback();
23
23
  }
24
24
  if (!parsed.attributes[1].value) {
25
- // empty reference lists separator only
26
- const namespace = connection.server.storage[parsed.attributes[1].value || connection.server.referenceNamespace];
25
+ // RFC 3501 section 6.3.8: "An empty ("" string) mailbox name argument is a special request to return the
26
+ // hierarchy delimiter and the root name of the name given in the reference. The value returned as the root
27
+ // MAY be the empty string if the reference is non-rooted or is an empty string."
28
+ const server = connection.server;
29
+ const reference = parsed.attributes[0].value || '';
30
+ // the namespace of the reference, the personal one for a reference that is not a valid name
31
+ let key = server.referenceNamespace;
32
+ try {
33
+ key = server.getNamespace(connection.importMailboxName(reference)) || key;
34
+ }
35
+ catch {
36
+ // not a mailbox name, so not in another namespace
37
+ }
38
+ const namespace = key !== false ? server.storage[key] : undefined;
39
+ // the root of a name in another namespace is the prefix of that namespace, like "#news." in the RFC example
40
+ const root = key !== false && key !== server.referenceNamespace ? connection.exportMailboxName(key) : '';
27
41
  if (namespace) {
28
42
  connection.send({
29
43
  tag: '*',
@@ -36,7 +50,7 @@ function listCommand(connection, parsed, data, callback) {
36
50
  }
37
51
  ],
38
52
  namespace.separator,
39
- ''
53
+ root
40
54
  ]
41
55
  }, 'LIST ITEM', parsed, data);
42
56
  }
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.default = renameCommand;
4
+ const store_operations_js_1 = require("../store-operations.js");
4
5
  function renameCommand(connection, parsed, data, callback) {
5
6
  if (!parsed.attributes ||
6
7
  parsed.attributes.length !== 2 ||
@@ -22,56 +23,15 @@ function renameCommand(connection, parsed, data, callback) {
22
23
  }
23
24
  const source = parsed.attributes[0].value;
24
25
  const destination = parsed.attributes[1].value;
25
- const server = connection.server;
26
- const mailbox = server.getMailbox(source);
27
- const target = server.getMailbox(destination);
28
- const sendError = (code, message) => {
29
- connection.sendStatus(parsed, data, 'NO', message, code, 'RENAME FAILED');
30
- return callback();
31
- };
32
- // check everything that can be checked up front, so that a failed RENAME never loses the source
33
- if (!mailbox || mailbox.flags.indexOf('\\Noselect') >= 0) {
34
- return sendError('NONEXISTENT', 'Mailbox does not exist');
35
- }
36
- if (target && target.flags.indexOf('\\Noselect') < 0) {
37
- return sendError('ALREADYEXISTS', 'Mailbox already exists');
38
- }
39
- const separator = server.getSeparator(mailbox);
40
- if (destination.substr(0, mailbox.path.length + separator.length) === mailbox.path + separator) {
41
- return sendError('CANNOT', 'Can not move a mailbox into itself');
42
- }
43
- const oldPath = mailbox.path;
44
- let newPath;
26
+ let renamed;
45
27
  try {
46
- if (source.toUpperCase() === 'INBOX') {
47
- // Renaming INBOX moves its messages to the new mailbox and leaves INBOX empty (RFC 3501 section 6.3.5)
48
- const newMailbox = server.createMailbox(destination);
49
- newPath = newMailbox.path;
50
- mailbox.messages.forEach((message) => {
51
- server.copyMessage(newMailbox, message);
52
- });
53
- connection.expungeSpecificMessages(mailbox, () => true);
54
- }
55
- else {
56
- server.deleteMailbox(source, true);
57
- try {
58
- server.createMailbox(destination, mailbox);
59
- }
60
- catch (E) {
61
- // put the source mailbox back where it was
62
- server.createMailbox(source, mailbox);
63
- server.indexFolders();
64
- throw E;
65
- }
66
- server.indexFolders();
67
- newPath = mailbox.path;
68
- }
28
+ renamed = (0, store_operations_js_1.renameMailbox)(connection.server, source, destination);
69
29
  }
70
30
  catch (err) {
71
31
  const E = err;
72
- return sendError(E.code, E.message);
32
+ connection.sendStatus(parsed, data, 'NO', E.message, E.code, 'RENAME FAILED');
33
+ return callback();
73
34
  }
74
- server.mailboxChanged('rename', newPath, { oldPath });
75
35
  connection.send({
76
36
  tag: parsed.tag,
77
37
  command: 'OK',
@@ -81,7 +41,7 @@ function renameCommand(connection, parsed, data, callback) {
81
41
  value: 'RENAME completed'
82
42
  }
83
43
  ]
84
- }, 'RENAME', parsed, data, mailbox);
44
+ }, 'RENAME', parsed, data, renamed.mailbox);
85
45
  return callback();
86
46
  }
87
47
  {
@@ -60,6 +60,7 @@ function selectMailbox(connection, parsed, data, callback, readOnly) {
60
60
  connection.state = 'Selected';
61
61
  connection.selectedMailbox = mailbox;
62
62
  connection.readOnly = readOnly;
63
+ connection.emitSession('select');
63
64
  connection.notificationQueue = [];
64
65
  // \Recent is a session flag: a read-write session takes over the messages
65
66
  // that no other session has seen yet (RFC 3501 section 2.3.2)
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.default = subscribeCommand;
4
+ const store_operations_js_1 = require("../store-operations.js");
4
5
  function subscribeCommand(connection, parsed, data, callback) {
5
6
  if (!parsed.attributes || parsed.attributes.length !== 1 || !parsed.attributes[0] || ['STRING', 'LITERAL', 'ATOM'].indexOf(parsed.attributes[0].type) < 0) {
6
7
  connection.send({
@@ -15,15 +16,13 @@ function subscribeCommand(connection, parsed, data, callback) {
15
16
  }, 'INVALID COMMAND', parsed, data);
16
17
  return callback();
17
18
  }
18
- const path = parsed.attributes[0].value;
19
- const mailbox = connection.server.getMailbox(path);
20
- if (!mailbox || mailbox.flags.indexOf('\\Noselect') >= 0) {
21
- connection.sendStatus(parsed, data, 'NO', 'Mailbox does not exist', 'NONEXISTENT', 'SUBSCRIBE FAILED');
22
- return callback();
19
+ try {
20
+ (0, store_operations_js_1.subscribeMailbox)(connection.server, parsed.attributes[0].value);
23
21
  }
24
- if (!mailbox.subscribed) {
25
- mailbox.subscribed = true;
26
- connection.server.mailboxChanged('subscribe', mailbox.path);
22
+ catch (err) {
23
+ const E = err;
24
+ connection.sendStatus(parsed, data, 'NO', E.message, E.code, 'SUBSCRIBE FAILED');
25
+ return callback();
27
26
  }
28
27
  connection.send({
29
28
  tag: parsed.tag,
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.default = unsubscribeCommand;
4
+ const store_operations_js_1 = require("../store-operations.js");
4
5
  function unsubscribeCommand(connection, parsed, data, callback) {
5
6
  if (!parsed.attributes || parsed.attributes.length !== 1 || !parsed.attributes[0] || ['STRING', 'LITERAL', 'ATOM'].indexOf(parsed.attributes[0].type) < 0) {
6
7
  connection.send({
@@ -15,15 +16,8 @@ function unsubscribeCommand(connection, parsed, data, callback) {
15
16
  }, 'INVALID COMMAND', parsed, data);
16
17
  return callback();
17
18
  }
18
- let path = parsed.attributes[0].value;
19
- if (path.toUpperCase() === 'INBOX') {
20
- path = 'INBOX';
21
- }
22
- // The subscription list holds names, so a name stays removable after its mailbox is deleted
23
- // (RFC 3501 section 6.3.6). Removing a name that is not subscribed is not an error (RFC 9051 section 6.3.8)
24
- if (connection.server.subscriptions.delete(path)) {
25
- connection.server.mailboxChanged('unsubscribe', path);
26
- }
19
+ // removing a name that is not subscribed is not an error (RFC 9051 section 6.3.8)
20
+ (0, store_operations_js_1.unsubscribeMailbox)(connection.server, parsed.attributes[0].value);
27
21
  connection.send({
28
22
  tag: parsed.tag,
29
23
  command: 'OK',