imapkit 0.0.0-stage → 4.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.
Files changed (114) hide show
  1. package/LICENSE +16 -0
  2. package/README.md +608 -2
  3. package/bin/help.txt +98 -0
  4. package/bin/imapkit.js +108 -0
  5. package/cert/server.crt +20 -0
  6. package/cert/server.key +28 -0
  7. package/lib/addressparser.js +283 -0
  8. package/lib/arguments.js +112 -0
  9. package/lib/bodystructure.js +149 -0
  10. package/lib/command-states.js +109 -0
  11. package/lib/commands/append.js +313 -0
  12. package/lib/commands/capability.js +47 -0
  13. package/lib/commands/check.js +21 -0
  14. package/lib/commands/close.js +30 -0
  15. package/lib/commands/copy.js +115 -0
  16. package/lib/commands/create.js +52 -0
  17. package/lib/commands/delete.js +64 -0
  18. package/lib/commands/examine.js +7 -0
  19. package/lib/commands/expunge.js +27 -0
  20. package/lib/commands/fetch.js +229 -0
  21. package/lib/commands/handlers/fetch.js +209 -0
  22. package/lib/commands/handlers/flags.js +42 -0
  23. package/lib/commands/handlers/search.js +519 -0
  24. package/lib/commands/handlers/status.js +85 -0
  25. package/lib/commands/handlers/store.js +127 -0
  26. package/lib/commands/list.js +100 -0
  27. package/lib/commands/login.js +67 -0
  28. package/lib/commands/logout.js +41 -0
  29. package/lib/commands/lsub.js +87 -0
  30. package/lib/commands/noop.js +21 -0
  31. package/lib/commands/rename.js +102 -0
  32. package/lib/commands/search.js +76 -0
  33. package/lib/commands/select.js +289 -0
  34. package/lib/commands/status.js +63 -0
  35. package/lib/commands/store.js +151 -0
  36. package/lib/commands/subscribe.js +53 -0
  37. package/lib/commands/uid copy.js +7 -0
  38. package/lib/commands/uid fetch.js +5 -0
  39. package/lib/commands/uid search.js +5 -0
  40. package/lib/commands/uid store.js +5 -0
  41. package/lib/commands/unsubscribe.js +50 -0
  42. package/lib/dates.js +123 -0
  43. package/lib/deflate-layer.js +232 -0
  44. package/lib/envelope.js +82 -0
  45. package/lib/esearch.js +208 -0
  46. package/lib/framing.js +102 -0
  47. package/lib/list-extensions.js +36 -0
  48. package/lib/load-plugins.js +109 -0
  49. package/lib/mailbox-name.js +133 -0
  50. package/lib/mimeparser.js +778 -0
  51. package/lib/mock-client.js +233 -0
  52. package/lib/numbers.js +52 -0
  53. package/lib/plugins/acl.js +964 -0
  54. package/lib/plugins/appendlimit.js +83 -0
  55. package/lib/plugins/auth-plain.js +94 -0
  56. package/lib/plugins/binary.js +256 -0
  57. package/lib/plugins/catenate.js +253 -0
  58. package/lib/plugins/compress.js +76 -0
  59. package/lib/plugins/condstore.js +563 -0
  60. package/lib/plugins/context-search.js +321 -0
  61. package/lib/plugins/context-sort.js +19 -0
  62. package/lib/plugins/create-special-use.js +108 -0
  63. package/lib/plugins/enable.js +155 -0
  64. package/lib/plugins/esearch.js +156 -0
  65. package/lib/plugins/esort.js +60 -0
  66. package/lib/plugins/id.js +138 -0
  67. package/lib/plugins/idle.js +105 -0
  68. package/lib/plugins/imap4rev2.js +202 -0
  69. package/lib/plugins/list-extended.js +258 -0
  70. package/lib/plugins/list-status.js +31 -0
  71. package/lib/plugins/literalminus.js +20 -0
  72. package/lib/plugins/literalplus.js +18 -0
  73. package/lib/plugins/logindisabled.js +50 -0
  74. package/lib/plugins/messagelimit.js +234 -0
  75. package/lib/plugins/metadata-server.js +13 -0
  76. package/lib/plugins/metadata.js +475 -0
  77. package/lib/plugins/move.js +110 -0
  78. package/lib/plugins/multiappend.js +26 -0
  79. package/lib/plugins/multisearch.js +269 -0
  80. package/lib/plugins/namespace.js +67 -0
  81. package/lib/plugins/notify.js +654 -0
  82. package/lib/plugins/oauthbearer.js +217 -0
  83. package/lib/plugins/objectid.js +243 -0
  84. package/lib/plugins/partial.js +68 -0
  85. package/lib/plugins/preview.js +400 -0
  86. package/lib/plugins/qresync.js +525 -0
  87. package/lib/plugins/quota.js +285 -0
  88. package/lib/plugins/replace.js +145 -0
  89. package/lib/plugins/sasl-ir.js +12 -0
  90. package/lib/plugins/savedate.js +59 -0
  91. package/lib/plugins/savelimit.js +18 -0
  92. package/lib/plugins/searchres.js +82 -0
  93. package/lib/plugins/sort-display.js +23 -0
  94. package/lib/plugins/sort.js +132 -0
  95. package/lib/plugins/special-use.js +95 -0
  96. package/lib/plugins/starttls.js +57 -0
  97. package/lib/plugins/status-size.js +19 -0
  98. package/lib/plugins/thread-orderedsubject.js +16 -0
  99. package/lib/plugins/thread-references.js +16 -0
  100. package/lib/plugins/uidonly.js +135 -0
  101. package/lib/plugins/uidplus.js +124 -0
  102. package/lib/plugins/unauthenticate.js +28 -0
  103. package/lib/plugins/unselect.js +36 -0
  104. package/lib/plugins/utf8-accept.js +68 -0
  105. package/lib/plugins/x-gm-ext-1.js +456 -0
  106. package/lib/plugins/xoauth2.js +188 -0
  107. package/lib/plugins/xtoybird.js +282 -0
  108. package/lib/server.js +2880 -0
  109. package/lib/smtp-listener.js +51 -0
  110. package/lib/sorting.js +373 -0
  111. package/lib/threading.js +357 -0
  112. package/lib/utf8-session.js +123 -0
  113. package/lib/vanished.js +57 -0
  114. package/package.json +61 -5
