Haraka 2.8.28 → 3.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 (135) hide show
  1. package/.eslintrc.yaml +2 -10
  2. package/Changes.md +84 -2
  3. package/Dockerfile +1 -1
  4. package/Plugins.md +9 -4
  5. package/README.md +2 -6
  6. package/bin/haraka +5 -4
  7. package/config/outbound.ini +0 -7
  8. package/config/plugins +1 -1
  9. package/config/smtp.ini +1 -1
  10. package/config/smtp_forward.ini +2 -8
  11. package/config/smtp_proxy.ini +0 -6
  12. package/connection.js +178 -204
  13. package/coverage/lcov.info +13863 -0
  14. package/coverage/tmp/coverage-42958-1658373250585-0.json +1 -0
  15. package/coverage/tmp/coverage-42961-1658373250529-0.json +1 -0
  16. package/dkim.js +66 -73
  17. package/docs/Body.md +1 -22
  18. package/docs/CoreConfig.md +2 -2
  19. package/docs/Header.md +1 -47
  20. package/docs/Outbound.md +8 -36
  21. package/endpoint.js +1 -1
  22. package/haraka.js +1 -1
  23. package/host_pool.js +8 -12
  24. package/logger.js +25 -32
  25. package/outbound/client_pool.js +11 -153
  26. package/outbound/config.js +5 -11
  27. package/outbound/hmail.js +109 -143
  28. package/outbound/index.js +13 -25
  29. package/outbound/mx_lookup.js +10 -7
  30. package/outbound/queue.js +8 -12
  31. package/outbound/timer_queue.js +2 -4
  32. package/outbound/tls.js +17 -18
  33. package/outbound/todo.js +1 -0
  34. package/package.json +57 -55
  35. package/plugins/auth/auth_base.js +39 -63
  36. package/plugins/auth/auth_bridge.js +3 -4
  37. package/plugins/auth/auth_proxy.js +16 -16
  38. package/plugins/auth/auth_vpopmaild.js +30 -37
  39. package/plugins/auth/flat_file.js +9 -13
  40. package/plugins/avg.js +9 -11
  41. package/plugins/backscatterer.js +1 -1
  42. package/plugins/block_me.js +2 -6
  43. package/plugins/bounce.js +106 -124
  44. package/plugins/clamd.js +59 -63
  45. package/plugins/data.signatures.js +6 -6
  46. package/plugins/data.uribl.js +1 -415
  47. package/plugins/delay_deny.js +19 -20
  48. package/plugins/dkim_sign.js +56 -62
  49. package/plugins/dkim_verify.js +9 -8
  50. package/plugins/dns_list_base.js +43 -42
  51. package/plugins/dnsbl.js +41 -46
  52. package/plugins/dnswl.js +23 -26
  53. package/plugins/early_talker.js +24 -28
  54. package/plugins/esets.js +8 -11
  55. package/plugins/greylist.js +161 -190
  56. package/plugins/helo.checks.js +175 -197
  57. package/plugins/mail_from.is_resolvable.js +38 -38
  58. package/plugins/messagesniffer.js +33 -40
  59. package/plugins/prevent_credential_leaks.js +7 -5
  60. package/plugins/process_title.js +16 -17
  61. package/plugins/queue/deliver.js +2 -2
  62. package/plugins/queue/lmtp.js +5 -6
  63. package/plugins/queue/qmail-queue.js +11 -13
  64. package/plugins/queue/quarantine.js +25 -34
  65. package/plugins/queue/rabbitmq.js +3 -2
  66. package/plugins/queue/rabbitmq_amqplib.js +9 -9
  67. package/plugins/queue/smtp_bridge.js +5 -4
  68. package/plugins/queue/smtp_forward.js +81 -89
  69. package/plugins/queue/smtp_proxy.js +21 -22
  70. package/plugins/queue/test.js +2 -1
  71. package/plugins/rcpt_to.host_list_base.js +20 -30
  72. package/plugins/rcpt_to.in_host_list.js +12 -14
  73. package/plugins/rcpt_to.max_count.js +7 -5
  74. package/plugins/record_envelope_addresses.js +4 -6
  75. package/plugins/relay.js +64 -74
  76. package/plugins/reseed_rng.js +1 -2
  77. package/plugins/spamassassin.js +56 -68
  78. package/plugins/status.js +2 -3
  79. package/plugins/tarpit.js +8 -11
  80. package/plugins/tls.js +14 -17
  81. package/plugins/toobusy.js +6 -8
  82. package/plugins/xclient.js +14 -25
  83. package/plugins.js +24 -29
  84. package/rfc1869.js +2 -2
  85. package/server.js +3 -13
  86. package/smtp_client.js +138 -215
  87. package/tests/config/smtp_forward.ini +0 -6
  88. package/tests/fixtures/line_socket.js +1 -1
  89. package/tests/fixtures/util_hmailitem.js +5 -7
  90. package/tests/fixtures/vm_harness.js +2 -2
  91. package/tests/host_pool.js +13 -14
  92. package/tests/installation/plugins/inherits.js +1 -2
  93. package/tests/logger.js +2 -2
  94. package/tests/plugins/bounce.js +6 -8
  95. package/tests/plugins/dkim_signer.js +7 -7
  96. package/tests/plugins/dns_list_base.js +7 -7
  97. package/tests/plugins/helo.checks.js +1 -1
  98. package/tests/plugins/mail_from.is_resolvable.js +10 -54
  99. package/tests/plugins/queue/smtp_forward.js +11 -11
  100. package/tests/plugins/rcpt_to.host_list_base.js +1 -1
  101. package/tests/plugins/rcpt_to.in_host_list.js +1 -1
  102. package/tests/plugins/spamassassin.js +1 -1
  103. package/tests/queue/multibyte +0 -0
  104. package/tests/queue/plain +0 -0
  105. package/tests/rfc1869.js +4 -1
  106. package/tests/server.js +15 -9
  107. package/tests/smtp_client/auth.js +4 -14
  108. package/tests/smtp_client/basic.js +5 -15
  109. package/tests/smtp_client.js +7 -3
  110. package/tests/transaction.js +72 -19
  111. package/tls_socket.js +75 -85
  112. package/transaction.js +7 -9
  113. package/attachment_stream.js +0 -118
  114. package/bin/spf +0 -48
  115. package/chunkemitter.js +0 -75
  116. package/config/data.uribl.excludes +0 -202
  117. package/config/data.uribl.ini +0 -37
  118. package/config/spf.ini +0 -1
  119. package/docs/plugins/attachment.md +0 -92
  120. package/docs/plugins/data.uribl.md +0 -120
  121. package/docs/plugins/spf.md +0 -142
  122. package/mailbody.js +0 -502
  123. package/mailheader.js +0 -304
  124. package/messagestream.js +0 -441
  125. package/plugins/aliases.js +0 -120
  126. package/plugins/attachment.js +0 -503
  127. package/plugins/connect.p0f.js +0 -5
  128. package/plugins/spf.js +0 -327
  129. package/spf.js +0 -689
  130. package/tests/mailbody.js +0 -348
  131. package/tests/mailheader.js +0 -138
  132. package/tests/messagestream.js +0 -34
  133. package/tests/plugins/aliases.js +0 -376
  134. package/tests/plugins/spf.js +0 -251
  135. package/tests/spf.js +0 -96
