Haraka 2.8.28 → 3.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/.eslintrc.yaml +2 -10
- package/Changes.md +68 -2
- package/Dockerfile +1 -1
- package/Plugins.md +7 -4
- package/README.md +2 -6
- package/config/outbound.ini +0 -7
- package/config/plugins +1 -1
- package/config/smtp.ini +1 -1
- package/config/smtp_forward.ini +2 -8
- package/config/smtp_proxy.ini +0 -6
- package/connection.js +178 -204
- package/coverage/lcov.info +13863 -0
- package/coverage/tmp/coverage-42958-1658373250585-0.json +1 -0
- package/coverage/tmp/coverage-42961-1658373250529-0.json +1 -0
- package/dkim.js +65 -73
- package/docs/Body.md +1 -22
- package/docs/CoreConfig.md +2 -2
- package/docs/Header.md +1 -47
- package/docs/Outbound.md +8 -36
- package/endpoint.js +1 -1
- package/haraka.js +1 -1
- package/host_pool.js +8 -12
- package/logger.js +25 -32
- package/outbound/client_pool.js +11 -153
- package/outbound/config.js +5 -11
- package/outbound/hmail.js +109 -143
- package/outbound/index.js +13 -25
- package/outbound/mx_lookup.js +10 -7
- package/outbound/queue.js +8 -12
- package/outbound/timer_queue.js +2 -4
- package/outbound/tls.js +17 -18
- package/outbound/todo.js +1 -0
- package/package.json +42 -40
- package/plugins/auth/auth_base.js +39 -63
- package/plugins/auth/auth_bridge.js +3 -4
- package/plugins/auth/auth_proxy.js +16 -16
- package/plugins/auth/auth_vpopmaild.js +30 -37
- package/plugins/auth/flat_file.js +9 -13
- package/plugins/avg.js +9 -11
- package/plugins/backscatterer.js +1 -1
- package/plugins/block_me.js +2 -6
- package/plugins/bounce.js +106 -124
- package/plugins/clamd.js +59 -63
- package/plugins/data.signatures.js +6 -6
- package/plugins/data.uribl.js +1 -415
- package/plugins/delay_deny.js +19 -20
- package/plugins/dkim_sign.js +56 -62
- package/plugins/dkim_verify.js +9 -8
- package/plugins/dns_list_base.js +43 -42
- package/plugins/dnsbl.js +41 -46
- package/plugins/dnswl.js +23 -26
- package/plugins/early_talker.js +24 -28
- package/plugins/esets.js +8 -11
- package/plugins/greylist.js +161 -190
- package/plugins/helo.checks.js +175 -197
- package/plugins/mail_from.is_resolvable.js +38 -38
- package/plugins/messagesniffer.js +33 -40
- package/plugins/prevent_credential_leaks.js +7 -5
- package/plugins/process_title.js +16 -17
- package/plugins/queue/deliver.js +2 -2
- package/plugins/queue/lmtp.js +5 -6
- package/plugins/queue/qmail-queue.js +11 -13
- package/plugins/queue/quarantine.js +25 -34
- package/plugins/queue/rabbitmq.js +3 -2
- package/plugins/queue/rabbitmq_amqplib.js +9 -9
- package/plugins/queue/smtp_bridge.js +5 -4
- package/plugins/queue/smtp_forward.js +81 -89
- package/plugins/queue/smtp_proxy.js +21 -22
- package/plugins/queue/test.js +2 -1
- package/plugins/rcpt_to.host_list_base.js +20 -30
- package/plugins/rcpt_to.in_host_list.js +12 -14
- package/plugins/rcpt_to.max_count.js +7 -5
- package/plugins/record_envelope_addresses.js +4 -6
- package/plugins/relay.js +64 -74
- package/plugins/reseed_rng.js +1 -2
- package/plugins/spamassassin.js +56 -68
- package/plugins/status.js +2 -3
- package/plugins/tarpit.js +8 -11
- package/plugins/tls.js +14 -17
- package/plugins/toobusy.js +6 -8
- package/plugins/xclient.js +14 -25
- package/plugins.js +24 -29
- package/rfc1869.js +2 -2
- package/server.js +3 -13
- package/smtp_client.js +138 -215
- package/tests/config/smtp_forward.ini +0 -6
- package/tests/fixtures/line_socket.js +1 -1
- package/tests/fixtures/util_hmailitem.js +5 -7
- package/tests/fixtures/vm_harness.js +2 -2
- package/tests/host_pool.js +13 -14
- package/tests/installation/plugins/inherits.js +1 -2
- package/tests/logger.js +2 -2
- package/tests/plugins/bounce.js +6 -8
- package/tests/plugins/dkim_signer.js +7 -7
- package/tests/plugins/dns_list_base.js +7 -7
- package/tests/plugins/helo.checks.js +1 -1
- package/tests/plugins/mail_from.is_resolvable.js +10 -54
- package/tests/plugins/queue/smtp_forward.js +11 -11
- package/tests/plugins/rcpt_to.host_list_base.js +1 -1
- package/tests/plugins/rcpt_to.in_host_list.js +1 -1
- package/tests/plugins/spamassassin.js +1 -1
- package/tests/queue/multibyte +0 -0
- package/tests/queue/plain +0 -0
- package/tests/rfc1869.js +4 -1
- package/tests/server.js +15 -9
- package/tests/smtp_client/auth.js +4 -14
- package/tests/smtp_client/basic.js +5 -15
- package/tests/smtp_client.js +7 -3
- package/tests/transaction.js +72 -19
- package/tls_socket.js +75 -85
- package/transaction.js +7 -9
- package/attachment_stream.js +0 -118
- package/bin/spf +0 -48
- package/chunkemitter.js +0 -75
- package/config/data.uribl.excludes +0 -202
- package/config/data.uribl.ini +0 -37
- package/config/spf.ini +0 -1
- package/docs/plugins/attachment.md +0 -92
- package/docs/plugins/data.uribl.md +0 -120
- package/docs/plugins/spf.md +0 -142
- package/mailbody.js +0 -502
- package/mailheader.js +0 -304
- package/messagestream.js +0 -441
- package/plugins/aliases.js +0 -120
- package/plugins/attachment.js +0 -503
- package/plugins/connect.p0f.js +0 -5
- package/plugins/spf.js +0 -327
- package/spf.js +0 -689
- package/tests/mailbody.js +0 -348
- package/tests/mailheader.js +0 -138
- package/tests/messagestream.js +0 -34
- package/tests/plugins/aliases.js +0 -376
- package/tests/plugins/spf.js +0 -251
- package/tests/spf.js +0 -96
package/tests/mailbody.js
DELETED
|
@@ -1,348 +0,0 @@
|
|
|
1
|
-
const Body = require('../mailbody').Body;
|
|
2
|
-
|
|
3
|
-
function _fill_body (body, quote) {
|
|
4
|
-
// Body.bodytext retains the original received text before filters are
|
|
5
|
-
// applied so the filtered text isn't tested against URIBLs, etc. Since we
|
|
6
|
-
// want to test filter output, we use this hack to pull out the parsed body
|
|
7
|
-
// parts that will be passed onward to the transaction.
|
|
8
|
-
|
|
9
|
-
quote = quote || "";
|
|
10
|
-
|
|
11
|
-
body.state = 'headers';
|
|
12
|
-
body.parse_more(`Content-Type: multipart/alternative; boundary=${quote}abcdef${quote}\n`);
|
|
13
|
-
body.parse_more("From: =?US-ASCII*EN?Q?Keith_Moore?= <moore@cs.utk.edu>\n");
|
|
14
|
-
body.parse_more("\n");
|
|
15
|
-
body.parse_more("--abcdef\n");
|
|
16
|
-
body.parse_more("Content-Type: text/plain; charset=UTF-8; format=flowed;\n");
|
|
17
|
-
body.parse_more(" URL*0=\"ftp://\"\n");
|
|
18
|
-
body.parse_more(" URL*1=\"cs.utk.edu/pub/moore/bulk-mailer/bulk-mailer.tar\"\n");
|
|
19
|
-
body.parse_more(" title*=us-ascii'en-us'This%20is%20%2A%2A%2Afun%2A%2A%2A\n");
|
|
20
|
-
body.parse_more("Content-Transfer-Encoding: quoted-printable\n");
|
|
21
|
-
body.parse_more("\n");
|
|
22
|
-
body.parse_more("Some text for your =\n");
|
|
23
|
-
body.parse_more("testing pleasure. \n");
|
|
24
|
-
body.parse_more("Yup that was some text all right.\n");
|
|
25
|
-
body.parse_more("\n");
|
|
26
|
-
let text = body.parse_more("--abcdef\n");
|
|
27
|
-
body.parse_more("Content-Type: text/html; charset=UTF-8;\n");
|
|
28
|
-
body.parse_more(" title*0*=us-ascii'en'This%20is%20even%20more%20\n");
|
|
29
|
-
body.parse_more(" title*1*=%2A%2A%2Afun%2A%2A%2A%20\n");
|
|
30
|
-
body.parse_more(" title*2=\"isn't it!\"\n");
|
|
31
|
-
body.parse_more("Content-Disposition: inline; \n");
|
|
32
|
-
body.parse_more(" filename*0=\"marketron_lbasubmission_FTA Cricket Brentwood 3006445\n");
|
|
33
|
-
body.parse_more(" Jackso\"; filename*1=\"n TN_08282017.xlsx\"\n");
|
|
34
|
-
body.parse_more("\n");
|
|
35
|
-
body.parse_more("<p>This is some HTML, yo.<br>\n");
|
|
36
|
-
body.parse_more("It's pretty rad.</p>\n");
|
|
37
|
-
body.parse_more("\n");
|
|
38
|
-
let html = body.parse_more("--abcdef--\n");
|
|
39
|
-
body.parse_end();
|
|
40
|
-
|
|
41
|
-
text = text.toString().replace(/--abcdef\n$/, '').trim();
|
|
42
|
-
html = html.toString().replace(/--abcdef--\n$/, '').trim();
|
|
43
|
-
|
|
44
|
-
return [text, html];
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function _fill_empty_body (body) {
|
|
48
|
-
// Body.bodytext retains the original received text before filters are
|
|
49
|
-
// applied so the filtered text isn't tested against URIBLs, etc. Since we
|
|
50
|
-
// want to test filter output, we use this hack to pull out the parsed body
|
|
51
|
-
// parts that will be passed onward to the transaction.
|
|
52
|
-
|
|
53
|
-
body.state = 'headers';
|
|
54
|
-
body.parse_more("Content-Type: multipart/alternative; boundary=abcdef\n");
|
|
55
|
-
body.parse_more("\n");
|
|
56
|
-
body.parse_more("--abcdef\n");
|
|
57
|
-
body.parse_more("Content-Type: text/plain; charset=UTF-8; format=flowed;\n");
|
|
58
|
-
body.parse_more("\n");
|
|
59
|
-
let text = body.parse_more("--abcdef\n");
|
|
60
|
-
body.parse_more("Content-Type: text/html; charset=UTF-8;\n");
|
|
61
|
-
body.parse_more("\n");
|
|
62
|
-
let html = body.parse_more("--abcdef--\n");
|
|
63
|
-
body.parse_end();
|
|
64
|
-
|
|
65
|
-
text = text.toString().replace(/--abcdef\n$/, '').trim();
|
|
66
|
-
html = html.toString().replace(/--abcdef--\n$/, '').trim();
|
|
67
|
-
|
|
68
|
-
return [text, html];
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
exports.basic = {
|
|
72
|
-
'children': test => {
|
|
73
|
-
test.expect(1);
|
|
74
|
-
|
|
75
|
-
const body = new Body();
|
|
76
|
-
_fill_body(body);
|
|
77
|
-
|
|
78
|
-
test.equal(body.children.length, 2);
|
|
79
|
-
test.done();
|
|
80
|
-
},
|
|
81
|
-
|
|
82
|
-
'correct mime parsing (#2548)': test => {
|
|
83
|
-
const tests = [
|
|
84
|
-
['utf-8', '8-bit', Buffer.from("Grüße, Buß\n"), Buffer.from("Grüße, Buß\n")],
|
|
85
|
-
['utf-8', 'quoted-printable', Buffer.from("Gr=C3=BC=C3=9Fe, Bu=C3=9F\n"), Buffer.from("Grüße, Buß\n")],
|
|
86
|
-
['utf-8', 'base64', Buffer.from("R3LDvMOfZSwgQnXDnw==\n"), Buffer.from("Grüße, Buß")],
|
|
87
|
-
['iso-8859-2', '8-bit', Buffer.from([0x50,0xF8,0x69,0x68,0x6C,0x61,0xB9,0x6F,0x76,0x61,0x63,0xED,0x20,0xFA,0x64,0x61,0x6A,0x65,0x0A]), Buffer.from("Přihlašovací údaje\n")],
|
|
88
|
-
['iso-8859-2', 'quoted-printable', Buffer.from("P=F8ihla=B9ovac=ED =FAdaje\n"), Buffer.from("Přihlašovací údaje\n")],
|
|
89
|
-
['iso-8859-2', 'base64', Buffer.from("UPhpaGxhuW92YWPtIPpkYWplCgo=\n"), Buffer.from("Přihlašovací údaje\n\n")],
|
|
90
|
-
['utf-8', '8-bit', Buffer.from("どうぞ宜しくお願い申し上げます"), Buffer.from("どうぞ宜しくお願い申し上げます")]
|
|
91
|
-
];
|
|
92
|
-
|
|
93
|
-
test.expect(tests.length);
|
|
94
|
-
|
|
95
|
-
tests.forEach(data => {
|
|
96
|
-
const body = new Body();
|
|
97
|
-
body.add_filter(() => {});
|
|
98
|
-
|
|
99
|
-
body.state = 'headers'; // HACK
|
|
100
|
-
[
|
|
101
|
-
"Content-type: multipart/alternative;\n",
|
|
102
|
-
" boundary=------------D0A00162984CC178E2583417\n",
|
|
103
|
-
"\n",
|
|
104
|
-
"This is a multi-part message in MIME format.\n",
|
|
105
|
-
"--------------D0A00162984CC178E2583417\n",
|
|
106
|
-
`Content-Type: text/plain; charset=${data[0]}; format=flowed\n`,
|
|
107
|
-
`Content-Transfer-Encoding: ${data[1]}\n`,
|
|
108
|
-
"\n",
|
|
109
|
-
data[2],
|
|
110
|
-
"--------------D0A00162984CC178E2583417--"
|
|
111
|
-
].forEach((line) => body.parse_more(line));
|
|
112
|
-
body.parse_end();
|
|
113
|
-
|
|
114
|
-
test.equal(data[3], body.children[0].bodytext, `charset: ${data[0]}, encoding: ${data[1]}`);
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
test.done();
|
|
118
|
-
},
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
exports.banners = {
|
|
122
|
-
'banner': test => {
|
|
123
|
-
test.expect(2);
|
|
124
|
-
|
|
125
|
-
const body = new Body();
|
|
126
|
-
body.set_banner(['A text banner', 'An HTML banner']);
|
|
127
|
-
const parts = _fill_body(body);
|
|
128
|
-
|
|
129
|
-
test.ok(/A text banner$/.test(parts[0]));
|
|
130
|
-
test.ok(/<P>An HTML banner<\/P>$/.test(parts[1]));
|
|
131
|
-
test.done();
|
|
132
|
-
},
|
|
133
|
-
|
|
134
|
-
'insert_banner': test => {
|
|
135
|
-
test.expect(2);
|
|
136
|
-
|
|
137
|
-
let content_type;
|
|
138
|
-
let buf;
|
|
139
|
-
let new_buf;
|
|
140
|
-
const enc = 'UTF-8';
|
|
141
|
-
|
|
142
|
-
const body = new Body();
|
|
143
|
-
const banners = [ 'textbanner', 'htmlbanner' ];
|
|
144
|
-
|
|
145
|
-
// this is a kind of roundabout way to get at the insert_banners code
|
|
146
|
-
body.set_banner(banners);
|
|
147
|
-
const insert_banners_fn = body.filters[0];
|
|
148
|
-
|
|
149
|
-
content_type = 'text/html';
|
|
150
|
-
buf = Buffer.from("winter </html>");
|
|
151
|
-
new_buf = insert_banners_fn (content_type, enc, buf);
|
|
152
|
-
test.equal(new_buf.toString(), "winter <P>htmlbanner</P></html>",
|
|
153
|
-
"html banner looks ok");
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
content_type = 'text/plain';
|
|
157
|
-
buf = Buffer.from("winter");
|
|
158
|
-
new_buf = insert_banners_fn (content_type, enc, buf);
|
|
159
|
-
test.equal(new_buf.toString(), "winter\ntextbanner\n",
|
|
160
|
-
"text banner looks ok");
|
|
161
|
-
|
|
162
|
-
test.done();
|
|
163
|
-
},
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
// found and fixed bug, if the buffer is empty this was throwing a:
|
|
167
|
-
// RangeError: out of range index
|
|
168
|
-
'insert_banner_empty_buffer': test => {
|
|
169
|
-
test.expect(2);
|
|
170
|
-
|
|
171
|
-
let content_type;
|
|
172
|
-
let new_buf;
|
|
173
|
-
const enc = 'UTF-8';
|
|
174
|
-
|
|
175
|
-
const body = new Body();
|
|
176
|
-
const banners = [ 'textbanner', 'htmlbanner' ];
|
|
177
|
-
|
|
178
|
-
// this is a kind of roundabout way to get at the insert_banners code
|
|
179
|
-
body.set_banner(banners);
|
|
180
|
-
const insert_banners_fn = body.filters[0];
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
content_type = 'text/html';
|
|
184
|
-
const empty_buf = Buffer.from('');
|
|
185
|
-
new_buf = insert_banners_fn (content_type, enc, empty_buf);
|
|
186
|
-
test.equal(new_buf.toString(), "<P>htmlbanner</P>",
|
|
187
|
-
"empty html part gets a banner" );
|
|
188
|
-
|
|
189
|
-
content_type = 'text/plain';
|
|
190
|
-
new_buf = insert_banners_fn (content_type, enc, empty_buf);
|
|
191
|
-
test.equal(new_buf.toString(), "\ntextbanner\n",
|
|
192
|
-
"empty text part gets a banner");
|
|
193
|
-
|
|
194
|
-
test.done();
|
|
195
|
-
},
|
|
196
|
-
|
|
197
|
-
'insert_banner_empty_body': test => {
|
|
198
|
-
test.expect(2);
|
|
199
|
-
|
|
200
|
-
const body = new Body();
|
|
201
|
-
const banners = [ 'textbanner', 'htmlbanner' ];
|
|
202
|
-
|
|
203
|
-
body.set_banner(banners);
|
|
204
|
-
const results = _fill_empty_body(body);
|
|
205
|
-
|
|
206
|
-
test.equal(results[0], banners[0]);
|
|
207
|
-
test.equal(results[1], `<P>${banners[1]}</P>`);
|
|
208
|
-
|
|
209
|
-
test.done();
|
|
210
|
-
},
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
exports.filters = {
|
|
214
|
-
'empty': test => {
|
|
215
|
-
test.expect(2);
|
|
216
|
-
|
|
217
|
-
const body = new Body();
|
|
218
|
-
body.add_filter((ct, enc, buf) => { });
|
|
219
|
-
const parts = _fill_body(body);
|
|
220
|
-
|
|
221
|
-
test.ok(/Some text/.test(parts[0]));
|
|
222
|
-
test.ok(/This is some HTML/.test(parts[1]));
|
|
223
|
-
test.done();
|
|
224
|
-
},
|
|
225
|
-
|
|
226
|
-
'search/replace': test => {
|
|
227
|
-
test.expect(2);
|
|
228
|
-
|
|
229
|
-
const body = new Body();
|
|
230
|
-
body.add_filter((ct, enc, buf) => {
|
|
231
|
-
if (/^text\/plain/.test(ct)) {
|
|
232
|
-
return Buffer.from("TEXT FILTERED");
|
|
233
|
-
}
|
|
234
|
-
else if (/text\/html/.test(ct)) {
|
|
235
|
-
return Buffer.from("<p>HTML FILTERED</p>");
|
|
236
|
-
}
|
|
237
|
-
});
|
|
238
|
-
const parts = _fill_body(body);
|
|
239
|
-
|
|
240
|
-
test.equal(parts[0], "TEXT FILTERED");
|
|
241
|
-
test.equal(parts[1], "<p>HTML FILTERED</p>");
|
|
242
|
-
test.done();
|
|
243
|
-
},
|
|
244
|
-
|
|
245
|
-
'regression: duplicate multi-part preamble when filters added':
|
|
246
|
-
test => {
|
|
247
|
-
test.expect(1);
|
|
248
|
-
|
|
249
|
-
const body = new Body();
|
|
250
|
-
body.add_filter(() => {});
|
|
251
|
-
|
|
252
|
-
let lines = [];
|
|
253
|
-
|
|
254
|
-
body.state = 'headers'; // HACK
|
|
255
|
-
[
|
|
256
|
-
"Content-Type: multipart/mixed; boundary=abcd\n",
|
|
257
|
-
"\n",
|
|
258
|
-
"This is a multi-part message in MIME format.\n",
|
|
259
|
-
"--abcd\n",
|
|
260
|
-
"Content-Type: text/plain\n",
|
|
261
|
-
"\n",
|
|
262
|
-
"Testing, 1, 2, 3.\n",
|
|
263
|
-
"--abcd--\n",
|
|
264
|
-
].forEach(line => {
|
|
265
|
-
lines.push(body.parse_more(line));
|
|
266
|
-
});
|
|
267
|
-
lines.push(body.parse_end());
|
|
268
|
-
|
|
269
|
-
// Ignore blank lines.
|
|
270
|
-
lines = lines.filter(l => l.toString().trim());
|
|
271
|
-
|
|
272
|
-
let dupe = false;
|
|
273
|
-
let line;
|
|
274
|
-
while ((line = lines.pop())) {
|
|
275
|
-
lines.forEach(l => {
|
|
276
|
-
dupe = dupe || line === l;
|
|
277
|
-
});
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
test.ok(!dupe, 'no duplicate lines found');
|
|
281
|
-
test.done();
|
|
282
|
-
},
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
exports.rfc2231 = {
|
|
286
|
-
'multi-value': test => {
|
|
287
|
-
test.expect(2);
|
|
288
|
-
|
|
289
|
-
const body = new Body();
|
|
290
|
-
_fill_body(body);
|
|
291
|
-
|
|
292
|
-
test.ok(body.children[0].header.get_decoded('content-type').indexOf('URL="ftp://cs.utk.edu/pub/moore/bulk-mailer/bulk-mailer.tar";') > 0);
|
|
293
|
-
test.ok(body.children[1].header.get_decoded('content-disposition').indexOf('filename="marketron_lbasubmission_FTA Cricket Brentwood 3006445 Jackson TN_08282017.xlsx"') > 0);
|
|
294
|
-
test.done();
|
|
295
|
-
},
|
|
296
|
-
|
|
297
|
-
'enc-and-lang': test => {
|
|
298
|
-
test.expect(1);
|
|
299
|
-
|
|
300
|
-
const body = new Body();
|
|
301
|
-
_fill_body(body);
|
|
302
|
-
|
|
303
|
-
test.ok(body.children[0].header.get_decoded('content-type').indexOf('title="This is ***fun***";') > 0);
|
|
304
|
-
test.done();
|
|
305
|
-
},
|
|
306
|
-
|
|
307
|
-
'multi-value-enc-and-lang': test => {
|
|
308
|
-
test.expect(1);
|
|
309
|
-
|
|
310
|
-
const body = new Body();
|
|
311
|
-
_fill_body(body);
|
|
312
|
-
|
|
313
|
-
test.ok(body.children[1].header.get_decoded('content-type').indexOf('title="This is even more ***fun*** isn\'t it!";') > 0);
|
|
314
|
-
test.done();
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
exports.boundaries = {
|
|
319
|
-
'with-quotes': test => {
|
|
320
|
-
test.expect(1);
|
|
321
|
-
|
|
322
|
-
const body = new Body();
|
|
323
|
-
_fill_body(body, '"');
|
|
324
|
-
|
|
325
|
-
test.equal(body.children.length, 2);
|
|
326
|
-
test.done();
|
|
327
|
-
},
|
|
328
|
-
|
|
329
|
-
'without-quotes': test => {
|
|
330
|
-
test.expect(1);
|
|
331
|
-
|
|
332
|
-
const body = new Body();
|
|
333
|
-
_fill_body(body, "");
|
|
334
|
-
|
|
335
|
-
test.equal(body.children.length, 2);
|
|
336
|
-
test.done();
|
|
337
|
-
},
|
|
338
|
-
|
|
339
|
-
'with-bad-quotes': test => {
|
|
340
|
-
test.expect(1);
|
|
341
|
-
|
|
342
|
-
const body = new Body();
|
|
343
|
-
_fill_body(body, "'");
|
|
344
|
-
|
|
345
|
-
test.equal(body.children.length, 0);
|
|
346
|
-
test.done();
|
|
347
|
-
}
|
|
348
|
-
}
|
package/tests/mailheader.js
DELETED
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
const Header = require('../mailheader').Header;
|
|
2
|
-
|
|
3
|
-
const lines = [
|
|
4
|
-
'Return-Path: <helpme@gmail.com>',
|
|
5
|
-
'Received: from [1.1.1.1] ([2.2.2.2])',
|
|
6
|
-
' by smtp.gmail.com with ESMTPSA id abcdef.28.2016.03.31.12.51.37',
|
|
7
|
-
' for <foo@bar.com>',
|
|
8
|
-
' (version=TLSv1/SSLv3 cipher=OTHER);',
|
|
9
|
-
' Thu, 31 Mar 2016 12:51:37 -0700 (PDT)',
|
|
10
|
-
'From: Matt Sergeant <helpme@gmail.com>',
|
|
11
|
-
`FromUTF8: =?UTF-8?B?S29obOKAmXM=?=
|
|
12
|
-
<Kohls@s.kohls.com>`,
|
|
13
|
-
'Content-Type: multipart/alternative;',
|
|
14
|
-
' boundary=Apple-Mail-F2C5DAD3-7EB3-409D-9FE0-135C9FD43B69',
|
|
15
|
-
'Content-Type2: multipart/mixed; boundary="nqp=nb64=()I9WT8XjoN"',
|
|
16
|
-
'Content-Transfer-Encoding: 7bit',
|
|
17
|
-
'Mime-Version: 1.0 (1.0)',
|
|
18
|
-
'Subject: Re: Haraka Rocks!',
|
|
19
|
-
'Message-Id: <616DF75E-D799-4F3C-9901-1642B494C45D@gmail.com>',
|
|
20
|
-
'Date: Thu, 31 Mar 2016 15:51:36 -0400',
|
|
21
|
-
'To: The World <world@example.com>',
|
|
22
|
-
'X-Mailer: iPhone Mail (13E233)',
|
|
23
|
-
];
|
|
24
|
-
|
|
25
|
-
for (let i=0; i<lines.length; i++) {
|
|
26
|
-
lines[i] = `${lines[i] }\n`;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function _set_up (done) {
|
|
30
|
-
this.h = new Header()
|
|
31
|
-
this.h.parse(lines)
|
|
32
|
-
done();
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
exports.parse = {
|
|
36
|
-
setUp: _set_up,
|
|
37
|
-
'get_decoded' (test) {
|
|
38
|
-
test.expect(3);
|
|
39
|
-
test.equal(this.h.lines().length, 13);
|
|
40
|
-
test.equal(
|
|
41
|
-
this.h.get_decoded('content-type'),
|
|
42
|
-
'multipart/alternative; boundary=Apple-Mail-F2C5DAD3-7EB3-409D-9FE0-135C9FD43B69'
|
|
43
|
-
);
|
|
44
|
-
test.equal(this.h.get_decoded('fromUTF8'), 'Kohl’s <Kohls@s.kohls.com>');
|
|
45
|
-
test.done();
|
|
46
|
-
},
|
|
47
|
-
'content type w/parens' (test) {
|
|
48
|
-
test.expect(2);
|
|
49
|
-
test.equal(this.h.lines().length, 13);
|
|
50
|
-
const ct = this.h.get_decoded('content-type2');
|
|
51
|
-
test.equal(ct, 'multipart/mixed; boundary="nqp=nb64=()I9WT8XjoN"');
|
|
52
|
-
test.done();
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
exports.add_headers = {
|
|
57
|
-
setUp: _set_up,
|
|
58
|
-
add_basic (test) {
|
|
59
|
-
test.expect(2);
|
|
60
|
-
this.h.add('Foo', 'bar');
|
|
61
|
-
test.equal(this.h.lines()[0], 'Foo: bar\n');
|
|
62
|
-
this.h.add_end('Fizz', 'buzz');
|
|
63
|
-
test.equal(this.h.lines()[14], 'Fizz: buzz\n');
|
|
64
|
-
test.done();
|
|
65
|
-
},
|
|
66
|
-
add_utf8 (test) {
|
|
67
|
-
test.expect(4);
|
|
68
|
-
this.h.add('Foo', 'bøø');
|
|
69
|
-
test.equal(this.h.lines()[0], 'Foo: =?UTF-8?Q?b=C3=B8=C3=B8?=\n');
|
|
70
|
-
test.equal(this.h.get_decoded('Foo'), 'bøø');
|
|
71
|
-
// test wrapping
|
|
72
|
-
this.h.add('Bar', 'bøø 1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890');
|
|
73
|
-
test.equal(this.h.lines()[0], 'Bar: =?UTF-8?Q?b=C3=B8=C3=B8?= 1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890\n');
|
|
74
|
-
test.equal(this.h.get_decoded('Bar'), 'bøø 1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890');
|
|
75
|
-
test.done();
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
exports.continuations = {
|
|
80
|
-
setUp: _set_up,
|
|
81
|
-
continuations_decoded (test) {
|
|
82
|
-
test.expect(1);
|
|
83
|
-
test.ok(!/\n/.test(this.h.get_decoded('content-type')));
|
|
84
|
-
test.done();
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
exports.remove = {
|
|
89
|
-
setUp: _set_up,
|
|
90
|
-
'removes only specified header' (test) {
|
|
91
|
-
test.expect(3)
|
|
92
|
-
this.h.add('X-Test', 'remove-me')
|
|
93
|
-
this.h.add('X-Test-1', 'do-not-remove-me')
|
|
94
|
-
this.h.remove('X-Test')
|
|
95
|
-
test.equal(this.h.get('X-Test'), '')
|
|
96
|
-
test.equal(this.h.get('X-Test-1'), 'do-not-remove-me')
|
|
97
|
-
test.ok(this.h.header_list.find(name => name === 'X-Test-1: do-not-remove-me\n'));
|
|
98
|
-
test.done()
|
|
99
|
-
},
|
|
100
|
-
'removes multiple matching headers' (test) {
|
|
101
|
-
test.expect(3)
|
|
102
|
-
this.h.add('X-Test', 'remove me')
|
|
103
|
-
this.h.add('X-Test', 'and remove me')
|
|
104
|
-
this.h.add('X-Test-No', 'leave me')
|
|
105
|
-
this.h.remove('X-Test')
|
|
106
|
-
test.equal(this.h.get('X-Test'), '')
|
|
107
|
-
test.equal(this.h.get('X-Test-No'), 'leave me')
|
|
108
|
-
test.ok(this.h.header_list.find(name => name === 'X-Test-No: leave me\n'));
|
|
109
|
-
test.done()
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
exports.decode = {
|
|
114
|
-
'multiline 8bit header (#2675)': test => {
|
|
115
|
-
test.expect(1);
|
|
116
|
-
this.h = new Header();
|
|
117
|
-
this.h.parse ([
|
|
118
|
-
"Content-Disposition: attachment;\n",
|
|
119
|
-
" filename*0*=utf-8''%E8%AC%9B%E6%BC%94%E4%BC%9A%E6;\n",
|
|
120
|
-
" filename*1*=%A1%88%E5%86%85%E6%9B%B8%EF%BC%86%E7%94%B3%E8%BE%BC%E6%9B%B8;\n",
|
|
121
|
-
" filename*2*=%E6%94%B9%2Etxt\n"
|
|
122
|
-
]);
|
|
123
|
-
console.log(this.h.get_decoded('content-disposition'));
|
|
124
|
-
test.ok(this.h.get_decoded('content-disposition').includes('講演会案内書&申込書改.txt'));
|
|
125
|
-
test.done();
|
|
126
|
-
},
|
|
127
|
-
'unfolding (#2702)': test => {
|
|
128
|
-
test.expect(1);
|
|
129
|
-
this.h = new Header();
|
|
130
|
-
this.h.parse ([
|
|
131
|
-
"Subject: =?UTF-8?Q?Die_beliebtesten_CAD-_und_AVA-Programme;_die_kl=C3=BCgsten_K?=\n",
|
|
132
|
-
" =?UTF-8?Q?=C3=B6pfe_der_Branche;_Abschluss_eines_BIM-Pilotprojekts;_Bauen?=\n",
|
|
133
|
-
" =?UTF-8?Q?_in_Zeiten_des_Klimawandels;_u.v.m?=\n"
|
|
134
|
-
]);
|
|
135
|
-
test.equal(this.h.get_decoded('subject'), 'Die beliebtesten CAD- und AVA-Programme; die klügsten Köpfe der Branche; Abschluss eines BIM-Pilotprojekts; Bauen in Zeiten des Klimawandels; u.v.m');
|
|
136
|
-
test.done();
|
|
137
|
-
}
|
|
138
|
-
}
|
package/tests/messagestream.js
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
const stream = require('stream')
|
|
3
|
-
|
|
4
|
-
const MessageStream = require('../messagestream')
|
|
5
|
-
|
|
6
|
-
function _set_up (done) {
|
|
7
|
-
this.ms = new MessageStream({ main: { } }, 'msg', []);
|
|
8
|
-
done();
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
function _tear_down (done) {
|
|
12
|
-
done();
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
exports.messagestream = {
|
|
16
|
-
setUp : _set_up,
|
|
17
|
-
tearDown : _tear_down,
|
|
18
|
-
'is a Stream' (test) {
|
|
19
|
-
test.expect(2);
|
|
20
|
-
test.ok(this.ms instanceof MessageStream);
|
|
21
|
-
test.ok(this.ms instanceof stream.Stream);
|
|
22
|
-
test.done();
|
|
23
|
-
},
|
|
24
|
-
'gets message data' (test) {
|
|
25
|
-
this.ms.add_line('Header: test\r\n');
|
|
26
|
-
this.ms.add_line('\r\n');
|
|
27
|
-
this.ms.add_line('I am body text\r\n');
|
|
28
|
-
this.ms.add_line_end();
|
|
29
|
-
this.ms.get_data((data) => {
|
|
30
|
-
test.ok(/^[A-Za-z]+: /.test(data.toString()))
|
|
31
|
-
test.done();
|
|
32
|
-
})
|
|
33
|
-
},
|
|
34
|
-
}
|