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.
- package/LICENSE +16 -0
- package/README.md +608 -2
- package/bin/help.txt +98 -0
- package/bin/imapkit.js +108 -0
- package/cert/server.crt +20 -0
- package/cert/server.key +28 -0
- package/lib/addressparser.js +283 -0
- package/lib/arguments.js +112 -0
- package/lib/bodystructure.js +149 -0
- package/lib/command-states.js +109 -0
- package/lib/commands/append.js +313 -0
- package/lib/commands/capability.js +47 -0
- package/lib/commands/check.js +21 -0
- package/lib/commands/close.js +30 -0
- package/lib/commands/copy.js +115 -0
- package/lib/commands/create.js +52 -0
- package/lib/commands/delete.js +64 -0
- package/lib/commands/examine.js +7 -0
- package/lib/commands/expunge.js +27 -0
- package/lib/commands/fetch.js +229 -0
- package/lib/commands/handlers/fetch.js +209 -0
- package/lib/commands/handlers/flags.js +42 -0
- package/lib/commands/handlers/search.js +519 -0
- package/lib/commands/handlers/status.js +85 -0
- package/lib/commands/handlers/store.js +127 -0
- package/lib/commands/list.js +100 -0
- package/lib/commands/login.js +67 -0
- package/lib/commands/logout.js +41 -0
- package/lib/commands/lsub.js +87 -0
- package/lib/commands/noop.js +21 -0
- package/lib/commands/rename.js +102 -0
- package/lib/commands/search.js +76 -0
- package/lib/commands/select.js +289 -0
- package/lib/commands/status.js +63 -0
- package/lib/commands/store.js +151 -0
- package/lib/commands/subscribe.js +53 -0
- package/lib/commands/uid copy.js +7 -0
- package/lib/commands/uid fetch.js +5 -0
- package/lib/commands/uid search.js +5 -0
- package/lib/commands/uid store.js +5 -0
- package/lib/commands/unsubscribe.js +50 -0
- package/lib/dates.js +123 -0
- package/lib/deflate-layer.js +232 -0
- package/lib/envelope.js +82 -0
- package/lib/esearch.js +208 -0
- package/lib/framing.js +102 -0
- package/lib/list-extensions.js +36 -0
- package/lib/load-plugins.js +109 -0
- package/lib/mailbox-name.js +133 -0
- package/lib/mimeparser.js +778 -0
- package/lib/mock-client.js +233 -0
- package/lib/numbers.js +52 -0
- package/lib/plugins/acl.js +964 -0
- package/lib/plugins/appendlimit.js +83 -0
- package/lib/plugins/auth-plain.js +94 -0
- package/lib/plugins/binary.js +256 -0
- package/lib/plugins/catenate.js +253 -0
- package/lib/plugins/compress.js +76 -0
- package/lib/plugins/condstore.js +563 -0
- package/lib/plugins/context-search.js +321 -0
- package/lib/plugins/context-sort.js +19 -0
- package/lib/plugins/create-special-use.js +108 -0
- package/lib/plugins/enable.js +155 -0
- package/lib/plugins/esearch.js +156 -0
- package/lib/plugins/esort.js +60 -0
- package/lib/plugins/id.js +138 -0
- package/lib/plugins/idle.js +105 -0
- package/lib/plugins/imap4rev2.js +202 -0
- package/lib/plugins/list-extended.js +258 -0
- package/lib/plugins/list-status.js +31 -0
- package/lib/plugins/literalminus.js +20 -0
- package/lib/plugins/literalplus.js +18 -0
- package/lib/plugins/logindisabled.js +50 -0
- package/lib/plugins/messagelimit.js +234 -0
- package/lib/plugins/metadata-server.js +13 -0
- package/lib/plugins/metadata.js +475 -0
- package/lib/plugins/move.js +110 -0
- package/lib/plugins/multiappend.js +26 -0
- package/lib/plugins/multisearch.js +269 -0
- package/lib/plugins/namespace.js +67 -0
- package/lib/plugins/notify.js +654 -0
- package/lib/plugins/oauthbearer.js +217 -0
- package/lib/plugins/objectid.js +243 -0
- package/lib/plugins/partial.js +68 -0
- package/lib/plugins/preview.js +400 -0
- package/lib/plugins/qresync.js +525 -0
- package/lib/plugins/quota.js +285 -0
- package/lib/plugins/replace.js +145 -0
- package/lib/plugins/sasl-ir.js +12 -0
- package/lib/plugins/savedate.js +59 -0
- package/lib/plugins/savelimit.js +18 -0
- package/lib/plugins/searchres.js +82 -0
- package/lib/plugins/sort-display.js +23 -0
- package/lib/plugins/sort.js +132 -0
- package/lib/plugins/special-use.js +95 -0
- package/lib/plugins/starttls.js +57 -0
- package/lib/plugins/status-size.js +19 -0
- package/lib/plugins/thread-orderedsubject.js +16 -0
- package/lib/plugins/thread-references.js +16 -0
- package/lib/plugins/uidonly.js +135 -0
- package/lib/plugins/uidplus.js +124 -0
- package/lib/plugins/unauthenticate.js +28 -0
- package/lib/plugins/unselect.js +36 -0
- package/lib/plugins/utf8-accept.js +68 -0
- package/lib/plugins/x-gm-ext-1.js +456 -0
- package/lib/plugins/xoauth2.js +188 -0
- package/lib/plugins/xtoybird.js +282 -0
- package/lib/server.js +2880 -0
- package/lib/smtp-listener.js +51 -0
- package/lib/sorting.js +373 -0
- package/lib/threading.js +357 -0
- package/lib/utf8-session.js +123 -0
- package/lib/vanished.js +57 -0
- 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
|
+
};
|