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.
Files changed (134) hide show
  1. package/.eslintrc.yaml +2 -10
  2. package/Changes.md +68 -2
  3. package/Dockerfile +1 -1
  4. package/Plugins.md +7 -4
  5. package/README.md +2 -6
  6. package/config/outbound.ini +0 -7
  7. package/config/plugins +1 -1
  8. package/config/smtp.ini +1 -1
  9. package/config/smtp_forward.ini +2 -8
  10. package/config/smtp_proxy.ini +0 -6
  11. package/connection.js +178 -204
  12. package/coverage/lcov.info +13863 -0
  13. package/coverage/tmp/coverage-42958-1658373250585-0.json +1 -0
  14. package/coverage/tmp/coverage-42961-1658373250529-0.json +1 -0
  15. package/dkim.js +65 -73
  16. package/docs/Body.md +1 -22
  17. package/docs/CoreConfig.md +2 -2
  18. package/docs/Header.md +1 -47
  19. package/docs/Outbound.md +8 -36
  20. package/endpoint.js +1 -1
  21. package/haraka.js +1 -1
  22. package/host_pool.js +8 -12
  23. package/logger.js +25 -32
  24. package/outbound/client_pool.js +11 -153
  25. package/outbound/config.js +5 -11
  26. package/outbound/hmail.js +109 -143
  27. package/outbound/index.js +13 -25
  28. package/outbound/mx_lookup.js +10 -7
  29. package/outbound/queue.js +8 -12
  30. package/outbound/timer_queue.js +2 -4
  31. package/outbound/tls.js +17 -18
  32. package/outbound/todo.js +1 -0
  33. package/package.json +42 -40
  34. package/plugins/auth/auth_base.js +39 -63
  35. package/plugins/auth/auth_bridge.js +3 -4
  36. package/plugins/auth/auth_proxy.js +16 -16
  37. package/plugins/auth/auth_vpopmaild.js +30 -37
  38. package/plugins/auth/flat_file.js +9 -13
  39. package/plugins/avg.js +9 -11
  40. package/plugins/backscatterer.js +1 -1
  41. package/plugins/block_me.js +2 -6
  42. package/plugins/bounce.js +106 -124
  43. package/plugins/clamd.js +59 -63
  44. package/plugins/data.signatures.js +6 -6
  45. package/plugins/data.uribl.js +1 -415
  46. package/plugins/delay_deny.js +19 -20
  47. package/plugins/dkim_sign.js +56 -62
  48. package/plugins/dkim_verify.js +9 -8
  49. package/plugins/dns_list_base.js +43 -42
  50. package/plugins/dnsbl.js +41 -46
  51. package/plugins/dnswl.js +23 -26
  52. package/plugins/early_talker.js +24 -28
  53. package/plugins/esets.js +8 -11
  54. package/plugins/greylist.js +161 -190
  55. package/plugins/helo.checks.js +175 -197
  56. package/plugins/mail_from.is_resolvable.js +38 -38
  57. package/plugins/messagesniffer.js +33 -40
  58. package/plugins/prevent_credential_leaks.js +7 -5
  59. package/plugins/process_title.js +16 -17
  60. package/plugins/queue/deliver.js +2 -2
  61. package/plugins/queue/lmtp.js +5 -6
  62. package/plugins/queue/qmail-queue.js +11 -13
  63. package/plugins/queue/quarantine.js +25 -34
  64. package/plugins/queue/rabbitmq.js +3 -2
  65. package/plugins/queue/rabbitmq_amqplib.js +9 -9
  66. package/plugins/queue/smtp_bridge.js +5 -4
  67. package/plugins/queue/smtp_forward.js +81 -89
  68. package/plugins/queue/smtp_proxy.js +21 -22
  69. package/plugins/queue/test.js +2 -1
  70. package/plugins/rcpt_to.host_list_base.js +20 -30
  71. package/plugins/rcpt_to.in_host_list.js +12 -14
  72. package/plugins/rcpt_to.max_count.js +7 -5
  73. package/plugins/record_envelope_addresses.js +4 -6
  74. package/plugins/relay.js +64 -74
  75. package/plugins/reseed_rng.js +1 -2
  76. package/plugins/spamassassin.js +56 -68
  77. package/plugins/status.js +2 -3
  78. package/plugins/tarpit.js +8 -11
  79. package/plugins/tls.js +14 -17
  80. package/plugins/toobusy.js +6 -8
  81. package/plugins/xclient.js +14 -25
  82. package/plugins.js +24 -29
  83. package/rfc1869.js +2 -2
  84. package/server.js +3 -13
  85. package/smtp_client.js +138 -215
  86. package/tests/config/smtp_forward.ini +0 -6
  87. package/tests/fixtures/line_socket.js +1 -1
  88. package/tests/fixtures/util_hmailitem.js +5 -7
  89. package/tests/fixtures/vm_harness.js +2 -2
  90. package/tests/host_pool.js +13 -14
  91. package/tests/installation/plugins/inherits.js +1 -2
  92. package/tests/logger.js +2 -2
  93. package/tests/plugins/bounce.js +6 -8
  94. package/tests/plugins/dkim_signer.js +7 -7
  95. package/tests/plugins/dns_list_base.js +7 -7
  96. package/tests/plugins/helo.checks.js +1 -1
  97. package/tests/plugins/mail_from.is_resolvable.js +10 -54
  98. package/tests/plugins/queue/smtp_forward.js +11 -11
  99. package/tests/plugins/rcpt_to.host_list_base.js +1 -1
  100. package/tests/plugins/rcpt_to.in_host_list.js +1 -1
  101. package/tests/plugins/spamassassin.js +1 -1
  102. package/tests/queue/multibyte +0 -0
  103. package/tests/queue/plain +0 -0
  104. package/tests/rfc1869.js +4 -1
  105. package/tests/server.js +15 -9
  106. package/tests/smtp_client/auth.js +4 -14
  107. package/tests/smtp_client/basic.js +5 -15
  108. package/tests/smtp_client.js +7 -3
  109. package/tests/transaction.js +72 -19
  110. package/tls_socket.js +75 -85
  111. package/transaction.js +7 -9
  112. package/attachment_stream.js +0 -118
  113. package/bin/spf +0 -48
  114. package/chunkemitter.js +0 -75
  115. package/config/data.uribl.excludes +0 -202
  116. package/config/data.uribl.ini +0 -37
  117. package/config/spf.ini +0 -1
  118. package/docs/plugins/attachment.md +0 -92
  119. package/docs/plugins/data.uribl.md +0 -120
  120. package/docs/plugins/spf.md +0 -142
  121. package/mailbody.js +0 -502
  122. package/mailheader.js +0 -304
  123. package/messagestream.js +0 -441
  124. package/plugins/aliases.js +0 -120
  125. package/plugins/attachment.js +0 -503
  126. package/plugins/connect.p0f.js +0 -5
  127. package/plugins/spf.js +0 -327
  128. package/spf.js +0 -689
  129. package/tests/mailbody.js +0 -348
  130. package/tests/mailheader.js +0 -138
  131. package/tests/messagestream.js +0 -34
  132. package/tests/plugins/aliases.js +0 -376
  133. package/tests/plugins/spf.js +0 -251
  134. 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
- }