package/mailheader.js DELETED
@@ -1,304 +0,0 @@
1
- 'use strict';
2
- // An RFC 2822 email header parser
3
- /* eslint no-control-regex: 0 */
4
-
5
- const logger = require('./logger');
6
- const libmime = require('libmime');
7
- const libqp = require('libqp');
8
- let Iconv;
9
- try { Iconv = require('iconv').Iconv }
10
- catch (err) {
11
- logger.logdebug("No iconv available - install with 'npm install iconv'");
12
- }
13
-
14
- class Header {
15
- constructor (options) {
16
- this.headers = {};
17
- this.headers_decoded = {};
18
- this.header_list = [];
19
- this.options = options;
20
- }
21
-
22
- parse (lines) {
23
- const self = this;
24
-
25
- for (const line of lines) {
26
- if (/^[ \t]/.test(line)) {
27
- // continuation
28
- this.header_list[this.header_list.length - 1] += line;
29
- }
30
- else {
31
- this.header_list.push(line);
32
- }
33
- }
34
-
35
- for (const header of this.header_list) {
36
- const match = header.match(/^([^\s:]*):\s*([\s\S]*)$/);
37
- if (match) {
38
- const key = match[1].toLowerCase();
39
- const val = match[2];
40
-
41
- this._add_header(key, val, "push");
42
- }
43
- else {
44
- logger.lognotice(`Header did not look right: ${header}`);
45
- }
46
- }
47
-
48
- // Now add decoded versions
49
- Object.keys(this.headers).forEach((key2) => {
50
- self.headers[key2].forEach((val2) => {
51
- self._add_header_decode(key2, val2, 'push');
52
- })
53
- })
54
- }
55
-
56
- decode_header (val) {
57
- // Fold continuations
58
- val = val.replace(/\r?\n/g, '');
59
-
60
- const rfc2231_params = {
61
- kv: {},
62
- keys: {},
63
- cur_key: '',
64
- cur_enc: '',
65
- cur_lang: '', // Secondary languages are ignored for our purposes
66
- };
67
-
68
- val = _decode_rfc2231(rfc2231_params, val);
69
-
70
- // console.log(rfc2231_params);
71
-
72
- // strip 822 comments in the most basic way - does not support nested comments
73
- // val = val.replace(/\([^\)]*\)/, '');
74
-
75
- if (Iconv && !/^[\x00-\x7f]*$/.test(val)) {
76
- // 8 bit values in the header
77
- const matches = /\bcharset\s*=\s*["']?([\w_-]*)/.exec(this.get('content-type'));
78
- if (matches && !/UTF-?8/i.test(matches[1])) {
79
- const encoding = matches[1];
80
- const source = Buffer.from(val, 'binary');
81
- val = try_convert(source, encoding).toString();
82
- }
83
- }
84
-
85
- if (! (/=\?/.test(val)) ) {
86
- // no encoded stuff
87
- return val;
88
- }
89
-
90
- return val
91
- // strip whitespace between encoded-words, rfc 2047 6.2
92
- .replace(/(=\?.+?\?=)\s+(?==\?.+?\?=)/g,"$1")
93
- // decode each encoded match
94
- .replace(/=\?([\w_-]+)(\*[\w_-]+)?\?([bqBQ])\?([\s\S]*?)\?=/g, _decode_header);
95
- }
96
-
97
- get (key) {
98
- return (this.headers[key.toLowerCase()] || []).join("\n");
99
- }
100
-
101
- get_all (key) {
102
- return Object.freeze([...(this.headers[key.toLowerCase()] || [])]);
103
- }
104
-
105
- get_decoded (key) {
106
- return (this.headers_decoded[key.toLowerCase()] || []).join("\n");
107
- }
108
-
109
- remove (key) {
110
- key = key.toLowerCase();
111
- delete this.headers[key];
112
- delete this.headers_decoded[key];
113
-
114
- this._remove_more(key);
115
- }
116
-
117
- _remove_more (key) {
118
- const key_len = key.length;
119
- for (let i=0, l=this.header_list.length; i < l; i++) {
120
- if (this.header_list[i].substring(0, key_len + 1).toLowerCase() === `${key}:`) {
121
- this.header_list.splice(i, 1);
122
- return this._remove_more(key);
123
- }
124
- }
125
- }
126
-
127
- add (key, value) {
128
- if (!key) key = 'X-Haraka-Blank';
129
- value = value.replace(/(\r?\n)*$/, '');
130
- if (/[^\x00-\x7f]/.test(value)) {
131
- value = libmime.encodeWords(value, 'Q');
132
- }
133
- this._add_header(key.toLowerCase(), value, "unshift");
134
- this._add_header_decode(key.toLowerCase(), value, "unshift");
135
- this.header_list.unshift(`${key}: ${value}\n`);
136
- }
137
-
138
- _add_header (key, value, method) {
139
- this.headers[key] = this.headers[key] || [];
140
- this.headers[key][method](value);
141
- }
142
-
143
- _add_header_decode (key, value, method) {
144
- const val = this.decode_header(value);
145
- // console.log(key + ': ' + val);
146
- this.headers_decoded[key] = this.headers_decoded[key] || [];
147
- this.headers_decoded[key][method](val);
148
- }
149
-
150
- add_end (key, value) {
151
- if (!key) key = 'X-Haraka-Blank';
152
- value = value.replace(/(\r?\n)*$/, '');
153
- if (/[^\x00-\x7f]/.test(value)) {
154
- value = libmime.encodeWords(value, 'Q');
155
- }
156
- this._add_header(key.toLowerCase(), value, "push");
157
- this._add_header_decode(key.toLowerCase(), value, "push");
158
- this.header_list.push(`${key}: ${value}\n`);
159
- }
160
-
161
- lines () {
162
- return Object.freeze([...this.header_list]);
163
- }
164
-
165
- toString () {
166
- return this.header_list.join("\n");
167
- }
168
- }
169
-
170
- exports.Header = Header;
171
- exports.Iconv = Iconv;
172
-
173
- function try_convert (data, encoding) {
174
- try {
175
- const converter = new Iconv(encoding, "UTF-8");
176
- data = converter.convert(data);
177
- }
178
- catch (err) {
179
- // TODO: raise a flag for this for possible scoring
180
- logger.logwarn(`initial iconv conversion from ${encoding} to UTF-8 failed: ${err.message}`);
181
- if (err.code !== 'EINVAL') {
182
- try {
183
- const converter = new Iconv(encoding, "UTF-8//TRANSLIT//IGNORE");
184
- data = converter.convert(data);
185
- }
186
- catch (e) {
187
- logger.logerror(`iconv from ${encoding} to UTF-8 failed: ${e.message}`);
188
- }
189
- }
190
- }
191
-
192
- return data;
193
- }
194
-
195
- function _decode_header (matched, encoding, lang, cte, data) {
196
- cte = cte.toUpperCase();
197
-
198
- switch (cte) {
199
- case 'Q':
200
- data = libqp.decode(data.replace(/_/g, ' '));
201
- break;
202
- case 'B':
203
- data = Buffer.from(data, "base64");
204
- break;
205
- default:
206
- logger.logerror(`Invalid header encoding type: ${cte}`);
207
- }
208
-
209
- // convert with iconv if encoding != UTF-8
210
- if (Iconv && !(/UTF-?8/i.test(encoding))) {
211
- data = try_convert(data, encoding);
212
- }
213
-
214
- return data.toString();
215
- }
216
-
217
- function _decode_rfc2231 (params, str) {
218
- _parse_rfc2231(params, str);
219
-
220
- for (const key in params.keys) {
221
- str += ` ${key}="`;
222
- /* eslint no-constant-condition: 0 */
223
- let merged = '';
224
- for (let i=0; true; i++) {
225
- const _key = `${key}*${i}`;
226
- const _val = params.kv[_key];
227
- if (_val === undefined) break;
228
- merged += _val;
229
- }
230
-
231
- try {
232
- merged = decodeURIComponent(merged);
233
- }
234
- catch (e) {
235
- logger.logerror(`Decode header failed: ${key}: ${merged}`);
236
- }
237
- merged = params.cur_enc ? try_convert(merged, params.cur_enc) : merged;
238
-
239
- str += `${merged}";`;
240
- }
241
-
242
- return str;
243
- }
244
-
245
- function _parse_rfc2231 (params, str) {
246
- /*
247
- To explain the regexp below, the params are:
248
-
249
- parameter := attribute "=" value
250
-
251
- attribute := token
252
- ; Matching of attributes
253
- ; is ALWAYS case-insensitive.
254
-
255
- token := 1*<any (US-ASCII) CHAR except SPACE, CTLs,
256
- or tspecials>
257
-
258
- tspecials := "(" / ")" / "<" / ">" / "@" /
259
- "," / ";" / ":" / "\" / <">
260
- "/" / "[" / "]" / "?" / "="
261
- ; Must be in quoted-string,
262
- ; to use within parameter values
263
- */
264
- const sub_matches = /(([!#$%&'*+.0-9A-Zdiff^_`a-z{|}~-]*)\*)(\d*)=(\s*".*?[^\\]";?|\S*)/.exec(str);
265
- if (!sub_matches) {
266
- return;
267
- }
268
- const key = sub_matches[1];
269
- let key_actual = sub_matches[2];
270
- let key_id = sub_matches[3] || '0';
271
- let value = sub_matches[4].replace(/;$/, '');
272
-
273
- str = str.replace(sub_matches[0], ''); // strip it out, so we move to next
274
-
275
- const key_extract = /^(.*?)(\*(\d+)\*)$/.exec(key);
276
- if (key_extract) {
277
- key_actual = key_extract[1];
278
- key_id = key_extract[3];
279
- }
280
-
281
- const quote = /^\s*"(.*)"$/.exec(value);
282
- if (quote) {
283
- value = quote[1];
284
- }
285
-
286
- const lang_match = /^(.*?)'(.*?)'(.*)/.exec(value);
287
- if (lang_match) {
288
- if (key_actual == params.cur_key && lang_match[2] != params.cur_lang) {
289
- return _parse_rfc2231(params, str); // same key, different lang, throw it away
290
- }
291
- params.cur_enc = lang_match[1];
292
- params.cur_lang = lang_match[2];
293
- value = lang_match[3];
294
- }
295
- else if (key_actual != params.cur_key) {
296
- params.cur_lang = '';
297
- params.cur_enc = '';
298
- }
299
-
300
- params.cur_key = key_actual;
301
- params.keys[key_actual] = '';
302
- params.kv[`${key_actual}*${key_id}`] = value;
303
- return _parse_rfc2231(params, str); // Get next one
304
- }