@@ -0,0 +1,209 @@
1
+ 'use strict';
2
+
3
+ const fetchHandlers = {};
4
+ const mimeParser = require('../../mimeparser');
5
+ const bodystructure = require('../../bodystructure');
6
+ const envelope = require('../../envelope');
7
+
8
+ const { getMessageData, resolveNode, embeddedMessage, headerSection, render } = mimeParser;
9
+
10
+ module.exports = fetchHandlers;
11
+ // not an IMAP item, so not enumerable among the handlers
12
+ Object.defineProperty(fetchHandlers, 'applyPartial', { value: applyPartial });
13
+
14
+ fetchHandlers.UID = function (connection, message) {
15
+ return message.uid;
16
+ };
17
+
18
+ fetchHandlers.FLAGS = function (connection, message) {
19
+ return connection.getFlags(message).map(flag => {
20
+ return {
21
+ type: 'ATOM',
22
+ value: flag
23
+ };
24
+ });
25
+ };
26
+
27
+ fetchHandlers.BODYSTRUCTURE = function (connection, message) {
28
+ return bodystructure(getMessageData(message).tree, {
29
+ upperCaseKeys: true,
30
+ skipContentLocation: true,
31
+ messageGlobal: connection.messageGlobal
32
+ });
33
+ };
34
+
35
+ fetchHandlers.ENVELOPE = function (connection, message) {
36
+ return envelope(getMessageData(message).tree.parsedHeader);
37
+ };
38
+
39
+ fetchHandlers['BODY.PEEK'] = function (connection, message, query) {
40
+ if (!query.section) {
41
+ throw new Error('BODY.PEEK requires ans argument list');
42
+ }
43
+ return fetchHandlers.BODY(connection, message, query);
44
+ };
45
+
46
+ /**
47
+ * Lists the header field names of a HEADER.FIELDS or HEADER.FIELDS.NOT section
48
+ */
49
+ function getFieldNames(query, key) {
50
+ if (query.section.length !== 2 || !Array.isArray(query.section[1]) || !query.section[1].length) {
51
+ throw new Error(key + ' expects a list of header fields');
52
+ }
53
+ return query.section[1].map(queryKey => {
54
+ if (['ATOM', 'STRING', 'LITERAL'].indexOf(queryKey.type) < 0) {
55
+ throw new Error('Invalid header field name in list');
56
+ }
57
+ queryKey.type = 'ATOM'; // ensure that literals are not passed back in the response
58
+ return queryKey.value.toUpperCase();
59
+ });
60
+ }
61
+
62
+ /**
63
+ * Returns the contents of a BODY[<section>]
64
+ *
65
+ * @param {Object} data Message data from getMessageData()
66
+ * @param {Object} query Parsed fetch item
67
+ * @param {Boolean} [global] message/global parts encapsulate a message, see IMAPConnection#messageGlobal
68
+ * @return {String} Section contents
69
+ */
70
+ function getSection(data, query, global) {
71
+ if (!query.section.length) {
72
+ return data.raw;
73
+ }
74
+
75
+ if (query.section[0].type !== 'ATOM') {
76
+ throw new Error('Invalid BODY[<section>] identifier' + (query.section[0].value ? ' ' + query.section[0].type : ''));
77
+ }
78
+
79
+ // RFC 3501 9: section-part = nz-number *("." nz-number), optionally followed by a section-msgtext
80
+ // (or MIME for a part)
81
+ const match = (query.section[0].value || '')
82
+ .toUpperCase()
83
+ .match(/^((?:[1-9]\d*)(?:\.[1-9]\d*)*)?(?:(?:^|\.)(HEADER|HEADER\.FIELDS|HEADER\.FIELDS\.NOT|TEXT|MIME))?$/);
84
+ if (!match || (match[2] === 'MIME' && !match[1])) {
85
+ throw new Error('Invalid BODY[<section>] identifier ' + query.section[0].value);
86
+ }
87
+
88
+ const path = match[1] || '';
89
+ const key = match[2] || '';
90
+ let fields;
91
+
92
+ if (['HEADER.FIELDS', 'HEADER.FIELDS.NOT'].indexOf(key) >= 0) {
93
+ fields = getFieldNames(query, key);
94
+ } else if (query.section.length > 1) {
95
+ throw new Error((key || 'Part number') + ' does not take any arguments');
96
+ }
97
+
98
+ // RFC 3501 6.4.5: a part that does not exist is returned as an empty string
99
+ const node = path ? resolveNode(data.tree, path, global) : data.tree;
100
+ if (!node) {
101
+ return '';
102
+ }
103
+
104
+ // HEADER and TEXT with a part number refer to the encapsulated message of a message/rfc822 part
105
+ // (and message/global in IMAP4rev2, RFC 9051 section 6.4.5.1), not to the part itself
106
+ const message = path ? embeddedMessage(node, global) : node;
107
+
108
+ switch (key) {
109
+ case '':
110
+ // BODY[1.2.3] is a part without its MIME header
111
+ return render(node, true);
112
+
113
+ case 'MIME':
114
+ return headerSection(node);
115
+
116
+ case 'TEXT':
117
+ return message ? render(message, true) : '';
118
+
119
+ case 'HEADER':
120
+ return message ? headerSection(message) : '';
121
+
122
+ default: {
123
+ // HEADER.FIELDS and HEADER.FIELDS.NOT
124
+ if (!message) {
125
+ return '';
126
+ }
127
+ const wanted = key === 'HEADER.FIELDS';
128
+ let value = '';
129
+ (message.header || []).forEach(line => {
130
+ const name = line.split(':').shift().toUpperCase().trim();
131
+ if (fields.indexOf(name) >= 0 === wanted) {
132
+ value += line + '\r\n';
133
+ }
134
+ });
135
+ return value + '\r\n';
136
+ }
137
+ }
138
+ }
139
+
140
+ /**
141
+ * Applies the partial range of a BODY[]<start.length> or BINARY[]<start.length> item
142
+ *
143
+ * @param {String} value Section contents
144
+ * @param {Array} [partial] Parsed partial range
145
+ * @return {String} The requested slice, or the whole value without a range
146
+ */
147
+ function applyPartial(value, partial) {
148
+ if (!partial) {
149
+ return value;
150
+ }
151
+ // RFC 3501 9: partial = "<" number "." nz-number ">"
152
+ if (partial.length !== 2 || !(partial[1] > 0)) {
153
+ throw new Error('Invalid partial range, expecting <start.length>');
154
+ }
155
+ return value.substr(partial[0], partial[1]);
156
+ }
157
+
158
+ fetchHandlers.BODY = function (connection, message, query) {
159
+ const data = getMessageData(message);
160
+
161
+ if (!query.section) {
162
+ if (query.partial) {
163
+ throw new Error('BODY without a section does not take a partial range');
164
+ }
165
+ return bodystructure(data.tree, {
166
+ body: true,
167
+ upperCaseKeys: true,
168
+ messageGlobal: connection.messageGlobal
169
+ });
170
+ }
171
+
172
+ return {
173
+ type: 'LITERAL',
174
+ value: applyPartial(getSection(data, query, connection.messageGlobal), query.partial)
175
+ };
176
+ };
177
+
178
+ fetchHandlers.INTERNALDATE = function (connection, message) {
179
+ return message.internaldate;
180
+ };
181
+
182
+ fetchHandlers.RFC822 = function (connection, message) {
183
+ return {
184
+ type: 'LITERAL',
185
+ value: getMessageData(message).raw
186
+ };
187
+ };
188
+
189
+ // RFC 3501 6.4.5: RFC822 and RFC822.TEXT set \Seen, RFC822.HEADER is BODY.PEEK[HEADER] and does not
190
+ fetchHandlers.RFC822.setsSeen = true;
191
+
192
+ fetchHandlers['RFC822.SIZE'] = function (connection, message) {
193
+ return getMessageData(message).raw.length;
194
+ };
195
+
196
+ fetchHandlers['RFC822.HEADER'] = function (connection, message) {
197
+ return {
198
+ type: 'LITERAL',
199
+ value: headerSection(getMessageData(message).tree)
200
+ };
201
+ };
202
+
203
+ fetchHandlers['RFC822.TEXT'] = function (connection, message) {
204
+ return {
205
+ type: 'LITERAL',
206
+ value: render(getMessageData(message).tree, true)
207
+ };
208
+ };
209
+ fetchHandlers['RFC822.TEXT'].setsSeen = true;
@@ -0,0 +1,42 @@
1
+ 'use strict';
2
+
3
+ // RFC 3501 9: flag-keyword = atom = 1*ATOM-CHAR, so a keyword can not contain atom-specials:
4
+ // "(" / ")" / "{" / SP / CTL / list-wildcards / quoted-specials / resp-specials. 8-bit octets are
5
+ // allowed, the parser accepts them in atoms as well
6
+ // eslint-disable-next-line no-control-regex
7
+ const INVALID_KEYWORD_CHAR = /[\x00-\x1f\x7f (){%*"\\\]]/;
8
+
9
+ /**
10
+ * System flags are case-insensitive, so "\seen" becomes "\Seen"
11
+ *
12
+ * @param {String} flag Flag value
13
+ * @return {String} Normalized flag
14
+ */
15
+ function normalizeSystemFlag(flag) {
16
+ if (flag.charAt(0) === '\\') {
17
+ flag = flag.charAt(0) + flag.charAt(1).toUpperCase() + flag.substr(2).toLowerCase();
18
+ }
19
+ return flag;
20
+ }
21
+
22
+ /**
23
+ * Throws if a flag can not be stored: an unknown system flag (including \Recent) or an invalid keyword
24
+ *
25
+ * @param {Object} connection IMAP connection
26
+ * @param {String} flag Normalized flag value
27
+ */
28
+ function checkSystemFlags(connection, flag) {
29
+ if (flag.charAt(0) === '\\') {
30
+ if (connection.server.systemFlags.indexOf(flag) < 0) {
31
+ throw new Error('Invalid system flag ' + flag);
32
+ }
33
+ } else if (!flag || INVALID_KEYWORD_CHAR.test(flag)) {
34
+ throw new Error('Invalid flag keyword ' + JSON.stringify(flag));
35
+ }
36
+ }
37
+
38
+ module.exports = {
39
+ INVALID_KEYWORD_CHAR,
40
+ normalizeSystemFlag,
41
+ checkSystemFlags
42
+ };